Home | History | Annotate | Download | only in src
      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 
     38 void HandleDebugEvent(DebugEvent event,
     39                       Handle<Object> exec_state,
     40                       Handle<Object> event_data,
     41                       Handle<Value> data) {
     42   HandleScope scope;
     43 
     44   // Check for handled event.
     45   if (event != Break && event != Exception && event != AfterCompile) {
     46     return;
     47   }
     48 
     49   TryCatch try_catch;
     50 
     51   // Get the toJSONProtocol function on the event and get the JSON format.
     52   Local<String> to_json_fun_name = String::New("toJSONProtocol");
     53   Local<Function> to_json_fun =
     54       Function::Cast(*event_data->Get(to_json_fun_name));
     55   Local<Value> event_json = to_json_fun->Call(event_data, 0, NULL);
     56   if (try_catch.HasCaught()) {
     57     Shell::ReportException(&try_catch);
     58     return;
     59   }
     60 
     61   // Print the event details.
     62   Handle<Object> details =
     63       Shell::DebugMessageDetails(Handle<String>::Cast(event_json));
     64   if (try_catch.HasCaught()) {
     65     Shell::ReportException(&try_catch);
     66     return;
     67   }
     68   String::Utf8Value str(details->Get(String::New("text")));
     69   if (str.length() == 0) {
     70     // Empty string is used to signal not to process this event.
     71     return;
     72   }
     73   printf("%s\n", *str);
     74 
     75   // Get the debug command processor.
     76   Local<String> fun_name = String::New("debugCommandProcessor");
     77   Local<Function> fun = Function::Cast(*exec_state->Get(fun_name));
     78   Local<Object> cmd_processor =
     79       Object::Cast(*fun->Call(exec_state, 0, NULL));
     80   if (try_catch.HasCaught()) {
     81     Shell::ReportException(&try_catch);
     82     return;
     83   }
     84 
     85   static const int kBufferSize = 256;
     86   bool running = false;
     87   while (!running) {
     88     char command[kBufferSize];
     89     printf("dbg> ");
     90     char* str = fgets(command, kBufferSize, stdin);
     91     if (str == NULL) break;
     92 
     93     // Ignore empty commands.
     94     if (strlen(command) == 0) continue;
     95 
     96     TryCatch try_catch;
     97 
     98     // Convert the debugger command to a JSON debugger request.
     99     Handle<Value> request =
    100         Shell::DebugCommandToJSONRequest(String::New(command));
    101     if (try_catch.HasCaught()) {
    102       Shell::ReportException(&try_catch);
    103       continue;
    104     }
    105 
    106     // If undefined is returned the command was handled internally and there is
    107     // no JSON to send.
    108     if (request->IsUndefined()) {
    109       continue;
    110     }
    111 
    112     Handle<String> fun_name;
    113     Handle<Function> fun;
    114     // All the functions used below take one argument.
    115     static const int kArgc = 1;
    116     Handle<Value> args[kArgc];
    117 
    118     // Invoke the JavaScript to convert the debug command line to a JSON
    119     // request, invoke the JSON request and convert the JSON respose to a text
    120     // representation.
    121     fun_name = String::New("processDebugRequest");
    122     fun = Handle<Function>::Cast(cmd_processor->Get(fun_name));
    123     args[0] = request;
    124     Handle<Value> response_val = fun->Call(cmd_processor, kArgc, args);
    125     if (try_catch.HasCaught()) {
    126       Shell::ReportException(&try_catch);
    127       continue;
    128     }
    129     Handle<String> response = Handle<String>::Cast(response_val);
    130 
    131     // Convert the debugger response into text details and the running state.
    132     Handle<Object> response_details = Shell::DebugMessageDetails(response);
    133     if (try_catch.HasCaught()) {
    134       Shell::ReportException(&try_catch);
    135       continue;
    136     }
    137     String::Utf8Value text_str(response_details->Get(String::New("text")));
    138     if (text_str.length() > 0) {
    139       printf("%s\n", *text_str);
    140     }
    141     running =
    142         response_details->Get(String::New("running"))->ToBoolean()->Value();
    143   }
    144 }
    145 
    146 
    147 void RunRemoteDebugger(int port) {
    148   RemoteDebugger debugger(port);
    149   debugger.Run();
    150 }
    151 
    152 
    153 void RemoteDebugger::Run() {
    154   bool ok;
    155 
    156   // Make sure that socket support is initialized.
    157   ok = i::Socket::Setup();
    158   if (!ok) {
    159     printf("Unable to initialize socket support %d\n", i::Socket::LastError());
    160     return;
    161   }
    162 
    163   // Connect to the debugger agent.
    164   conn_ = i::OS::CreateSocket();
    165   static const int kPortStrSize = 6;
    166   char port_str[kPortStrSize];
    167   i::OS::SNPrintF(i::Vector<char>(port_str, kPortStrSize), "%d", port_);
    168   ok = conn_->Connect("localhost", port_str);
    169   if (!ok) {
    170     printf("Unable to connect to debug agent %d\n", i::Socket::LastError());
    171     return;
    172   }
    173 
    174   // Start the receiver thread.
    175   ReceiverThread receiver(this);
    176   receiver.Start();
    177 
    178   // Start the keyboard thread.
    179   KeyboardThread keyboard(this);
    180   keyboard.Start();
    181 
    182   // Process events received from debugged VM and from the keyboard.
    183   bool terminate = false;
    184   while (!terminate) {
    185     event_available_->Wait();
    186     RemoteDebuggerEvent* event = GetEvent();
    187     switch (event->type()) {
    188       case RemoteDebuggerEvent::kMessage:
    189         HandleMessageReceived(event->data());
    190         break;
    191       case RemoteDebuggerEvent::kKeyboard:
    192         HandleKeyboardCommand(event->data());
    193         break;
    194       case RemoteDebuggerEvent::kDisconnect:
    195         terminate = true;
    196         break;
    197 
    198       default:
    199         UNREACHABLE();
    200     }
    201     delete event;
    202   }
    203 
    204   // Wait for the receiver thread to end.
    205   receiver.Join();
    206 }
    207 
    208 
    209 void RemoteDebugger::MessageReceived(i::SmartPointer<char> message) {
    210   RemoteDebuggerEvent* event =
    211       new RemoteDebuggerEvent(RemoteDebuggerEvent::kMessage, message);
    212   AddEvent(event);
    213 }
    214 
    215 
    216 void RemoteDebugger::KeyboardCommand(i::SmartPointer<char> command) {
    217   RemoteDebuggerEvent* event =
    218       new RemoteDebuggerEvent(RemoteDebuggerEvent::kKeyboard, command);
    219   AddEvent(event);
    220 }
    221 
    222 
    223 void RemoteDebugger::ConnectionClosed() {
    224   RemoteDebuggerEvent* event =
    225       new RemoteDebuggerEvent(RemoteDebuggerEvent::kDisconnect,
    226                               i::SmartPointer<char>());
    227   AddEvent(event);
    228 }
    229 
    230 
    231 void RemoteDebugger::AddEvent(RemoteDebuggerEvent* event) {
    232   i::ScopedLock lock(event_access_);
    233   if (head_ == NULL) {
    234     ASSERT(tail_ == NULL);
    235     head_ = event;
    236     tail_ = event;
    237   } else {
    238     ASSERT(tail_ != NULL);
    239     tail_->set_next(event);
    240     tail_ = event;
    241   }
    242   event_available_->Signal();
    243 }
    244 
    245 
    246 RemoteDebuggerEvent* RemoteDebugger::GetEvent() {
    247   i::ScopedLock lock(event_access_);
    248   ASSERT(head_ != NULL);
    249   RemoteDebuggerEvent* result = head_;
    250   head_ = head_->next();
    251   if (head_ == NULL) {
    252     ASSERT(tail_ == result);
    253     tail_ = NULL;
    254   }
    255   return result;
    256 }
    257 
    258 
    259 void RemoteDebugger::HandleMessageReceived(char* message) {
    260   HandleScope scope;
    261 
    262   // Print the event details.
    263   TryCatch try_catch;
    264   Handle<Object> details =
    265       Shell::DebugMessageDetails(Handle<String>::Cast(String::New(message)));
    266   if (try_catch.HasCaught()) {
    267       Shell::ReportException(&try_catch);
    268     return;
    269   }
    270   String::Utf8Value str(details->Get(String::New("text")));
    271   if (str.length() == 0) {
    272     // Empty string is used to signal not to process this event.
    273     return;
    274   }
    275   if (*str != NULL) {
    276     printf("%s\n", *str);
    277   } else {
    278     printf("???\n");
    279   }
    280   printf("dbg> ");
    281 }
    282 
    283 
    284 void RemoteDebugger::HandleKeyboardCommand(char* command) {
    285   HandleScope scope;
    286 
    287   // Convert the debugger command to a JSON debugger request.
    288   TryCatch try_catch;
    289   Handle<Value> request =
    290       Shell::DebugCommandToJSONRequest(String::New(command));
    291   if (try_catch.HasCaught()) {
    292     Shell::ReportException(&try_catch);
    293     return;
    294   }
    295 
    296   // If undefined is returned the command was handled internally and there is
    297   // no JSON to send.
    298   if (request->IsUndefined()) {
    299     return;
    300   }
    301 
    302   // Send the JSON debugger request.
    303   i::DebuggerAgentUtil::SendMessage(conn_, Handle<String>::Cast(request));
    304 }
    305 
    306 
    307 void ReceiverThread::Run() {
    308   // Receive the connect message (with empty body).
    309   i::SmartPointer<char> message =
    310     i::DebuggerAgentUtil::ReceiveMessage(remote_debugger_->conn());
    311   ASSERT(*message == NULL);
    312 
    313   while (true) {
    314     // Receive a message.
    315     i::SmartPointer<char> message =
    316       i::DebuggerAgentUtil::ReceiveMessage(remote_debugger_->conn());
    317     if (*message == NULL) {
    318       remote_debugger_->ConnectionClosed();
    319       return;
    320     }
    321 
    322     // Pass the message to the main thread.
    323     remote_debugger_->MessageReceived(message);
    324   }
    325 }
    326 
    327 
    328 void KeyboardThread::Run() {
    329   static const int kBufferSize = 256;
    330   while (true) {
    331     // read keyboard input.
    332     char command[kBufferSize];
    333     char* str = fgets(command, kBufferSize, stdin);
    334     if (str == NULL) {
    335       break;
    336     }
    337 
    338     // Pass the keyboard command to the main thread.
    339     remote_debugger_->KeyboardCommand(
    340         i::SmartPointer<char>(i::StrDup(command)));
    341   }
    342 }
    343 
    344 
    345 }  // namespace v8
    346