History

Basic debugger implementation - No way to enable it without altering exec main.cpp.

arr committed 10 years ago

Commit f2c6ef31 parent 7c21b7718 files · +776 −72Browse files

Showing the changes to src/exec/debugger.cpp (1 of 18 files). Show all

Unified · Split

src/exec/debugger.cpp+386−0
@@ -0,0 +1,386 @@
1//
2// Dis VM
3// File: debugger.cpp
4// Author: arr
5//
6
7#include <iostream>
8#include <sstream>
9#include <string>
10#include <locale>
11#include <vm_tools.h>
12#include <utils.h>
13#include <vm_memory.h>
14#include "exec.h"
15
16using namespace disvm;
17using namespace disvm::runtime;
18
19namespace
20{
21 std::string to_string_stack(vm_registers_t &r)
22 {
23 auto msg = std::stringstream{};
24 walk_stack(r, [&msg](const pointer_t, const vm_pc_t pc, const vm_module_ref_t &module_ref)
25 {
26 auto module_name = "<No Name>";
27 if (module_ref.module->module_name != nullptr)
28 module_name = module_ref.module->module_name->str();
29
30 msg << module_name << "@" << pc << "\n";
31 return true;
32 });
33
34 return msg.str();
35 }
36
37 std::ostream& operator<<(std::ostream &ss, const vm_string_t *s)
38 {
39 ss << "string\n"
40 << "\tlength: " << s->get_length() << "\n"
41 << "\tvalue: " << s->str() << "\n";
42
43 return ss;
44 }
45
46 std::ostream& operator<<(std::ostream &ss, const vm_array_t *a)
47 {
48 ss << "array\n"
49 << "\tlength: " << a->get_length() << "\n";
50
51 return ss;
52 }
53
54 std::ostream& operator<<(std::ostream &ss, const vm_list_t *l)
55 {
56 ss << "list\n"
57 << "\tlength: " << l->get_length() << "\n";
58
59 return ss;
60 }
61
62 std::ostream& operator<<(std::ostream &ss, const vm_channel_t *c)
63 {
64 ss << "channel\n"
65 << "\tbuffer size: " << c->get_buffer_size() << "\n";
66
67 return ss;
68 }
69
70 std::ostream& operator<<(std::ostream &ss, const vm_alloc_t *a)
71 {
72 ss << "alloc\n"
73 << "\tsize: " << a->alloc_type->size_in_bytes << "\n";
74
75 return ss;
76 }
77
78 std::ostream& operator<<(std::ostream &ss, const inst_data_generic_t &m)
79 {
80 switch (m.mode)
81 {
82 case address_mode_t::offset_indirect_mp:
83 ss << m.register1 << "(mp)";
84 break;
85 case address_mode_t::offset_indirect_fp:
86 ss << m.register1 << "(fp)";
87 break;
88 case address_mode_t::immediate:
89 ss << "$" << m.register1;
90 break;
91 case address_mode_t::none:
92 ss << "_";
93 break;
94 case address_mode_t::offset_double_indirect_mp:
95 ss << m.register2 << "(" << m.register1 << "(mp))";
96 break;
97 case address_mode_t::offset_double_indirect_fp:
98 ss << m.register2 << "(" << m.register1 << "(fp))";
99 break;
100
101 case address_mode_t::reserved_1:
102 case address_mode_t::reserved_2:
103 default:
104 ss << "??";
105 }
106
107 return ss;
108 }
109
110 std::ostream& operator<<(std::ostream &ss, const middle_data_t &m)
111 {
112 switch (m.mode)
113 {
114 case address_mode_middle_t::none:
115 ss << "_";
116 break;
117 case address_mode_middle_t::small_immediate:
118 ss << "$" << m.register1;
119 break;
120 case address_mode_middle_t::small_offset_indirect_fp:
121 ss << m.register1 << "(fp)";
122 break;
123 case address_mode_middle_t::small_offset_indirect_mp:
124 ss << m.register1 << "(mp)";
125 break;
126 default:
127 ss << "??";
128 }
129
130 return ss;
131 }
132
133 std::ostream& operator<<(std::ostream &ss, const vm_exec_op_t &m)
134 {
135 const auto print_d = m.destination.mode != address_mode_t::none;
136 const auto print_m = print_d || m.middle.mode != address_mode_middle_t::none;
137 const auto print_s = print_m || m.source.mode != address_mode_t::none;
138
139 // [TODO] Convert the opcode value to a string
140 ss << static_cast<uint32_t>(m.opcode);
141
142 if (print_s)
143 ss << " " << m.source;
144
145 if (print_m)
146 ss << " " << m.middle;
147
148 if (print_d)
149 ss << " " << m.destination;
150
151 return ss;
152 }
153
154 std::string to_string_frame_pointers(vm_registers_t &r)
155 {
156 auto frame = r.stack.peek_frame();
157 if (frame == nullptr)
158 return "<empty>";
159
160 auto frame_string = std::stringstream{};
161 enum_pointer_fields(*frame->frame_type, frame->base(), [&frame_string](pointer_t p)
162 {
163 frame_string << std::showbase << std::hex << reinterpret_cast<uintptr_t>(p) << " : " << std::dec;
164
165 auto alloc = vm_alloc_t::from_allocation(p);
166 if (alloc->alloc_type == intrinsic_type_desc::type<vm_string_t>())
167 frame_string << vm_alloc_t::from_allocation<vm_string_t>(p);
168 else if (alloc->alloc_type == intrinsic_type_desc::type<vm_array_t>())
169 frame_string << vm_alloc_t::from_allocation<vm_array_t>(p);
170 else if (alloc->alloc_type == intrinsic_type_desc::type<vm_list_t>())
171 frame_string << vm_alloc_t::from_allocation<vm_list_t>(p);
172 else if (alloc->alloc_type == intrinsic_type_desc::type<vm_channel_t>())
173 frame_string << vm_alloc_t::from_allocation<vm_channel_t>(p);
174 else
175 frame_string << alloc;
176 });
177
178 return frame_string.str();
179 }
180
181 std::string to_string_registers(const vm_registers_t &r)
182 {
183 auto register_string = std::stringstream{};
184
185 register_string
186 << "Registers:\n"
187 << " Thread ID: " << r.thread.get_thread_id() << "\n"
188 << " Module: " << r.module_ref->module->module_name->str() << "\n"
189 << "Program counter: " << r.pc << "\n"
190 << " Opcode: " << r.module_ref->code_section[r.pc].op << "\n";
191
192 return register_string.str();
193 }
194
195 namespace console_modifiers
196 {
197#define CSI "\33["
198
199 const auto black_font = CSI "30m";
200 const auto red_font = CSI "31m";
201 const auto green_font = CSI "32m";
202 const auto yellow_font = CSI "33m";
203 const auto blue_font = CSI "34m";
204 const auto magenta_font = CSI "35m";
205 const auto cyan_font = CSI "36m";
206 const auto white_font = CSI "37m";
207
208 const auto black_bkg = CSI "40m";
209 const auto red_bkg = CSI "41m";
210 const auto green_bkg = CSI "42m";
211 const auto yellow_bkg = CSI "43m";
212 const auto blue_bkg = CSI "44m";
213 const auto magenta_bkg = CSI "45m";
214 const auto cyan_bkg = CSI "46m";
215 const auto white_bkg = CSI "47m";
216
217 const auto bold = CSI "1m";
218
219 const auto reset_all = CSI "0m";
220
221#undef CSI
222 }
223
224 void debug_print_error(const std::string &str)
225 {
226 std::cout
227 << console_modifiers::red_font
228 << console_modifiers::bold
229 << "Error: "
230 << str
231 << console_modifiers::reset_all
232 << "\n";
233 }
234
235 void debug_print_info(const std::string &str)
236 {
237 std::cout
238 << console_modifiers::yellow_font
239 << console_modifiers::bold
240 << str
241 << console_modifiers::reset_all
242 << "\n";
243 }
244
245 auto prompt_help =
246R"(Help:
247 c C - continue
248 r R - print registers
249 w W - stack walk
250 ? - help
251)";
252
253 void prompt(const vm_registers_t &r)
254 {
255 std::locale loc;
256 std::string cmd;
257
258 for (;;)
259 {
260 std::cout
261 << console_modifiers::green_font
262 << console_modifiers::bold
263 << r.thread.get_thread_id()
264 << " >>> "
265 << console_modifiers::reset_all;
266
267 std::getline(std::cin, cmd);
268 if (cmd.empty())
269 continue;
270
271 auto c = std::toupper(cmd[0], loc);
272 if (c == 'R')
273 {
274 auto r_str = to_string_registers(r);
275 debug_print_info(r_str);
276 }
277 else if (c == 'W')
278 {
279 auto msg = std::stringstream{};
280 walk_stack(r, [&msg](const pointer_t, const vm_pc_t pc, const vm_module_ref_t &module_ref) {
281 auto module_name = "<No Name>";
282 if (module_ref.module->module_name != nullptr)
283 module_name = module_ref.module->module_name->str();
284
285 msg << "\t" << module_name << "@" << pc << "\n";
286 return true;
287 });
288
289 debug_print_info(msg.str());
290 }
291 else if (c == 'C')
292 {
293 break;
294 }
295 else if (c == '?')
296 {
297 debug_print_info(prompt_help);
298 }
299 else
300 {
301 debug_print_error("Unknown command");
302 debug_print_info(prompt_help);
303 }
304 }
305 }
306}
307
308void debugger::on_load(vm_t &vm, vm_tool_controller_t &controller, std::size_t tool_id)
309{
310 _tool_id = tool_id;
311
312 _event_cookies.push_back(controller.subscribe_event(vm_event_t::thread_begin, [](vm_event_t, vm_event_context_t &cxt)
313 {
314 auto t = cxt.value1.thread;
315 auto ss = std::stringstream{};
316 ss << "Thread "
317 << t->get_thread_id()
318 << " started in "
319 << t->get_registers().module_ref->module->module_name->str()
320 << " @ "
321 << t->get_registers().pc;
322
323 debug_print_info(ss.str());
324 }));
325
326 _event_cookies.push_back(controller.subscribe_event(vm_event_t::thread_end, [](vm_event_t, vm_event_context_t &cxt)
327 {
328 auto ss = std::stringstream{};
329 ss << "Thread "
330 << cxt.value1.thread->get_thread_id()
331 << " ended.";
332
333 debug_print_info(ss.str());
334 }));
335
336 _event_cookies.push_back(controller.subscribe_event(vm_event_t::exception_raised, [](vm_event_t, vm_event_context_t &cxt)
337 {
338 auto ss = std::stringstream{};
339 ss << "Exception '"
340 << cxt.value2.str->str()
341 << "' thrown.";
342
343 debug_print_info(ss.str());
344 }));
345
346 _event_cookies.push_back(controller.subscribe_event(vm_event_t::exception_unhandled, [](vm_event_t, vm_event_context_t &cxt)
347 {
348 auto ss = std::stringstream{};
349 ss << "Unhandled exception '"
350 << cxt.value2.str->str()
351 << "' thrown.";
352
353 debug_print_error(ss.str());
354 prompt(*cxt.value1.registers);
355 }));
356
357 _event_cookies.push_back(controller.subscribe_event(vm_event_t::module_load, [](vm_event_t, vm_event_context_t &cxt)
358 {
359 auto m = (*cxt.value1.module);
360 auto ss = std::stringstream{};
361 ss << "Module "
362 << m->module_name->str()
363 << " loaded (";
364
365 if (!util::has_flag(m->header.runtime_flag, runtime_flags_t::builtin))
366 ss << cxt.value2.str->str();
367 else
368 ss << "built-in";
369
370 ss << ")";
371
372 debug_print_info(ss.str());
373 }));
374
375 _controller = &controller;
376}
377
378void debugger::on_unload()
379{
380 assert(_controller != nullptr);
381
382 for (auto ec : _event_cookies)
383 _controller->unsubscribe_event(ec);
384
385 _controller = nullptr;
386}