Commit cda92a61 authored by Sigurd Schneider's avatar Sigurd Schneider Committed by V8 LUCI CQ

Add exception metadata handling to V8 inspector

This interface allows associating meta information to
exceptions. This meta information can be used by debugging
tools, like DevTools, to learn about e.g. a network request
or a DevTools issue that is associated with the exception.
To do so the inspector client (i.e. embedder) has to provide
the data.

Bug: chromium:1213393
Change-Id: Ia86221f4f04b21024d592bafb2f74886ead8a6a8
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2928496
Commit-Queue: Sigurd Schneider <sigurds@chromium.org>
Reviewed-by: 's avatarPhilip Pfaffe <pfaffe@chromium.org>
Cr-Commit-Position: refs/heads/master@{#74909}
parent 96f3103a
......@@ -1254,6 +1254,10 @@ domain Runtime
optional RemoteObject exception
# Identifier of the context where exception happened.
optional ExecutionContextId executionContextId
# Dictionary with entries of meta deta that the client associated
# with this exception, such as information about associated network
# requests, etc.
experimental optional object exceptionMetaData
# Number of milliseconds since epoch.
type Timestamp extends number
......
......@@ -301,6 +301,10 @@ class V8_EXPORT V8Inspector {
int scriptId) = 0;
virtual void exceptionRevoked(v8::Local<v8::Context>, unsigned exceptionId,
StringView message) = 0;
virtual bool associateExceptionData(v8::Local<v8::Context>,
v8::Local<v8::Value> exception,
v8::Local<v8::Name> key,
v8::Local<v8::Value> value) = 0;
// Connection.
class V8_EXPORT Channel {
......
......@@ -4,6 +4,7 @@
#include "src/inspector/v8-console-message.h"
#include "include/v8-inspector.h"
#include "src/debug/debug-interface.h"
#include "src/inspector/inspected-context.h"
#include "src/inspector/protocol/Protocol.h"
......@@ -13,10 +14,9 @@
#include "src/inspector/v8-inspector-session-impl.h"
#include "src/inspector/v8-runtime-agent-impl.h"
#include "src/inspector/v8-stack-trace-impl.h"
#include "src/inspector/value-mirror.h"
#include "src/tracing/trace-event.h"
#include "include/v8-inspector.h"
namespace v8_inspector {
namespace {
......@@ -332,6 +332,8 @@ void V8ConsoleMessage::reportToFrontend(protocol::Runtime::Frontend* frontend,
}
if (m_contextId) exceptionDetails->setExecutionContextId(m_contextId);
if (exception) exceptionDetails->setException(std::move(exception));
auto data = getAssociatedExceptionData(inspector, session);
if (data) exceptionDetails->setExceptionMetaData(std::move(data));
frontend->exceptionThrown(m_timestamp, std::move(exceptionDetails));
return;
}
......@@ -381,6 +383,31 @@ void V8ConsoleMessage::reportToFrontend(protocol::Runtime::Frontend* frontend,
UNREACHABLE();
}
std::unique_ptr<protocol::DictionaryValue>
V8ConsoleMessage::getAssociatedExceptionData(
V8InspectorImpl* inspector, V8InspectorSessionImpl* session) const {
if (!m_arguments.size() || !m_contextId) return nullptr;
DCHECK_EQ(1u, m_arguments.size());
InspectedContext* inspectedContext =
session->inspector()->getContext(session->contextGroupId(), m_contextId);
if (!inspectedContext) return nullptr;
v8::Isolate* isolate = inspectedContext->isolate();
v8::HandleScope handles(isolate);
v8::MaybeLocal<v8::Value> maybe_exception = m_arguments[0]->Get(isolate);
v8::Local<v8::Value> exception;
if (!maybe_exception.ToLocal(&exception)) return nullptr;
v8::MaybeLocal<v8::Object> maybe_data = inspector->getAssociatedExceptionData(
inspectedContext->context(), exception);
v8::Local<v8::Object> data;
if (!maybe_data.ToLocal(&data)) return nullptr;
std::unique_ptr<protocol::DictionaryValue> jsonObject;
objectToProtocolValue(inspectedContext->context(), data, 2, &jsonObject);
return jsonObject;
}
std::unique_ptr<protocol::Runtime::RemoteObject>
V8ConsoleMessage::wrapException(V8InspectorSessionImpl* session,
bool generatePreview) const {
......@@ -502,7 +529,8 @@ void V8ConsoleMessage::contextDestroyed(int contextId) {
m_v8Size = 0;
}
// ------------------------ V8ConsoleMessageStorage ----------------------------
// ------------------------ V8ConsoleMessageStorage
// ----------------------------
V8ConsoleMessageStorage::V8ConsoleMessageStorage(V8InspectorImpl* inspector,
int contextGroupId)
......
......@@ -85,6 +85,8 @@ class V8ConsoleMessage {
void setLocation(const String16& url, unsigned lineNumber,
unsigned columnNumber, std::unique_ptr<V8StackTraceImpl>,
int scriptId);
std::unique_ptr<protocol::DictionaryValue> getAssociatedExceptionData(
V8InspectorImpl* inspector, V8InspectorSessionImpl* session) const;
V8MessageOrigin m_origin;
double m_timestamp;
......
......@@ -32,7 +32,9 @@
#include <vector>
#include "include/v8-platform.h"
#include "src/base/platform/mutex.h"
#include "src/debug/debug-interface.h"
#include "src/inspector/inspected-context.h"
#include "src/inspector/string-util.h"
#include "src/inspector/v8-console-agent-impl.h"
......@@ -45,8 +47,6 @@
#include "src/inspector/v8-runtime-agent-impl.h"
#include "src/inspector/v8-stack-trace-impl.h"
#include "include/v8-platform.h"
namespace v8_inspector {
std::unique_ptr<V8Inspector> V8Inspector::create(v8::Isolate* isolate,
......@@ -492,4 +492,39 @@ protocol::Response V8InspectorImpl::EvaluateScope::setTimeout(double timeout) {
return protocol::Response::Success();
}
bool V8InspectorImpl::associateExceptionData(v8::Local<v8::Context> context,
v8::Local<v8::Value> exception,
v8::Local<v8::Name> key,
v8::Local<v8::Value> value) {
v8::HandleScope handles(m_isolate);
if (m_excepetionMetaData.IsEmpty())
m_excepetionMetaData.Reset(m_isolate, v8::debug::WeakMap::New(m_isolate));
v8::Local<v8::debug::WeakMap> map = m_excepetionMetaData.Get(m_isolate);
v8::MaybeLocal<v8::Value> entry = map->Get(context, exception);
v8::Local<v8::Object> object;
if (entry.IsEmpty() || !entry.ToLocalChecked()->IsObject()) {
object = v8::Object::New(m_isolate);
v8::MaybeLocal<v8::debug::WeakMap> new_map =
map->Set(context, exception, object);
if (!new_map.IsEmpty()) {
m_excepetionMetaData.Reset(m_isolate, new_map.ToLocalChecked());
}
} else {
object = entry.ToLocalChecked().As<v8::Object>();
}
CHECK(object->IsObject());
v8::Maybe<bool> result = object->CreateDataProperty(context, key, value);
return result.FromMaybe(false);
}
v8::MaybeLocal<v8::Object> V8InspectorImpl::getAssociatedExceptionData(
v8::Local<v8::Context> context, v8::Local<v8::Value> exception) {
if (m_excepetionMetaData.IsEmpty()) return v8::MaybeLocal<v8::Object>();
v8::Local<v8::debug::WeakMap> map = m_excepetionMetaData.Get(m_isolate);
auto entry = map->Get(context, exception);
if (entry.IsEmpty()) return v8::MaybeLocal<v8::Object>();
return entry.ToLocalChecked().As<v8::Object>();
}
} // namespace v8_inspector
......@@ -112,6 +112,11 @@ class V8InspectorImpl : public V8Inspector {
std::shared_ptr<Counters> enableCounters() override;
bool associateExceptionData(v8::Local<v8::Context>,
v8::Local<v8::Value> exception,
v8::Local<v8::Name> key,
v8::Local<v8::Value> value) override;
unsigned nextExceptionId() { return ++m_lastExceptionId; }
void enableStackCapturingIfNeeded();
void disableStackCapturingIfNeeded();
......@@ -131,6 +136,8 @@ class V8InspectorImpl : public V8Inspector {
int contextGroupId,
const std::function<void(V8InspectorSessionImpl*)>& callback);
int64_t generateUniqueId();
v8::MaybeLocal<v8::Object> getAssociatedExceptionData(
v8::Local<v8::Context> context, v8::Local<v8::Value> exception);
class EvaluateScope {
public:
......@@ -156,6 +163,7 @@ class V8InspectorImpl : public V8Inspector {
V8InspectorClient* m_client;
std::unique_ptr<V8Debugger> m_debugger;
v8::Global<v8::Context> m_regexContext;
v8::Global<v8::debug::WeakMap> m_excepetionMetaData;
int m_capturingStackTracesCount;
unsigned m_lastExceptionId;
int m_lastContextId;
......
......@@ -21,28 +21,6 @@ using protocol::Runtime::ObjectPreview;
using protocol::Runtime::PropertyPreview;
using protocol::Runtime::RemoteObject;
namespace {
// WebAssembly memory is organized in pages of size 64KiB.
const size_t kWasmPageSize = 64 * 1024;
V8InspectorClient* clientFor(v8::Local<v8::Context> context) {
return static_cast<V8InspectorImpl*>(
v8::debug::GetInspector(context->GetIsolate()))
->client();
}
V8InternalValueType v8InternalValueTypeFrom(v8::Local<v8::Context> context,
v8::Local<v8::Value> value) {
if (!value->IsObject()) return V8InternalValueType::kNone;
V8InspectorImpl* inspector = static_cast<V8InspectorImpl*>(
v8::debug::GetInspector(context->GetIsolate()));
int contextId = InspectedContext::contextId(context);
InspectedContext* inspectedContext = inspector->getContext(contextId);
if (!inspectedContext) return V8InternalValueType::kNone;
return inspectedContext->getInternalType(value.As<v8::Object>());
}
Response toProtocolValue(v8::Local<v8::Context> context,
v8::Local<v8::Value> value, int maxDepth,
std::unique_ptr<protocol::Value>* result);
......@@ -174,6 +152,28 @@ Response toProtocolValue(v8::Local<v8::Context> context,
return toProtocolValue(context, value, kMaxDepth, result);
}
namespace {
// WebAssembly memory is organized in pages of size 64KiB.
const size_t kWasmPageSize = 64 * 1024;
V8InspectorClient* clientFor(v8::Local<v8::Context> context) {
return static_cast<V8InspectorImpl*>(
v8::debug::GetInspector(context->GetIsolate()))
->client();
}
V8InternalValueType v8InternalValueTypeFrom(v8::Local<v8::Context> context,
v8::Local<v8::Value> value) {
if (!value->IsObject()) return V8InternalValueType::kNone;
V8InspectorImpl* inspector = static_cast<V8InspectorImpl*>(
v8::debug::GetInspector(context->GetIsolate()));
int contextId = InspectedContext::contextId(context);
InspectedContext* inspectedContext = inspector->getContext(contextId);
if (!inspectedContext) return V8InternalValueType::kNone;
return inspectedContext->getInternalType(value.As<v8::Object>());
}
enum AbbreviateMode { kMiddle, kEnd };
String16 abbreviateString(const String16& value, AbbreviateMode mode) {
......
......@@ -7,14 +7,13 @@
#include <memory>
#include "include/v8-inspector.h"
#include "include/v8.h"
#include "src/base/macros.h"
#include "src/inspector/protocol/Protocol.h"
#include "src/inspector/protocol/Runtime.h"
#include "src/inspector/string-16.h"
#include "include/v8-inspector.h"
#include "include/v8.h"
namespace v8_inspector {
class ValueMirror;
......@@ -82,6 +81,17 @@ class ValueMirror {
static std::vector<PrivatePropertyMirror> getPrivateProperties(
v8::Local<v8::Context> context, v8::Local<v8::Object> object);
};
protocol::Response toProtocolValue(v8::Local<v8::Context> context,
v8::Local<v8::Value> value, int maxDepth,
std::unique_ptr<protocol::Value>* result);
protocol::Response arrayToProtocolValue(
v8::Local<v8::Context> context, v8::Local<v8::Array> array, int maxDepth,
std::unique_ptr<protocol::ListValue>* result);
protocol::Response objectToProtocolValue(
v8::Local<v8::Context> context, v8::Local<v8::Object> object, int maxDepth,
std::unique_ptr<protocol::DictionaryValue>* result);
} // namespace v8_inspector
#endif // V8_INSPECTOR_VALUE_MIRROR_H_
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment