v8-inspector-impl.cc 17.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
/*
 * Copyright (c) 2010-2011 Google Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

31
#include "src/inspector/v8-inspector-impl.h"
32

33 34
#include <vector>

35
#include "src/base/platform/mutex.h"
36 37 38 39
#include "src/inspector/inspected-context.h"
#include "src/inspector/string-util.h"
#include "src/inspector/v8-console-agent-impl.h"
#include "src/inspector/v8-console-message.h"
40
#include "src/inspector/v8-console.h"
41 42 43 44 45 46
#include "src/inspector/v8-debugger-agent-impl.h"
#include "src/inspector/v8-debugger.h"
#include "src/inspector/v8-inspector-session-impl.h"
#include "src/inspector/v8-profiler-agent-impl.h"
#include "src/inspector/v8-runtime-agent-impl.h"
#include "src/inspector/v8-stack-trace-impl.h"
47

48 49
#include "include/v8-platform.h"

50 51 52 53
namespace v8_inspector {

std::unique_ptr<V8Inspector> V8Inspector::create(v8::Isolate* isolate,
                                                 V8InspectorClient* client) {
54
  return std::unique_ptr<V8Inspector>(new V8InspectorImpl(isolate, client));
55 56 57 58 59 60 61 62
}

V8InspectorImpl::V8InspectorImpl(v8::Isolate* isolate,
                                 V8InspectorClient* client)
    : m_isolate(isolate),
      m_client(client),
      m_debugger(new V8Debugger(isolate, this)),
      m_capturingStackTracesCount(0),
63
      m_lastExceptionId(0),
64 65
      m_lastContextId(0),
      m_isolateId(v8::debug::GetNextRandomInt64(m_isolate)) {
66
  v8::debug::SetInspector(m_isolate, this);
67 68
  v8::debug::SetConsoleDelegate(m_isolate, console());
}
69

70
V8InspectorImpl::~V8InspectorImpl() {
71
  v8::debug::SetInspector(m_isolate, nullptr);
72 73
  v8::debug::SetConsoleDelegate(m_isolate, nullptr);
}
74

75
int V8InspectorImpl::contextGroupId(v8::Local<v8::Context> context) const {
76 77 78
  return contextGroupId(InspectedContext::contextId(context));
}

79 80
int V8InspectorImpl::contextGroupId(int contextId) const {
  auto it = m_contextIdToGroupIdMap.find(contextId);
81 82 83
  return it != m_contextIdToGroupIdMap.end() ? it->second : 0;
}

84 85
v8::MaybeLocal<v8::Value> V8InspectorImpl::compileAndRunInternalScript(
    v8::Local<v8::Context> context, v8::Local<v8::String> source) {
86
  v8::Local<v8::UnboundScript> unboundScript;
87
  if (!v8::debug::CompileInspectorScript(m_isolate, source)
88 89
           .ToLocal(&unboundScript))
    return v8::MaybeLocal<v8::Value>();
90 91
  v8::MicrotasksScope microtasksScope(m_isolate,
                                      v8::MicrotasksScope::kDoNotRunMicrotasks);
92
  v8::Context::Scope contextScope(context);
93
  v8::Isolate::SafeForTerminationScope allowTermination(m_isolate);
94
  return unboundScript->BindToCurrentContext()->Run(context);
95 96
}

97 98 99
v8::MaybeLocal<v8::Script> V8InspectorImpl::compileScript(
    v8::Local<v8::Context> context, const String16& code,
    const String16& fileName) {
100 101 102
  v8::ScriptOrigin origin(
      toV8String(m_isolate, fileName), v8::Integer::New(m_isolate, 0),
      v8::Integer::New(m_isolate, 0),
103 104 105
      v8::False(m_isolate),                                         // sharable
      v8::Local<v8::Integer>(), toV8String(m_isolate, String16()),  // sourceMap
      v8::True(m_isolate));  // opaqueresource
106 107 108
  v8::ScriptCompiler::Source source(toV8String(m_isolate, code), origin);
  return v8::ScriptCompiler::Compile(context, &source,
                                     v8::ScriptCompiler::kNoCompileOptions);
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
}

void V8InspectorImpl::enableStackCapturingIfNeeded() {
  if (!m_capturingStackTracesCount)
    V8StackTraceImpl::setCaptureStackTraceForUncaughtExceptions(m_isolate,
                                                                true);
  ++m_capturingStackTracesCount;
}

void V8InspectorImpl::disableStackCapturingIfNeeded() {
  if (!(--m_capturingStackTracesCount))
    V8StackTraceImpl::setCaptureStackTraceForUncaughtExceptions(m_isolate,
                                                                false);
}

void V8InspectorImpl::muteExceptions(int contextGroupId) {
  m_muteExceptionsMap[contextGroupId]++;
}

void V8InspectorImpl::unmuteExceptions(int contextGroupId) {
  m_muteExceptionsMap[contextGroupId]--;
}

V8ConsoleMessageStorage* V8InspectorImpl::ensureConsoleMessageStorage(
    int contextGroupId) {
  ConsoleStorageMap::iterator storageIt =
      m_consoleStorageMap.find(contextGroupId);
  if (storageIt == m_consoleStorageMap.end())
137 138 139 140 141 142
    storageIt = m_consoleStorageMap
                    .insert(std::make_pair(
                        contextGroupId,
                        std::unique_ptr<V8ConsoleMessageStorage>(
                            new V8ConsoleMessageStorage(this, contextGroupId))))
                    .first;
143 144 145
  return storageIt->second.get();
}

146 147 148 149 150 151
bool V8InspectorImpl::hasConsoleMessageStorage(int contextGroupId) {
  ConsoleStorageMap::iterator storageIt =
      m_consoleStorageMap.find(contextGroupId);
  return storageIt != m_consoleStorageMap.end();
}

152 153 154 155 156 157
std::unique_ptr<V8StackTrace> V8InspectorImpl::createStackTrace(
    v8::Local<v8::StackTrace> stackTrace) {
  return m_debugger->createStackTrace(stackTrace);
}

std::unique_ptr<V8InspectorSession> V8InspectorImpl::connect(
158
    int contextGroupId, V8Inspector::Channel* channel, StringView state) {
159
  int sessionId = ++m_lastSessionId;
160
  std::unique_ptr<V8InspectorSessionImpl> session =
161 162
      V8InspectorSessionImpl::create(this, contextGroupId, sessionId, channel,
                                     state);
163
  m_sessions[contextGroupId][sessionId] = session.get();
164 165 166 167
  return std::move(session);
}

void V8InspectorImpl::disconnect(V8InspectorSessionImpl* session) {
168 169 170
  auto& map = m_sessions[session->contextGroupId()];
  map.erase(session->sessionId());
  if (map.empty()) m_sessions.erase(session->contextGroupId());
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185
}

InspectedContext* V8InspectorImpl::getContext(int groupId,
                                              int contextId) const {
  if (!groupId || !contextId) return nullptr;

  ContextsByGroupMap::const_iterator contextGroupIt = m_contexts.find(groupId);
  if (contextGroupIt == m_contexts.end()) return nullptr;

  ContextByIdMap::iterator contextIt = contextGroupIt->second->find(contextId);
  if (contextIt == contextGroupIt->second->end()) return nullptr;

  return contextIt->second.get();
}

186 187 188 189
InspectedContext* V8InspectorImpl::getContext(int contextId) const {
  return getContext(contextGroupId(contextId), contextId);
}

190 191
v8::MaybeLocal<v8::Context> V8InspectorImpl::contextById(int contextId) {
  InspectedContext* context = getContext(contextId);
192 193 194
  return context ? context->context() : v8::MaybeLocal<v8::Context>();
}

195
void V8InspectorImpl::contextCreated(const V8ContextInfo& info) {
196 197 198
  int contextId = ++m_lastContextId;
  InspectedContext* context = new InspectedContext(this, info, contextId);
  m_contextIdToGroupIdMap[contextId] = info.contextGroupId;
199 200 201 202

  ContextsByGroupMap::iterator contextIt = m_contexts.find(info.contextGroupId);
  if (contextIt == m_contexts.end())
    contextIt = m_contexts
203 204 205
                    .insert(std::make_pair(
                        info.contextGroupId,
                        std::unique_ptr<ContextByIdMap>(new ContextByIdMap())))
206 207 208 209
                    .first;
  const auto& contextById = contextIt->second;

  DCHECK(contextById->find(contextId) == contextById->cend());
210
  (*contextById)[contextId].reset(context);
211 212
  forEachSession(
      info.contextGroupId, [&context](V8InspectorSessionImpl* session) {
213
        session->runtimeAgent()->addBindings(context);
214 215
        session->runtimeAgent()->reportExecutionContextCreated(context);
      });
216 217 218
}

void V8InspectorImpl::contextDestroyed(v8::Local<v8::Context> context) {
219 220
  int contextId = InspectedContext::contextId(context);
  int groupId = contextGroupId(context);
221 222 223 224
  contextCollected(groupId, contextId);
}

void V8InspectorImpl::contextCollected(int groupId, int contextId) {
225
  m_contextIdToGroupIdMap.erase(contextId);
226

227
  ConsoleStorageMap::iterator storageIt = m_consoleStorageMap.find(groupId);
228 229 230
  if (storageIt != m_consoleStorageMap.end())
    storageIt->second->contextDestroyed(contextId);

231
  InspectedContext* inspectedContext = getContext(groupId, contextId);
232 233
  if (!inspectedContext) return;

234 235 236
  forEachSession(groupId, [&inspectedContext](V8InspectorSessionImpl* session) {
    session->runtimeAgent()->reportExecutionContextDestroyed(inspectedContext);
  });
237
  discardInspectedContext(groupId, contextId);
238 239 240 241 242
}

void V8InspectorImpl::resetContextGroup(int contextGroupId) {
  m_consoleStorageMap.erase(contextGroupId);
  m_muteExceptionsMap.erase(contextGroupId);
243 244 245 246 247
  std::vector<int> contextIdsToClear;
  forEachContext(contextGroupId,
                 [&contextIdsToClear](InspectedContext* context) {
                   contextIdsToClear.push_back(context->contextId());
                 });
248 249
  forEachSession(contextGroupId,
                 [](V8InspectorSessionImpl* session) { session->reset(); });
250 251 252
  m_contexts.erase(contextGroupId);
}

253
void V8InspectorImpl::idleStarted() { m_isolate->SetIdle(true); }
254

255
void V8InspectorImpl::idleFinished() { m_isolate->SetIdle(false); }
256 257

unsigned V8InspectorImpl::exceptionThrown(
258 259 260
    v8::Local<v8::Context> context, StringView message,
    v8::Local<v8::Value> exception, StringView detailedMessage, StringView url,
    unsigned lineNumber, unsigned columnNumber,
261
    std::unique_ptr<V8StackTrace> stackTrace, int scriptId) {
262 263
  int groupId = contextGroupId(context);
  if (!groupId || m_muteExceptionsMap[groupId]) return 0;
264 265
  std::unique_ptr<V8StackTraceImpl> stackTraceImpl(
      static_cast<V8StackTraceImpl*>(stackTrace.release()));
266 267 268 269 270 271
  unsigned exceptionId = nextExceptionId();
  std::unique_ptr<V8ConsoleMessage> consoleMessage =
      V8ConsoleMessage::createForException(
          m_client->currentTimeMS(), toString16(detailedMessage),
          toString16(url), lineNumber, columnNumber, std::move(stackTraceImpl),
          scriptId, m_isolate, toString16(message),
272 273
          InspectedContext::contextId(context), exception, exceptionId);
  ensureConsoleMessageStorage(groupId)->addMessage(std::move(consoleMessage));
274 275 276 277 278
  return exceptionId;
}

void V8InspectorImpl::exceptionRevoked(v8::Local<v8::Context> context,
                                       unsigned exceptionId,
279
                                       StringView message) {
280 281
  int groupId = contextGroupId(context);
  if (!groupId) return;
282 283 284 285

  std::unique_ptr<V8ConsoleMessage> consoleMessage =
      V8ConsoleMessage::createForRevokedException(
          m_client->currentTimeMS(), toString16(message), exceptionId);
286
  ensureConsoleMessageStorage(groupId)->addMessage(std::move(consoleMessage));
287 288 289 290 291 292 293
}

std::unique_ptr<V8StackTrace> V8InspectorImpl::captureStackTrace(
    bool fullStack) {
  return m_debugger->captureStackTrace(fullStack);
}

294
V8StackTraceId V8InspectorImpl::storeCurrentStackTrace(StringView description) {
295 296 297 298 299 300 301 302 303 304 305
  return m_debugger->storeCurrentStackTrace(description);
}

void V8InspectorImpl::externalAsyncTaskStarted(const V8StackTraceId& parent) {
  m_debugger->externalAsyncTaskStarted(parent);
}

void V8InspectorImpl::externalAsyncTaskFinished(const V8StackTraceId& parent) {
  m_debugger->externalAsyncTaskFinished(parent);
}

306
void V8InspectorImpl::asyncTaskScheduled(StringView taskName, void* task,
307
                                         bool recurring) {
308
  if (!task) return;
309 310 311 312
  m_debugger->asyncTaskScheduled(taskName, task, recurring);
}

void V8InspectorImpl::asyncTaskCanceled(void* task) {
313
  if (!task) return;
314 315 316 317
  m_debugger->asyncTaskCanceled(task);
}

void V8InspectorImpl::asyncTaskStarted(void* task) {
318
  if (!task) return;
319 320 321 322
  m_debugger->asyncTaskStarted(task);
}

void V8InspectorImpl::asyncTaskFinished(void* task) {
323
  if (!task) return;
324 325 326 327 328 329 330
  m_debugger->asyncTaskFinished(task);
}

void V8InspectorImpl::allAsyncTasksCanceled() {
  m_debugger->allAsyncTasksCanceled();
}

331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363
V8Inspector::Counters::Counters(v8::Isolate* isolate) : m_isolate(isolate) {
  CHECK(m_isolate);
  V8InspectorImpl* inspector =
      static_cast<V8InspectorImpl*>(v8::debug::GetInspector(m_isolate));
  CHECK(inspector);
  CHECK(!inspector->m_counters);
  inspector->m_counters = this;
  m_isolate->SetCounterFunction(&Counters::getCounterPtr);
}

V8Inspector::Counters::~Counters() {
  V8InspectorImpl* inspector =
      static_cast<V8InspectorImpl*>(v8::debug::GetInspector(m_isolate));
  CHECK(inspector);
  inspector->m_counters = nullptr;
  m_isolate->SetCounterFunction(nullptr);
}

int* V8Inspector::Counters::getCounterPtr(const char* name) {
  v8::Isolate* isolate = v8::Isolate::GetCurrent();
  DCHECK(isolate);
  V8Inspector* inspector = v8::debug::GetInspector(isolate);
  DCHECK(inspector);
  auto* instance = static_cast<V8InspectorImpl*>(inspector)->m_counters;
  DCHECK(instance);
  return &(instance->m_countersMap[name]);
}

std::shared_ptr<V8Inspector::Counters> V8InspectorImpl::enableCounters() {
  if (m_counters) return m_counters->shared_from_this();
  return std::make_shared<Counters>(m_isolate);
}

364 365 366 367 368 369 370 371 372 373 374 375 376
v8::Local<v8::Context> V8InspectorImpl::regexContext() {
  if (m_regexContext.IsEmpty())
    m_regexContext.Reset(m_isolate, v8::Context::New(m_isolate));
  return m_regexContext.Get(m_isolate);
}

void V8InspectorImpl::discardInspectedContext(int contextGroupId,
                                              int contextId) {
  if (!getContext(contextGroupId, contextId)) return;
  m_contexts[contextGroupId]->erase(contextId);
  if (m_contexts[contextGroupId]->empty()) m_contexts.erase(contextGroupId);
}

377 378 379 380 381 382
V8InspectorSessionImpl* V8InspectorImpl::sessionById(int contextGroupId,
                                                     int sessionId) {
  auto it = m_sessions.find(contextGroupId);
  if (it == m_sessions.end()) return nullptr;
  auto it2 = it->second.find(sessionId);
  return it2 == it->second.end() ? nullptr : it2->second;
383 384
}

385 386 387 388 389
V8Console* V8InspectorImpl::console() {
  if (!m_console) m_console.reset(new V8Console(this));
  return m_console.get();
}

390
void V8InspectorImpl::forEachContext(
391 392
    int contextGroupId,
    const std::function<void(InspectedContext*)>& callback) {
393 394 395 396 397 398 399 400 401 402 403 404 405 406 407
  auto it = m_contexts.find(contextGroupId);
  if (it == m_contexts.end()) return;
  std::vector<int> ids;
  ids.reserve(it->second->size());
  for (auto& contextIt : *(it->second)) ids.push_back(contextIt.first);

  // Retrieve by ids each time since |callback| may destroy some contexts.
  for (auto& contextId : ids) {
    it = m_contexts.find(contextGroupId);
    if (it == m_contexts.end()) continue;
    auto contextIt = it->second->find(contextId);
    if (contextIt != it->second->end()) callback(contextIt->second.get());
  }
}

408
void V8InspectorImpl::forEachSession(
409 410
    int contextGroupId,
    const std::function<void(V8InspectorSessionImpl*)>& callback) {
411 412 413 414 415 416 417 418 419 420 421 422 423 424 425
  auto it = m_sessions.find(contextGroupId);
  if (it == m_sessions.end()) return;
  std::vector<int> ids;
  ids.reserve(it->second.size());
  for (auto& sessionIt : it->second) ids.push_back(sessionIt.first);

  // Retrieve by ids each time since |callback| may destroy some contexts.
  for (auto& sessionId : ids) {
    it = m_sessions.find(contextGroupId);
    if (it == m_sessions.end()) continue;
    auto sessionIt = it->second.find(sessionId);
    if (sessionIt != it->second.end()) callback(sessionIt->second);
  }
}

426 427 428 429 430
V8InspectorImpl::EvaluateScope::EvaluateScope(
    const InjectedScript::Scope& scope)
    : m_scope(scope),
      m_isolate(scope.inspector()->isolate()),
      m_safeForTerminationScope(m_isolate) {}
431 432 433 434 435

struct V8InspectorImpl::EvaluateScope::CancelToken {
  v8::base::Mutex m_mutex;
  bool m_canceled = false;
};
436

437
V8InspectorImpl::EvaluateScope::~EvaluateScope() {
438 439 440
  if (m_scope.tryCatch().HasTerminated()) {
    m_scope.inspector()->debugger()->reportTermination();
  }
441
  if (m_cancelToken) {
442
    v8::base::MutexGuard lock(&m_cancelToken->m_mutex);
443 444 445
    m_cancelToken->m_canceled = true;
    m_isolate->CancelTerminateExecution();
  }
446
}
447

448 449 450
class V8InspectorImpl::EvaluateScope::TerminateTask : public v8::Task {
 public:
  TerminateTask(v8::Isolate* isolate, std::shared_ptr<CancelToken> token)
451
      : m_isolate(isolate), m_token(std::move(token)) {}
452

453
  void Run() override {
454 455
    // CancelToken contains m_canceled bool which may be changed from main
    // thread, so lock mutex first.
456
    v8::base::MutexGuard lock(&m_token->m_mutex);
457 458 459 460 461 462 463 464 465 466 467
    if (m_token->m_canceled) return;
    m_isolate->TerminateExecution();
  }

 private:
  v8::Isolate* m_isolate;
  std::shared_ptr<CancelToken> m_token;
};

protocol::Response V8InspectorImpl::EvaluateScope::setTimeout(double timeout) {
  if (m_isolate->IsExecutionTerminating()) {
468
    return protocol::Response::ServerError("Execution was terminated");
469 470
  }
  m_cancelToken.reset(new CancelToken());
471
  v8::debug::GetCurrentPlatform()->CallDelayedOnWorkerThread(
472
      std::make_unique<TerminateTask>(m_isolate, m_cancelToken), timeout);
473
  return protocol::Response::Success();
474 475
}

476
}  // namespace v8_inspector