History

Various work for tool support in the VM. Update some type names (i.e. _t) and alter API signatures to be more consistent.

arr committed 10 years ago

Commit e5d3f861 parent bcb413516 files · +514 −45Browse files

Showing the changes to src/vm/tool_dispatch.cpp (1 of 16 files). Show all

Unified · Split

src/vm/tool_dispatch.cpp+232−0
@@ -0,0 +1,232 @@
1+//
2+// Dis VM
3+// File: tool_dispatch.cpp
4+// Author: arr
5+//
6+
7+#include <cassert>
8+#include <debug.h>
9+#include <vm_tools.h>
10+#include <exceptions.h>
11+#include <unordered_set>
12+#include <utils.h>
13+#include "tool_dispatch.h"
14+
15+using namespace disvm;
16+using namespace disvm::runtime;
17+
18+// Empty destructor for vm tool 'interface'
19+vm_tool_t::~vm_tool_t()
20+{
21+}
22+
23+vm_tool_dispatch_t::vm_tool_dispatch_t(vm_t &vm)
24+ : _vm{ vm }
25+{
26+ _events.cookie_counter = 1;
27+ _breakpoints.cookie_counter = 1;
28+}
29+
30+vm_tool_dispatch_t::~vm_tool_dispatch_t()
31+{
32+ std::lock_guard<std::mutex> lock{ _tools_lock };
33+ for (auto &t : _tools)
34+ t.second->on_unload();
35+}
36+
37+std::size_t vm_tool_dispatch_t::load_tool(std::shared_ptr<runtime::vm_tool_t> tool)
38+{
39+ static std::size_t _tool_id_counter{ 1 };
40+
41+ if (tool == nullptr)
42+ throw vm_system_exception{ "Tool cannot be null" };
43+
44+ // Keep the lock for the collection until the tool has been completed being loaded.
45+ std::lock_guard<std::mutex> lock{ _tools_lock };
46+ const auto current_id = _tool_id_counter++;
47+ _tools[current_id] = tool;
48+
49+ try
50+ {
51+ // [TODO] For debug builds, it might be useful to wrap the controller in a proxy that records
52+ // the subscribed callbacks and asserts they are all properly unsubscribed during agent unload.
53+ tool->on_load(_vm, *this, current_id);
54+ }
55+ catch (...)
56+ {
57+ if (debug::is_component_tracing_enabled<debug::component_trace_t::tool>())
58+ debug::log_msg(debug::component_trace_t::tool, debug::log_level_t::warning, "load: tool: failure");
59+
60+ // The ID is tainted and will not be re-used
61+ _tools.erase(current_id);
62+ throw;
63+ }
64+
65+ if (debug::is_component_tracing_enabled<debug::component_trace_t::tool>())
66+ debug::log_msg(debug::component_trace_t::tool, debug::log_level_t::debug, "load: tool: %d", current_id);
67+
68+ return current_id;
69+}
70+
71+std::size_t vm_tool_dispatch_t::unload_tool(std::size_t tool_id)
72+{
73+ // Keep the lock for the collection until the tool has been completed being unloaded.
74+ std::lock_guard<std::mutex> lock{ _tools_lock };
75+ auto iter = _tools.find(tool_id);
76+ if (iter == _tools.cend())
77+ return _tools.size();
78+
79+ auto tool = iter->second;
80+ _tools.erase(iter);
81+
82+ if (debug::is_component_tracing_enabled<debug::component_trace_t::tool>())
83+ debug::log_msg(debug::component_trace_t::tool, debug::log_level_t::debug, "unload: tool: %d", tool_id);
84+
85+ assert(tool != nullptr);
86+ tool->on_unload();
87+
88+ return _tools.size();
89+}
90+
91+opcode_t vm_tool_dispatch_t::on_breakpoint(vm_registers_t &r, vm_t &vm)
92+{
93+ return get_original_opcode(r.module_ref->module.get(), r.pc - 1);
94+}
95+
96+std::size_t vm_tool_dispatch_t::subscribe_event(vm_event_t evt, vm_event_callback_t cb)
97+{
98+ if (!cb)
99+ throw vm_system_exception{ "Invalid callback for event" };
100+
101+ std::lock_guard<std::mutex> lock{ _events.lock };
102+
103+ const auto cookie_id = _events.cookie_counter++;
104+
105+ // Store the event callback
106+ auto &event_callbacks = _events.callbacks[evt];
107+ event_callbacks[cookie_id] = cb;
108+
109+ // Store the cookie with the associate event
110+ _events.cookie_to_event[cookie_id] = evt;
111+
112+ if (debug::is_component_tracing_enabled<debug::component_trace_t::tool>())
113+ debug::log_msg(debug::component_trace_t::tool, debug::log_level_t::debug, "subscribe: event: %d %d", evt, cookie_id);
114+
115+ return cookie_id;
116+}
117+
118+void vm_tool_dispatch_t::unsubscribe_event(std::size_t cookie_id)
119+{
120+ std::lock_guard<std::mutex> lock{ _events.lock };
121+
122+ auto iter = _events.cookie_to_event.find(cookie_id);
123+ if (iter == _events.cookie_to_event.cend())
124+ return;
125+
126+ // Erase cookie and event
127+ const auto evt = iter->second;
128+ auto &event_callbacks = _events.callbacks[evt];
129+ event_callbacks.erase(cookie_id);
130+ _events.cookie_to_event.erase(iter);
131+
132+ if (debug::is_component_tracing_enabled<debug::component_trace_t::tool>())
133+ debug::log_msg(debug::component_trace_t::tool, debug::log_level_t::debug, "unsubscribe: event: %d %d", evt, cookie_id);
134+}
135+
136+std::size_t vm_tool_dispatch_t::set_breakpoint(std::shared_ptr<const vm_module_t> module, vm_pc_t pc)
137+{
138+ if (module == nullptr || util::has_flag(module->header.runtime_flag, runtime_flags_t::builtin))
139+ throw vm_system_exception{ "Unable to set breakpoint in supplied module" };
140+
141+ // [TODO] This should be re-considered. Removing 'const' is less than ideal.
142+ // Remove the const value since the code section needs to be updated.
143+ auto &code_section = const_cast<code_section_t &>(module->code_section);
144+ if (pc >= code_section.size())
145+ throw vm_system_exception{ "Invalid PC for module" };
146+
147+ const auto real_opcode = code_section[pc].op.opcode;
148+
149+ {
150+ std::lock_guard<std::mutex> lock{ _breakpoints.lock };
151+ if (real_opcode == opcode_t::brkpt)
152+ throw vm_system_exception{ "Breakpoint already set at PC" };
153+
154+ const auto cookie_id = _breakpoints.cookie_counter++;
155+
156+ // Map the cookie to the module/pc pair.
157+ _breakpoints.cookie_to_modulepc[cookie_id] = std::make_pair(module, pc);
158+
159+ // Record the original opcode
160+ auto &pc_map = _breakpoints.original_opcodes[reinterpret_cast<std::uintptr_t>(module.get())];
161+ pc_map[pc] = real_opcode;
162+
163+ // Replace the current opcode with breakpoint
164+ code_section[pc].op.opcode = opcode_t::brkpt;
165+
166+ if (debug::is_component_tracing_enabled<debug::component_trace_t::tool>())
167+ debug::log_msg(debug::component_trace_t::tool, debug::log_level_t::debug, "breakpoint: set: %d %d >>%s<<", cookie_id, pc, module->module_name->str());
168+
169+ return cookie_id;
170+ }
171+}
172+
173+void vm_tool_dispatch_t::clear_breakpoint(std::size_t cookie_id)
174+{
175+ std::lock_guard<std::mutex> lock{ _breakpoints.lock };
176+
177+ // Resolve the cookie to the module/pc pair
178+ auto iter_cookie = _breakpoints.cookie_to_modulepc.find(cookie_id);
179+ if (iter_cookie == _breakpoints.cookie_to_modulepc.cend())
180+ return;
181+
182+ auto mod_pc_pair = iter_cookie->second;
183+ const auto target_pc = mod_pc_pair.second;
184+
185+ // Determine the original opcode
186+ auto iter_orig = _breakpoints.original_opcodes.find(reinterpret_cast<std::uintptr_t>(mod_pc_pair.first.get()));
187+ assert(iter_orig != _breakpoints.original_opcodes.cend());
188+
189+ auto &pc_map = iter_orig->second;
190+ auto iter_pc = pc_map.find(target_pc);
191+ assert(iter_pc != pc_map.cend());
192+
193+ auto original_opcode = iter_pc->second;
194+
195+ // Replace the breakpoint opcode with the original
196+ auto &code_section = const_cast<code_section_t &>(mod_pc_pair.first->code_section);
197+ assert(code_section[target_pc].op.opcode == opcode_t::brkpt);
198+ code_section[target_pc].op.opcode = original_opcode;
199+
200+ if (debug::is_component_tracing_enabled<debug::component_trace_t::tool>())
201+ debug::log_msg(debug::component_trace_t::tool, debug::log_level_t::debug, "breakpoint: unset: %d %d >>%s<<", cookie_id, target_pc, mod_pc_pair.first->module_name->str());
202+
203+ // Clean-up the PC mapping to opcode
204+ pc_map.erase(iter_pc);
205+
206+ // If the PC map is empty, erase the module mapping as well
207+ if (pc_map.size() == 0)
208+ _breakpoints.original_opcodes.erase(iter_orig);
209+
210+ // Remove the cookie map
211+ _breakpoints.cookie_to_modulepc.erase(iter_cookie);
212+}
213+
214+opcode_t vm_tool_dispatch_t::get_original_opcode(const vm_module_t *module, vm_pc_t pc)
215+{
216+ if (module == nullptr || util::has_flag(module->header.runtime_flag, runtime_flags_t::builtin))
217+ throw vm_system_exception{ "Unable to determine original opcode in supplied module" };
218+
219+ std::lock_guard<std::mutex> lock{ _breakpoints.lock };
220+
221+ auto iter_orig = _breakpoints.original_opcodes.find(reinterpret_cast<std::uintptr_t>(module));
222+ if (iter_orig != _breakpoints.original_opcodes.cend())
223+ {
224+ auto &pc_map = iter_orig->second;
225+ auto iter_pc = pc_map.find(pc);
226+ if (iter_pc != pc_map.cend())
227+ return iter_pc->second;
228+ }
229+
230+ assert(false && "Why are we missing this information?");
231+ return opcode_t::runt; // Return NOP instruction?
232+}