1 /*
2 * Copyright (c) 2010-2011 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 #include "src/inspector/v8-inspector-impl.h"
32
33 #include <vector>
34
35 #include "src/base/platform/mutex.h"
36 #include "src/inspector/inspected-context.h"
37 #include "src/inspector/string-util.h"
38 #include "src/inspector/v8-console-agent-impl.h"
39 #include "src/inspector/v8-console-message.h"
40 #include "src/inspector/v8-console.h"
41 #include "src/inspector/v8-debugger-agent-impl.h"
42 #include "src/inspector/v8-debugger.h"
43 #include "src/inspector/v8-inspector-session-impl.h"
44 #include "src/inspector/v8-profiler-agent-impl.h"
45 #include "src/inspector/v8-runtime-agent-impl.h"
46 #include "src/inspector/v8-stack-trace-impl.h"
47
48 #include "include/v8-platform.h"
49
50 namespace v8_inspector {
51
create(v8::Isolate * isolate,V8InspectorClient * client)52 std::unique_ptr<V8Inspector> V8Inspector::create(v8::Isolate* isolate,
53 V8InspectorClient* client) {
54 return std::unique_ptr<V8Inspector>(new V8InspectorImpl(isolate, client));
55 }
56
V8InspectorImpl(v8::Isolate * isolate,V8InspectorClient * client)57 V8InspectorImpl::V8InspectorImpl(v8::Isolate* isolate,
58 V8InspectorClient* client)
59 : m_isolate(isolate),
60 m_client(client),
61 m_debugger(new V8Debugger(isolate, this)),
62 m_capturingStackTracesCount(0),
63 m_lastExceptionId(0),
64 m_lastContextId(0),
65 m_isolateId(v8::debug::GetNextRandomInt64(m_isolate)) {
66 v8::debug::SetInspector(m_isolate, this);
67 v8::debug::SetConsoleDelegate(m_isolate, console());
68 }
69
~V8InspectorImpl()70 V8InspectorImpl::~V8InspectorImpl() {
71 v8::debug::SetInspector(m_isolate, nullptr);
72 v8::debug::SetConsoleDelegate(m_isolate, nullptr);
73 }
74
contextGroupId(v8::Local<v8::Context> context) const75 int V8InspectorImpl::contextGroupId(v8::Local<v8::Context> context) const {
76 return contextGroupId(InspectedContext::contextId(context));
77 }
78
contextGroupId(int contextId) const79 int V8InspectorImpl::contextGroupId(int contextId) const {
80 auto it = m_contextIdToGroupIdMap.find(contextId);
81 return it != m_contextIdToGroupIdMap.end() ? it->second : 0;
82 }
83
compileAndRunInternalScript(v8::Local<v8::Context> context,v8::Local<v8::String> source)84 v8::MaybeLocal<v8::Value> V8InspectorImpl::compileAndRunInternalScript(
85 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 .ToLocal(&unboundScript))
89 return v8::MaybeLocal<v8::Value>();
90 v8::MicrotasksScope microtasksScope(m_isolate,
91 v8::MicrotasksScope::kDoNotRunMicrotasks);
92 v8::Context::Scope contextScope(context);
93 v8::Isolate::SafeForTerminationScope allowTermination(m_isolate);
94 return unboundScript->BindToCurrentContext()->Run(context);
95 }
96
compileScript(v8::Local<v8::Context> context,const String16 & code,const String16 & fileName)97 v8::MaybeLocal<v8::Script> V8InspectorImpl::compileScript(
98 v8::Local<v8::Context> context, const String16& code,
99 const String16& fileName) {
100 v8::ScriptOrigin origin(
101 toV8String(m_isolate, fileName), v8::Integer::New(m_isolate, 0),
102 v8::Integer::New(m_isolate, 0),
103 v8::False(m_isolate), // sharable
104 v8::Local<v8::Integer>(), toV8String(m_isolate, String16()), // sourceMap
105 v8::True(m_isolate)); // opaqueresource
106 v8::ScriptCompiler::Source source(toV8String(m_isolate, code), origin);
107 return v8::ScriptCompiler::Compile(context, &source,
108 v8::ScriptCompiler::kNoCompileOptions);
109 }
110
enableStackCapturingIfNeeded()111 void V8InspectorImpl::enableStackCapturingIfNeeded() {
112 if (!m_capturingStackTracesCount)
113 V8StackTraceImpl::setCaptureStackTraceForUncaughtExceptions(m_isolate,
114 true);
115 ++m_capturingStackTracesCount;
116 }
117
disableStackCapturingIfNeeded()118 void V8InspectorImpl::disableStackCapturingIfNeeded() {
119 if (!(--m_capturingStackTracesCount))
120 V8StackTraceImpl::setCaptureStackTraceForUncaughtExceptions(m_isolate,
121 false);
122 }
123
muteExceptions(int contextGroupId)124 void V8InspectorImpl::muteExceptions(int contextGroupId) {
125 m_muteExceptionsMap[contextGroupId]++;
126 }
127
unmuteExceptions(int contextGroupId)128 void V8InspectorImpl::unmuteExceptions(int contextGroupId) {
129 m_muteExceptionsMap[contextGroupId]--;
130 }
131
ensureConsoleMessageStorage(int contextGroupId)132 V8ConsoleMessageStorage* V8InspectorImpl::ensureConsoleMessageStorage(
133 int contextGroupId) {
134 auto storageIt = m_consoleStorageMap.find(contextGroupId);
135 if (storageIt == m_consoleStorageMap.end())
136 storageIt = m_consoleStorageMap
137 .insert(std::make_pair(
138 contextGroupId,
139 std::unique_ptr<V8ConsoleMessageStorage>(
140 new V8ConsoleMessageStorage(this, contextGroupId))))
141 .first;
142 return storageIt->second.get();
143 }
144
hasConsoleMessageStorage(int contextGroupId)145 bool V8InspectorImpl::hasConsoleMessageStorage(int contextGroupId) {
146 auto storageIt = m_consoleStorageMap.find(contextGroupId);
147 return storageIt != m_consoleStorageMap.end();
148 }
149
createStackTrace(v8::Local<v8::StackTrace> stackTrace)150 std::unique_ptr<V8StackTrace> V8InspectorImpl::createStackTrace(
151 v8::Local<v8::StackTrace> stackTrace) {
152 return m_debugger->createStackTrace(stackTrace);
153 }
154
connect(int contextGroupId,V8Inspector::Channel * channel,StringView state)155 std::unique_ptr<V8InspectorSession> V8InspectorImpl::connect(
156 int contextGroupId, V8Inspector::Channel* channel, StringView state) {
157 int sessionId = ++m_lastSessionId;
158 std::unique_ptr<V8InspectorSessionImpl> session =
159 V8InspectorSessionImpl::create(this, contextGroupId, sessionId, channel,
160 state);
161 m_sessions[contextGroupId][sessionId] = session.get();
162 return std::move(session);
163 }
164
disconnect(V8InspectorSessionImpl * session)165 void V8InspectorImpl::disconnect(V8InspectorSessionImpl* session) {
166 auto& map = m_sessions[session->contextGroupId()];
167 map.erase(session->sessionId());
168 if (map.empty()) m_sessions.erase(session->contextGroupId());
169 }
170
getContext(int groupId,int contextId) const171 InspectedContext* V8InspectorImpl::getContext(int groupId,
172 int contextId) const {
173 if (!groupId || !contextId) return nullptr;
174
175 auto contextGroupIt = m_contexts.find(groupId);
176 if (contextGroupIt == m_contexts.end()) return nullptr;
177
178 auto contextIt = contextGroupIt->second->find(contextId);
179 if (contextIt == contextGroupIt->second->end()) return nullptr;
180
181 return contextIt->second.get();
182 }
183
getContext(int contextId) const184 InspectedContext* V8InspectorImpl::getContext(int contextId) const {
185 return getContext(contextGroupId(contextId), contextId);
186 }
187
contextById(int contextId)188 v8::MaybeLocal<v8::Context> V8InspectorImpl::contextById(int contextId) {
189 InspectedContext* context = getContext(contextId);
190 return context ? context->context() : v8::MaybeLocal<v8::Context>();
191 }
192
contextCreated(const V8ContextInfo & info)193 void V8InspectorImpl::contextCreated(const V8ContextInfo& info) {
194 int contextId = ++m_lastContextId;
195 auto* context = new InspectedContext(this, info, contextId);
196 m_contextIdToGroupIdMap[contextId] = info.contextGroupId;
197
198 auto contextIt = m_contexts.find(info.contextGroupId);
199 if (contextIt == m_contexts.end())
200 contextIt = m_contexts
201 .insert(std::make_pair(
202 info.contextGroupId,
203 std::unique_ptr<ContextByIdMap>(new ContextByIdMap())))
204 .first;
205 const auto& contextById = contextIt->second;
206
207 DCHECK(contextById->find(contextId) == contextById->cend());
208 (*contextById)[contextId].reset(context);
209 forEachSession(
210 info.contextGroupId, [&context](V8InspectorSessionImpl* session) {
211 session->runtimeAgent()->addBindings(context);
212 session->runtimeAgent()->reportExecutionContextCreated(context);
213 });
214 }
215
contextDestroyed(v8::Local<v8::Context> context)216 void V8InspectorImpl::contextDestroyed(v8::Local<v8::Context> context) {
217 int contextId = InspectedContext::contextId(context);
218 int groupId = contextGroupId(context);
219 contextCollected(groupId, contextId);
220 }
221
contextCollected(int groupId,int contextId)222 void V8InspectorImpl::contextCollected(int groupId, int contextId) {
223 m_contextIdToGroupIdMap.erase(contextId);
224
225 auto storageIt = m_consoleStorageMap.find(groupId);
226 if (storageIt != m_consoleStorageMap.end())
227 storageIt->second->contextDestroyed(contextId);
228
229 InspectedContext* inspectedContext = getContext(groupId, contextId);
230 if (!inspectedContext) return;
231
232 forEachSession(groupId, [&inspectedContext](V8InspectorSessionImpl* session) {
233 session->runtimeAgent()->reportExecutionContextDestroyed(inspectedContext);
234 });
235 discardInspectedContext(groupId, contextId);
236 }
237
resetContextGroup(int contextGroupId)238 void V8InspectorImpl::resetContextGroup(int contextGroupId) {
239 m_consoleStorageMap.erase(contextGroupId);
240 m_muteExceptionsMap.erase(contextGroupId);
241 std::vector<int> contextIdsToClear;
242 forEachContext(contextGroupId,
243 [&contextIdsToClear](InspectedContext* context) {
244 contextIdsToClear.push_back(context->contextId());
245 });
246 forEachSession(contextGroupId,
247 [](V8InspectorSessionImpl* session) { session->reset(); });
248 m_contexts.erase(contextGroupId);
249 }
250
idleStarted()251 void V8InspectorImpl::idleStarted() { m_isolate->SetIdle(true); }
252
idleFinished()253 void V8InspectorImpl::idleFinished() { m_isolate->SetIdle(false); }
254
exceptionThrown(v8::Local<v8::Context> context,StringView message,v8::Local<v8::Value> exception,StringView detailedMessage,StringView url,unsigned lineNumber,unsigned columnNumber,std::unique_ptr<V8StackTrace> stackTrace,int scriptId)255 unsigned V8InspectorImpl::exceptionThrown(
256 v8::Local<v8::Context> context, StringView message,
257 v8::Local<v8::Value> exception, StringView detailedMessage, StringView url,
258 unsigned lineNumber, unsigned columnNumber,
259 std::unique_ptr<V8StackTrace> stackTrace, int scriptId) {
260 int groupId = contextGroupId(context);
261 if (!groupId || m_muteExceptionsMap[groupId]) return 0;
262 std::unique_ptr<V8StackTraceImpl> stackTraceImpl(
263 static_cast<V8StackTraceImpl*>(stackTrace.release()));
264 unsigned exceptionId = nextExceptionId();
265 std::unique_ptr<V8ConsoleMessage> consoleMessage =
266 V8ConsoleMessage::createForException(
267 m_client->currentTimeMS(), toString16(detailedMessage),
268 toString16(url), lineNumber, columnNumber, std::move(stackTraceImpl),
269 scriptId, m_isolate, toString16(message),
270 InspectedContext::contextId(context), exception, exceptionId);
271 ensureConsoleMessageStorage(groupId)->addMessage(std::move(consoleMessage));
272 return exceptionId;
273 }
274
exceptionRevoked(v8::Local<v8::Context> context,unsigned exceptionId,StringView message)275 void V8InspectorImpl::exceptionRevoked(v8::Local<v8::Context> context,
276 unsigned exceptionId,
277 StringView message) {
278 int groupId = contextGroupId(context);
279 if (!groupId) return;
280
281 std::unique_ptr<V8ConsoleMessage> consoleMessage =
282 V8ConsoleMessage::createForRevokedException(
283 m_client->currentTimeMS(), toString16(message), exceptionId);
284 ensureConsoleMessageStorage(groupId)->addMessage(std::move(consoleMessage));
285 }
286
captureStackTrace(bool fullStack)287 std::unique_ptr<V8StackTrace> V8InspectorImpl::captureStackTrace(
288 bool fullStack) {
289 return m_debugger->captureStackTrace(fullStack);
290 }
291
storeCurrentStackTrace(StringView description)292 V8StackTraceId V8InspectorImpl::storeCurrentStackTrace(StringView description) {
293 return m_debugger->storeCurrentStackTrace(description);
294 }
295
externalAsyncTaskStarted(const V8StackTraceId & parent)296 void V8InspectorImpl::externalAsyncTaskStarted(const V8StackTraceId& parent) {
297 m_debugger->externalAsyncTaskStarted(parent);
298 }
299
externalAsyncTaskFinished(const V8StackTraceId & parent)300 void V8InspectorImpl::externalAsyncTaskFinished(const V8StackTraceId& parent) {
301 m_debugger->externalAsyncTaskFinished(parent);
302 }
303
asyncTaskScheduled(StringView taskName,void * task,bool recurring)304 void V8InspectorImpl::asyncTaskScheduled(StringView taskName, void* task,
305 bool recurring) {
306 if (!task) return;
307 m_debugger->asyncTaskScheduled(taskName, task, recurring);
308 }
309
asyncTaskCanceled(void * task)310 void V8InspectorImpl::asyncTaskCanceled(void* task) {
311 if (!task) return;
312 m_debugger->asyncTaskCanceled(task);
313 }
314
asyncTaskStarted(void * task)315 void V8InspectorImpl::asyncTaskStarted(void* task) {
316 if (!task) return;
317 m_debugger->asyncTaskStarted(task);
318 }
319
asyncTaskFinished(void * task)320 void V8InspectorImpl::asyncTaskFinished(void* task) {
321 if (!task) return;
322 m_debugger->asyncTaskFinished(task);
323 }
324
allAsyncTasksCanceled()325 void V8InspectorImpl::allAsyncTasksCanceled() {
326 m_debugger->allAsyncTasksCanceled();
327 }
328
Counters(v8::Isolate * isolate)329 V8Inspector::Counters::Counters(v8::Isolate* isolate) : m_isolate(isolate) {
330 CHECK(m_isolate);
331 auto* inspector =
332 static_cast<V8InspectorImpl*>(v8::debug::GetInspector(m_isolate));
333 CHECK(inspector);
334 CHECK(!inspector->m_counters);
335 inspector->m_counters = this;
336 m_isolate->SetCounterFunction(&Counters::getCounterPtr);
337 }
338
~Counters()339 V8Inspector::Counters::~Counters() {
340 auto* inspector =
341 static_cast<V8InspectorImpl*>(v8::debug::GetInspector(m_isolate));
342 CHECK(inspector);
343 inspector->m_counters = nullptr;
344 m_isolate->SetCounterFunction(nullptr);
345 }
346
getCounterPtr(const char * name)347 int* V8Inspector::Counters::getCounterPtr(const char* name) {
348 v8::Isolate* isolate = v8::Isolate::GetCurrent();
349 DCHECK(isolate);
350 V8Inspector* inspector = v8::debug::GetInspector(isolate);
351 DCHECK(inspector);
352 auto* instance = static_cast<V8InspectorImpl*>(inspector)->m_counters;
353 DCHECK(instance);
354 return &(instance->m_countersMap[name]);
355 }
356
enableCounters()357 std::shared_ptr<V8Inspector::Counters> V8InspectorImpl::enableCounters() {
358 if (m_counters) return m_counters->shared_from_this();
359 return std::make_shared<Counters>(m_isolate);
360 }
361
regexContext()362 v8::Local<v8::Context> V8InspectorImpl::regexContext() {
363 if (m_regexContext.IsEmpty())
364 m_regexContext.Reset(m_isolate, v8::Context::New(m_isolate));
365 return m_regexContext.Get(m_isolate);
366 }
367
discardInspectedContext(int contextGroupId,int contextId)368 void V8InspectorImpl::discardInspectedContext(int contextGroupId,
369 int contextId) {
370 if (!getContext(contextGroupId, contextId)) return;
371 m_contexts[contextGroupId]->erase(contextId);
372 if (m_contexts[contextGroupId]->empty()) m_contexts.erase(contextGroupId);
373 }
374
sessionById(int contextGroupId,int sessionId)375 V8InspectorSessionImpl* V8InspectorImpl::sessionById(int contextGroupId,
376 int sessionId) {
377 auto it = m_sessions.find(contextGroupId);
378 if (it == m_sessions.end()) return nullptr;
379 auto it2 = it->second.find(sessionId);
380 return it2 == it->second.end() ? nullptr : it2->second;
381 }
382
console()383 V8Console* V8InspectorImpl::console() {
384 if (!m_console) m_console.reset(new V8Console(this));
385 return m_console.get();
386 }
387
forEachContext(int contextGroupId,const std::function<void (InspectedContext *)> & callback)388 void V8InspectorImpl::forEachContext(
389 int contextGroupId,
390 const std::function<void(InspectedContext*)>& callback) {
391 auto it = m_contexts.find(contextGroupId);
392 if (it == m_contexts.end()) return;
393 std::vector<int> ids;
394 ids.reserve(it->second->size());
395 for (auto& contextIt : *(it->second)) ids.push_back(contextIt.first);
396
397 // Retrieve by ids each time since |callback| may destroy some contexts.
398 for (auto& contextId : ids) {
399 it = m_contexts.find(contextGroupId);
400 if (it == m_contexts.end()) continue;
401 auto contextIt = it->second->find(contextId);
402 if (contextIt != it->second->end()) callback(contextIt->second.get());
403 }
404 }
405
forEachSession(int contextGroupId,const std::function<void (V8InspectorSessionImpl *)> & callback)406 void V8InspectorImpl::forEachSession(
407 int contextGroupId,
408 const std::function<void(V8InspectorSessionImpl*)>& callback) {
409 auto it = m_sessions.find(contextGroupId);
410 if (it == m_sessions.end()) return;
411 std::vector<int> ids;
412 ids.reserve(it->second.size());
413 for (auto& sessionIt : it->second) ids.push_back(sessionIt.first);
414
415 // Retrieve by ids each time since |callback| may destroy some contexts.
416 for (auto& sessionId : ids) {
417 it = m_sessions.find(contextGroupId);
418 if (it == m_sessions.end()) continue;
419 auto sessionIt = it->second.find(sessionId);
420 if (sessionIt != it->second.end()) callback(sessionIt->second);
421 }
422 }
423
EvaluateScope(const InjectedScript::Scope & scope)424 V8InspectorImpl::EvaluateScope::EvaluateScope(
425 const InjectedScript::Scope& scope)
426 : m_scope(scope),
427 m_isolate(scope.inspector()->isolate()),
428 m_safeForTerminationScope(m_isolate) {}
429
430 struct V8InspectorImpl::EvaluateScope::CancelToken {
431 v8::base::Mutex m_mutex;
432 bool m_canceled = false;
433 };
434
~EvaluateScope()435 V8InspectorImpl::EvaluateScope::~EvaluateScope() {
436 if (m_scope.tryCatch().HasTerminated()) {
437 m_scope.inspector()->debugger()->reportTermination();
438 }
439 if (m_cancelToken) {
440 v8::base::MutexGuard lock(&m_cancelToken->m_mutex);
441 m_cancelToken->m_canceled = true;
442 m_isolate->CancelTerminateExecution();
443 }
444 }
445
446 class V8InspectorImpl::EvaluateScope::TerminateTask : public v8::Task {
447 public:
TerminateTask(v8::Isolate * isolate,std::shared_ptr<CancelToken> token)448 TerminateTask(v8::Isolate* isolate, std::shared_ptr<CancelToken> token)
449 : m_isolate(isolate), m_token(std::move(token)) {}
450
Run()451 void Run() override {
452 // CancelToken contains m_canceled bool which may be changed from main
453 // thread, so lock mutex first.
454 v8::base::MutexGuard lock(&m_token->m_mutex);
455 if (m_token->m_canceled) return;
456 m_isolate->TerminateExecution();
457 }
458
459 private:
460 v8::Isolate* m_isolate;
461 std::shared_ptr<CancelToken> m_token;
462 };
463
setTimeout(double timeout)464 protocol::Response V8InspectorImpl::EvaluateScope::setTimeout(double timeout) {
465 if (m_isolate->IsExecutionTerminating()) {
466 return protocol::Response::ServerError("Execution was terminated");
467 }
468 m_cancelToken.reset(new CancelToken());
469 v8::debug::GetCurrentPlatform()->CallDelayedOnWorkerThread(
470 std::make_unique<TerminateTask>(m_isolate, m_cancelToken), timeout);
471 return protocol::Response::Success();
472 }
473
474 } // namespace v8_inspector
475