Commit 6f60c362 authored by Dmitry Gozman's avatar Dmitry Gozman Committed by Commit Bot

inspector: V8StackTraceId serialization/deserialization

This makes it possible to plumb string representation of stack trace id
across various channels, e.g. for network requests.

Drive-by: extracted class V8DebuggerId, which encapsulates operations
with pair<int64_t, int64_t>.

Bug: chromium:988842
Change-Id: I348c91390a85bf07c746d1b1c4a7775f44c7d769
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1725193
Commit-Queue: Dmitry Gozman <dgozman@chromium.org>
Reviewed-by: 's avatarYang Guo <yangguo@chromium.org>
Reviewed-by: 's avatarAleksey Kozyatinskiy <kozyatinskiy@chromium.org>
Cr-Commit-Position: refs/heads/master@{#63900}
parent 8d1b4774
...@@ -24,6 +24,7 @@ namespace Runtime { ...@@ -24,6 +24,7 @@ namespace Runtime {
namespace API { namespace API {
class RemoteObject; class RemoteObject;
class StackTrace; class StackTrace;
class StackTraceId;
} }
} }
namespace Schema { namespace Schema {
...@@ -236,11 +237,13 @@ struct V8_EXPORT V8StackTraceId { ...@@ -236,11 +237,13 @@ struct V8_EXPORT V8StackTraceId {
V8StackTraceId(uintptr_t id, const std::pair<int64_t, int64_t> debugger_id); V8StackTraceId(uintptr_t id, const std::pair<int64_t, int64_t> debugger_id);
V8StackTraceId(uintptr_t id, const std::pair<int64_t, int64_t> debugger_id, V8StackTraceId(uintptr_t id, const std::pair<int64_t, int64_t> debugger_id,
bool should_pause); bool should_pause);
explicit V8StackTraceId(const StringView&);
V8StackTraceId& operator=(const V8StackTraceId&) = default; V8StackTraceId& operator=(const V8StackTraceId&) = default;
V8StackTraceId& operator=(V8StackTraceId&&) noexcept = default; V8StackTraceId& operator=(V8StackTraceId&&) noexcept = default;
~V8StackTraceId() = default; ~V8StackTraceId() = default;
bool IsInvalid() const; bool IsInvalid() const;
std::unique_ptr<StringBuffer> ToString();
}; };
class V8_EXPORT V8Inspector { class V8_EXPORT V8Inspector {
......
...@@ -83,6 +83,13 @@ String16 String16::fromInteger(size_t number) { ...@@ -83,6 +83,13 @@ String16 String16::fromInteger(size_t number) {
return String16(buffer); return String16(buffer);
} }
// static
String16 String16::fromInteger64(int64_t number) {
char buffer[50];
v8::base::OS::SNPrintF(buffer, arraysize(buffer), "%" PRId64 "", number);
return String16(buffer);
}
// static // static
String16 String16::fromDouble(double number) { String16 String16::fromDouble(double number) {
char arr[50]; char arr[50];
......
...@@ -37,6 +37,7 @@ class String16 { ...@@ -37,6 +37,7 @@ class String16 {
static String16 fromInteger(int); static String16 fromInteger(int);
static String16 fromInteger(size_t); static String16 fromInteger(size_t);
static String16 fromInteger64(int64_t);
static String16 fromDouble(double); static String16 fromDouble(double);
static String16 fromDouble(double, int precision); static String16 fromDouble(double, int precision);
......
...@@ -169,15 +169,6 @@ StringBufferImpl::StringBufferImpl(String16& string) { ...@@ -169,15 +169,6 @@ StringBufferImpl::StringBufferImpl(String16& string) {
m_string = toStringView(m_owner); m_string = toStringView(m_owner);
} }
String16 debuggerIdToString(const std::pair<int64_t, int64_t>& debuggerId) {
const size_t kBufferSize = 35;
char buffer[kBufferSize];
v8::base::OS::SNPrintF(buffer, kBufferSize, "(%08" PRIX64 "%08" PRIX64 ")",
debuggerId.first, debuggerId.second);
return String16(buffer);
}
String16 stackTraceIdToString(uintptr_t id) { String16 stackTraceIdToString(uintptr_t id) {
String16Builder builder; String16Builder builder;
builder.appendNumber(static_cast<size_t>(id)); builder.appendNumber(static_cast<size_t>(id));
......
...@@ -149,7 +149,6 @@ class BinaryStringBuffer : public StringBuffer { ...@@ -149,7 +149,6 @@ class BinaryStringBuffer : public StringBuffer {
DISALLOW_COPY_AND_ASSIGN(BinaryStringBuffer); DISALLOW_COPY_AND_ASSIGN(BinaryStringBuffer);
}; };
String16 debuggerIdToString(const std::pair<int64_t, int64_t>& debuggerId);
String16 stackTraceIdToString(uintptr_t id); String16 stackTraceIdToString(uintptr_t id);
} // namespace v8_inspector } // namespace v8_inspector
......
...@@ -353,8 +353,8 @@ Response V8DebuggerAgentImpl::enable(Maybe<double> maxScriptsCacheSize, ...@@ -353,8 +353,8 @@ Response V8DebuggerAgentImpl::enable(Maybe<double> maxScriptsCacheSize,
String16* outDebuggerId) { String16* outDebuggerId) {
m_maxScriptCacheSize = v8::base::saturated_cast<size_t>( m_maxScriptCacheSize = v8::base::saturated_cast<size_t>(
maxScriptsCacheSize.fromMaybe(std::numeric_limits<double>::max())); maxScriptsCacheSize.fromMaybe(std::numeric_limits<double>::max()));
*outDebuggerId = debuggerIdToString( *outDebuggerId =
m_debugger->debuggerIdFor(m_session->contextGroupId())); m_debugger->debuggerIdFor(m_session->contextGroupId()).toString();
if (enabled()) return Response::OK(); if (enabled()) return Response::OK();
if (!m_inspector->client()->canExecuteScripts(m_session->contextGroupId())) if (!m_inspector->client()->canExecuteScripts(m_session->contextGroupId()))
...@@ -752,17 +752,19 @@ Response V8DebuggerAgentImpl::getStackTrace( ...@@ -752,17 +752,19 @@ Response V8DebuggerAgentImpl::getStackTrace(
std::unique_ptr<protocol::Runtime::StackTrace>* outStackTrace) { std::unique_ptr<protocol::Runtime::StackTrace>* outStackTrace) {
bool isOk = false; bool isOk = false;
int64_t id = inStackTraceId->getId().toInteger64(&isOk); int64_t id = inStackTraceId->getId().toInteger64(&isOk);
std::pair<int64_t, int64_t> debuggerId; if (!isOk) return Response::Error("Invalid stack trace id");
V8DebuggerId debuggerId;
if (inStackTraceId->hasDebuggerId()) { if (inStackTraceId->hasDebuggerId()) {
debuggerId = debuggerId = V8DebuggerId(inStackTraceId->getDebuggerId(String16()));
m_debugger->debuggerIdFor(inStackTraceId->getDebuggerId(String16()));
} else { } else {
debuggerId = m_debugger->debuggerIdFor(m_session->contextGroupId()); debuggerId = m_debugger->debuggerIdFor(m_session->contextGroupId());
} }
V8StackTraceId v8StackTraceId(id, debuggerId); if (!debuggerId.isValid()) return Response::Error("Invalid stack trace id");
if (!isOk || v8StackTraceId.IsInvalid()) {
V8StackTraceId v8StackTraceId(id, debuggerId.pair());
if (v8StackTraceId.IsInvalid())
return Response::Error("Invalid stack trace id"); return Response::Error("Invalid stack trace id");
}
auto stack = auto stack =
m_debugger->stackTraceFor(m_session->contextGroupId(), v8StackTraceId); m_debugger->stackTraceFor(m_session->contextGroupId(), v8StackTraceId);
if (!stack) { if (!stack) {
...@@ -1366,7 +1368,7 @@ V8DebuggerAgentImpl::currentExternalStackTrace() { ...@@ -1366,7 +1368,7 @@ V8DebuggerAgentImpl::currentExternalStackTrace() {
if (externalParent.IsInvalid()) return nullptr; if (externalParent.IsInvalid()) return nullptr;
return protocol::Runtime::StackTraceId::create() return protocol::Runtime::StackTraceId::create()
.setId(stackTraceIdToString(externalParent.id)) .setId(stackTraceIdToString(externalParent.id))
.setDebuggerId(debuggerIdToString(externalParent.debugger_id)) .setDebuggerId(V8DebuggerId(externalParent.debugger_id).toString())
.build(); .build();
} }
......
...@@ -64,6 +64,42 @@ class MatchPrototypePredicate : public v8::debug::QueryObjectPredicate { ...@@ -64,6 +64,42 @@ class MatchPrototypePredicate : public v8::debug::QueryObjectPredicate {
} // namespace } // namespace
V8DebuggerId::V8DebuggerId(std::pair<int64_t, int64_t> pair)
: m_first(pair.first), m_second(pair.second) {}
// static
V8DebuggerId V8DebuggerId::generate(v8::Isolate* isolate) {
V8DebuggerId debuggerId;
debuggerId.m_first = v8::debug::GetNextRandomInt64(isolate);
debuggerId.m_second = v8::debug::GetNextRandomInt64(isolate);
if (!debuggerId.m_first && !debuggerId.m_second) ++debuggerId.m_first;
return debuggerId;
}
V8DebuggerId::V8DebuggerId(const String16& debuggerId) {
const UChar dot = '.';
size_t pos = debuggerId.find(dot);
if (pos == String16::kNotFound) return;
bool ok = false;
int64_t first = debuggerId.substring(0, pos).toInteger64(&ok);
if (!ok) return;
int64_t second = debuggerId.substring(pos + 1).toInteger64(&ok);
if (!ok) return;
m_first = first;
m_second = second;
}
String16 V8DebuggerId::toString() const {
return String16::fromInteger64(m_first) + "." +
String16::fromInteger64(m_second);
}
bool V8DebuggerId::isValid() const { return m_first || m_second; }
std::pair<int64_t, int64_t> V8DebuggerId::pair() const {
return std::make_pair(m_first, m_second);
}
V8Debugger::V8Debugger(v8::Isolate* isolate, V8InspectorImpl* inspector) V8Debugger::V8Debugger(v8::Isolate* isolate, V8InspectorImpl* inspector)
: m_isolate(isolate), : m_isolate(isolate),
m_inspector(inspector), m_inspector(inspector),
...@@ -787,7 +823,7 @@ void V8Debugger::setAsyncCallStackDepth(V8DebuggerAgentImpl* agent, int depth) { ...@@ -787,7 +823,7 @@ void V8Debugger::setAsyncCallStackDepth(V8DebuggerAgentImpl* agent, int depth) {
std::shared_ptr<AsyncStackTrace> V8Debugger::stackTraceFor( std::shared_ptr<AsyncStackTrace> V8Debugger::stackTraceFor(
int contextGroupId, const V8StackTraceId& id) { int contextGroupId, const V8StackTraceId& id) {
if (debuggerIdFor(contextGroupId) != id.debugger_id) return nullptr; if (debuggerIdFor(contextGroupId).pair() != id.debugger_id) return nullptr;
auto it = m_storedStackTraces.find(id.id); auto it = m_storedStackTraces.find(id.id);
if (it == m_storedStackTraces.end()) return nullptr; if (it == m_storedStackTraces.end()) return nullptr;
return it->second.lock(); return it->second.lock();
...@@ -818,7 +854,7 @@ V8StackTraceId V8Debugger::storeCurrentStackTrace( ...@@ -818,7 +854,7 @@ V8StackTraceId V8Debugger::storeCurrentStackTrace(
m_pauseOnAsyncCall = false; m_pauseOnAsyncCall = false;
v8::debug::ClearStepping(m_isolate); // Cancel step into. v8::debug::ClearStepping(m_isolate); // Cancel step into.
} }
return V8StackTraceId(id, debuggerIdFor(contextGroupId), shouldPause); return V8StackTraceId(id, debuggerIdFor(contextGroupId).pair(), shouldPause);
} }
uintptr_t V8Debugger::storeStackTrace( uintptr_t V8Debugger::storeStackTrace(
...@@ -1061,27 +1097,15 @@ void V8Debugger::setMaxAsyncTaskStacksForTest(int limit) { ...@@ -1061,27 +1097,15 @@ void V8Debugger::setMaxAsyncTaskStacksForTest(int limit) {
m_maxAsyncCallStacks = limit; m_maxAsyncCallStacks = limit;
} }
std::pair<int64_t, int64_t> V8Debugger::debuggerIdFor(int contextGroupId) { V8DebuggerId V8Debugger::debuggerIdFor(int contextGroupId) {
auto it = m_contextGroupIdToDebuggerId.find(contextGroupId); auto it = m_contextGroupIdToDebuggerId.find(contextGroupId);
if (it != m_contextGroupIdToDebuggerId.end()) return it->second; if (it != m_contextGroupIdToDebuggerId.end()) return it->second;
std::pair<int64_t, int64_t> debuggerId( V8DebuggerId debuggerId = V8DebuggerId::generate(m_isolate);
v8::debug::GetNextRandomInt64(m_isolate),
v8::debug::GetNextRandomInt64(m_isolate));
if (!debuggerId.first && !debuggerId.second) ++debuggerId.first;
m_contextGroupIdToDebuggerId.insert( m_contextGroupIdToDebuggerId.insert(
it, std::make_pair(contextGroupId, debuggerId)); it, std::make_pair(contextGroupId, debuggerId));
m_serializedDebuggerIdToDebuggerId.insert(
std::make_pair(debuggerIdToString(debuggerId), debuggerId));
return debuggerId; return debuggerId;
} }
std::pair<int64_t, int64_t> V8Debugger::debuggerIdFor(
const String16& serializedDebuggerId) {
auto it = m_serializedDebuggerIdToDebuggerId.find(serializedDebuggerId);
if (it != m_serializedDebuggerIdToDebuggerId.end()) return it->second;
return std::make_pair(0, 0);
}
bool V8Debugger::addInternalObject(v8::Local<v8::Context> context, bool V8Debugger::addInternalObject(v8::Local<v8::Context> context,
v8::Local<v8::Object> object, v8::Local<v8::Object> object,
V8InternalValueType type) { V8InternalValueType type) {
......
...@@ -37,6 +37,31 @@ using protocol::Response; ...@@ -37,6 +37,31 @@ using protocol::Response;
using TerminateExecutionCallback = using TerminateExecutionCallback =
protocol::Runtime::Backend::TerminateExecutionCallback; protocol::Runtime::Backend::TerminateExecutionCallback;
// This debugger id tries to be unique by generating two random
// numbers, which should most likely avoid collisions.
// Debugger id has a 1:1 mapping to context group. It is used to
// attribute stack traces to a particular debugging, when doing any
// cross-debugger operations (e.g. async step in).
// See also Runtime.UniqueDebuggerId in the protocol.
class V8DebuggerId {
public:
V8DebuggerId() = default;
explicit V8DebuggerId(std::pair<int64_t, int64_t>);
explicit V8DebuggerId(const String16&);
V8DebuggerId(const V8DebuggerId&) V8_NOEXCEPT = default;
~V8DebuggerId() = default;
static V8DebuggerId generate(v8::Isolate*);
String16 toString() const;
bool isValid() const;
std::pair<int64_t, int64_t> pair() const;
private:
int64_t m_first = 0;
int64_t m_second = 0;
};
class V8Debugger : public v8::debug::DebugDelegate, class V8Debugger : public v8::debug::DebugDelegate,
public v8::debug::AsyncEventDelegate { public v8::debug::AsyncEventDelegate {
public: public:
...@@ -120,9 +145,7 @@ class V8Debugger : public v8::debug::DebugDelegate, ...@@ -120,9 +145,7 @@ class V8Debugger : public v8::debug::DebugDelegate,
void setMaxAsyncTaskStacksForTest(int limit); void setMaxAsyncTaskStacksForTest(int limit);
void dumpAsyncTaskStacksStateForTest(); void dumpAsyncTaskStacksStateForTest();
std::pair<int64_t, int64_t> debuggerIdFor(int contextGroupId); V8DebuggerId debuggerIdFor(int contextGroupId);
std::pair<int64_t, int64_t> debuggerIdFor(
const String16& serializedDebuggerId);
std::shared_ptr<AsyncStackTrace> stackTraceFor(int contextGroupId, std::shared_ptr<AsyncStackTrace> stackTraceFor(int contextGroupId,
const V8StackTraceId& id); const V8StackTraceId& id);
...@@ -247,10 +270,7 @@ class V8Debugger : public v8::debug::DebugDelegate, ...@@ -247,10 +270,7 @@ class V8Debugger : public v8::debug::DebugDelegate,
StackTraceIdToStackTrace m_storedStackTraces; StackTraceIdToStackTrace m_storedStackTraces;
uintptr_t m_lastStackTraceId = 0; uintptr_t m_lastStackTraceId = 0;
std::unordered_map<int, std::pair<int64_t, int64_t>> std::unordered_map<int, V8DebuggerId> m_contextGroupIdToDebuggerId;
m_contextGroupIdToDebuggerId;
std::unordered_map<String16, std::pair<int64_t, int64_t>>
m_serializedDebuggerIdToDebuggerId;
std::unique_ptr<TerminateExecutionCallback> m_terminateExecutionCallback; std::unique_ptr<TerminateExecutionCallback> m_terminateExecutionCallback;
......
...@@ -16,6 +16,10 @@ int V8StackTraceImpl::maxCallStackSizeToCapture = 200; ...@@ -16,6 +16,10 @@ int V8StackTraceImpl::maxCallStackSizeToCapture = 200;
namespace { namespace {
static const char kId[] = "id";
static const char kDebuggerId[] = "debuggerId";
static const char kShouldPause[] = "shouldPause";
static const v8::StackTrace::StackTraceOptions stackTraceOptions = static const v8::StackTrace::StackTraceOptions stackTraceOptions =
static_cast<v8::StackTrace::StackTraceOptions>( static_cast<v8::StackTrace::StackTraceOptions>(
v8::StackTrace::kDetailed | v8::StackTrace::kDetailed |
...@@ -101,7 +105,7 @@ std::unique_ptr<protocol::Runtime::StackTrace> buildInspectorObjectCommon( ...@@ -101,7 +105,7 @@ std::unique_ptr<protocol::Runtime::StackTrace> buildInspectorObjectCommon(
stackTrace->setParentId( stackTrace->setParentId(
protocol::Runtime::StackTraceId::create() protocol::Runtime::StackTraceId::create()
.setId(stackTraceIdToString(externalParent.id)) .setId(stackTraceIdToString(externalParent.id))
.setDebuggerId(debuggerIdToString(externalParent.debugger_id)) .setDebuggerId(V8DebuggerId(externalParent.debugger_id).toString())
.build()); .build());
} }
return stackTrace; return stackTrace;
...@@ -109,7 +113,7 @@ std::unique_ptr<protocol::Runtime::StackTrace> buildInspectorObjectCommon( ...@@ -109,7 +113,7 @@ std::unique_ptr<protocol::Runtime::StackTrace> buildInspectorObjectCommon(
} // namespace } // namespace
V8StackTraceId::V8StackTraceId() : id(0), debugger_id(std::make_pair(0, 0)) {} V8StackTraceId::V8StackTraceId() : id(0), debugger_id(V8DebuggerId().pair()) {}
V8StackTraceId::V8StackTraceId(uintptr_t id, V8StackTraceId::V8StackTraceId(uintptr_t id,
const std::pair<int64_t, int64_t> debugger_id) const std::pair<int64_t, int64_t> debugger_id)
...@@ -120,8 +124,36 @@ V8StackTraceId::V8StackTraceId(uintptr_t id, ...@@ -120,8 +124,36 @@ V8StackTraceId::V8StackTraceId(uintptr_t id,
bool should_pause) bool should_pause)
: id(id), debugger_id(debugger_id), should_pause(should_pause) {} : id(id), debugger_id(debugger_id), should_pause(should_pause) {}
V8StackTraceId::V8StackTraceId(const StringView& json)
: id(0), debugger_id(V8DebuggerId().pair()) {
auto dict =
protocol::DictionaryValue::cast(protocol::StringUtil::parseJSON(json));
if (!dict) return;
String16 s;
if (!dict->getString(kId, &s)) return;
bool isOk = false;
int64_t parsedId = s.toInteger64(&isOk);
if (!isOk || !parsedId) return;
if (!dict->getString(kDebuggerId, &s)) return;
V8DebuggerId debuggerId(s);
if (!debuggerId.isValid()) return;
if (!dict->getBoolean(kShouldPause, &should_pause)) return;
id = parsedId;
debugger_id = debuggerId.pair();
}
bool V8StackTraceId::IsInvalid() const { return !id; } bool V8StackTraceId::IsInvalid() const { return !id; }
std::unique_ptr<StringBuffer> V8StackTraceId::ToString() {
if (IsInvalid()) return nullptr;
auto dict = protocol::DictionaryValue::create();
dict->setString(kId, String16::fromInteger64(id));
dict->setString(kDebuggerId, V8DebuggerId(debugger_id).toString());
dict->setBoolean(kShouldPause, should_pause);
String16 json = dict->toJSONString();
return StringBufferImpl::adopt(json);
}
StackFrame::StackFrame(v8::Isolate* isolate, v8::Local<v8::StackFrame> v8Frame) StackFrame::StackFrame(v8::Isolate* isolate, v8::Local<v8::StackFrame> v8Frame)
: m_functionName(toProtocolString(isolate, v8Frame->GetFunctionName())), : m_functionName(toProtocolString(isolate, v8Frame->GetFunctionName())),
m_scriptId(String16::fromInteger(v8Frame->GetScriptId())), m_scriptId(String16::fromInteger(v8Frame->GetScriptId())),
......
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