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