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+
16+using namespace disvm;
17+using namespace disvm::runtime;
18+
19+namespace
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 =
246+R"(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+
308+void 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+
378+void 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+}