Upstream version 5.34.92.0
[platform/framework/web/crosswalk.git] / src / v8 / src / d8-debug.cc
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 "debug-agent.h"
33 #include "platform/socket.h"
34
35
36 namespace v8 {
37
38 static bool was_running = true;
39
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
48 void PrintPrompt() {
49   PrintPrompt(was_running);
50 }
51
52
53 void HandleDebugEvent(const Debug::EventDetails& event_details) {
54   // TODO(svenpanne) There should be a way to retrieve this in the callback.
55   Isolate* isolate = Isolate::GetCurrent();
56   HandleScope scope(isolate);
57
58   DebugEvent event = event_details.GetEvent();
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 =
68       String::NewFromUtf8(isolate, "toJSONProtocol");
69   Handle<Object> event_data = event_details.GetEventData();
70   Local<Function> to_json_fun =
71       Local<Function>::Cast(event_data->Get(to_json_fun_name));
72   Local<Value> event_json = to_json_fun->Call(event_data, 0, NULL);
73   if (try_catch.HasCaught()) {
74     Shell::ReportException(isolate, &try_catch);
75     return;
76   }
77
78   // Print the event details.
79   Handle<Object> details =
80       Shell::DebugMessageDetails(isolate, Handle<String>::Cast(event_json));
81   if (try_catch.HasCaught()) {
82     Shell::ReportException(isolate, &try_catch);
83     return;
84   }
85   String::Utf8Value str(details->Get(String::NewFromUtf8(isolate, "text")));
86   if (str.length() == 0) {
87     // Empty string is used to signal not to process this event.
88     return;
89   }
90   printf("%s\n", *str);
91
92   // Get the debug command processor.
93   Local<String> fun_name =
94       String::NewFromUtf8(isolate, "debugCommandProcessor");
95   Handle<Object> exec_state = event_details.GetExecutionState();
96   Local<Function> fun = Local<Function>::Cast(exec_state->Get(fun_name));
97   Local<Object> cmd_processor =
98       Local<Object>::Cast(fun->Call(exec_state, 0, NULL));
99   if (try_catch.HasCaught()) {
100     Shell::ReportException(isolate, &try_catch);
101     return;
102   }
103
104   static const int kBufferSize = 256;
105   bool running = false;
106   while (!running) {
107     char command[kBufferSize];
108     PrintPrompt(running);
109     char* str = fgets(command, kBufferSize, stdin);
110     if (str == NULL) break;
111
112     // Ignore empty commands.
113     if (strlen(command) == 0) continue;
114
115     TryCatch try_catch;
116
117     // Convert the debugger command to a JSON debugger request.
118     Handle<Value> request = Shell::DebugCommandToJSONRequest(
119         isolate, String::NewFromUtf8(isolate, command));
120     if (try_catch.HasCaught()) {
121       Shell::ReportException(isolate, &try_catch);
122       continue;
123     }
124
125     // If undefined is returned the command was handled internally and there is
126     // no JSON to send.
127     if (request->IsUndefined()) {
128       continue;
129     }
130
131     Handle<String> fun_name;
132     Handle<Function> fun;
133     // All the functions used below take one argument.
134     static const int kArgc = 1;
135     Handle<Value> args[kArgc];
136
137     // Invoke the JavaScript to convert the debug command line to a JSON
138     // request, invoke the JSON request and convert the JSON respose to a text
139     // representation.
140     fun_name = String::NewFromUtf8(isolate, "processDebugRequest");
141     fun = Handle<Function>::Cast(cmd_processor->Get(fun_name));
142     args[0] = request;
143     Handle<Value> response_val = fun->Call(cmd_processor, kArgc, args);
144     if (try_catch.HasCaught()) {
145       Shell::ReportException(isolate, &try_catch);
146       continue;
147     }
148     Handle<String> response = Handle<String>::Cast(response_val);
149
150     // Convert the debugger response into text details and the running state.
151     Handle<Object> response_details =
152         Shell::DebugMessageDetails(isolate, response);
153     if (try_catch.HasCaught()) {
154       Shell::ReportException(isolate, &try_catch);
155       continue;
156     }
157     String::Utf8Value text_str(
158         response_details->Get(String::NewFromUtf8(isolate, "text")));
159     if (text_str.length() > 0) {
160       printf("%s\n", *text_str);
161     }
162     running = response_details->Get(String::NewFromUtf8(isolate, "running"))
163                   ->ToBoolean()
164                   ->Value();
165   }
166 }
167
168
169 void RunRemoteDebugger(Isolate* isolate, int port) {
170   RemoteDebugger debugger(isolate, port);
171   debugger.Run();
172 }
173
174
175 void RemoteDebugger::Run() {
176   bool ok;
177
178   // Connect to the debugger agent.
179   conn_ = new i::Socket;
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::GetLastError());
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
225 void RemoteDebugger::MessageReceived(i::SmartArrayPointer<char> message) {
226   RemoteDebuggerEvent* event =
227       new RemoteDebuggerEvent(RemoteDebuggerEvent::kMessage, message);
228   AddEvent(event);
229 }
230
231
232 void RemoteDebugger::KeyboardCommand(i::SmartArrayPointer<char> command) {
233   RemoteDebuggerEvent* event =
234       new RemoteDebuggerEvent(RemoteDebuggerEvent::kKeyboard, command);
235   AddEvent(event);
236 }
237
238
239 void RemoteDebugger::ConnectionClosed() {
240   RemoteDebuggerEvent* event =
241       new RemoteDebuggerEvent(RemoteDebuggerEvent::kDisconnect,
242                               i::SmartArrayPointer<char>());
243   AddEvent(event);
244 }
245
246
247 void RemoteDebugger::AddEvent(RemoteDebuggerEvent* event) {
248   i::LockGuard<i::Mutex> lock_guard(&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
262 RemoteDebuggerEvent* RemoteDebugger::GetEvent() {
263   i::LockGuard<i::Mutex> lock_guard(&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
275 void RemoteDebugger::HandleMessageReceived(char* message) {
276   Locker lock(isolate_);
277   HandleScope scope(isolate_);
278
279   // Print the event details.
280   TryCatch try_catch;
281   Handle<Object> details = Shell::DebugMessageDetails(
282       isolate_, Handle<String>::Cast(String::NewFromUtf8(isolate_, message)));
283   if (try_catch.HasCaught()) {
284     Shell::ReportException(isolate_, &try_catch);
285     PrintPrompt();
286     return;
287   }
288   String::Utf8Value str(details->Get(String::NewFromUtf8(isolate_, "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::NewFromUtf8(isolate_, "running"))
300                         ->ToBoolean()
301                         ->Value();
302   PrintPrompt(is_running);
303 }
304
305
306 void RemoteDebugger::HandleKeyboardCommand(char* command) {
307   Locker lock(isolate_);
308   HandleScope scope(isolate_);
309
310   // Convert the debugger command to a JSON debugger request.
311   TryCatch try_catch;
312   Handle<Value> request = Shell::DebugCommandToJSONRequest(
313       isolate_, String::NewFromUtf8(isolate_, command));
314   if (try_catch.HasCaught()) {
315     Shell::ReportException(isolate_, &try_catch);
316     PrintPrompt();
317     return;
318   }
319
320   // If undefined is returned the command was handled internally and there is
321   // no JSON to send.
322   if (request->IsUndefined()) {
323     PrintPrompt();
324     return;
325   }
326
327   // Send the JSON debugger request.
328   i::DebuggerAgentUtil::SendMessage(conn_, Handle<String>::Cast(request));
329 }
330
331
332 void ReceiverThread::Run() {
333   // Receive the connect message (with empty body).
334   i::SmartArrayPointer<char> message =
335       i::DebuggerAgentUtil::ReceiveMessage(remote_debugger_->conn());
336   ASSERT(message.get() == NULL);
337
338   while (true) {
339     // Receive a message.
340     i::SmartArrayPointer<char> message =
341         i::DebuggerAgentUtil::ReceiveMessage(remote_debugger_->conn());
342     if (message.get() == NULL) {
343       remote_debugger_->ConnectionClosed();
344       return;
345     }
346
347     // Pass the message to the main thread.
348     remote_debugger_->MessageReceived(message);
349   }
350 }
351
352
353 void KeyboardThread::Run() {
354   static const int kBufferSize = 256;
355   while (true) {
356     // read keyboard input.
357     char command[kBufferSize];
358     char* str = fgets(command, kBufferSize, stdin);
359     if (str == NULL) {
360       break;
361     }
362
363     // Pass the keyboard command to the main thread.
364     remote_debugger_->KeyboardCommand(
365         i::SmartArrayPointer<char>(i::StrDup(command)));
366   }
367 }
368
369
370 }  // namespace v8
371
372 #endif  // ENABLE_DEBUGGER_SUPPORT