5 #include "src/inspector/v8-console-message.h" 7 #include "src/debug/debug-interface.h" 8 #include "src/inspector/inspected-context.h" 9 #include "src/inspector/protocol/Protocol.h" 10 #include "src/inspector/string-util.h" 11 #include "src/inspector/v8-console-agent-impl.h" 12 #include "src/inspector/v8-inspector-impl.h" 13 #include "src/inspector/v8-inspector-session-impl.h" 14 #include "src/inspector/v8-runtime-agent-impl.h" 15 #include "src/inspector/v8-stack-trace-impl.h" 16 #include "src/tracing/trace-event.h" 18 #include "include/v8-inspector.h" 24 String16 consoleAPITypeValue(ConsoleAPIType type) {
26 case ConsoleAPIType::kLog:
27 return protocol::Runtime::ConsoleAPICalled::TypeEnum::Log;
28 case ConsoleAPIType::kDebug:
29 return protocol::Runtime::ConsoleAPICalled::TypeEnum::Debug;
30 case ConsoleAPIType::kInfo:
31 return protocol::Runtime::ConsoleAPICalled::TypeEnum::Info;
32 case ConsoleAPIType::kError:
33 return protocol::Runtime::ConsoleAPICalled::TypeEnum::Error;
34 case ConsoleAPIType::kWarning:
35 return protocol::Runtime::ConsoleAPICalled::TypeEnum::Warning;
36 case ConsoleAPIType::kClear:
37 return protocol::Runtime::ConsoleAPICalled::TypeEnum::Clear;
38 case ConsoleAPIType::kDir:
39 return protocol::Runtime::ConsoleAPICalled::TypeEnum::Dir;
40 case ConsoleAPIType::kDirXML:
41 return protocol::Runtime::ConsoleAPICalled::TypeEnum::Dirxml;
42 case ConsoleAPIType::kTable:
43 return protocol::Runtime::ConsoleAPICalled::TypeEnum::Table;
44 case ConsoleAPIType::kTrace:
45 return protocol::Runtime::ConsoleAPICalled::TypeEnum::Trace;
46 case ConsoleAPIType::kStartGroup:
47 return protocol::Runtime::ConsoleAPICalled::TypeEnum::StartGroup;
48 case ConsoleAPIType::kStartGroupCollapsed:
49 return protocol::Runtime::ConsoleAPICalled::TypeEnum::StartGroupCollapsed;
50 case ConsoleAPIType::kEndGroup:
51 return protocol::Runtime::ConsoleAPICalled::TypeEnum::EndGroup;
52 case ConsoleAPIType::kAssert:
53 return protocol::Runtime::ConsoleAPICalled::TypeEnum::Assert;
54 case ConsoleAPIType::kTimeEnd:
55 return protocol::Runtime::ConsoleAPICalled::TypeEnum::TimeEnd;
56 case ConsoleAPIType::kCount:
57 return protocol::Runtime::ConsoleAPICalled::TypeEnum::Count;
59 return protocol::Runtime::ConsoleAPICalled::TypeEnum::Log;
62 const char kGlobalConsoleMessageHandleLabel[] =
"DevTools console";
63 const unsigned maxConsoleMessageCount = 1000;
64 const int maxConsoleMessageV8Size = 10 * 1024 * 1024;
65 const unsigned maxArrayItemsLimit = 10000;
66 const unsigned maxStackDepthLimit = 32;
68 class V8ValueStringBuilder {
72 V8ValueStringBuilder builder(context);
73 if (!builder.append(value))
return String16();
74 return builder.toString();
80 IgnoreUndefined = 1 << 1,
84 : m_arrayLimit(maxArrayItemsLimit),
85 m_isolate(context->GetIsolate()),
86 m_tryCatch(context->GetIsolate()),
90 if (value.IsEmpty())
return true;
91 if ((ignoreOptions & IgnoreNull) && value->IsNull())
return true;
92 if ((ignoreOptions & IgnoreUndefined) && value->IsUndefined())
return true;
94 if (value->IsStringObject())
97 if (value->IsBigIntObject())
100 if (value->IsSymbolObject())
102 if (value->IsNumberObject()) {
103 m_builder.append(String16::fromDouble(
107 if (value->IsBooleanObject()) {
114 if (value->IsProxy()) {
115 m_builder.append(
"[object Proxy]");
118 if (value->IsObject() && !value->IsDate() && !value->IsFunction() &&
119 !value->IsNativeError() && !value->IsRegExp()) {
123 return append(stringValue);
126 if (!value->ToString(m_context).ToLocal(&stringValue))
return false;
127 return append(stringValue);
131 for (
const auto& it : m_visitedArrays) {
132 if (it == array)
return true;
135 if (length > m_arrayLimit)
return false;
136 if (m_visitedArrays.size() > maxStackDepthLimit)
return false;
139 m_arrayLimit -= length;
140 m_visitedArrays.push_back(array);
142 if (
i) m_builder.append(
',');
144 if (!array->Get(m_context,
i).
ToLocal(&value))
continue;
145 if (!append(value, IgnoreNull | IgnoreUndefined)) {
150 m_visitedArrays.pop_back();
155 m_builder.append(
"Symbol(");
156 bool result = append(symbol->
Name(), IgnoreUndefined);
157 m_builder.append(
')');
163 if (!bigint->ToString(m_context).ToLocal(&bigint_string))
return false;
164 bool result = append(bigint_string);
165 if (m_tryCatch.HasCaught())
return false;
166 m_builder.append(
'n');
171 if (m_tryCatch.HasCaught())
return false;
172 if (!
string.IsEmpty()) {
173 m_builder.append(toProtocolString(m_isolate,
string));
178 String16 toString() {
179 if (m_tryCatch.HasCaught())
return String16();
180 return m_builder.toString();
184 v8::Isolate* m_isolate;
185 String16Builder m_builder;
186 std::vector<v8::Local<v8::Array>> m_visitedArrays;
187 v8::TryCatch m_tryCatch;
193 V8ConsoleMessage::V8ConsoleMessage(V8MessageOrigin origin,
double timestamp,
194 const String16& message)
196 m_timestamp(timestamp),
202 m_type(ConsoleAPIType::kLog),
204 m_revokedExceptionId(0) {}
206 V8ConsoleMessage::~V8ConsoleMessage() =
default;
208 void V8ConsoleMessage::setLocation(
const String16& url,
unsigned lineNumber,
209 unsigned columnNumber,
210 std::unique_ptr<V8StackTraceImpl> stackTrace,
213 m_lineNumber = lineNumber;
214 m_columnNumber = columnNumber;
215 m_stackTrace = std::move(stackTrace);
216 m_scriptId = scriptId;
219 void V8ConsoleMessage::reportToFrontend(
220 protocol::Console::Frontend* frontend)
const {
221 DCHECK_EQ(V8MessageOrigin::kConsole, m_origin);
222 String16 level = protocol::Console::ConsoleMessage::LevelEnum::Log;
223 if (m_type == ConsoleAPIType::kDebug || m_type == ConsoleAPIType::kCount ||
224 m_type == ConsoleAPIType::kTimeEnd)
225 level = protocol::Console::ConsoleMessage::LevelEnum::Debug;
226 else if (m_type == ConsoleAPIType::kError ||
227 m_type == ConsoleAPIType::kAssert)
228 level = protocol::Console::ConsoleMessage::LevelEnum::Error;
229 else if (m_type == ConsoleAPIType::kWarning)
230 level = protocol::Console::ConsoleMessage::LevelEnum::Warning;
231 else if (m_type == ConsoleAPIType::kInfo)
232 level = protocol::Console::ConsoleMessage::LevelEnum::Info;
233 std::unique_ptr<protocol::Console::ConsoleMessage> result =
234 protocol::Console::ConsoleMessage::create()
235 .setSource(protocol::Console::ConsoleMessage::SourceEnum::ConsoleApi)
239 result->setLine(static_cast<int>(m_lineNumber));
240 result->setColumn(static_cast<int>(m_columnNumber));
241 result->setUrl(m_url);
242 frontend->messageAdded(std::move(result));
245 std::unique_ptr<protocol::Array<protocol::Runtime::RemoteObject>>
246 V8ConsoleMessage::wrapArguments(V8InspectorSessionImpl* session,
247 bool generatePreview)
const {
248 V8InspectorImpl* inspector = session->inspector();
249 int contextGroupId = session->contextGroupId();
250 int contextId = m_contextId;
251 if (!m_arguments.size() || !contextId)
return nullptr;
252 InspectedContext* inspectedContext =
253 inspector->getContext(contextGroupId, contextId);
254 if (!inspectedContext)
return nullptr;
256 v8::Isolate* isolate = inspectedContext->isolate();
260 std::unique_ptr<protocol::Array<protocol::Runtime::RemoteObject>> args =
261 protocol::Array<protocol::Runtime::RemoteObject>::create();
264 if (value->IsObject() && m_type == ConsoleAPIType::kTable &&
267 if (m_arguments.size() > 1) {
269 if (secondArgument->
IsArray()) {
271 }
else if (secondArgument->
IsString()) {
272 v8::TryCatch tryCatch(isolate);
274 if (array->Set(context, 0, secondArgument).IsJust()) {
279 std::unique_ptr<protocol::Runtime::RemoteObject> wrapped =
282 inspectedContext = inspector->getContext(contextGroupId, contextId);
283 if (!inspectedContext)
return nullptr;
285 args->addItem(std::move(wrapped));
290 for (
size_t i = 0;
i < m_arguments.size(); ++
i) {
291 std::unique_ptr<protocol::Runtime::RemoteObject> wrapped =
292 session->wrapObject(context, m_arguments[
i]->Get(isolate),
"console",
294 inspectedContext = inspector->getContext(contextGroupId, contextId);
295 if (!inspectedContext)
return nullptr;
300 args->addItem(std::move(wrapped));
306 void V8ConsoleMessage::reportToFrontend(protocol::Runtime::Frontend* frontend,
307 V8InspectorSessionImpl* session,
308 bool generatePreview)
const {
309 int contextGroupId = session->contextGroupId();
310 V8InspectorImpl* inspector = session->inspector();
312 if (m_origin == V8MessageOrigin::kException) {
313 std::unique_ptr<protocol::Runtime::RemoteObject> exception =
314 wrapException(session, generatePreview);
315 if (!inspector->hasConsoleMessageStorage(contextGroupId))
return;
316 std::unique_ptr<protocol::Runtime::ExceptionDetails> exceptionDetails =
317 protocol::Runtime::ExceptionDetails::create()
318 .setExceptionId(m_exceptionId)
319 .setText(exception ? m_message : m_detailedMessage)
320 .setLineNumber(m_lineNumber ? m_lineNumber - 1 : 0)
321 .setColumnNumber(m_columnNumber ? m_columnNumber - 1 : 0)
324 exceptionDetails->setScriptId(String16::fromInteger(m_scriptId));
325 if (!m_url.isEmpty()) exceptionDetails->setUrl(m_url);
327 exceptionDetails->setStackTrace(
328 m_stackTrace->buildInspectorObjectImpl(inspector->debugger()));
330 if (m_contextId) exceptionDetails->setExecutionContextId(m_contextId);
331 if (exception) exceptionDetails->setException(std::move(exception));
332 frontend->exceptionThrown(m_timestamp, std::move(exceptionDetails));
335 if (m_origin == V8MessageOrigin::kRevokedException) {
336 frontend->exceptionRevoked(m_message, m_revokedExceptionId);
339 if (m_origin == V8MessageOrigin::kConsole) {
340 std::unique_ptr<protocol::Array<protocol::Runtime::RemoteObject>>
341 arguments = wrapArguments(session, generatePreview);
342 if (!inspector->hasConsoleMessageStorage(contextGroupId))
return;
344 arguments = protocol::Array<protocol::Runtime::RemoteObject>::create();
345 if (!m_message.isEmpty()) {
346 std::unique_ptr<protocol::Runtime::RemoteObject> messageArg =
347 protocol::Runtime::RemoteObject::create()
348 .setType(protocol::Runtime::RemoteObject::TypeEnum::String)
350 messageArg->setValue(protocol::StringValue::create(m_message));
351 arguments->addItem(std::move(messageArg));
354 Maybe<String16> consoleContext;
355 if (!m_consoleContext.isEmpty()) consoleContext = m_consoleContext;
356 frontend->consoleAPICalled(
357 consoleAPITypeValue(m_type), std::move(arguments), m_contextId,
360 ? m_stackTrace->buildInspectorObjectImpl(inspector->debugger())
362 std::move(consoleContext));
368 std::unique_ptr<protocol::Runtime::RemoteObject>
369 V8ConsoleMessage::wrapException(V8InspectorSessionImpl* session,
370 bool generatePreview)
const {
371 if (!m_arguments.size() || !m_contextId)
return nullptr;
372 DCHECK_EQ(1u, m_arguments.size());
373 InspectedContext* inspectedContext =
374 session->inspector()->getContext(session->contextGroupId(), m_contextId);
375 if (!inspectedContext)
return nullptr;
377 v8::Isolate* isolate = inspectedContext->isolate();
380 return session->wrapObject(inspectedContext->context(),
381 m_arguments[0]->Get(isolate),
"console",
385 V8MessageOrigin V8ConsoleMessage::origin()
const {
return m_origin; }
387 ConsoleAPIType V8ConsoleMessage::type()
const {
return m_type; }
390 std::unique_ptr<V8ConsoleMessage> V8ConsoleMessage::createForConsoleAPI(
392 V8InspectorImpl* inspector,
double timestamp, ConsoleAPIType type,
394 const String16& consoleContext,
395 std::unique_ptr<V8StackTraceImpl> stackTrace) {
396 v8::Isolate* isolate = v8Context->GetIsolate();
398 std::unique_ptr<V8ConsoleMessage> message(
399 new V8ConsoleMessage(V8MessageOrigin::kConsole, timestamp, String16()));
400 if (stackTrace && !stackTrace->isEmpty()) {
401 message->m_url = toString16(stackTrace->topSourceURL());
402 message->m_lineNumber = stackTrace->topLineNumber();
403 message->m_columnNumber = stackTrace->topColumnNumber();
405 message->m_stackTrace = std::move(stackTrace);
406 message->m_consoleContext = consoleContext;
407 message->m_type = type;
408 message->m_contextId = contextId;
409 for (
size_t i = 0;
i < arguments.size(); ++
i) {
410 std::unique_ptr<v8::Global<v8::Value>> argument(
412 argument->AnnotateStrongRetainer(kGlobalConsoleMessageHandleLabel);
413 message->m_arguments.push_back(std::move(argument));
415 v8::debug::EstimatedValueSize(isolate, arguments.at(
i));
417 if (arguments.size())
419 V8ValueStringBuilder::toString(arguments[0], v8Context);
421 v8::Isolate::MessageErrorLevel clientLevel = v8::Isolate::kMessageInfo;
422 if (type == ConsoleAPIType::kDebug || type == ConsoleAPIType::kCount ||
423 type == ConsoleAPIType::kTimeEnd) {
424 clientLevel = v8::Isolate::kMessageDebug;
425 }
else if (type == ConsoleAPIType::kError ||
426 type == ConsoleAPIType::kAssert) {
427 clientLevel = v8::Isolate::kMessageError;
428 }
else if (type == ConsoleAPIType::kWarning) {
429 clientLevel = v8::Isolate::kMessageWarning;
430 }
else if (type == ConsoleAPIType::kInfo || type == ConsoleAPIType::kLog) {
431 clientLevel = v8::Isolate::kMessageInfo;
434 if (type != ConsoleAPIType::kClear) {
435 inspector->client()->consoleAPIMessage(
436 groupId, clientLevel, toStringView(message->m_message),
437 toStringView(message->m_url), message->m_lineNumber,
438 message->m_columnNumber, message->m_stackTrace.get());
445 std::unique_ptr<V8ConsoleMessage> V8ConsoleMessage::createForException(
446 double timestamp,
const String16& detailedMessage,
const String16& url,
447 unsigned lineNumber,
unsigned columnNumber,
448 std::unique_ptr<V8StackTraceImpl> stackTrace,
int scriptId,
449 v8::Isolate* isolate,
const String16& message,
int contextId,
451 std::unique_ptr<V8ConsoleMessage> consoleMessage(
452 new V8ConsoleMessage(V8MessageOrigin::kException, timestamp, message));
453 consoleMessage->setLocation(url, lineNumber, columnNumber,
454 std::move(stackTrace), scriptId);
455 consoleMessage->m_exceptionId = exceptionId;
456 consoleMessage->m_detailedMessage = detailedMessage;
457 if (contextId && !exception.
IsEmpty()) {
458 consoleMessage->m_contextId = contextId;
459 consoleMessage->m_arguments.push_back(
462 consoleMessage->m_v8Size +=
463 v8::debug::EstimatedValueSize(isolate, exception);
465 return consoleMessage;
469 std::unique_ptr<V8ConsoleMessage> V8ConsoleMessage::createForRevokedException(
470 double timestamp,
const String16& messageText,
471 unsigned revokedExceptionId) {
472 std::unique_ptr<V8ConsoleMessage> message(
new V8ConsoleMessage(
473 V8MessageOrigin::kRevokedException, timestamp, messageText));
474 message->m_revokedExceptionId = revokedExceptionId;
478 void V8ConsoleMessage::contextDestroyed(
int contextId) {
479 if (contextId != m_contextId)
return;
481 if (m_message.isEmpty()) m_message =
"<message collected>";
483 m_arguments.swap(empty);
489 V8ConsoleMessageStorage::V8ConsoleMessageStorage(V8InspectorImpl* inspector,
491 : m_inspector(inspector), m_contextGroupId(contextGroupId) {}
493 V8ConsoleMessageStorage::~V8ConsoleMessageStorage() { clear(); }
497 void TraceV8ConsoleMessageEvent(V8MessageOrigin origin, ConsoleAPIType type) {
501 if (origin == V8MessageOrigin::kException) {
502 TRACE_EVENT_INSTANT0(
"v8.console",
"V8ConsoleMessage::Exception",
503 TRACE_EVENT_SCOPE_THREAD);
504 }
else if (type == ConsoleAPIType::kError) {
505 TRACE_EVENT_INSTANT0(
"v8.console",
"V8ConsoleMessage::Error",
506 TRACE_EVENT_SCOPE_THREAD);
507 }
else if (type == ConsoleAPIType::kAssert) {
508 TRACE_EVENT_INSTANT0(
"v8.console",
"V8ConsoleMessage::Assert",
509 TRACE_EVENT_SCOPE_THREAD);
515 void V8ConsoleMessageStorage::addMessage(
516 std::unique_ptr<V8ConsoleMessage> message) {
517 int contextGroupId = m_contextGroupId;
518 V8InspectorImpl* inspector = m_inspector;
519 if (message->type() == ConsoleAPIType::kClear) clear();
521 TraceV8ConsoleMessageEvent(message->origin(), message->type());
523 inspector->forEachSession(
524 contextGroupId, [&message](V8InspectorSessionImpl* session) {
525 if (message->origin() == V8MessageOrigin::kConsole)
526 session->consoleAgent()->messageAdded(message.get());
527 session->runtimeAgent()->messageAdded(message.get());
529 if (!inspector->hasConsoleMessageStorage(contextGroupId))
return;
531 DCHECK(m_messages.size() <= maxConsoleMessageCount);
532 if (m_messages.size() == maxConsoleMessageCount) {
533 m_estimatedSize -= m_messages.front()->estimatedSize();
534 m_messages.pop_front();
536 while (m_estimatedSize + message->estimatedSize() > maxConsoleMessageV8Size &&
537 !m_messages.empty()) {
538 m_estimatedSize -= m_messages.front()->estimatedSize();
539 m_messages.pop_front();
542 m_messages.push_back(std::move(message));
543 m_estimatedSize += m_messages.back()->estimatedSize();
546 void V8ConsoleMessageStorage::clear() {
549 m_inspector->forEachSession(m_contextGroupId,
550 [](V8InspectorSessionImpl* session) {
551 session->releaseObjectGroup(
"console");
556 bool V8ConsoleMessageStorage::shouldReportDeprecationMessage(
557 int contextId,
const String16& method) {
558 std::set<String16>& reportedDeprecationMessages =
559 m_data[contextId].m_reportedDeprecationMessages;
560 auto it = reportedDeprecationMessages.find(method);
561 if (it != reportedDeprecationMessages.end())
return false;
562 reportedDeprecationMessages.insert(it, method);
566 int V8ConsoleMessageStorage::count(
int contextId,
const String16&
id) {
567 return ++m_data[contextId].m_count[id];
570 void V8ConsoleMessageStorage::time(
int contextId,
const String16&
id) {
571 m_data[contextId].m_time[id] = m_inspector->client()->currentTimeMS();
574 bool V8ConsoleMessageStorage::countReset(
int contextId,
const String16&
id) {
575 std::map<String16, int>& count_map = m_data[contextId].m_count;
576 if (count_map.find(
id) == count_map.end())
return false;
582 double V8ConsoleMessageStorage::timeLog(
int contextId,
const String16&
id) {
583 std::map<String16, double>& time = m_data[contextId].m_time;
584 auto it = time.find(
id);
585 if (it == time.end())
return 0.0;
586 return m_inspector->client()->currentTimeMS() - it->second;
589 double V8ConsoleMessageStorage::timeEnd(
int contextId,
const String16&
id) {
590 std::map<String16, double>& time = m_data[contextId].m_time;
591 auto it = time.find(
id);
592 if (it == time.end())
return 0.0;
593 double elapsed = m_inspector->client()->currentTimeMS() - it->second;
598 bool V8ConsoleMessageStorage::hasTimer(
int contextId,
const String16&
id) {
599 const std::map<String16, double>& time = m_data[contextId].m_time;
600 return time.find(
id) != time.end();
603 void V8ConsoleMessageStorage::contextDestroyed(
int contextId) {
605 for (
size_t i = 0;
i < m_messages.size(); ++
i) {
606 m_messages[
i]->contextDestroyed(contextId);
607 m_estimatedSize += m_messages[
i]->estimatedSize();
609 auto it = m_data.find(contextId);
610 if (it != m_data.end()) m_data.erase(contextId);
V8_INLINE bool IsString() const
static V8_INLINE Local< T > Cast(Local< S > that)
V8_INLINE bool IsEmpty() const
static Local< Array > New(Isolate *isolate, int length=0)
V8_WARN_UNUSED_RESULT V8_INLINE bool ToLocal(Local< S > *out) const
V8_WARN_UNUSED_RESULT MaybeLocal< String > ObjectProtoToString(Local< Context > context)
Local< Value > Name() const