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