mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-07-31 21:29:06 +00:00
LibJS: Cache symbolicated stack frames on ExecutionContext
Instead of re-symbolicating entire stacks from scratch every time we want a JS VM backtrace, we now use the ExecutionContext object as cache storage via a new CachedSourceRange object. This means that once a stack frame has been symbolicated, we don't have to resymbolicate it again (unless the program counter moves within that stack frame). This drastically reduces time spent in symbolication in some WPT tests.
This commit is contained in:
parent
797902229c
commit
5b060da4f4
Notes:
github-actions[bot]
2024-10-14 07:52:03 +00:00
Author: https://github.com/awesomekling
Commit: 5b060da4f4
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/1782
5 changed files with 36 additions and 16 deletions
|
@ -156,6 +156,7 @@ class Accessor;
|
||||||
struct AsyncGeneratorRequest;
|
struct AsyncGeneratorRequest;
|
||||||
class BigInt;
|
class BigInt;
|
||||||
class BoundFunction;
|
class BoundFunction;
|
||||||
|
struct CachedSourceRange;
|
||||||
class Cell;
|
class Cell;
|
||||||
class CellAllocator;
|
class CellAllocator;
|
||||||
class ClassExpression;
|
class ClassExpression;
|
||||||
|
|
|
@ -17,19 +17,21 @@ namespace JS {
|
||||||
|
|
||||||
JS_DEFINE_ALLOCATOR(Error);
|
JS_DEFINE_ALLOCATOR(Error);
|
||||||
|
|
||||||
|
static SourceRange dummy_source_range { SourceCode::create(String {}, String {}), {}, {} };
|
||||||
|
|
||||||
SourceRange const& TracebackFrame::source_range() const
|
SourceRange const& TracebackFrame::source_range() const
|
||||||
{
|
{
|
||||||
if (auto* unrealized = source_range_storage.get_pointer<UnrealizedSourceRange>()) {
|
if (!cached_source_range)
|
||||||
|
return dummy_source_range;
|
||||||
|
if (auto* unrealized = cached_source_range->source_range.get_pointer<UnrealizedSourceRange>()) {
|
||||||
auto source_range = [&] {
|
auto source_range = [&] {
|
||||||
if (!unrealized->source_code) {
|
if (!unrealized->source_code)
|
||||||
static auto dummy_source_code = SourceCode::create(String {}, String {});
|
return dummy_source_range;
|
||||||
return SourceRange { dummy_source_code, {}, {} };
|
|
||||||
}
|
|
||||||
return unrealized->realize();
|
return unrealized->realize();
|
||||||
}();
|
}();
|
||||||
source_range_storage = move(source_range);
|
cached_source_range->source_range = move(source_range);
|
||||||
}
|
}
|
||||||
return source_range_storage.get<SourceRange>();
|
return cached_source_range->source_range.get<SourceRange>();
|
||||||
}
|
}
|
||||||
|
|
||||||
NonnullGCPtr<Error> Error::create(Realm& realm)
|
NonnullGCPtr<Error> Error::create(Realm& realm)
|
||||||
|
@ -81,12 +83,9 @@ void Error::populate_stack()
|
||||||
m_traceback.ensure_capacity(stack_trace.size());
|
m_traceback.ensure_capacity(stack_trace.size());
|
||||||
for (auto& element : stack_trace) {
|
for (auto& element : stack_trace) {
|
||||||
auto* context = element.execution_context;
|
auto* context = element.execution_context;
|
||||||
UnrealizedSourceRange range = {};
|
|
||||||
if (element.source_range.has_value())
|
|
||||||
range = element.source_range.value();
|
|
||||||
TracebackFrame frame {
|
TracebackFrame frame {
|
||||||
.function_name = context->function_name ? context->function_name->byte_string() : "",
|
.function_name = context->function_name ? context->function_name->byte_string() : "",
|
||||||
.source_range_storage = range,
|
.cached_source_range = element.source_range,
|
||||||
};
|
};
|
||||||
|
|
||||||
m_traceback.append(move(frame));
|
m_traceback.append(move(frame));
|
||||||
|
|
|
@ -19,7 +19,7 @@ struct TracebackFrame {
|
||||||
DeprecatedFlyString function_name;
|
DeprecatedFlyString function_name;
|
||||||
[[nodiscard]] SourceRange const& source_range() const;
|
[[nodiscard]] SourceRange const& source_range() const;
|
||||||
|
|
||||||
mutable Variant<SourceRange, UnrealizedSourceRange> source_range_storage;
|
RefPtr<CachedSourceRange> cached_source_range;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum CompactTraceback {
|
enum CompactTraceback {
|
||||||
|
|
|
@ -21,6 +21,16 @@ namespace JS {
|
||||||
|
|
||||||
using ScriptOrModule = Variant<Empty, NonnullGCPtr<Script>, NonnullGCPtr<Module>>;
|
using ScriptOrModule = Variant<Empty, NonnullGCPtr<Script>, NonnullGCPtr<Module>>;
|
||||||
|
|
||||||
|
struct CachedSourceRange : public RefCounted<CachedSourceRange> {
|
||||||
|
CachedSourceRange(size_t program_counter, Variant<UnrealizedSourceRange, SourceRange> source_range)
|
||||||
|
: program_counter(program_counter)
|
||||||
|
, source_range(move(source_range))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
size_t program_counter { 0 };
|
||||||
|
Variant<UnrealizedSourceRange, SourceRange> source_range;
|
||||||
|
};
|
||||||
|
|
||||||
// 9.4 Execution Contexts, https://tc39.es/ecma262/#sec-execution-contexts
|
// 9.4 Execution Contexts, https://tc39.es/ecma262/#sec-execution-contexts
|
||||||
struct ExecutionContext {
|
struct ExecutionContext {
|
||||||
static NonnullOwnPtr<ExecutionContext> create();
|
static NonnullOwnPtr<ExecutionContext> create();
|
||||||
|
@ -49,6 +59,9 @@ public:
|
||||||
GCPtr<Cell> context_owner;
|
GCPtr<Cell> context_owner;
|
||||||
|
|
||||||
Optional<size_t> program_counter;
|
Optional<size_t> program_counter;
|
||||||
|
|
||||||
|
mutable RefPtr<CachedSourceRange> cached_source_range;
|
||||||
|
|
||||||
GCPtr<PrimitiveString> function_name;
|
GCPtr<PrimitiveString> function_name;
|
||||||
Value this_value;
|
Value this_value;
|
||||||
|
|
||||||
|
@ -82,7 +95,7 @@ public:
|
||||||
|
|
||||||
struct StackTraceElement {
|
struct StackTraceElement {
|
||||||
ExecutionContext* execution_context;
|
ExecutionContext* execution_context;
|
||||||
Optional<UnrealizedSourceRange> source_range;
|
RefPtr<CachedSourceRange> source_range;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -731,7 +731,7 @@ struct [[gnu::packed]] NativeStackFrame {
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static Optional<UnrealizedSourceRange> get_source_range(ExecutionContext const* context)
|
static RefPtr<CachedSourceRange> get_source_range(ExecutionContext const* context)
|
||||||
{
|
{
|
||||||
// native function
|
// native function
|
||||||
if (!context->executable)
|
if (!context->executable)
|
||||||
|
@ -740,7 +740,14 @@ static Optional<UnrealizedSourceRange> get_source_range(ExecutionContext const*
|
||||||
if (!context->program_counter.has_value())
|
if (!context->program_counter.has_value())
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
return context->executable->source_range_at(context->program_counter.value());
|
if (!context->cached_source_range
|
||||||
|
|| context->cached_source_range->program_counter != context->program_counter.value()) {
|
||||||
|
auto unrealized_source_range = context->executable->source_range_at(context->program_counter.value());
|
||||||
|
context->cached_source_range = adopt_ref(*new CachedSourceRange(
|
||||||
|
context->program_counter.value(),
|
||||||
|
move(unrealized_source_range)));
|
||||||
|
}
|
||||||
|
return context->cached_source_range;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector<StackTraceElement> VM::stack_trace() const
|
Vector<StackTraceElement> VM::stack_trace() const
|
||||||
|
@ -750,7 +757,7 @@ Vector<StackTraceElement> VM::stack_trace() const
|
||||||
auto* context = m_execution_context_stack[i];
|
auto* context = m_execution_context_stack[i];
|
||||||
stack_trace.append({
|
stack_trace.append({
|
||||||
.execution_context = context,
|
.execution_context = context,
|
||||||
.source_range = get_source_range(context).value_or({}),
|
.source_range = get_source_range(context),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue