OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011 The Native Client Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 #include "debugger/core/debug_api.h" | |
5 #include <algorithm> | |
6 #include "debugger/core/debug_execution_engine.h" | |
7 #include "debugger/core/debug_logger.h" | |
8 #include "debugger/core/debuggee_process.h" | |
9 | |
10 namespace { | |
11 /// Timeout for exiting processes. If nothing happens for | |
12 /// kWaitOnExitMs milliseconds, ExecutionEngine quits waiting | |
13 /// for debug events. | |
14 int kWaitOnExitMs = 300; | |
15 | |
16 bool not_dead_proc(debug::IDebuggeeProcess* proc) { | |
17 return debug::IDebuggeeProcess::kDead != proc->state(); | |
18 } | |
19 void delete_proc(debug::IDebuggeeProcess* proc) { delete proc; } | |
20 void detach_proc(debug::IDebuggeeProcess* proc) { | |
21 proc->Detach(); | |
22 } | |
23 void kill_proc(debug::IDebuggeeProcess* proc) { | |
24 proc->Kill(); | |
25 } | |
26 } // namespace | |
27 | |
28 namespace debug { | |
29 | |
30 ExecutionEngine::ExecutionEngine(DebugAPI* debug_api) | |
31 : debug_api_(*debug_api) { | |
32 } | |
33 | |
34 ExecutionEngine::~ExecutionEngine() { | |
35 Stop(kWaitOnExitMs); | |
36 std::for_each(processes_.begin(), processes_.end(), delete_proc); | |
37 processes_.clear(); | |
38 } | |
39 | |
40 IDebuggeeProcess* ExecutionEngine::CreateDebuggeeProcess(int pid, | |
41 HANDLE handle, | |
42 HANDLE file_handle) { | |
43 return new DebuggeeProcess(pid, handle, file_handle, &debug_api_); | |
44 } | |
45 | |
46 bool ExecutionEngine::StartProcess(const char* cmd, const char* work_dir) { | |
47 STARTUPINFO si; | |
48 memset(&si, 0, sizeof(si)); | |
49 si.cb = sizeof(si); | |
50 PROCESS_INFORMATION pi; | |
51 memset(&pi, 0, sizeof(pi)); | |
52 | |
53 char* cmd_dup = _strdup(cmd); | |
54 if (NULL == cmd_dup) { | |
55 DBG_LOG("TR01.00", "Memory allocation error."); | |
56 return false; | |
57 } | |
58 BOOL res = debug_api_.CreateProcess(NULL, | |
59 cmd_dup, | |
60 NULL, | |
61 NULL, | |
62 FALSE, | |
63 DEBUG_PROCESS | CREATE_NEW_CONSOLE, | |
64 NULL, | |
65 work_dir, | |
66 &si, | |
67 &pi); | |
68 free(cmd_dup); | |
69 if (!res) | |
70 return false; | |
71 | |
72 debug_api_.CloseHandle(pi.hThread); | |
73 debug_api_.CloseHandle(pi.hProcess); | |
74 return true; | |
75 } | |
76 | |
77 bool ExecutionEngine::AttachToProcess(int pid) { | |
78 return (TRUE == debug_api_.DebugActiveProcess(pid)) ? true : false; | |
79 } | |
80 | |
81 void ExecutionEngine::DetachAll() { | |
82 std::for_each(processes_.begin(), processes_.end(), detach_proc); | |
83 std::for_each(processes_.begin(), processes_.end(), delete_proc); | |
84 processes_.clear(); | |
85 } | |
86 | |
87 IDebuggeeProcess* ExecutionEngine::GetProcess(int pid) { | |
88 ProcessConstIter it = processes_.begin(); | |
89 while (it != processes_.end()) { | |
90 IDebuggeeProcess* proc = *it; | |
91 ++it; | |
92 if (pid == proc->id()) | |
93 return proc; | |
94 } | |
95 return NULL; | |
96 } | |
97 | |
98 void ExecutionEngine::GetProcessIds(std::deque<int>* processes) const { | |
99 processes->clear(); | |
100 ProcessConstIter it = processes_.begin(); | |
101 while (it != processes_.end()) { | |
102 processes->push_back((*it)->id()); | |
103 ++it; | |
104 } | |
105 } | |
106 | |
107 void ExecutionEngine::RemoveDeadProcesses() { | |
108 ProcessIter it = std::partition(processes_.begin(), | |
109 processes_.end(), | |
110 not_dead_proc); | |
111 std::for_each(it, processes_.end(), delete_proc); | |
112 processes_.erase(it, processes_.end()); | |
113 } | |
114 | |
115 bool ExecutionEngine::WaitForDebugEventAndDispatchIt(int wait_ms, | |
116 int* halted_pid) { | |
117 RemoveDeadProcesses(); | |
118 DEBUG_EVENT de; | |
119 if (debug_api_.WaitForDebugEvent(&de, wait_ms)) { | |
120 int pid = OnDebugEvent(de); | |
121 if (NULL != halted_pid) | |
122 *halted_pid = pid; | |
123 return true; | |
124 } | |
125 return false; | |
126 } | |
127 | |
128 int ExecutionEngine::OnDebugEvent(const DEBUG_EVENT& debug_event) { | |
129 debug_event_.set_windows_debug_event(debug_event); | |
130 debug_event_.set_nacl_debug_event_code(DebugEvent::kNotNaClDebugEvent); | |
131 | |
132 IDebuggeeProcess* process = GetProcess(debug_event.dwProcessId); | |
133 | |
134 if (CREATE_PROCESS_DEBUG_EVENT == debug_event.dwDebugEventCode) { | |
135 process = CreateDebuggeeProcess(debug_event.dwProcessId, | |
136 debug_event.u.CreateProcessInfo.hProcess, | |
137 debug_event.u.CreateProcessInfo.hFile); | |
138 if (NULL != process) | |
139 processes_.push_back(process); | |
140 } | |
141 | |
142 char tmp[1000]; | |
143 debug_event_.ToString(tmp, sizeof(tmp)); | |
144 DBG_LOG("TR01.01", "msg='ExecutionEngine::OnDebugEvent' event='%s'", tmp); | |
145 | |
146 if (NULL != process) { | |
147 process->OnDebugEvent(&debug_event_); | |
148 if (process->IsHalted()) | |
149 return process->id(); | |
150 } | |
151 return 0; | |
152 } | |
153 | |
154 void ExecutionEngine::Stop(int wait_ms) { | |
155 std::for_each(processes_.begin(), processes_.end(), kill_proc); | |
156 while (processes_.size() > 0) { | |
157 int halted_pid = 0; | |
158 WaitForDebugEventAndDispatchIt(wait_ms, &halted_pid); | |
159 if (0 != halted_pid) { | |
160 IDebuggeeProcess* proc = GetProcess(halted_pid); | |
161 proc->ContinueAndPassExceptionToDebuggee(); | |
162 } else { | |
163 break; // Timed-out, stop waiting for processes to shut down. | |
164 } | |
165 } | |
166 } | |
167 } // namespace debug | |
168 | |
OLD | NEW |