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 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(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 161 void RunRemoteDebugger(int port) { 162 RemoteDebugger debugger(i::Isolate::Current(), port); 163 debugger.Run(); 164 } 165 166 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 224 void RemoteDebugger::MessageReceived(i::SmartPointer<char> message) { 225 RemoteDebuggerEvent* event = 226 new RemoteDebuggerEvent(RemoteDebuggerEvent::kMessage, message); 227 AddEvent(event); 228 } 229 230 231 void RemoteDebugger::KeyboardCommand(i::SmartPointer<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::SmartPointer<char>()); 242 AddEvent(event); 243 } 244 245 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 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 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 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 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 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