v8toolkit  0.0.1
Utility library for embedding V8 Javascript engine in a c++ program
debugger.cpp
Go to the documentation of this file.
1 #include "debugger.h"
2 
3 
4 #include <regex>
5 #include <v8-debug.h>
6 
7 #include <websocketpp/endpoint.hpp>
8 
9 #include <nlohmann/json.hpp>
11 
12 namespace v8toolkit {
13 
14 using namespace ::v8toolkit::literals;
15 
17 
18 #if 0
19 //RequestMessage::RequestMessage(v8toolkit::DebugContext & context, nlohmann::json const & json) :
20 // RequestResponseMessage(context, json["id"].get<int>()),
21 // method_name(json["method"].get<std::string>())
22 //{}
23 //
24 //json RequestMessage::to_json() const {
25 // throw InvalidCallException("Request message types shouldn't every be re-serialized to json");
26 //}
27 //
28 //ResponseMessage::ResponseMessage(RequestMessage const & request_message) :
29 // RequestResponseMessage(request_message)
30 //{}
31 //
32 //
33 //MessageManager::MessageManager(v8toolkit::DebugContext & debug_context) :
34 // debug_context(debug_context)
35 //{
36 //// this->add_request_message_handler<Page_GetResourceTree>();
37 //
38 // std::cerr << fmt::format("mapped method names:") << std::endl;
39 // for(auto & pair : this->message_map) {
40 // std::cerr << fmt::format("{}", pair.first) << std::endl;
41 // }
42 //};
43 //
44 //
45 //
46 //
47 //
48 //
49 //void MessageManager::process_request_message(std::string const & message_payload) {
50 //
51 // json json = json::parse(message_payload);
52 // std::string method_name = json["method"];
53 // std::cerr << fmt::format("processing request message with method name: {}", method_name) << std::endl;
54 //
55 // // Try to find a custom handler for the message
56 // auto matching_message_pair = this->message_map.find(method_name);
57 //
58 // // if a custom matcher is found, use that
59 // if (matching_message_pair != this->message_map.end()) {
60 // std::cerr << fmt::format("found custom handler for message type") << std::endl;
61 // auto request_message = matching_message_pair->second(message_payload);
62 //
63 // // send the required response message
64 // this->debug_context.get_channel().send_message(nlohmann::json(*request_message->generate_response_message()).dump());
65 //
66 // // send any other messages which may be generated based on actions taken because of RequestMessage
67 // for (auto & debug_message : request_message->generate_additional_messages()) {
68 // nlohmann::json json = *debug_message;
69 // this->debug_context.get_channel().send_message(json.dump());
70 // }
71 // }
72 // // otherwise, if no custom behavior is specified for this message type, send it to v8-inspector to handle
73 // else {
74 // std::cerr << fmt::format("sending message type to v8 inspector") << std::endl;
75 // v8_inspector::StringView message_view((uint8_t const *)message_payload.c_str(), message_payload.length());
76 // this->debug_context.get_session().dispatchProtocolMessage(message_view);
77 // }
78 //}
79 #endif
80 
82 
84  isolate(debug_context.get_isolate()),
85  port(port),
86  debug_context(debug_context)
87 // ,
88 // message_manager(debug_context)
89 {
90  // disables all websocketpp debugging messages
91  //websocketpp::set_access_channels(websocketpp::log::alevel::none);
92  this->debug_server.get_alog().clear_channels(websocketpp::log::alevel::all);
93  this->debug_server.get_elog().clear_channels(websocketpp::log::elevel::all);
94 
95  // only allow one connection
96  this->debug_server.set_validate_handler(
97  bind(&WebsocketChannel::websocket_validation_handler, this, websocketpp::lib::placeholders::_1));
98 
99  // store the connection so events can be sent back to the client without the client sending something first
100  this->debug_server.set_open_handler(bind(&WebsocketChannel::on_open, this, websocketpp::lib::placeholders::_1));
101 
102  // note that the client disconnected
103  this->debug_server.set_close_handler(
104  bind(&WebsocketChannel::on_close, this, websocketpp::lib::placeholders::_1));
105 
106  // handle websocket messages from the client
107  this->debug_server.set_message_handler(
108  bind(&WebsocketChannel::on_message, this, websocketpp::lib::placeholders::_1,
109  websocketpp::lib::placeholders::_2));
110  this->debug_server.set_open_handler(
111  websocketpp::lib::bind(&WebsocketChannel::on_open, this, websocketpp::lib::placeholders::_1));
112  this->debug_server.set_http_handler(
113  websocketpp::lib::bind(&WebsocketChannel::on_http, this, websocketpp::lib::placeholders::_1));
114 
115  this->debug_server.init_asio();
116  this->debug_server.set_reuse_addr(true);
117  std::cerr << "Listening on port " << this->port << std::endl;
118  this->debug_server.listen(this->port);
119 
120  this->debug_server.start_accept();
121 
122 }
123 
124 void WebsocketChannel::on_http(websocketpp::connection_hdl hdl) {
125  WebsocketChannel::DebugServerType::connection_ptr con = this->debug_server.get_con_from_hdl(hdl);
126 
127  std::stringstream output;
128  output << "<!doctype html><html><body>You requested "
129  << con->get_resource()
130  << "</body></html>";
131 
132  // Set status to 200 rather than the default error code
133  con->set_status(websocketpp::http::status_code::ok);
134  // Set body text to the HTML created above
135  con->set_body(output.str());
136 }
137 
138 
139 void WebsocketChannel::on_message(websocketpp::connection_hdl hdl, WebsocketChannel::DebugServerType::message_ptr msg) {
140  std::smatch matches;
141  std::string message_payload = msg->get_payload();
142  std::cerr << "Got debugger message: " << message_payload << std::endl;
143 // this->message_manager.process_request_message(message_payload);
144  v8_inspector::StringView message_view((uint8_t const *)message_payload.c_str(), message_payload.length());
145 
146  GLOBAL_CONTEXT_SCOPED_RUN(this->debug_context.get_isolate(), this->debug_context.get_global_context());
147  this->debug_context.get_session().dispatchProtocolMessage(message_view);
148  this->message_received_time = std::chrono::high_resolution_clock::now();
149 }
150 
151 
152 void WebsocketChannel::on_open(websocketpp::connection_hdl hdl) {
153  std::cerr << fmt::format("on open") << std::endl;
154  assert(this->connections.size() == 0);
155  this->connections.insert(hdl);
156 }
157 
158 
159 
160 void WebsocketChannel::on_close(websocketpp::connection_hdl hdl) {
161  assert(this->connections.size() == 1);
162  this->connections.erase(hdl);
163  assert(this->connections.size() == 0);
164 
165  // not sure if this is right, but unpause when debugger disconnects
166  this->debug_context.reset_session();
167  this->debug_context.paused = false;
168 }
169 
170 
171 void WebsocketChannel::Send(const v8_inspector::StringView &string) {
172  std::cerr << fmt::format("sending message of length: {}", string.length()) << std::endl;
173  std::cerr << string.characters8() << std::endl;
174 
175  if (string.is8Bit()) {
176  this->send_message((void *)string.characters8(), string.length());
177  } else {
178  size_t length = string.length();
179  auto source= string.characters16();
180  auto buffer = new char[length];
181  for(int i = 0; i < length; i++) {
182  buffer[i] = source[i];
183  }
184 // std::string str;
185 // str.reserve(string.length() + 1);
186 // str.resize(string.length());
187 // char const * view_ptr = (char const *)string.characters8();
188 // for (int i = 0; view_ptr[i] != '\0'; i += 2) {
189 // ((char *)str.data())[i/2] = view_ptr[i];
190 // }
191 // std::wstring wide_string((wchar_t *) (string.characters16()), string.length());
192  this->send_message((void*)buffer, length);
193  delete [] buffer;
194  }
195 }
196 
197 bool WebsocketChannel::websocket_validation_handler(websocketpp::connection_hdl hdl) {
198  // only allow one connection
199  std::cerr << fmt::format("validation handler") << std::endl;
200  return this->connections.size() == 0;
201 }
202 //
203 //void WebsocketChannel::send_message(std::wstring const & message) {
204 // std::wcerr << message << std::endl;
205 //
206 // if (!this->connections.empty()) {
207 //
208 // std::cerr << fmt::format("Sending wide message: {}", str) << std::endl;
209 // this->debug_server.send(*this->connections.begin(), str, websocketpp::frame::opcode::TEXT);
210 // } else {
211 // std::cerr << fmt::format("Not sending wide message because no connections: {}", str) << std::endl;
212 // }
213 //}
214 
215 void WebsocketChannel::send_message(void * data, size_t length) {
216  if (!this->connections.empty()) {
217 // std::cerr << fmt::format("Sending message: {}", message) << std::endl;
218  this->debug_server.send(*this->connections.begin(), data, length, websocketpp::frame::opcode::TEXT);
219  } else {
220  std::cerr << fmt::format("Not sending message because no connections: {}") << std::endl;
221  }
222  this->message_sent_time = std::chrono::high_resolution_clock::now();
223 }
224 
225 
227  this->debug_server.run_one();
228 }
229 
231  this->debug_server.poll();
232 }
233 
235  this->debug_server.poll_one();
236 }
237 
238 void WebsocketChannel::poll_until_idle(float idle_time) {
239  do {
240  this->poll();
241  } while(this->seconds_since_message() > idle_time);
242 }
243 
244 
245 using namespace std::literals::chrono_literals;
246 
247 void WebsocketChannel::wait_for_connection(std::chrono::duration<float> sleep_between_polls) {
248  while(this->connections.empty()) {
249  this->poll_one();
250  if (this->connections.empty()) {
251  // don't suck up 100% cpu while waiting for connection
252  std::this_thread::sleep_for(sleep_between_polls);
253  }
254  }
255 }
256 
258 }
259 
260 DebugContext::DebugContext(std::shared_ptr<v8toolkit::Isolate> isolate_helper, v8::Local<v8::Context> context, short port) :
261  v8toolkit::Context(isolate_helper, context),
262  port(port),
263  channel(std::make_unique<WebsocketChannel>(*this, port)),
264  inspector(v8_inspector::V8Inspector::create(this->get_isolate(), this)),
265  session(inspector->connect(1, channel.get(), v8_inspector::StringView()))
266 
267 {
268 
269 
270  this->reset_session();
271  this->get_context()->SetAlignedPointerInEmbedderData(kInspectorClientIndex, this);
272 
273  inspector->contextCreated(v8_inspector::V8ContextInfo(*this, kContextGroupId, v8_inspector::StringView()));
274 
275  v8::Debug::SetLiveEditEnabled(this->isolate, true);
276 
277 }
278 
279 #if 0
280 void to_json(nlohmann::json &j, const ResponseMessage & response_message) {
281  j = {
282  {"id", response_message.message_id},
283  {"result", response_message.to_json()}
284  };
285 }
286 
287 
288 void to_json(nlohmann::json &j, const InformationalMessage & informational_message) {
289  j = informational_message.to_json();
290 }
291 #endif
292 
293 
295  std::chrono::duration<float> duration = std::chrono::high_resolution_clock::now() - this->message_received_time;
296  return duration.count();
297 }
298 
300  std::chrono::duration<float> duration = std::chrono::high_resolution_clock::now() - this->message_sent_time;
301  return duration.count();
302 
303 }
304 
305 
307  auto sent_time = this->seconds_since_message_sent();
308  auto received_time = this->seconds_since_message_received();
309  return sent_time < received_time ? sent_time : received_time;
310 }
311 
312 
313 } // end v8toolkit namespace
float seconds_since_message_received()
Definition: debugger.cpp:294
::std::string string
Definition: gtest-port.h:1097
STL namespace.
void poll_until_idle(float idle_time=1.0f)
Definition: debugger.cpp:238
void wait_for_connection(std::chrono::duration< float > sleep_between_polls=std::chrono::duration< float >(0.1f))
Definition: debugger.cpp:247
void send_message(void *data, size_t length)
Definition: debugger.cpp:215
v8::Isolate *const isolate
shortcut to the v8::isolate object instead of always going through the Isolate
Definition: javascript.h:69
v8::Global< v8::Context > const & get_global_context() const
Definition: javascript.cpp:160
v8_inspector::V8InspectorSession & get_session()
Definition: debugger.h:311
func::function< R(Args...)> bind(CLASS &object, R(METHOD_CLASS::*method)(Args...))
Definition: v8toolkit.h:855
nlohmann::json json
Definition: debugger.cpp:16
WebsocketChannel(v8toolkit::DebugContext &debug_context, short port)
Definition: debugger.cpp:83
#define GLOBAL_CONTEXT_SCOPED_RUN(isolate, global_context)
Definition: v8toolkit.h:85
DebugContext(std::shared_ptr< v8toolkit::Isolate > isolate_helper, v8::Local< v8::Context > context, short port)
Definition: debugger.cpp:260
v8::Local< v8::Context > get_context() const
Definition: javascript.cpp:24
v8::Isolate * get_isolate() const
Definition: javascript.cpp:29
static const int kContextGroupId
Definition: debugger.h:307