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/nacl-gdb_server/debug_server.h" | |
5 #include <stdio.h> | |
6 #include <signal.h> | |
7 #include <algorithm> | |
8 #include "debugger/core/debuggee_thread.h" | |
9 #include "debugger/rsp/rsp_common_replies.h" | |
10 #include "debugger/rsp/rsp_control_packets.h" | |
11 #include "debugger/rsp/rsp_info_packets.h" | |
12 #include "debugger/rsp/rsp_packet_utils.h" | |
13 #include "debugger/rsp/rsp_packets.h" | |
14 #include "debugger/rsp/rsp_threads_packets.h" | |
15 #include "debugger/nacl-gdb_server/gdb_registers.h" | |
16 | |
17 namespace { | |
18 const int kReadBufferSize = 1024; | |
19 const int kVS2008_THREAD_INFO = 0x406D1388; | |
20 const int kStopTimeoutMs = 1000; | |
21 const int kMaxPacketsToReadAtOnce = 100; | |
22 | |
23 // Error codes returned to the client (debugger). | |
24 const int kErrorNoFocusedThread = 1; | |
25 const int kErrorNoFocusedProcess = 2; | |
26 const int kErrorSetFocusToAllThreadsIsNotSupported = 3; | |
27 const int kErrorReadMemoryFailed = 4; | |
28 const int kErrorPacketIsTooLarge = 5; | |
29 const int kErrorWriteMemoryFailed = 6; | |
30 const int kErrorGetThreadContextFailed = 7; | |
31 const int kErrorSetThreadContextFailed = 8; | |
32 const int kErrorSingleStepFailed = 9; | |
33 const int kErrorThreadIsDead = 10; | |
34 const int kErrorThreadNotFound = 11; | |
35 | |
36 rsp::StopReply CreateStopReplyFromDebugEvent(const debug::DebugEvent& de) { | |
37 switch (de.process_state_) { | |
38 case debug::RUNNING: return rsp::StopReply(rsp::StopReply::STILL
_RUNNING); | |
39 case debug::PROCESS_STOPPED: { | |
40 rsp::StopReply msg(rsp::StopReply::SIGNALED); | |
41 msg.set_signal_number(de.signal_no_); | |
42 return msg; | |
43 } | |
44 case debug::PROCESS_TERMINATED: { | |
45 rsp::StopReply msg(rsp::StopReply::TERMINATED); | |
46 msg.set_signal_number(de.signal_no_); | |
47 return msg; | |
48 } | |
49 case debug::PROCESS_EXITED: { | |
50 rsp::StopReply msg(rsp::StopReply::EXITED); | |
51 msg.set_exit_code(de.exit_code_); | |
52 return msg; | |
53 } | |
54 } | |
55 return rsp::StopReply(rsp::StopReply::EXITED); | |
56 } | |
57 | |
58 #define AAA(x) printf("\t%s = 0x%lx\n", #x, context.x) | |
59 | |
60 void PrintContext(const user_regs_struct& context) { | |
61 printf("Context {\n"); | |
62 AAA(r15); | |
63 AAA(r14); | |
64 AAA(r13); | |
65 AAA(r12); | |
66 AAA(rbp); | |
67 AAA(rbx); | |
68 AAA(r11); | |
69 AAA(r10); | |
70 AAA(r9); | |
71 AAA(r8); | |
72 AAA(rax); | |
73 AAA(rcx); | |
74 AAA(rdx); | |
75 AAA(rsi); | |
76 AAA(rdi); | |
77 AAA(rip); | |
78 AAA(cs); | |
79 AAA(eflags); | |
80 AAA(rsp); | |
81 AAA(ss); | |
82 AAA(ds); | |
83 AAA(es); | |
84 AAA(fs); | |
85 AAA(gs); | |
86 printf("}\n"); | |
87 } | |
88 | |
89 } // namespace | |
90 | |
91 namespace debug { | |
92 DebugServer::DebugServer(DebugAPI* debug_api, int listen_port) | |
93 : debug_api_(debug_api), | |
94 client_connected_(false), | |
95 debuggee_process_(NULL), | |
96 focused_thread_id_(0), | |
97 listen_port_(listen_port), | |
98 continue_from_halt_(false) { | |
99 } | |
100 | |
101 DebugServer::~DebugServer() { | |
102 if (NULL != debuggee_process_) { | |
103 delete debuggee_process_; | |
104 debuggee_process_ = NULL; | |
105 } | |
106 } | |
107 | |
108 bool DebugServer::Init() { | |
109 rsp_packetizer_.SetPacketConsumer(this); | |
110 debuggee_process_ = new DebuggeeProcess(debug_api_); | |
111 if (NULL == debuggee_process_) | |
112 return false; | |
113 | |
114 return ListenForRspConnection(); | |
115 } | |
116 | |
117 bool DebugServer::ListenForRspConnection() { | |
118 bool res = listening_socket_.Listen(listen_port_); | |
119 if (res) | |
120 printf("Started listening on port %d ...\n", listen_port_); | |
121 else | |
122 printf("DebugServer::ListenForRspConnection failed port=%d", | |
123 listen_port_); | |
124 | |
125 return res; | |
126 } | |
127 | |
128 void DebugServer::HandleNetwork() { | |
129 if (!client_connection_.IsConnected()) { | |
130 if (client_connected_) { | |
131 client_connected_ = false; | |
132 printf("Dropped connection from debugger.\n"); | |
133 } | |
134 if (listening_socket_.Accept(20, &client_connection_)) { | |
135 client_connected_ = true; | |
136 printf("Got connection from debugger.\n"); | |
137 } | |
138 } else { | |
139 char buff[kReadBufferSize]; | |
140 for (int i = 0; i < kMaxPacketsToReadAtOnce; i++) { | |
141 size_t read_bytes = client_connection_.Read(buff, | |
142 sizeof(buff) - 1, | |
143
20); | |
144 if (read_bytes > 0) { | |
145 buff[read_bytes] = 0; | |
146 printf("r>[%s]\n", buff); | |
147 rsp_packetizer_.OnData(buff, read_bytes); | |
148 } else { | |
149 break; | |
150 } | |
151 } | |
152 } | |
153 } | |
154 | |
155 void DebugServer::HandleExecutionEngine() { | |
156 DebugEvent de; | |
157 if (debuggee_process_->WaitForDebugEventAndDispatchIt(&de)) { | |
158 // Now, we have a halted NaCl process on our hands. | |
159 if (0 == focused_thread_id_) | |
160 focused_thread_id_ = de.pid_; | |
161 if (continue_from_halt_) { | |
162 continue_from_halt_ = false; | |
163 SendRspMessageToClient(CreateStopReplyFromDebugEvent(de)
); | |
164 } | |
165 } | |
166 } | |
167 | |
168 void DebugServer::DoWork() { | |
169 HandleNetwork(); | |
170 HandleExecutionEngine(); | |
171 } | |
172 | |
173 void DebugServer::SendRspMessageToClient(const rsp::Packet& msg) { | |
174 Blob text; | |
175 msg.ToBlob(&text); | |
176 Blob wire_msg; | |
177 rsp::PacketUtils::AddEnvelope(text, &wire_msg); | |
178 client_connection_.WriteAll(wire_msg); | |
179 printf("T>[%s]\n", text.ToString().c_str()); | |
180 printf("t>[%s]\n", wire_msg.ToString().c_str()); | |
181 } | |
182 | |
183 DebuggeeThread* DebugServer::GetFocusedThread() { | |
184 DebuggeeThread* thread = debuggee_process_->GetThread(focused_thread_id_
); | |
185 if (NULL == thread) | |
186 SendErrorReply(kErrorNoFocusedThread); | |
187 return thread; | |
188 } | |
189 | |
190 void DebugServer::OnUnknownCommand() { | |
191 // Empty RSP packet is returned for unsupported commands. | |
192 SendRspMessageToClient(rsp::EmptyPacket()); | |
193 } | |
194 | |
195 void DebugServer::OnPacket(const Blob& body, bool valid_checksum) { | |
196 if (valid_checksum) | |
197 client_connection_.WriteAll("+", 1); // Send low-level RSP acknowledgment. | |
198 | |
199 Blob msg = body; | |
200 rsp::Packet* command = rsp::Packet::CreateFromBlob(&msg, NULL); | |
201 if (NULL != command) { | |
202 command->AcceptVisitor(this); | |
203 delete command; | |
204 } else { | |
205 OnUnknownCommand(); | |
206 } | |
207 } | |
208 | |
209 void DebugServer::OnUnexpectedByte(uint8_t unexpected_byte) { | |
210 printf("msg='DebugServer::OnUnexpectedChar' c='0x%x'", | |
211 static_cast<int>(unexpected_byte)); | |
212 } | |
213 | |
214 void DebugServer::SendErrorReply(int error) { | |
215 SendRspMessageToClient(rsp::ErrorReply(error)); | |
216 } | |
217 | |
218 void DebugServer::OnBreak() { | |
219 debug_api_->PostSignal(focused_thread_id_, SIGSTOP); | |
220 } | |
221 | |
222 void DebugServer::Visit(rsp::GetStopReasonCommand* packet) { | |
223 DebuggeeThread* thread = GetFocusedThread(); | |
224 if (NULL == thread) | |
225 return; | |
226 SendRspMessageToClient(CreateStopReplyFromDebugEvent(thread->last_debug_
event())); | |
227 } | |
228 | |
229 void DebugServer::Visit(rsp::ContinueCommand* packet) { | |
230 DebuggeeThread* thread = GetFocusedThread(); | |
231 if (NULL != thread) { | |
232 continue_from_halt_ = true; | |
233 debuggee_process_->Continue(focused_thread_id_); | |
234 } | |
235 } | |
236 | |
237 void DebugServer::Visit(rsp::QuerySupportedCommand* packet) { | |
238 rsp::QuerySupportedReply reply; | |
239 reply.AddFeature("PacketSize", "7cf"); | |
240 reply.AddFeature("qXfer:libraries:read", "+"); | |
241 reply.AddFeature("qXfer:features:read", "+"); | |
242 SendRspMessageToClient(reply); | |
243 } | |
244 | |
245 void DebugServer::Visit(rsp::QXferFeaturesReadCommand* packet) { | |
246 // qXfer:features:read:target.xml:0,7ca | |
247 if (packet->file_name() == "target.xml") { | |
248 rsp::QXferReply reply; | |
249 reply.set_body("<target><architecture>i386:x86-64</architecture></target>"); | |
250 reply.set_eom(true); | |
251 SendRspMessageToClient(reply); | |
252 } else { | |
253 OnUnknownCommand(); | |
254 } | |
255 } | |
256 | |
257 void DebugServer::Visit(rsp::SetCurrentThreadCommand* packet) { | |
258 int tid = static_cast<int>(packet->thread_id()); | |
259 bool res = false; | |
260 if (-1 == tid) { // all threads | |
261 res = true; | |
262 } else if (0 == tid) { // any thread | |
263 res = true; | |
264 } else { | |
265 DebuggeeThread* thread = debuggee_process_->GetThread(tid); | |
266 if (NULL != thread) { | |
267 res = true; | |
268 focused_thread_id_ = tid; | |
269 } | |
270 } | |
271 if (res) | |
272 SendRspMessageToClient(rsp::OkReply()); | |
273 else | |
274 SendErrorReply(kErrorThreadNotFound); | |
275 } | |
276 | |
277 void DebugServer::Visit(rsp::ReadMemoryCommand* packet) { | |
278 size_t sz = packet->num_of_bytes(); | |
279 if (0 == sz) { | |
280 SendRspMessageToClient(rsp::EmptyPacket()); | |
281 return; | |
282 } | |
283 char buff[rsp::kMaxRspPacketSize / 2]; // 2 characters per byte. | |
284 if (sizeof(buff) < sz) | |
285 sz = sizeof(buff); | |
286 | |
287 DebuggeeThread* thread = GetFocusedThread(); | |
288 if (NULL != thread) { | |
289 uint64_t addr = packet->addr(); | |
290 // massage address to support gdb | |
291 if (addr < debuggee_process_->nexe_mem_base()) | |
292 addr += debuggee_process_->nexe_mem_base(); | |
293 | |
294 size_t rd = 0; | |
295 if (debug_api_->ReadMemory(focused_thread_id_, addr, buff, sz, &
rd)) { | |
296 rsp::BlobReply reply; | |
297 reply.set_data(buff, sz); | |
298 SendRspMessageToClient(reply); | |
299 } else { | |
300 SendErrorReply(kErrorReadMemoryFailed); | |
301 } | |
302 } | |
303 } | |
304 | |
305 void DebugServer::Visit(rsp::WriteMemoryCommand* packet) { | |
306 const debug::Blob& data = packet->data(); | |
307 DebuggeeThread* thread = GetFocusedThread(); | |
308 if (NULL != thread) { | |
309 char tmp[rsp::kMaxRspPacketSize]; | |
310 if (data.size() > sizeof(tmp)) { | |
311 SendErrorReply(kErrorPacketIsTooLarge); | |
312 return; | |
313 } | |
314 data.Peek(0, tmp, data.size()); | |
315 uint64_t addr = packet->addr(); | |
316 // massage address to support gdb | |
317 if (addr < debuggee_process_->nexe_mem_base()) | |
318 addr += debuggee_process_->nexe_mem_base(); | |
319 | |
320 size_t wr = 0; | |
321 bool res = debug_api_->WriteMemory(focused_thread_id_, addr, tmp
, data.size(), &wr); | |
322 if (res) | |
323 SendRspMessageToClient(rsp::OkReply()); | |
324 else | |
325 SendErrorReply(kErrorWriteMemoryFailed); | |
326 } | |
327 } | |
328 | |
329 void DebugServer::Visit(rsp::ReadRegistersCommand* packet) { | |
330 DebuggeeThread* thread = GetFocusedThread(); | |
331 if (NULL != thread) { | |
332 user_regs_struct context; | |
333 if (!debug_api_->ReadThreadContext(thread->id(), &context)) { | |
334 SendErrorReply(kErrorGetThreadContextFailed); | |
335 } else { | |
336 PrintContext(context); | |
337 Blob blob; | |
338 rsp::CONTEXTToGdbRegisters(context, &blob); | |
339 rsp::BlobReply reply; | |
340 reply.set_data(blob); | |
341 SendRspMessageToClient(reply); | |
342 } | |
343 } | |
344 } | |
345 | |
346 void DebugServer::Visit(rsp::WriteRegistersCommand* packet) { | |
347 DebuggeeThread* thread = GetFocusedThread(); | |
348 if (NULL != thread) { | |
349 user_regs_struct context; | |
350 if (!debug_api_->ReadThreadContext(thread->id(), &context)) { | |
351 SendErrorReply(kErrorGetThreadContextFailed); | |
352 return; | |
353 } | |
354 rsp::GdbRegistersToCONTEXT(packet->data(), &context); | |
355 if (!debug_api_->WriteThreadContext(thread->id(), &context)) | |
356 SendErrorReply(kErrorSetThreadContextFailed); | |
357 else | |
358 SendRspMessageToClient(rsp::OkReply()); | |
359 } | |
360 } | |
361 | |
362 void DebugServer::Visit(rsp::GetCurrentThreadCommand* packet) { | |
363 rsp::GetCurrentThreadReply reply; | |
364 reply.set_value(focused_thread_id_); | |
365 SendRspMessageToClient(reply); | |
366 } | |
367 | |
368 void DebugServer::Visit(rsp::StepCommand* packet) { | |
369 DebuggeeThread* thread = GetFocusedThread(); | |
370 if (NULL != thread) { | |
371 if (!debug_api_->SingleStep(focused_thread_id_)) { | |
372 SendErrorReply(kErrorSingleStepFailed); | |
373 } else { | |
374 thread->Continue(); | |
375 continue_from_halt_ = true; | |
376 } | |
377 } | |
378 } | |
379 | |
380 void DebugServer::Visit(rsp::IsThreadAliveCommand* packet) { | |
381 if (NULL != debuggee_process_->GetThread(packet->value())) | |
382 SendRspMessageToClient(rsp::OkReply()); | |
383 else | |
384 SendErrorReply(kErrorThreadNotFound); | |
385 } | |
386 | |
387 void DebugServer::Visit(rsp::GetThreadInfoCommand* packet) { | |
388 rsp::GetThreadInfoReply reply; | |
389 if (packet->get_more()) { | |
390 // TODO(garianov): add support for multi-packet replies. | |
391 reply.set_eom(true); | |
392 } else { | |
393 std::deque<int> tids; | |
394 debuggee_process_->GetThreadsIds(&tids); | |
395 reply.set_threads_ids(tids); | |
396 reply.set_eom(false); | |
397 } | |
398 SendRspMessageToClient(reply); | |
399 } | |
400 | |
401 void DebugServer::Visit(rsp::GetOffsetsCommand* packet) { | |
402 uint64_t mb = debuggee_process_->nexe_mem_base(); | |
403 rsp::GetOffsetsReply reply; | |
404 reply.set_data_offset(mb); | |
405 reply.set_text_offset(mb); | |
406 | |
407 SendRspMessageToClient(reply); | |
408 } | |
409 | |
410 } // namespace debug | |
411 | |
OLD | NEW |