v8-inspector-session-impl.cc 20.2 KB
Newer Older
1 2 3 4
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5
#include "src/inspector/v8-inspector-session-impl.h"
6

7
#include "../../third_party/inspector_protocol/crdtp/cbor.h"
8
#include "../../third_party/inspector_protocol/crdtp/dispatch.h"
9
#include "../../third_party/inspector_protocol/crdtp/json.h"
10 11
#include "src/base/logging.h"
#include "src/base/macros.h"
12 13
#include "src/inspector/injected-script.h"
#include "src/inspector/inspected-context.h"
14
#include "src/inspector/protocol/Protocol.h"
15 16 17 18 19 20 21 22 23 24 25
#include "src/inspector/remote-object-id.h"
#include "src/inspector/search-util.h"
#include "src/inspector/string-util.h"
#include "src/inspector/v8-console-agent-impl.h"
#include "src/inspector/v8-debugger-agent-impl.h"
#include "src/inspector/v8-debugger.h"
#include "src/inspector/v8-heap-profiler-agent-impl.h"
#include "src/inspector/v8-inspector-impl.h"
#include "src/inspector/v8-profiler-agent-impl.h"
#include "src/inspector/v8-runtime-agent-impl.h"
#include "src/inspector/v8-schema-agent-impl.h"
26 27

namespace v8_inspector {
28
namespace {
29 30
using v8_crdtp::span;
using v8_crdtp::SpanFrom;
31
using v8_crdtp::Status;
32
using v8_crdtp::cbor::CheckCBORMessage;
33 34
using v8_crdtp::json::ConvertCBORToJSON;
using v8_crdtp::json::ConvertJSONToCBOR;
35

36
bool IsCBORMessage(StringView msg) {
37 38 39 40
  if (!msg.is8Bit() || msg.length() < 3) return false;
  const uint8_t* bytes = msg.characters8();
  return bytes[0] == 0xd8 &&
         (bytes[1] == 0x5a || (bytes[1] == 0x18 && bytes[2] == 0x5a));
41 42
}

43
Status ConvertToCBOR(StringView state, std::vector<uint8_t>* cbor) {
44 45 46 47 48 49 50
  return state.is8Bit()
             ? ConvertJSONToCBOR(
                   span<uint8_t>(state.characters8(), state.length()), cbor)
             : ConvertJSONToCBOR(
                   span<uint16_t>(state.characters16(), state.length()), cbor);
}

51
std::unique_ptr<protocol::DictionaryValue> ParseState(StringView state) {
52 53 54 55 56 57 58 59 60
  std::vector<uint8_t> converted;
  span<uint8_t> cbor;
  if (IsCBORMessage(state))
    cbor = span<uint8_t>(state.characters8(), state.length());
  else if (ConvertToCBOR(state, &converted).ok())
    cbor = SpanFrom(converted);
  if (!cbor.empty()) {
    std::unique_ptr<protocol::Value> value =
        protocol::Value::parseBinary(cbor.data(), cbor.size());
61 62 63
    std::unique_ptr<protocol::DictionaryValue> dictionaryValue =
        protocol::DictionaryValue::cast(std::move(value));
    if (dictionaryValue) return dictionaryValue;
64 65 66
  }
  return protocol::DictionaryValue::create();
}
67
}  // namespace
68 69

// static
70
bool V8InspectorSession::canDispatchMethod(StringView method) {
71 72 73 74 75 76 77 78 79 80 81 82 83 84
  return stringViewStartsWith(method,
                              protocol::Runtime::Metainfo::commandPrefix) ||
         stringViewStartsWith(method,
                              protocol::Debugger::Metainfo::commandPrefix) ||
         stringViewStartsWith(method,
                              protocol::Profiler::Metainfo::commandPrefix) ||
         stringViewStartsWith(
             method, protocol::HeapProfiler::Metainfo::commandPrefix) ||
         stringViewStartsWith(method,
                              protocol::Console::Metainfo::commandPrefix) ||
         stringViewStartsWith(method,
                              protocol::Schema::Metainfo::commandPrefix);
}

85 86 87 88 89
// static
int V8ContextInfo::executionContextId(v8::Local<v8::Context> context) {
  return InspectedContext::contextId(context);
}

90
std::unique_ptr<V8InspectorSessionImpl> V8InspectorSessionImpl::create(
91
    V8InspectorImpl* inspector, int contextGroupId, int sessionId,
92 93
    V8Inspector::Channel* channel, StringView state,
    V8Inspector::ClientTrustLevel clientTrustLevel) {
94
  return std::unique_ptr<V8InspectorSessionImpl>(new V8InspectorSessionImpl(
95
      inspector, contextGroupId, sessionId, channel, state, clientTrustLevel));
96 97
}

98 99 100 101
V8InspectorSessionImpl::V8InspectorSessionImpl(
    V8InspectorImpl* inspector, int contextGroupId, int sessionId,
    V8Inspector::Channel* channel, StringView savedState,
    V8Inspector::ClientTrustLevel clientTrustLevel)
102
    : m_contextGroupId(contextGroupId),
103
      m_sessionId(sessionId),
104 105 106 107
      m_inspector(inspector),
      m_channel(channel),
      m_customObjectFormatterEnabled(false),
      m_dispatcher(this),
108
      m_state(ParseState(savedState)),
109 110 111 112 113
      m_runtimeAgent(nullptr),
      m_debuggerAgent(nullptr),
      m_heapProfilerAgent(nullptr),
      m_profilerAgent(nullptr),
      m_consoleAgent(nullptr),
114 115
      m_schemaAgent(nullptr),
      m_clientTrustLevel(clientTrustLevel) {
116 117
  m_state->getBoolean("use_binary_protocol", &use_binary_protocol_);

118
  m_runtimeAgent.reset(new V8RuntimeAgentImpl(
119 120 121
      this, this, agentState(protocol::Runtime::Metainfo::domainName)));
  protocol::Runtime::Dispatcher::wire(&m_dispatcher, m_runtimeAgent.get());

122
  m_debuggerAgent.reset(new V8DebuggerAgentImpl(
123 124 125
      this, this, agentState(protocol::Debugger::Metainfo::domainName)));
  protocol::Debugger::Dispatcher::wire(&m_dispatcher, m_debuggerAgent.get());

126
  m_consoleAgent.reset(new V8ConsoleAgentImpl(
127 128 129
      this, this, agentState(protocol::Console::Metainfo::domainName)));
  protocol::Console::Dispatcher::wire(&m_dispatcher, m_consoleAgent.get());

130 131 132 133
  if (m_clientTrustLevel == V8Inspector::kFullyTrusted) {
    m_profilerAgent.reset(new V8ProfilerAgentImpl(
        this, this, agentState(protocol::Profiler::Metainfo::domainName)));
    protocol::Profiler::Dispatcher::wire(&m_dispatcher, m_profilerAgent.get());
134

135 136 137 138 139 140 141 142 143
    m_heapProfilerAgent.reset(new V8HeapProfilerAgentImpl(
        this, this, agentState(protocol::HeapProfiler::Metainfo::domainName)));
    protocol::HeapProfiler::Dispatcher::wire(&m_dispatcher,
                                             m_heapProfilerAgent.get());

    m_schemaAgent.reset(new V8SchemaAgentImpl(
        this, this, agentState(protocol::Schema::Metainfo::domainName)));
    protocol::Schema::Dispatcher::wire(&m_dispatcher, m_schemaAgent.get());
  }
144 145 146
  if (savedState.length()) {
    m_runtimeAgent->restore();
    m_debuggerAgent->restore();
147 148
    if (m_heapProfilerAgent) m_heapProfilerAgent->restore();
    if (m_profilerAgent) m_profilerAgent->restore();
149 150 151 152 153
    m_consoleAgent->restore();
  }
}

V8InspectorSessionImpl::~V8InspectorSessionImpl() {
154
  v8::Isolate::Scope scope(m_inspector->isolate());
155
  discardInjectedScripts();
156
  m_consoleAgent->disable();
157 158
  if (m_profilerAgent) m_profilerAgent->disable();
  if (m_heapProfilerAgent) m_heapProfilerAgent->disable();
159
  m_debuggerAgent->disable();
160
  m_runtimeAgent->disable();
161 162 163
  m_inspector->disconnect(this);
}

164 165 166 167 168 169 170 171 172 173 174 175 176 177
std::unique_ptr<V8InspectorSession::CommandLineAPIScope>
V8InspectorSessionImpl::initializeCommandLineAPIScope(int executionContextId) {
  auto scope =
      std::make_unique<InjectedScript::ContextScope>(this, executionContextId);
  auto result = scope->initialize();
  if (!result.IsSuccess()) {
    return nullptr;
  }

  scope->installCommandLineAPI();

  return scope;
}

178 179 180 181 182 183 184 185 186 187 188 189
protocol::DictionaryValue* V8InspectorSessionImpl::agentState(
    const String16& name) {
  protocol::DictionaryValue* state = m_state->getObject(name);
  if (!state) {
    std::unique_ptr<protocol::DictionaryValue> newState =
        protocol::DictionaryValue::create();
    state = newState.get();
    m_state->setObject(name, std::move(newState));
  }
  return state;
}

190 191
std::unique_ptr<StringBuffer> V8InspectorSessionImpl::serializeForFrontend(
    std::unique_ptr<protocol::Serializable> message) {
192
  std::vector<uint8_t> cbor = message->Serialize();
193
  DCHECK(CheckCBORMessage(SpanFrom(cbor)).ok());
194
  if (use_binary_protocol_) return StringBufferFrom(std::move(cbor));
195
  std::vector<uint8_t> json;
196
  Status status = ConvertCBORToJSON(SpanFrom(cbor), &json);
197 198
  DCHECK(status.ok());
  USE(status);
199 200 201 202 203 204
  // TODO(johannes): It should be OK to make a StringBuffer from |json|
  // directly, since it's 7 Bit US-ASCII with anything else escaped.
  // However it appears that the Node.js tests (or perhaps even production)
  // assume that the StringBuffer is 16 Bit. It probably accesses
  // characters16() somehwere without checking is8Bit. Until it's fixed
  // we take a detour via String16 which makes the StringBuffer 16 bit.
205
  String16 string16(reinterpret_cast<const char*>(json.data()), json.size());
206
  return StringBufferFrom(std::move(string16));
207
}
208

209
void V8InspectorSessionImpl::SendProtocolResponse(
210
    int callId, std::unique_ptr<protocol::Serializable> message) {
211
  m_channel->sendResponse(callId, serializeForFrontend(std::move(message)));
212 213
}

214
void V8InspectorSessionImpl::SendProtocolNotification(
215
    std::unique_ptr<protocol::Serializable> message) {
216
  m_channel->sendNotification(serializeForFrontend(std::move(message)));
217 218
}

219 220
void V8InspectorSessionImpl::FallThrough(int callId,
                                         const v8_crdtp::span<uint8_t> method,
221
                                         v8_crdtp::span<uint8_t> message) {
222 223 224 225
  // There's no other layer to handle the command.
  UNREACHABLE();
}

226
void V8InspectorSessionImpl::FlushProtocolNotifications() {
227 228 229 230 231 232 233 234 235 236 237
  m_channel->flushProtocolNotifications();
}

void V8InspectorSessionImpl::reset() {
  m_debuggerAgent->reset();
  m_runtimeAgent->reset();
  discardInjectedScripts();
}

void V8InspectorSessionImpl::discardInjectedScripts() {
  m_inspectedObjects.clear();
238 239 240 241 242
  int sessionId = m_sessionId;
  m_inspector->forEachContext(m_contextGroupId,
                              [&sessionId](InspectedContext* context) {
                                context->discardInjectedScript(sessionId);
                              });
243 244
}

245 246 247
Response V8InspectorSessionImpl::findInjectedScript(
    int contextId, InjectedScript*& injectedScript) {
  injectedScript = nullptr;
248 249
  InspectedContext* context =
      m_inspector->getContext(m_contextGroupId, contextId);
250 251
  if (!context)
    return Response::ServerError("Cannot find context with specified id");
252 253
  injectedScript = context->getInjectedScript(m_sessionId);
  if (!injectedScript) {
254
    injectedScript = context->createInjectedScript(m_sessionId);
255
    if (m_customObjectFormatterEnabled)
256
      injectedScript->setCustomObjectFormatterEnabled(true);
257
  }
258
  return Response::Success();
259 260
}

261 262
Response V8InspectorSessionImpl::findInjectedScript(
    RemoteObjectIdBase* objectId, InjectedScript*& injectedScript) {
263 264
  if (objectId->isolateId() != m_inspector->isolateId())
    return Response::ServerError("Cannot find context with specified id");
265
  return findInjectedScript(objectId->contextId(), injectedScript);
266 267
}

268
void V8InspectorSessionImpl::releaseObjectGroup(StringView objectGroup) {
269 270 271 272
  releaseObjectGroup(toString16(objectGroup));
}

void V8InspectorSessionImpl::releaseObjectGroup(const String16& objectGroup) {
273
  int sessionId = m_sessionId;
274
  m_inspector->forEachContext(
275 276
      m_contextGroupId, [&objectGroup, &sessionId](InspectedContext* context) {
        InjectedScript* injectedScript = context->getInjectedScript(sessionId);
277 278
        if (injectedScript) injectedScript->releaseObjectGroup(objectGroup);
      });
279 280 281
}

bool V8InspectorSessionImpl::unwrapObject(
282
    std::unique_ptr<StringBuffer>* error, StringView objectId,
283 284 285
    v8::Local<v8::Value>* object, v8::Local<v8::Context>* context,
    std::unique_ptr<StringBuffer>* objectGroup) {
  String16 objectGroupString;
286 287
  Response response = unwrapObject(toString16(objectId), object, context,
                                   objectGroup ? &objectGroupString : nullptr);
288 289 290 291 292
  if (response.IsError()) {
    if (error) {
      const std::string& msg = response.Message();
      *error = StringBufferFrom(String16::fromUTF8(msg.data(), msg.size()));
    }
293 294
    return false;
  }
295 296
  if (objectGroup)
    *objectGroup = StringBufferFrom(std::move(objectGroupString));
297
  return true;
298 299
}

300 301 302 303 304 305
Response V8InspectorSessionImpl::unwrapObject(const String16& objectId,
                                              v8::Local<v8::Value>* object,
                                              v8::Local<v8::Context>* context,
                                              String16* objectGroup) {
  std::unique_ptr<RemoteObjectId> remoteId;
  Response response = RemoteObjectId::parse(objectId, &remoteId);
306
  if (!response.IsSuccess()) return response;
307 308
  InjectedScript* injectedScript = nullptr;
  response = findInjectedScript(remoteId.get(), injectedScript);
309
  if (!response.IsSuccess()) return response;
310
  response = injectedScript->findObject(*remoteId, object);
311
  if (!response.IsSuccess()) return response;
312 313
  *context = injectedScript->context()->context();
  if (objectGroup) *objectGroup = injectedScript->objectGroupName(*remoteId);
314
  return Response::Success();
315 316
}

317 318 319
std::unique_ptr<protocol::Runtime::API::RemoteObject>
V8InspectorSessionImpl::wrapObject(v8::Local<v8::Context> context,
                                   v8::Local<v8::Value> value,
320
                                   StringView groupName, bool generatePreview) {
321
  return wrapObject(context, value, toString16(groupName), generatePreview);
322 323 324 325 326 327 328
}

std::unique_ptr<protocol::Runtime::RemoteObject>
V8InspectorSessionImpl::wrapObject(v8::Local<v8::Context> context,
                                   v8::Local<v8::Value> value,
                                   const String16& groupName,
                                   bool generatePreview) {
329
  InjectedScript* injectedScript = nullptr;
330
  findInjectedScript(InspectedContext::contextId(context), injectedScript);
331
  if (!injectedScript) return nullptr;
332
  std::unique_ptr<protocol::Runtime::RemoteObject> result;
333 334 335
  injectedScript->wrapObject(
      value, groupName,
      generatePreview ? WrapMode::kWithPreview : WrapMode::kNoPreview, &result);
336
  return result;
337 338 339 340
}

std::unique_ptr<protocol::Runtime::RemoteObject>
V8InspectorSessionImpl::wrapTable(v8::Local<v8::Context> context,
341 342
                                  v8::Local<v8::Object> table,
                                  v8::MaybeLocal<v8::Array> columns) {
343
  InjectedScript* injectedScript = nullptr;
344
  findInjectedScript(InspectedContext::contextId(context), injectedScript);
345 346 347 348 349 350
  if (!injectedScript) return nullptr;
  return injectedScript->wrapTable(table, columns);
}

void V8InspectorSessionImpl::setCustomObjectFormatterEnabled(bool enabled) {
  m_customObjectFormatterEnabled = enabled;
351
  int sessionId = m_sessionId;
352
  m_inspector->forEachContext(
353 354
      m_contextGroupId, [&enabled, &sessionId](InspectedContext* context) {
        InjectedScript* injectedScript = context->getInjectedScript(sessionId);
355 356 357
        if (injectedScript)
          injectedScript->setCustomObjectFormatterEnabled(enabled);
      });
358 359 360
}

void V8InspectorSessionImpl::reportAllContexts(V8RuntimeAgentImpl* agent) {
361 362 363 364
  m_inspector->forEachContext(m_contextGroupId,
                              [&agent](InspectedContext* context) {
                                agent->reportExecutionContextCreated(context);
                              });
365 366
}

367
void V8InspectorSessionImpl::dispatchProtocolMessage(StringView message) {
368 369
  using v8_crdtp::span;
  using v8_crdtp::SpanFrom;
370 371 372
  span<uint8_t> cbor;
  std::vector<uint8_t> converted_cbor;
  if (IsCBORMessage(message)) {
373 374
    use_binary_protocol_ = true;
    m_state->setBoolean("use_binary_protocol", true);
375
    cbor = span<uint8_t>(message.characters8(), message.length());
376
  } else {
377 378
    // We're ignoring the return value of the conversion function
    // intentionally. It means the |parsed_message| below will be nullptr.
379 380 381 382 383 384 385
    auto status = ConvertToCBOR(message, &converted_cbor);
    if (!status.ok()) {
      m_channel->sendNotification(
          serializeForFrontend(v8_crdtp::CreateErrorNotification(
              v8_crdtp::DispatchResponse::ParseError(status.ToASCIIString()))));
      return;
    }
386
    cbor = SpanFrom(converted_cbor);
387
  }
388 389 390 391 392 393 394 395 396 397 398 399
  v8_crdtp::Dispatchable dispatchable(cbor);
  if (!dispatchable.ok()) {
    if (dispatchable.HasCallId()) {
      m_channel->sendNotification(serializeForFrontend(
          v8_crdtp::CreateErrorNotification(dispatchable.DispatchError())));
    } else {
      m_channel->sendResponse(
          dispatchable.CallId(),
          serializeForFrontend(v8_crdtp::CreateErrorResponse(
              dispatchable.CallId(), dispatchable.DispatchError())));
    }
    return;
400
  }
401
  m_dispatcher.Dispatch(dispatchable).Run();
402 403
}

404
std::vector<uint8_t> V8InspectorSessionImpl::state() {
405
  return m_state->Serialize();
406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457
}

std::vector<std::unique_ptr<protocol::Schema::API::Domain>>
V8InspectorSessionImpl::supportedDomains() {
  std::vector<std::unique_ptr<protocol::Schema::Domain>> domains =
      supportedDomainsImpl();
  std::vector<std::unique_ptr<protocol::Schema::API::Domain>> result;
  for (size_t i = 0; i < domains.size(); ++i)
    result.push_back(std::move(domains[i]));
  return result;
}

std::vector<std::unique_ptr<protocol::Schema::Domain>>
V8InspectorSessionImpl::supportedDomainsImpl() {
  std::vector<std::unique_ptr<protocol::Schema::Domain>> result;
  result.push_back(protocol::Schema::Domain::create()
                       .setName(protocol::Runtime::Metainfo::domainName)
                       .setVersion(protocol::Runtime::Metainfo::version)
                       .build());
  result.push_back(protocol::Schema::Domain::create()
                       .setName(protocol::Debugger::Metainfo::domainName)
                       .setVersion(protocol::Debugger::Metainfo::version)
                       .build());
  result.push_back(protocol::Schema::Domain::create()
                       .setName(protocol::Profiler::Metainfo::domainName)
                       .setVersion(protocol::Profiler::Metainfo::version)
                       .build());
  result.push_back(protocol::Schema::Domain::create()
                       .setName(protocol::HeapProfiler::Metainfo::domainName)
                       .setVersion(protocol::HeapProfiler::Metainfo::version)
                       .build());
  result.push_back(protocol::Schema::Domain::create()
                       .setName(protocol::Schema::Metainfo::domainName)
                       .setVersion(protocol::Schema::Metainfo::version)
                       .build());
  return result;
}

void V8InspectorSessionImpl::addInspectedObject(
    std::unique_ptr<V8InspectorSession::Inspectable> inspectable) {
  m_inspectedObjects.insert(m_inspectedObjects.begin(), std::move(inspectable));
  if (m_inspectedObjects.size() > kInspectedObjectBufferSize)
    m_inspectedObjects.resize(kInspectedObjectBufferSize);
}

V8InspectorSession::Inspectable* V8InspectorSessionImpl::inspectedObject(
    unsigned num) {
  if (num >= m_inspectedObjects.size()) return nullptr;
  return m_inspectedObjects[num].get();
}

void V8InspectorSessionImpl::schedulePauseOnNextStatement(
458
    StringView breakReason, StringView breakDetails) {
459 460
  std::vector<uint8_t> cbor;
  ConvertToCBOR(breakDetails, &cbor);
461 462
  m_debuggerAgent->schedulePauseOnNextStatement(
      toString16(breakReason),
463
      protocol::DictionaryValue::cast(
464
          protocol::Value::parseBinary(cbor.data(), cbor.size())));
465 466 467 468 469 470
}

void V8InspectorSessionImpl::cancelPauseOnNextStatement() {
  m_debuggerAgent->cancelPauseOnNextStatement();
}

471 472
void V8InspectorSessionImpl::breakProgram(StringView breakReason,
                                          StringView breakDetails) {
473 474
  std::vector<uint8_t> cbor;
  ConvertToCBOR(breakDetails, &cbor);
475 476
  m_debuggerAgent->breakProgram(
      toString16(breakReason),
477
      protocol::DictionaryValue::cast(
478
          protocol::Value::parseBinary(cbor.data(), cbor.size())));
479 480 481
}

void V8InspectorSessionImpl::setSkipAllPauses(bool skip) {
482
  m_debuggerAgent->setSkipAllPauses(skip);
483 484
}

485 486 487
void V8InspectorSessionImpl::resume(bool terminateOnResume) {
  m_debuggerAgent->resume(terminateOnResume);
}
488

489
void V8InspectorSessionImpl::stepOver() { m_debuggerAgent->stepOver({}); }
490 491

std::vector<std::unique_ptr<protocol::Debugger::API::SearchMatch>>
492
V8InspectorSessionImpl::searchInTextByLines(StringView text, StringView query,
493 494 495 496 497 498 499 500 501 502 503
                                            bool caseSensitive, bool isRegex) {
  // TODO(dgozman): search may operate on StringView and avoid copying |text|.
  std::vector<std::unique_ptr<protocol::Debugger::SearchMatch>> matches =
      searchInTextByLinesImpl(this, toString16(text), toString16(query),
                              caseSensitive, isRegex);
  std::vector<std::unique_ptr<protocol::Debugger::API::SearchMatch>> result;
  for (size_t i = 0; i < matches.size(); ++i)
    result.push_back(std::move(matches[i]));
  return result;
}

504
void V8InspectorSessionImpl::triggerPreciseCoverageDeltaUpdate(
505
    StringView occasion) {
506 507
  if (m_profilerAgent)
    m_profilerAgent->triggerPreciseCoverageDeltaUpdate(toString16(occasion));
508 509
}

510
}  // namespace v8_inspector