• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2008 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 
29 #include "d8.h"
30 #include "d8-debug.h"
31 #include "platform.h"
32 #include "debug-agent.h"
33 
34 
35 namespace v8 {
36 
37 static bool was_running = true;
38 
PrintPrompt(bool is_running)39 void PrintPrompt(bool is_running) {
40   const char* prompt = is_running? "> " : "dbg> ";
41   was_running = is_running;
42   printf("%s", prompt);
43   fflush(stdout);
44 }
45 
46 
PrintPrompt()47 void PrintPrompt() {
48   PrintPrompt(was_running);
49 }
50 
51 
HandleDebugEvent(DebugEvent event,Handle<Object> exec_state,Handle<Object> event_data,Handle<Value> data)52 void HandleDebugEvent(DebugEvent event,
53                       Handle<Object> exec_state,
54                       Handle<Object> event_data,
55                       Handle<Value> data) {
56   HandleScope scope;
57 
58   // Check for handled event.
59   if (event != Break && event != Exception && event != AfterCompile) {
60     return;
61   }
62 
63   TryCatch try_catch;
64 
65   // Get the toJSONProtocol function on the event and get the JSON format.
66   Local<String> to_json_fun_name = String::New("toJSONProtocol");
67   Local<Function> to_json_fun =
68       Function::Cast(*event_data->Get(to_json_fun_name));
69   Local<Value> event_json = to_json_fun->Call(event_data, 0, NULL);
70   if (try_catch.HasCaught()) {
71     Shell::ReportException(&try_catch);
72     return;
73   }
74 
75   // Print the event details.
76   Handle<Object> details =
77       Shell::DebugMessageDetails(Handle<String>::Cast(event_json));
78   if (try_catch.HasCaught()) {
79     Shell::ReportException(&try_catch);
80     return;
81   }
82   String::Utf8Value str(details->Get(String::New("text")));
83   if (str.length() == 0) {
84     // Empty string is used to signal not to process this event.
85     return;
86   }
87   printf("%s\n", *str);
88 
89   // Get the debug command processor.
90   Local<String> fun_name = String::New("debugCommandProcessor");
91   Local<Function> fun = Function::Cast(*exec_state->Get(fun_name));
92   Local<Object> cmd_processor =
93       Object::Cast(*fun->Call(exec_state, 0, NULL));
94   if (try_catch.HasCaught()) {
95     Shell::ReportException(&try_catch);
96     return;
97   }
98 
99   static const int kBufferSize = 256;
100   bool running = false;
101   while (!running) {
102     char command[kBufferSize];
103     PrintPrompt(running);
104     char* str = fgets(command, kBufferSize, stdin);
105     if (str == NULL) break;
106 
107     // Ignore empty commands.
108     if (strlen(command) == 0) continue;
109 
110     TryCatch try_catch;
111 
112     // Convert the debugger command to a JSON debugger request.
113     Handle<Value> request =
114         Shell::DebugCommandToJSONRequest(String::New(command));
115     if (try_catch.HasCaught()) {
116       Shell::ReportException(&try_catch);
117       continue;
118     }
119 
120     // If undefined is returned the command was handled internally and there is
121     // no JSON to send.
122     if (request->IsUndefined()) {
123       continue;
124     }
125 
126     Handle<String> fun_name;
127     Handle<Function> fun;
128     // All the functions used below take one argument.
129     static const int kArgc = 1;
130     Handle<Value> args[kArgc];
131 
132     // Invoke the JavaScript to convert the debug command line to a JSON
133     // request, invoke the JSON request and convert the JSON respose to a text
134     // representation.
135     fun_name = String::New("processDebugRequest");
136     fun = Handle<Function>::Cast(cmd_processor->Get(fun_name));
137     args[0] = request;
138     Handle<Value> response_val = fun->Call(cmd_processor, kArgc, args);
139     if (try_catch.HasCaught()) {
140       Shell::ReportException(&try_catch);
141       continue;
142     }
143     Handle<String> response = Handle<String>::Cast(response_val);
144 
145     // Convert the debugger response into text details and the running state.
146     Handle<Object> response_details = Shell::DebugMessageDetails(response);
147     if (try_catch.HasCaught()) {
148       Shell::ReportException(&try_catch);
149       continue;
150     }
151     String::Utf8Value text_str(response_details->Get(String::New("text")));
152     if (text_str.length() > 0) {
153       printf("%s\n", *text_str);
154     }
155     running =
156         response_details->Get(String::New("running"))->ToBoolean()->Value();
157   }
158 }
159 
160 
RunRemoteDebugger(int port)161 void RunRemoteDebugger(int port) {
162   RemoteDebugger debugger(i::Isolate::Current(), port);
163   debugger.Run();
164 }
165 
166 
Run()167 void RemoteDebugger::Run() {
168   bool ok;
169 
170   // Make sure that socket support is initialized.
171   ok = i::Socket::Setup();
172   if (!ok) {
173     printf("Unable to initialize socket support %d\n", i::Socket::LastError());
174     return;
175   }
176 
177   // Connect to the debugger agent.
178   conn_ = i::OS::CreateSocket();
179   static const int kPortStrSize = 6;
180   char port_str[kPortStrSize];
181   i::OS::SNPrintF(i::Vector<char>(port_str, kPortStrSize), "%d", port_);
182   ok = conn_->Connect("localhost", port_str);
183   if (!ok) {
184     printf("Unable to connect to debug agent %d\n", i::Socket::LastError());
185     return;
186   }
187 
188   // Start the receiver thread.
189   ReceiverThread receiver(isolate_, this);
190   receiver.Start();
191 
192   // Start the keyboard thread.
193   KeyboardThread keyboard(isolate_, this);
194   keyboard.Start();
195   PrintPrompt();
196 
197   // Process events received from debugged VM and from the keyboard.
198   bool terminate = false;
199   while (!terminate) {
200     event_available_->Wait();
201     RemoteDebuggerEvent* event = GetEvent();
202     switch (event->type()) {
203       case RemoteDebuggerEvent::kMessage:
204         HandleMessageReceived(event->data());
205         break;
206       case RemoteDebuggerEvent::kKeyboard:
207         HandleKeyboardCommand(event->data());
208         break;
209       case RemoteDebuggerEvent::kDisconnect:
210         terminate = true;
211         break;
212 
213       default:
214         UNREACHABLE();
215     }
216     delete event;
217   }
218 
219   // Wait for the receiver thread to end.
220   receiver.Join();
221 }
222 
223 
MessageReceived(i::SmartPointer<char> message)224 void RemoteDebugger::MessageReceived(i::SmartPointer<char> message) {
225   RemoteDebuggerEvent* event =
226       new RemoteDebuggerEvent(RemoteDebuggerEvent::kMessage, message);
227   AddEvent(event);
228 }
229 
230 
KeyboardCommand(i::SmartPointer<char> command)231 void RemoteDebugger::KeyboardCommand(i::SmartPointer<char> command) {
232   RemoteDebuggerEvent* event =
233       new RemoteDebuggerEvent(RemoteDebuggerEvent::kKeyboard, command);
234   AddEvent(event);
235 }
236 
237 
ConnectionClosed()238 void RemoteDebugger::ConnectionClosed() {
239   RemoteDebuggerEvent* event =
240       new RemoteDebuggerEvent(RemoteDebuggerEvent::kDisconnect,
241                               i::SmartPointer<char>());
242   AddEvent(event);
243 }
244 
245 
AddEvent(RemoteDebuggerEvent * event)246 void RemoteDebugger::AddEvent(RemoteDebuggerEvent* event) {
247   i::ScopedLock lock(event_access_);
248   if (head_ == NULL) {
249     ASSERT(tail_ == NULL);
250     head_ = event;
251     tail_ = event;
252   } else {
253     ASSERT(tail_ != NULL);
254     tail_->set_next(event);
255     tail_ = event;
256   }
257   event_available_->Signal();
258 }
259 
260 
GetEvent()261 RemoteDebuggerEvent* RemoteDebugger::GetEvent() {
262   i::ScopedLock lock(event_access_);
263   ASSERT(head_ != NULL);
264   RemoteDebuggerEvent* result = head_;
265   head_ = head_->next();
266   if (head_ == NULL) {
267     ASSERT(tail_ == result);
268     tail_ = NULL;
269   }
270   return result;
271 }
272 
273 
HandleMessageReceived(char * message)274 void RemoteDebugger::HandleMessageReceived(char* message) {
275   HandleScope scope;
276 
277   // Print the event details.
278   TryCatch try_catch;
279   Handle<Object> details =
280       Shell::DebugMessageDetails(Handle<String>::Cast(String::New(message)));
281   if (try_catch.HasCaught()) {
282     Shell::ReportException(&try_catch);
283     PrintPrompt();
284     return;
285   }
286   String::Utf8Value str(details->Get(String::New("text")));
287   if (str.length() == 0) {
288     // Empty string is used to signal not to process this event.
289     return;
290   }
291   if (*str != NULL) {
292     printf("%s\n", *str);
293   } else {
294     printf("???\n");
295   }
296 
297   bool is_running = details->Get(String::New("running"))->ToBoolean()->Value();
298   PrintPrompt(is_running);
299 }
300 
301 
HandleKeyboardCommand(char * command)302 void RemoteDebugger::HandleKeyboardCommand(char* command) {
303   HandleScope scope;
304 
305   // Convert the debugger command to a JSON debugger request.
306   TryCatch try_catch;
307   Handle<Value> request =
308       Shell::DebugCommandToJSONRequest(String::New(command));
309   if (try_catch.HasCaught()) {
310     v8::String::Utf8Value exception(try_catch.Exception());
311     const char* exception_string = Shell::ToCString(exception);
312     printf("%s\n", exception_string);
313     PrintPrompt();
314     return;
315   }
316 
317   // If undefined is returned the command was handled internally and there is
318   // no JSON to send.
319   if (request->IsUndefined()) {
320     PrintPrompt();
321     return;
322   }
323 
324   // Send the JSON debugger request.
325   i::DebuggerAgentUtil::SendMessage(conn_, Handle<String>::Cast(request));
326 }
327 
328 
Run()329 void ReceiverThread::Run() {
330   // Receive the connect message (with empty body).
331   i::SmartPointer<char> message =
332     i::DebuggerAgentUtil::ReceiveMessage(remote_debugger_->conn());
333   ASSERT(*message == NULL);
334 
335   while (true) {
336     // Receive a message.
337     i::SmartPointer<char> message =
338       i::DebuggerAgentUtil::ReceiveMessage(remote_debugger_->conn());
339     if (*message == NULL) {
340       remote_debugger_->ConnectionClosed();
341       return;
342     }
343 
344     // Pass the message to the main thread.
345     remote_debugger_->MessageReceived(message);
346   }
347 }
348 
349 
Run()350 void KeyboardThread::Run() {
351   static const int kBufferSize = 256;
352   while (true) {
353     // read keyboard input.
354     char command[kBufferSize];
355     char* str = fgets(command, kBufferSize, stdin);
356     if (str == NULL) {
357       break;
358     }
359 
360     // Pass the keyboard command to the main thread.
361     remote_debugger_->KeyboardCommand(
362         i::SmartPointer<char>(i::StrDup(command)));
363   }
364 }
365 
366 
367 }  // namespace v8
368