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 | |
6 #include <errno.h> | |
7 #include <memory.h> | |
8 #include <signal.h> | |
9 #include <stdio.h> | |
10 #include <stdlib.h> | |
11 #include <sys/syscall.h> | |
12 #include <sys/types.h> | |
13 #include <sys/user.h> | |
14 #include <sys/wait.h> | |
15 #include <unistd.h> | |
16 | |
17 #include <deque> | |
18 #include <string> | |
19 | |
20 typedef int64_t ptrace_result_t; | |
21 | |
22 void Split(const char* str_in, | |
23 const char* delimiters, | |
24 std::deque<std::string>* out) { | |
25 char c = 0; | |
26 std::string str; | |
27 while (0 != (c = *str_in++)) { | |
28 if (strchr(delimiters, c)) { | |
29 if (str.size()) { | |
30 out->push_back(str); | |
31 str.clear(); | |
32 } | |
33 } else if ((c != '\r') && (c != '\n')) { | |
34 str.push_back(c); | |
35 } | |
36 } | |
37 if (str.size()) | |
38 out->push_back(str); | |
39 } | |
40 | |
41 std::string GetAppPathOutOfCmdLine(const char* cmd_line) { | |
42 std::deque<std::string> words; | |
43 Split(cmd_line, " \t", &words); | |
44 if (words.size() > 0) | |
45 return words[0]; | |
46 return cmd_line; | |
47 } | |
48 | |
49 std::string GetAppNameOutOfCmdLine(const char* cmd_line) { | |
50 std::string path = GetAppPathOutOfCmdLine(cmd_line); | |
51 std::deque<std::string> words; | |
52 Split(path.c_str(), "/", &words); | |
53 if (words.size() > 0) | |
54 return words[words.size() - 1]; | |
55 return cmd_line; | |
56 } | |
57 | |
58 namespace { | |
59 const int kMaxOutputDebugStringSize = 256; | |
60 const char kNexePrefix[] = "{7AA7C9CF-89EC-4ed3-8DAD-6DC84302AB11}"; | |
61 } // namespace | |
62 | |
63 namespace debug { | |
64 bool DebugAPI::PostSignal(pid_t pid, int signo) { | |
65 return (0 == kill(pid, signo)); | |
66 } | |
67 | |
68 bool DebugAPI::ReadDebugString(const DebugEvent& de, std::string* string) { | |
69 if ((SIGTRAP != de.signal_no_) || (PROCESS_STOPPED != de.process_state_)) | |
70 return false; | |
71 | |
72 user_regs_struct context; | |
73 if (!ReadThreadContext(de.pid_, &context)) | |
74 return false; | |
75 | |
76 uint64_t addr = context.rax; | |
77 char buff[kMaxOutputDebugStringSize]; | |
78 size_t rd = 0; | |
79 ReadMemory(de.pid_, addr, buff, sizeof(buff) - 1, &rd); | |
80 if (rd < sizeof(kNexePrefix)) | |
81 return false; | |
82 if (strncmp(buff, kNexePrefix, sizeof(kNexePrefix) - 1) != 0) | |
83 return false; | |
84 | |
85 buff[sizeof(buff) - 1] = 0; | |
86 *string = &buff[sizeof(kNexePrefix) - 1]; | |
87 printf("DebugString-[%s]\n", string->c_str()); | |
88 return true; | |
89 } | |
90 | |
91 bool DebugAPI::ContinueDebugEvent(pid_t pid, int signo) { | |
92 ptrace_result_t res = ptrace(PTRACE_CONT, pid, 0, reinterpret_cast<void*>(sign
o)); | |
93 return (0 == res); | |
94 } | |
95 | |
96 bool DebugAPI::StartProcess(const char* cmd_line, | |
97 bool trace, | |
98 pid_t* child_pid_out) { | |
99 std::string path = GetAppPathOutOfCmdLine(cmd_line); | |
100 std::string app_name = GetAppNameOutOfCmdLine(cmd_line); | |
101 | |
102 pid_t child_pid = fork(); | |
103 printf("fork -> %d\n", child_pid); | |
104 if (-1 == child_pid) | |
105 return false; | |
106 | |
107 if (0 == child_pid) { | |
108 // in child | |
109 if (trace) | |
110 ptrace(PTRACE_TRACEME, 0, NULL, NULL); | |
111 int res = execl(path.c_str(), app_name.c_str(), "", NULL); | |
112 | |
113 // TODO(garianov): how to communicate failure of execl to the debugger | |
114 // process? | |
115 // My guess is parent proc/debugger will get SIGTERM signal or TERM debug ev
ent... | |
116 printf("in child: execl -> %d errno=%d\n", res, errno); | |
117 exit(13); | |
118 } else { | |
119 // in parent | |
120 if (NULL != child_pid_out) | |
121 *child_pid_out = child_pid; | |
122 } | |
123 return true; | |
124 } | |
125 | |
126 bool DebugAPI::SetupProc(pid_t pid) { | |
127 intptr_t mask = | |
128 PTRACE_O_TRACEFORK | | |
129 PTRACE_O_TRACEVFORK | | |
130 PTRACE_O_TRACECLONE ; | |
131 void* data = reinterpret_cast<void*>(mask); | |
132 ptrace_result_t res = ptrace(PTRACE_SETOPTIONS, pid, 0, data); | |
133 if (0 != res) { | |
134 printf("Error: ptrace(PTRACE_SETOPTIONS(pid=%d) -> %s\n", pid, strerror(errn
o)); | |
135 } | |
136 return (0 != res); | |
137 } | |
138 | |
139 bool DebugAPI::DebugBreak(pid_t pid) { | |
140 return PostSignal(pid, SIGSTOP); | |
141 } | |
142 | |
143 bool DebugAPI::SingleStep(pid_t pid) { | |
144 ptrace_result_t res = ptrace(PTRACE_SINGLESTEP, pid, 0, 0); | |
145 return (0 == res); | |
146 } | |
147 | |
148 | |
149 bool DebugAPI::WriteMemory(pid_t pid, | |
150 uint64_t addr, | |
151 void* src, | |
152 size_t size, | |
153 size_t* written_bytes_out) { | |
154 size_t written_bytes = 0; | |
155 size_t left_bytes = size; | |
156 ptrace_result_t res = 0; | |
157 const size_t bundle_sz = sizeof(res); | |
158 unsigned char* ptr_src = reinterpret_cast<unsigned char*>(src); | |
159 unsigned char* ptr_addr = reinterpret_cast<unsigned char*>(addr); | |
160 | |
161 // Read first few bytes that rea not aligned on 4 (or 8 on 64-bit) | |
162 // bytes, if any. | |
163 uint64_t offset = addr; | |
164 size_t offset_from_4bytes = offset % bundle_sz; | |
165 if (0 != offset_from_4bytes) { | |
166 // printf("offset_from_4bytes = %d\n", (int)offset_from_4bytes); | |
167 unsigned char* beg_addr = | |
168 reinterpret_cast<unsigned char*>(offset - offset_from_4bytes); | |
169 res = ptrace(PTRACE_PEEKDATA, pid, beg_addr, 0); | |
170 // printf("ptrace(PTRACE_PEEKDATA(%p) -> 0x%lX\n", beg_addr, res); | |
171 if (0 != errno) { | |
172 // printf("%d ptrace -> %d\n", __LINE__, errno); | |
173 return false; | |
174 } | |
175 unsigned char* p = | |
176 reinterpret_cast<unsigned char*>(&res) + offset_from_4bytes; | |
177 written_bytes = bundle_sz - offset_from_4bytes; | |
178 if (written_bytes > size) | |
179 written_bytes = size; | |
180 | |
181 memcpy(p, ptr_src, written_bytes); | |
182 ptrace_result_t res2 = ptrace(PTRACE_POKEDATA, pid, beg_addr, reinterpret_ca
st<void*>(res)); | |
183 // printf("ptrace(PTRACE_POKEDATA(%p, 0x%lX) -> 0x%lX\n", | |
184 // beg_addr, | |
185 // res, | |
186 // res2); | |
187 if (0 != res2) { | |
188 // printf("%d ptrace(PTRACE_POKEDATA) -> %d\n", errno, __LINE__); | |
189 return false; | |
190 } | |
191 | |
192 ptr_src += written_bytes; | |
193 ptr_addr += written_bytes; | |
194 left_bytes -= written_bytes; | |
195 } | |
196 | |
197 while (left_bytes) { | |
198 size_t wr_bytes = bundle_sz; | |
199 if (wr_bytes > left_bytes) | |
200 wr_bytes = left_bytes; | |
201 | |
202 ptrace_result_t data = 0; | |
203 if (left_bytes < bundle_sz) { | |
204 data = ptrace(PTRACE_PEEKDATA, pid, ptr_addr, 0); | |
205 if (0 != errno) { | |
206 // printf("%d ptrace(PTRACE_PEEKDATA) -> 0x%lX\n", __LINE__, data); | |
207 return false; | |
208 } | |
209 // printf("ptrace(PTRACE_PEEKDATA(%p) -> 0x%lX\n", ptr_addr, data); | |
210 } | |
211 memcpy(&data, ptr_src, wr_bytes); | |
212 // printf("memcpy... %X\n", (unsigned int)(*ptr_src)); | |
213 // printf("PTRACE_POKEDATA(%p) %lX\n", ptr_addr, data); | |
214 | |
215 ptrace_result_t res = ptrace(PTRACE_POKEDATA, pid, ptr_addr, reinterpret_cas
t<void*>(data)); | |
216 if (0 != res) { | |
217 return false; | |
218 } | |
219 | |
220 written_bytes += wr_bytes; | |
221 ptr_src += written_bytes; | |
222 ptr_addr += written_bytes; | |
223 left_bytes -= wr_bytes; | |
224 } | |
225 | |
226 if (NULL != written_bytes_out) | |
227 *written_bytes_out = written_bytes; | |
228 return (size == written_bytes); | |
229 } | |
230 | |
231 bool DebugAPI::ReadMemory(pid_t pid, | |
232 uint64_t addr, | |
233 void* dest, | |
234 size_t size, | |
235 size_t* readed_bytes_out) { | |
236 size_t readed_bytes = 0; | |
237 size_t left_bytes = size; | |
238 ptrace_result_t res = 0; | |
239 const size_t bundle_sz = sizeof(res); | |
240 char* ptr_dest = reinterpret_cast<char*>(dest); | |
241 char* ptr_addr = reinterpret_cast<char*>(addr); | |
242 //printf("DebugApi::ReadProcessMemory(%d, %p, %d)\n", pid, addr, (int)size); | |
243 //printf("left_bytes = %d\n", (int)left_bytes); | |
244 | |
245 // Read first few bytes that rea not aligned on 4 (or 8 on 64-bit) | |
246 // bytes, if any. | |
247 uint64_t offset = addr; | |
248 size_t offset_from_4bytes = offset % bundle_sz; | |
249 //printf("===>>>>> offset_from_4bytes=%ld\n", offset_from_4bytes); | |
250 fflush(stdout); | |
251 if (0 != offset_from_4bytes) { | |
252 char* beg_addr = reinterpret_cast<char*>(offset - offset_from_4bytes); | |
253 res = ptrace(PTRACE_PEEKDATA, pid, beg_addr, 0); | |
254 //printf("ptrace(PTRACE_PEEKDATA(%p) -> 0x%lX\n", beg_addr, res); | |
255 if (0 != errno) { | |
256 //printf("ptrace -> %d\n", errno); | |
257 //printf("Errno: %s\n", strerror(errno)); | |
258 return false; | |
259 } | |
260 char* src = reinterpret_cast<char*>(&res) + offset_from_4bytes; | |
261 readed_bytes = bundle_sz - offset_from_4bytes; | |
262 //printf("readed_bytes = %d\n", (int)readed_bytes); | |
263 memcpy(ptr_dest, src, readed_bytes); | |
264 ptr_dest += readed_bytes; | |
265 ptr_addr += readed_bytes; | |
266 if (readed_bytes > left_bytes) | |
267 left_bytes = 0; | |
268 else | |
269 left_bytes -= readed_bytes; | |
270 //printf("left_bytes = %d\n", (int)left_bytes); | |
271 } | |
272 | |
273 while (left_bytes) { | |
274 //printf("left_bytes = %d\n", (int)left_bytes); | |
275 res = ptrace(PTRACE_PEEKDATA, pid, ptr_addr, 0); | |
276 //printf("ptrace(PTRACE_PEEKDATA(%p) -> 0x%lX\n", ptr_addr, res); | |
277 if (0 != errno) { | |
278 //printf("Errno: %s\n", strerror(errno)); | |
279 break; | |
280 } | |
281 size_t rd_bytes = bundle_sz; | |
282 if (rd_bytes > left_bytes) | |
283 rd_bytes = left_bytes; | |
284 | |
285 //printf("rd_bytes = %d\n", (int)rd_bytes); | |
286 memcpy(ptr_dest, &res, rd_bytes); | |
287 readed_bytes += rd_bytes; | |
288 ptr_dest += rd_bytes; | |
289 ptr_addr += rd_bytes; | |
290 left_bytes -= rd_bytes; | |
291 } | |
292 if (NULL != readed_bytes_out) | |
293 *readed_bytes_out = readed_bytes; | |
294 //printf("size=%d readed_bytes=%d\n", (int)size, (int)readed_bytes); | |
295 return (size <= readed_bytes); | |
296 } | |
297 | |
298 bool DebugAPI::ReadThreadContext(pid_t pid, user_regs_struct* context) { | |
299 int ret = ptrace(PTRACE_GETREGS, pid, NULL, context); | |
300 return (0 == ret); | |
301 } | |
302 | |
303 bool DebugAPI::WriteThreadContext(pid_t pid, user_regs_struct* context) { | |
304 int ret = ptrace(PTRACE_SETREGS, pid, NULL, context); | |
305 return (0 == ret); | |
306 } | |
307 | |
308 bool DebugAPI::WaitForDebugEvent(DebugEvent* de) { | |
309 de->pid_ = 0; | |
310 de->signal_no_ = 0; | |
311 de->exit_code_ = 0; | |
312 de->process_state_ = PROCESS_STOPPED; | |
313 | |
314 int status = 0; | |
315 int options = WNOHANG | __WALL; | |
316 int res = waitpid(-1, &status, options); | |
317 if (-1 == res) | |
318 return false; | |
319 | |
320 bool event_received = (0 != res); | |
321 if (event_received) { | |
322 de->pid_ = res; | |
323 if (WIFEXITED(status)) { | |
324 de->process_state_ = PROCESS_EXITED; | |
325 de->exit_code_ = WEXITSTATUS(status); | |
326 } else if (WIFSIGNALED(status)) { | |
327 de->process_state_ = PROCESS_TERMINATED; | |
328 de->signal_no_ = WTERMSIG(status); | |
329 } else if (WIFSTOPPED(status)) { | |
330 de->process_state_ = PROCESS_STOPPED; | |
331 de->signal_no_ = WSTOPSIG(status); | |
332 } else if (WIFCONTINUED(status)) { | |
333 de->process_state_ = PROCESS_STOPPED; | |
334 de->signal_no_ = 0; | |
335 } | |
336 } | |
337 return event_received; | |
338 } | |
339 | |
340 } // namespace debug | |
341 | |
OLD | NEW |