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(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 162 void RunRemoteDebugger(int port) { 163 RemoteDebugger debugger(port); 164 debugger.Run(); 165 } 166 167 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 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::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 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 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 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 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 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