1 // Copyright 2012 the V8 project authors. All rights reserved.
2 // Redistribution and use in source and binary forms, with or without
3 // modification, are permitted provided that the following conditions are
4 // met:
5 //
6 // * Redistributions of source code must retain the above copyright
7 // notice, this list of conditions and the following disclaimer.
8 // * Redistributions in binary form must reproduce the above
9 // copyright notice, this list of conditions and the following
10 // disclaimer in the documentation and/or other materials provided
11 // with the distribution.
12 // * Neither the name of Google Inc. nor the names of its
13 // contributors may be used to endorse or promote products derived
14 // from this software without specific prior written permission.
15 //
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28 #ifdef ENABLE_DEBUGGER_SUPPORT
29
30 #include "d8.h"
31 #include "d8-debug.h"
32 #include "platform.h"
33 #include "debug-agent.h"
34
35
36 namespace v8 {
37
38 static bool was_running = true;
39
PrintPrompt(bool is_running)40 void PrintPrompt(bool is_running) {
41 const char* prompt = is_running? "> " : "dbg> ";
42 was_running = is_running;
43 printf("%s", prompt);
44 fflush(stdout);
45 }
46
47
PrintPrompt()48 void PrintPrompt() {
49 PrintPrompt(was_running);
50 }
51
52
HandleDebugEvent(DebugEvent event,Handle<Object> exec_state,Handle<Object> event_data,Handle<Value> data)53 void HandleDebugEvent(DebugEvent event,
54 Handle<Object> exec_state,
55 Handle<Object> event_data,
56 Handle<Value> data) {
57 HandleScope scope;
58
59 // Check for handled event.
60 if (event != Break && event != Exception && event != AfterCompile) {
61 return;
62 }
63
64 TryCatch try_catch;
65
66 // Get the toJSONProtocol function on the event and get the JSON format.
67 Local<String> to_json_fun_name = String::New("toJSONProtocol");
68 Local<Function> to_json_fun =
69 Function::Cast(*event_data->Get(to_json_fun_name));
70 Local<Value> event_json = to_json_fun->Call(event_data, 0, NULL);
71 if (try_catch.HasCaught()) {
72 Shell::ReportException(&try_catch);
73 return;
74 }
75
76 // Print the event details.
77 Handle<Object> details =
78 Shell::DebugMessageDetails(Handle<String>::Cast(event_json));
79 if (try_catch.HasCaught()) {
80 Shell::ReportException(&try_catch);
81 return;
82 }
83 String::Utf8Value str(details->Get(String::New("text")));
84 if (str.length() == 0) {
85 // Empty string is used to signal not to process this event.
86 return;
87 }
88 printf("%s\n", *str);
89
90 // Get the debug command processor.
91 Local<String> fun_name = String::New("debugCommandProcessor");
92 Local<Function> fun = Function::Cast(*exec_state->Get(fun_name));
93 Local<Object> cmd_processor =
94 Object::Cast(*fun->Call(exec_state, 0, NULL));
95 if (try_catch.HasCaught()) {
96 Shell::ReportException(&try_catch);
97 return;
98 }
99
100 static const int kBufferSize = 256;
101 bool running = false;
102 while (!running) {
103 char command[kBufferSize];
104 PrintPrompt(running);
105 char* str = fgets(command, kBufferSize, stdin);
106 if (str == NULL) break;
107
108 // Ignore empty commands.
109 if (strlen(command) == 0) continue;
110
111 TryCatch try_catch;
112
113 // Convert the debugger command to a JSON debugger request.
114 Handle<Value> request =
115 Shell::DebugCommandToJSONRequest(String::New(command));
116 if (try_catch.HasCaught()) {
117 Shell::ReportException(&try_catch);
118 continue;
119 }
120
121 // If undefined is returned the command was handled internally and there is
122 // no JSON to send.
123 if (request->IsUndefined()) {
124 continue;
125 }
126
127 Handle<String> fun_name;
128 Handle<Function> fun;
129 // All the functions used below take one argument.
130 static const int kArgc = 1;
131 Handle<Value> args[kArgc];
132
133 // Invoke the JavaScript to convert the debug command line to a JSON
134 // request, invoke the JSON request and convert the JSON respose to a text
135 // representation.
136 fun_name = String::New("processDebugRequest");
137 fun = Handle<Function>::Cast(cmd_processor->Get(fun_name));
138 args[0] = request;
139 Handle<Value> response_val = fun->Call(cmd_processor, kArgc, args);
140 if (try_catch.HasCaught()) {
141 Shell::ReportException(&try_catch);
142 continue;
143 }
144 Handle<String> response = Handle<String>::Cast(response_val);
145
146 // Convert the debugger response into text details and the running state.
147 Handle<Object> response_details = Shell::DebugMessageDetails(response);
148 if (try_catch.HasCaught()) {
149 Shell::ReportException(&try_catch);
150 continue;
151 }
152 String::Utf8Value text_str(response_details->Get(String::New("text")));
153 if (text_str.length() > 0) {
154 printf("%s\n", *text_str);
155 }
156 running =
157 response_details->Get(String::New("running"))->ToBoolean()->Value();
158 }
159 }
160
161
RunRemoteDebugger(int port)162 void RunRemoteDebugger(int port) {
163 RemoteDebugger debugger(port);
164 debugger.Run();
165 }
166
167
Run()168 void RemoteDebugger::Run() {
169 bool ok;
170
171 // Make sure that socket support is initialized.
172 ok = i::Socket::SetUp();
173 if (!ok) {
174 printf("Unable to initialize socket support %d\n", i::Socket::LastError());
175 return;
176 }
177
178 // Connect to the debugger agent.
179 conn_ = i::OS::CreateSocket();
180 static const int kPortStrSize = 6;
181 char port_str[kPortStrSize];
182 i::OS::SNPrintF(i::Vector<char>(port_str, kPortStrSize), "%d", port_);
183 ok = conn_->Connect("localhost", port_str);
184 if (!ok) {
185 printf("Unable to connect to debug agent %d\n", i::Socket::LastError());
186 return;
187 }
188
189 // Start the receiver thread.
190 ReceiverThread receiver(this);
191 receiver.Start();
192
193 // Start the keyboard thread.
194 KeyboardThread keyboard(this);
195 keyboard.Start();
196 PrintPrompt();
197
198 // Process events received from debugged VM and from the keyboard.
199 bool terminate = false;
200 while (!terminate) {
201 event_available_->Wait();
202 RemoteDebuggerEvent* event = GetEvent();
203 switch (event->type()) {
204 case RemoteDebuggerEvent::kMessage:
205 HandleMessageReceived(event->data());
206 break;
207 case RemoteDebuggerEvent::kKeyboard:
208 HandleKeyboardCommand(event->data());
209 break;
210 case RemoteDebuggerEvent::kDisconnect:
211 terminate = true;
212 break;
213
214 default:
215 UNREACHABLE();
216 }
217 delete event;
218 }
219
220 // Wait for the receiver thread to end.
221 receiver.Join();
222 }
223
224
MessageReceived(i::SmartArrayPointer<char> message)225 void RemoteDebugger::MessageReceived(i::SmartArrayPointer<char> message) {
226 RemoteDebuggerEvent* event =
227 new RemoteDebuggerEvent(RemoteDebuggerEvent::kMessage, message);
228 AddEvent(event);
229 }
230
231
KeyboardCommand(i::SmartArrayPointer<char> command)232 void RemoteDebugger::KeyboardCommand(i::SmartArrayPointer<char> command) {
233 RemoteDebuggerEvent* event =
234 new RemoteDebuggerEvent(RemoteDebuggerEvent::kKeyboard, command);
235 AddEvent(event);
236 }
237
238
ConnectionClosed()239 void RemoteDebugger::ConnectionClosed() {
240 RemoteDebuggerEvent* event =
241 new RemoteDebuggerEvent(RemoteDebuggerEvent::kDisconnect,
242 i::SmartArrayPointer<char>());
243 AddEvent(event);
244 }
245
246
AddEvent(RemoteDebuggerEvent * event)247 void RemoteDebugger::AddEvent(RemoteDebuggerEvent* event) {
248 i::ScopedLock lock(event_access_);
249 if (head_ == NULL) {
250 ASSERT(tail_ == NULL);
251 head_ = event;
252 tail_ = event;
253 } else {
254 ASSERT(tail_ != NULL);
255 tail_->set_next(event);
256 tail_ = event;
257 }
258 event_available_->Signal();
259 }
260
261
GetEvent()262 RemoteDebuggerEvent* RemoteDebugger::GetEvent() {
263 i::ScopedLock lock(event_access_);
264 ASSERT(head_ != NULL);
265 RemoteDebuggerEvent* result = head_;
266 head_ = head_->next();
267 if (head_ == NULL) {
268 ASSERT(tail_ == result);
269 tail_ = NULL;
270 }
271 return result;
272 }
273
274
HandleMessageReceived(char * message)275 void RemoteDebugger::HandleMessageReceived(char* message) {
276 Locker lock;
277 HandleScope scope;
278
279 // Print the event details.
280 TryCatch try_catch;
281 Handle<Object> details =
282 Shell::DebugMessageDetails(Handle<String>::Cast(String::New(message)));
283 if (try_catch.HasCaught()) {
284 Shell::ReportException(&try_catch);
285 PrintPrompt();
286 return;
287 }
288 String::Utf8Value str(details->Get(String::New("text")));
289 if (str.length() == 0) {
290 // Empty string is used to signal not to process this event.
291 return;
292 }
293 if (*str != NULL) {
294 printf("%s\n", *str);
295 } else {
296 printf("???\n");
297 }
298
299 bool is_running = details->Get(String::New("running"))->ToBoolean()->Value();
300 PrintPrompt(is_running);
301 }
302
303
HandleKeyboardCommand(char * command)304 void RemoteDebugger::HandleKeyboardCommand(char* command) {
305 Locker lock;
306 HandleScope scope;
307
308 // Convert the debugger command to a JSON debugger request.
309 TryCatch try_catch;
310 Handle<Value> request =
311 Shell::DebugCommandToJSONRequest(String::New(command));
312 if (try_catch.HasCaught()) {
313 Shell::ReportException(&try_catch);
314 PrintPrompt();
315 return;
316 }
317
318 // If undefined is returned the command was handled internally and there is
319 // no JSON to send.
320 if (request->IsUndefined()) {
321 PrintPrompt();
322 return;
323 }
324
325 // Send the JSON debugger request.
326 i::DebuggerAgentUtil::SendMessage(conn_, Handle<String>::Cast(request));
327 }
328
329
Run()330 void ReceiverThread::Run() {
331 // Receive the connect message (with empty body).
332 i::SmartArrayPointer<char> message =
333 i::DebuggerAgentUtil::ReceiveMessage(remote_debugger_->conn());
334 ASSERT(*message == NULL);
335
336 while (true) {
337 // Receive a message.
338 i::SmartArrayPointer<char> message =
339 i::DebuggerAgentUtil::ReceiveMessage(remote_debugger_->conn());
340 if (*message == NULL) {
341 remote_debugger_->ConnectionClosed();
342 return;
343 }
344
345 // Pass the message to the main thread.
346 remote_debugger_->MessageReceived(message);
347 }
348 }
349
350
Run()351 void KeyboardThread::Run() {
352 static const int kBufferSize = 256;
353 while (true) {
354 // read keyboard input.
355 char command[kBufferSize];
356 char* str = fgets(command, kBufferSize, stdin);
357 if (str == NULL) {
358 break;
359 }
360
361 // Pass the keyboard command to the main thread.
362 remote_debugger_->KeyboardCommand(
363 i::SmartArrayPointer<char>(i::StrDup(command)));
364 }
365 }
366
367
368 } // namespace v8
369
370 #endif // ENABLE_DEBUGGER_SUPPORT
371