Commit 765ca6a0 authored by Benedikt Meurer's avatar Benedikt Meurer Committed by V8 LUCI CQ

[inspector] Capture stack trace only once for JSError objects.

When creating a new JSError object (or using the non-standard API
`Error.captureStackTrace`) V8 would previously capture the "simple stack
trace" (as FixedArray of CallSiteInfo instances) to be used for the non-
standard `error.stack` property, and if the inspector was active also
capture the "detailed stack trace" (as FixedArray of StackFrameInfo
instances). This turns out to be quite a lot of overhead, both in terms
of execution time as well as memory pressure, especially since the
information needed for the inspector is a proper subset of the
information needed by `error.stack`.

So this CL addresses the above issue by capturing only the "simple stack
trace" (in the common case) and computing the "detailed stack trace"
from the "simple stack trace" when on demand. This is accomplished by
introducing a new ErrorStackData container that is used to store the
stack trace information on JSErrors when the inspector is active. When
capturing stack trace for a JSError object while the inspector is
active, we take the maximum of the program controlled stack trace limit
and the inspector requested stack trace limit, and memorize the program
controlled stack trace limit for later formatting (to ensure that the
presence of the inspector is not observable by the program).

On the `standalone.js` benchmark from crbug.com/1283162 (with the
default max call stack size of 200) we reduce execution time by around
16% compared to ToT. And compared to V8 9.9.4 (the version prior to the
regression in crbug.com/1280831), we are 6% faster now.

Doc: https://bit.ly/v8-cheaper-inspector-stack-traces
Bug: chromium:1280831, chromium:1278650, chromium:1258599
Bug: chromium:1280803, chromium:1280832, chromium:1280818
Fixed: chromium:1283162
Change-Id: I57dac73e0ecf7d50ea57c3eb4981067deb28133e
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3366660Reviewed-by: 's avatarLeszek Swirski <leszeks@chromium.org>
Reviewed-by: 's avatarYang Guo <yangguo@chromium.org>
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Cr-Commit-Position: refs/heads/main@{#78542}
parent 506c0cf2
......@@ -767,75 +767,28 @@ Handle<AccessorInfo> Accessors::MakeBoundFunctionNameInfo(Isolate* isolate) {
void Accessors::ErrorStackGetter(
v8::Local<v8::Name> key, const v8::PropertyCallbackInfo<v8::Value>& info) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(info.GetIsolate());
Isolate* isolate = reinterpret_cast<Isolate*>(info.GetIsolate());
HandleScope scope(isolate);
Handle<JSObject> holder =
Handle<Object> formatted_stack;
Handle<JSObject> error_object =
Handle<JSObject>::cast(Utils::OpenHandle(*info.Holder()));
// Retrieve the stack trace. It can either be structured data in the form of
// a FixedArray of CallSiteInfo objects, an already formatted stack trace
// (string) or whatever the "prepareStackTrace" callback produced.
Handle<Object> stack_trace;
Handle<Symbol> stack_trace_symbol = isolate->factory()->stack_trace_symbol();
MaybeHandle<Object> maybe_stack_trace =
JSObject::GetProperty(isolate, holder, stack_trace_symbol);
if (!maybe_stack_trace.ToHandle(&stack_trace) ||
stack_trace->IsUndefined(isolate)) {
Handle<Object> result = isolate->factory()->undefined_value();
info.GetReturnValue().Set(Utils::ToLocal(result));
return;
}
// Only format the stack-trace the first time around. The check for a
// FixedArray is sufficient as the user callback can not create plain
// FixedArrays and the result is a String in case we format the stack
// trace ourselves.
if (!stack_trace->IsFixedArray()) {
info.GetReturnValue().Set(Utils::ToLocal(stack_trace));
return;
}
Handle<Object> formatted_stack_trace;
if (!ErrorUtils::FormatStackTrace(isolate, holder, stack_trace)
.ToHandle(&formatted_stack_trace)) {
isolate->OptionalRescheduleException(false);
return;
}
// Replace the structured stack-trace with the formatting result.
MaybeHandle<Object> result = Object::SetProperty(
isolate, holder, isolate->factory()->stack_trace_symbol(),
formatted_stack_trace, StoreOrigin::kMaybeKeyed,
Just(ShouldThrow::kThrowOnError));
if (result.is_null()) {
if (!ErrorUtils::GetFormattedStack(isolate, error_object)
.ToHandle(&formatted_stack)) {
isolate->OptionalRescheduleException(false);
return;
}
v8::Local<v8::Value> value = Utils::ToLocal(formatted_stack_trace);
info.GetReturnValue().Set(value);
info.GetReturnValue().Set(Utils::ToLocal(formatted_stack));
}
void Accessors::ErrorStackSetter(
v8::Local<v8::Name> name, v8::Local<v8::Value> val,
v8::Local<v8::Name> name, v8::Local<v8::Value> value,
const v8::PropertyCallbackInfo<v8::Boolean>& info) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(info.GetIsolate());
Isolate* isolate = reinterpret_cast<Isolate*>(info.GetIsolate());
HandleScope scope(isolate);
Handle<JSObject> obj = Handle<JSObject>::cast(
Utils::OpenHandle(*v8::Local<v8::Value>(info.This())));
Handle<Object> value = Handle<Object>::cast(Utils::OpenHandle(*val));
// Store the value in the internal symbol to avoid reconfiguration to
// a data property.
MaybeHandle<Object> result = Object::SetProperty(
isolate, obj, isolate->factory()->stack_trace_symbol(), value,
StoreOrigin::kMaybeKeyed, Just(ShouldThrow::kThrowOnError));
if (result.is_null()) {
isolate->OptionalRescheduleException(false);
return;
}
Handle<JSObject> error_object =
Handle<JSObject>::cast(Utils::OpenHandle(*info.Holder()));
ErrorUtils::SetFormattedStack(isolate, error_object,
Utils::OpenHandle(*value));
}
Handle<AccessorInfo> Accessors::MakeErrorStackInfo(Isolate* isolate) {
......
......@@ -44,10 +44,8 @@ BUILTIN(ErrorCaptureStackTrace) {
// Collect the stack trace.
RETURN_FAILURE_ON_EXCEPTION(isolate,
isolate->CaptureAndSetDetailedStackTrace(object));
RETURN_FAILURE_ON_EXCEPTION(
isolate, isolate->CaptureAndSetSimpleStackTrace(object, mode, caller));
isolate, isolate->CaptureAndSetErrorStack(object, mode, caller));
// Add the stack accessors.
......
......@@ -1806,6 +1806,12 @@ void StackFrameInfo::StackFrameInfoVerify(Isolate* isolate) {
TorqueGeneratedClassVerifiers::StackFrameInfoVerify(*this, isolate);
}
void ErrorStackData::ErrorStackDataVerify(Isolate* isolate) {
TorqueGeneratedClassVerifiers::ErrorStackDataVerify(*this, isolate);
CHECK_IMPLIES(!call_site_infos_or_formatted_stack().IsFixedArray(),
limit_or_stack_frame_infos().IsFixedArray());
}
// Helper class for verifying the string table.
class StringTableVerifier : public RootVisitor {
public:
......
This diff is collapsed.
......@@ -900,13 +900,9 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory {
void* ptr5 = nullptr, void* ptr6 = nullptr);
Handle<FixedArray> CaptureDetailedStackTrace(
int limit, StackTrace::StackTraceOptions options);
Handle<FixedArray> CaptureSimpleStackTrace(int limit, FrameSkipMode mode,
Handle<Object> caller);
MaybeHandle<JSReceiver> CaptureAndSetDetailedStackTrace(
Handle<JSReceiver> error_object);
MaybeHandle<JSReceiver> CaptureAndSetSimpleStackTrace(
Handle<JSReceiver> error_object, FrameSkipMode mode,
Handle<Object> caller);
MaybeHandle<JSObject> CaptureAndSetErrorStack(Handle<JSObject> error_object,
FrameSkipMode mode,
Handle<Object> caller);
Handle<FixedArray> GetDetailedStackTrace(Handle<JSReceiver> error_object);
Handle<FixedArray> GetSimpleStackTrace(Handle<JSReceiver> error_object);
......
......@@ -499,7 +499,7 @@ MaybeHandle<JSObject> ErrorUtils::Construct(Isolate* isolate,
return ErrorUtils::Construct(isolate, target, new_target, message, options,
mode, caller,
ErrorUtils::StackTraceCollection::kDetailed);
ErrorUtils::StackTraceCollection::kEnabled);
}
MaybeHandle<JSObject> ErrorUtils::Construct(
......@@ -575,16 +575,12 @@ MaybeHandle<JSObject> ErrorUtils::Construct(
}
switch (stack_trace_collection) {
case StackTraceCollection::kDetailed:
RETURN_ON_EXCEPTION(
isolate, isolate->CaptureAndSetDetailedStackTrace(err), JSObject);
V8_FALLTHROUGH;
case StackTraceCollection::kSimple:
RETURN_ON_EXCEPTION(
isolate, isolate->CaptureAndSetSimpleStackTrace(err, mode, caller),
JSObject);
case StackTraceCollection::kEnabled:
RETURN_ON_EXCEPTION(isolate,
isolate->CaptureAndSetErrorStack(err, mode, caller),
JSObject);
break;
case StackTraceCollection::kNone:
case StackTraceCollection::kDisabled:
break;
}
return err;
......@@ -710,7 +706,7 @@ Handle<JSObject> ErrorUtils::MakeGenericError(
// The call below can't fail because constructor is a builtin.
DCHECK(constructor->shared().HasBuiltinId());
return ErrorUtils::Construct(isolate, constructor, constructor, msg, options,
mode, no_caller, StackTraceCollection::kDetailed)
mode, no_caller, StackTraceCollection::kEnabled)
.ToHandleChecked();
}
......@@ -994,5 +990,69 @@ Object ErrorUtils::ThrowLoadFromNullOrUndefined(Isolate* isolate,
return ReadOnlyRoots(isolate).exception();
}
// static
MaybeHandle<Object> ErrorUtils::GetFormattedStack(
Isolate* isolate, Handle<JSObject> error_object) {
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.stack_trace"), __func__);
Handle<Object> error_stack = JSReceiver::GetDataProperty(
error_object, isolate->factory()->error_stack_symbol());
if (error_stack->IsErrorStackData()) {
Handle<ErrorStackData> error_stack_data =
Handle<ErrorStackData>::cast(error_stack);
if (error_stack_data->HasFormattedStack()) {
return handle(error_stack_data->formatted_stack(), isolate);
}
ErrorStackData::EnsureStackFrameInfos(isolate, error_stack_data);
Handle<Object> formatted_stack;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, formatted_stack,
FormatStackTrace(isolate, error_object,
handle(error_stack_data->call_site_infos(), isolate)),
Object);
error_stack_data->set_formatted_stack(*formatted_stack);
return formatted_stack;
}
if (error_stack->IsFixedArray()) {
Handle<Object> formatted_stack;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, formatted_stack,
FormatStackTrace(isolate, error_object,
Handle<FixedArray>::cast(error_stack)),
Object);
RETURN_ON_EXCEPTION(
isolate,
JSObject::SetProperty(isolate, error_object,
isolate->factory()->error_stack_symbol(),
formatted_stack, StoreOrigin::kMaybeKeyed,
Just(ShouldThrow::kThrowOnError)),
Object);
return formatted_stack;
}
return error_stack;
}
// static
void ErrorUtils::SetFormattedStack(Isolate* isolate,
Handle<JSObject> error_object,
Handle<Object> formatted_stack) {
Handle<Object> error_stack = JSReceiver::GetDataProperty(
error_object, isolate->factory()->error_stack_symbol());
if (error_stack->IsErrorStackData()) {
Handle<ErrorStackData> error_stack_data =
Handle<ErrorStackData>::cast(error_stack);
ErrorStackData::EnsureStackFrameInfos(isolate, error_stack_data);
error_stack_data->set_formatted_stack(*formatted_stack);
} else {
JSObject::SetProperty(isolate, error_object,
isolate->factory()->error_stack_symbol(),
formatted_stack, StoreOrigin::kMaybeKeyed,
Just(ShouldThrow::kThrowOnError))
.Check();
}
}
} // namespace internal
} // namespace v8
......@@ -72,9 +72,9 @@ enum FrameSkipMode {
class ErrorUtils : public AllStatic {
public:
// |kNone| is useful when you don't need the stack information at all, for
// |kDisabled| is useful when you don't need the stack information at all, for
// example when creating a deserialized error.
enum class StackTraceCollection { kDetailed, kSimple, kNone };
enum class StackTraceCollection { kEnabled, kDisabled };
static MaybeHandle<JSObject> Construct(Isolate* isolate,
Handle<JSFunction> target,
Handle<Object> new_target,
......@@ -112,6 +112,11 @@ class ErrorUtils : public AllStatic {
static Object ThrowLoadFromNullOrUndefined(Isolate* isolate,
Handle<Object> object,
MaybeHandle<Object> key);
static MaybeHandle<Object> GetFormattedStack(Isolate* isolate,
Handle<JSObject> error_object);
static void SetFormattedStack(Isolate* isolate, Handle<JSObject> error_object,
Handle<Object> formatted_stack);
};
class MessageFormatter {
......
......@@ -1367,6 +1367,19 @@ Handle<AccessorInfo> Factory::NewAccessorInfo() {
return handle(info, isolate());
}
Handle<ErrorStackData> Factory::NewErrorStackData(
Handle<Object> call_site_infos_or_formatted_stack,
Handle<Object> limit_or_stack_frame_infos) {
ErrorStackData error_stack_data = NewStructInternal<ErrorStackData>(
ERROR_STACK_DATA_TYPE, AllocationType::kYoung);
DisallowGarbageCollection no_gc;
error_stack_data.set_call_site_infos_or_formatted_stack(
*call_site_infos_or_formatted_stack, SKIP_WRITE_BARRIER);
error_stack_data.set_limit_or_stack_frame_infos(*limit_or_stack_frame_infos,
SKIP_WRITE_BARRIER);
return handle(error_stack_data, isolate());
}
void Factory::AddToScriptList(Handle<Script> script) {
Handle<WeakArrayList> scripts = script_list();
scripts = WeakArrayList::Append(isolate(), scripts,
......@@ -2139,7 +2152,7 @@ Handle<JSObject> Factory::NewError(Handle<JSFunction> constructor,
Handle<Object> no_caller;
return ErrorUtils::Construct(isolate(), constructor, constructor, message,
undefined_value(), SKIP_NONE, no_caller,
ErrorUtils::StackTraceCollection::kDetailed)
ErrorUtils::StackTraceCollection::kEnabled)
.ToHandleChecked();
}
......
......@@ -391,6 +391,10 @@ class V8_EXPORT_PRIVATE Factory : public FactoryBase<Factory> {
Handle<AccessorInfo> NewAccessorInfo();
Handle<ErrorStackData> NewErrorStackData(
Handle<Object> call_site_infos_or_formatted_stack,
Handle<Object> limit_or_stack_frame_infos);
Handle<Script> CloneScript(Handle<Script> script);
Handle<BreakPointInfo> NewBreakPointInfo(int source_position);
......
......@@ -426,10 +426,10 @@
V(_, console_context_name_symbol) \
V(_, class_fields_symbol) \
V(_, class_positions_symbol) \
V(_, detailed_stack_trace_symbol) \
V(_, elements_transition_symbol) \
V(_, error_end_pos_symbol) \
V(_, error_script_symbol) \
V(_, error_stack_symbol) \
V(_, error_start_pos_symbol) \
V(_, frozen_symbol) \
V(_, interpreter_trampoline_symbol) \
......@@ -446,7 +446,6 @@
V(_, regexp_result_regexp_input_symbol) \
V(_, regexp_result_regexp_last_index_symbol) \
V(_, sealed_symbol) \
V(_, stack_trace_symbol) \
V(_, strict_function_transition_symbol) \
V(_, wasm_exception_tag_symbol) \
V(_, wasm_exception_values_symbol) \
......
......@@ -9,6 +9,10 @@
#include "src/objects/shared-function-info.h"
#include "src/strings/string-builder-inl.h"
#if V8_ENABLE_WEBASSEMBLY
#include "src/debug/debug-wasm-objects.h"
#endif // V8_ENABLE_WEBASSEMBLY
namespace v8 {
namespace internal {
......@@ -278,6 +282,24 @@ Handle<PrimitiveHeapObject> CallSiteInfo::GetFunctionName(
return isolate->factory()->null_value();
}
// static
Handle<String> CallSiteInfo::GetFunctionDebugName(Handle<CallSiteInfo> info) {
Isolate* isolate = info->GetIsolate();
#if V8_ENABLE_WEBASSEMBLY
if (info->IsWasm()) {
return GetWasmFunctionDebugName(isolate,
handle(info->GetWasmInstance(), isolate),
info->GetWasmFunctionIndex());
}
#endif // V8_ENABLE_WEBASSEMBLY
Handle<JSFunction> function(JSFunction::cast(info->function()), isolate);
Handle<String> name = JSFunction::GetDebugName(function);
if (name->length() == 0 && info->IsEval()) {
name = isolate->factory()->eval_string();
}
return name;
}
namespace {
PrimitiveHeapObject InferMethodNameFromFastObject(Isolate* isolate,
......
......@@ -36,6 +36,7 @@ class CallSiteInfo : public TorqueGeneratedCallSiteInfo<CallSiteInfo, Struct> {
inline bool IsAsync() const;
bool IsEval() const;
bool IsUserJavaScript() const;
bool IsSubjectToDebugging() const;
bool IsMethodCall() const;
bool IsToplevel() const;
bool IsPromiseAll() const;
......@@ -69,6 +70,7 @@ class CallSiteInfo : public TorqueGeneratedCallSiteInfo<CallSiteInfo, Struct> {
static Handle<PrimitiveHeapObject> GetEvalOrigin(Handle<CallSiteInfo> info);
V8_EXPORT_PRIVATE static Handle<PrimitiveHeapObject> GetFunctionName(
Handle<CallSiteInfo> info);
static Handle<String> GetFunctionDebugName(Handle<CallSiteInfo> info);
static Handle<Object> GetMethodName(Handle<CallSiteInfo> info);
static Handle<Object> GetTypeName(Handle<CallSiteInfo> info);
......
......@@ -68,6 +68,23 @@ BIT_FIELD_ACCESSORS(StackFrameInfo, flags, bytecode_offset_or_source_position,
BIT_FIELD_ACCESSORS(StackFrameInfo, flags, is_constructor,
StackFrameInfo::IsConstructorBit)
NEVER_READ_ONLY_SPACE_IMPL(ErrorStackData)
TQ_OBJECT_CONSTRUCTORS_IMPL(ErrorStackData)
bool ErrorStackData::HasFormattedStack() const {
return !call_site_infos_or_formatted_stack().IsFixedArray();
}
ACCESSORS_RELAXED_CHECKED(ErrorStackData, formatted_stack, Object,
kCallSiteInfosOrFormattedStackOffset,
!limit_or_stack_frame_infos().IsSmi())
bool ErrorStackData::HasCallSiteInfos() const { return !HasFormattedStack(); }
ACCESSORS_RELAXED_CHECKED(ErrorStackData, call_site_infos, FixedArray,
kCallSiteInfosOrFormattedStackOffset,
!HasFormattedStack())
} // namespace internal
} // namespace v8
......
......@@ -7,6 +7,7 @@
#include "src/base/platform/mutex.h"
#include "src/debug/debug-evaluate.h"
#include "src/handles/handles-inl.h"
#include "src/objects/call-site-info-inl.h"
#include "src/objects/debug-objects-inl.h"
#include "src/utils/ostreams.h"
......@@ -411,5 +412,50 @@ int StackFrameInfo::GetSourcePosition(Handle<StackFrameInfo> info) {
return source_position;
}
// static
void ErrorStackData::EnsureStackFrameInfos(Isolate* isolate,
Handle<ErrorStackData> error_stack) {
if (!error_stack->limit_or_stack_frame_infos().IsSmi()) {
return;
}
int limit = Smi::cast(error_stack->limit_or_stack_frame_infos()).value();
Handle<FixedArray> call_site_infos(error_stack->call_site_infos(), isolate);
Handle<FixedArray> stack_frame_infos =
isolate->factory()->NewFixedArray(call_site_infos->length());
int index = 0;
for (int i = 0; i < call_site_infos->length(); ++i) {
Handle<CallSiteInfo> call_site_info(
CallSiteInfo::cast(call_site_infos->get(i)), isolate);
if (call_site_info->IsAsync()) {
break;
}
Handle<Script> script;
if (!CallSiteInfo::GetScript(isolate, call_site_info).ToHandle(&script) ||
!script->IsSubjectToDebugging()) {
continue;
}
Handle<StackFrameInfo> stack_frame_info =
isolate->factory()->NewStackFrameInfo(
script, CallSiteInfo::GetSourcePosition(call_site_info),
CallSiteInfo::GetFunctionDebugName(call_site_info),
call_site_info->IsConstructor());
stack_frame_infos->set(index++, *stack_frame_info);
}
stack_frame_infos =
FixedArray::ShrinkOrEmpty(isolate, stack_frame_infos, index);
if (limit < 0 && -limit < index) {
// Negative limit encodes cap to be applied to |stack_frame_infos|.
stack_frame_infos =
FixedArray::ShrinkOrEmpty(isolate, stack_frame_infos, -limit);
} else if (limit >= 0 && limit < call_site_infos->length()) {
// Positive limit means we need to cap the |call_site_infos|
// to that number before exposing them to the world.
call_site_infos =
FixedArray::ShrinkOrEmpty(isolate, call_site_infos, limit);
error_stack->set_call_site_infos(*call_site_infos);
}
error_stack->set_limit_or_stack_frame_infos(*stack_frame_infos);
}
} // namespace internal
} // namespace v8
......@@ -231,6 +231,26 @@ class StackFrameInfo
TQ_OBJECT_CONSTRUCTORS(StackFrameInfo)
};
class ErrorStackData
: public TorqueGeneratedErrorStackData<ErrorStackData, Struct> {
public:
NEVER_READ_ONLY_SPACE
inline bool HasFormattedStack() const;
DECL_ACCESSORS(formatted_stack, Object)
inline bool HasCallSiteInfos() const;
DECL_ACCESSORS(call_site_infos, FixedArray)
static void EnsureStackFrameInfos(Isolate* isolate,
Handle<ErrorStackData> error_stack);
DECL_VERIFIER(ErrorStackData)
using BodyDescriptor = StructBodyDescriptor;
TQ_OBJECT_CONSTRUCTORS(ErrorStackData)
};
} // namespace internal
} // namespace v8
......
......@@ -84,3 +84,46 @@ extern class StackFrameInfo extends Struct {
function_name: String;
flags: SmiTagged<StackFrameInfoFlags>;
}
// This struct is used by V8 as error_data_symbol on JSError
// instances when the inspector asks V8 to keep (detailed)
// stack traces in addition to the (simple) stack traces that
// are collected by V8 for error.stack.
//
// This can have one of the following forms:
//
// (1) A pair of FixedArray<CallSiteInfo> and positive limit
// if the stack information is not formatted yet and the
// inspector did not yet request any information about the
// error's stack trace. The positive limit specifies the cap
// for the number of call sites exposed to error.stack.
// (2) A pair of FixedArray<CallSiteInfo> and negative limit
// is similar to the above, except that the limit should be
// applied to the inspector StackFrameInfo list once computed
// rather than the number of call sites exposed to error.stack.
// (3) A FixedArray<CallSiteInfo> and FixedArray<StackFrameInfo>
// pair indicates that the inspector already asked for the
// detailed stack information, but the error.stack property
// was not yet formatted. If any limit (negative or positive)
// was stored in the second field before, it was applied to the
// appropriate FixedArray now.
// (4) A valid JavaScript object and FixedArray<StackFrameInfo>
// once error.stack was accessed.
//
// Memorizing the limits is important to ensure that the fact that
// the inspector is active doesn't influence the script execution
// (i.e. the observable limit of call sites in error.stack is the
// same independent of whether the inspector is active or not).
extern class ErrorStackData extends Struct {
// This holds either the FixedArray of CallSiteInfo instances or
// the formatted stack value (usually a string) that's returned
// from the error.stack property.
call_site_infos_or_formatted_stack: FixedArray|JSAny;
// This holds either the FixedArray of StackFrameInfo instances
// for the inspector stack trace or a stack trace limit, which
// if positive specifies how many of the CallSiteInfo instances
// in the first field are to be revealed via error.stack or if
// negative specifies the (negated) limit for the inspector
// stack traces.
limit_or_stack_frame_infos: Smi|FixedArray;
}
......@@ -144,6 +144,7 @@ namespace internal {
V(_, CLASS_POSITIONS_TYPE, ClassPositions, class_positions) \
V(_, DEBUG_INFO_TYPE, DebugInfo, debug_info) \
V(_, ENUM_CACHE_TYPE, EnumCache, enum_cache) \
V(_, ERROR_STACK_DATA_TYPE, ErrorStackData, error_stack_data) \
V(_, FUNCTION_TEMPLATE_RARE_DATA_TYPE, FunctionTemplateRareData, \
function_template_rare_data) \
V(_, INTERCEPTOR_INFO_TYPE, InterceptorInfo, interceptor_info) \
......
......@@ -427,7 +427,7 @@ namespace {
bool IsErrorObject(Isolate* isolate, Handle<Object> object) {
if (!object->IsJSReceiver()) return false;
Handle<Symbol> symbol = isolate->factory()->stack_trace_symbol();
Handle<Symbol> symbol = isolate->factory()->error_stack_symbol();
return JSReceiver::HasOwnProperty(Handle<JSReceiver>::cast(object), symbol)
.FromMaybe(false);
}
......@@ -4812,6 +4812,17 @@ bool Script::GetPositionInfo(Handle<Script> script, int position,
return script->GetPositionInfo(position, info, offset_flag);
}
bool Script::IsSubjectToDebugging() const {
switch (type()) {
case TYPE_NORMAL:
#if V8_ENABLE_WEBASSEMBLY
case TYPE_WASM:
#endif // V8_ENABLE_WEBASSEMBLY
return true;
}
return false;
}
bool Script::IsUserJavaScript() const { return type() == Script::TYPE_NORMAL; }
#if V8_ENABLE_WEBASSEMBLY
......
......@@ -191,6 +191,13 @@ class Script : public TorqueGeneratedScript<Script, Struct> {
V8_EXPORT_PRIVATE bool GetPositionInfo(int position, PositionInfo* info,
OffsetFlag offset_flag) const;
// Tells whether this script should be subject to debugging, e.g. for
// - scope inspection
// - internal break points
// - coverage and type profile
// - error stack trace
bool IsSubjectToDebugging() const;
bool IsUserJavaScript() const;
// Wrappers for GetPositionInfo
......
......@@ -1978,20 +1978,15 @@ MaybeHandle<Object> ValueDeserializer::ReadJSError() {
}
}
Handle<Object> error;
Handle<JSObject> error;
if (!ErrorUtils::Construct(isolate_, constructor, constructor, message,
options, SKIP_NONE, no_caller,
ErrorUtils::StackTraceCollection::kNone)
ErrorUtils::StackTraceCollection::kDisabled)
.ToHandle(&error)) {
return MaybeHandle<Object>();
}
if (Object::SetProperty(
isolate_, error, isolate_->factory()->stack_trace_symbol(), stack,
StoreOrigin::kMaybeKeyed, Just(ShouldThrow::kThrowOnError))
.is_null()) {
return MaybeHandle<Object>();
}
ErrorUtils::SetFormattedStack(isolate_, error, stack);
return error;
}
......
......@@ -3558,12 +3558,8 @@ void DetailedErrorStackTraceTest(const char* src,
CHECK(try_catch.HasCaught());
Handle<Object> exception = v8::Utils::OpenHandle(*try_catch.Exception());
Isolate* isolate = CcTest::i_isolate();
Handle<Name> key = isolate->factory()->stack_trace_symbol();
Handle<FixedArray> stack_trace(Handle<FixedArray>::cast(
Object::GetProperty(isolate, exception, key).ToHandleChecked()));
test(stack_trace);
test(CcTest::i_isolate()->GetSimpleStackTrace(
Handle<JSReceiver>::cast(exception)));
}
FixedArray ParametersOf(Handle<FixedArray> stack_trace, int frame_index) {
......@@ -5211,7 +5207,7 @@ TEST(PreprocessStackTrace) {
CHECK(try_catch.HasCaught());
Isolate* isolate = CcTest::i_isolate();
Handle<Object> exception = v8::Utils::OpenHandle(*try_catch.Exception());
Handle<Name> key = isolate->factory()->stack_trace_symbol();
Handle<Name> key = isolate->factory()->error_stack_symbol();
Handle<Object> stack_trace =
Object::GetProperty(isolate, exception, key).ToHandleChecked();
Handle<Object> code =
......
......@@ -48,8 +48,7 @@ void CheckExceptionInfos(v8::internal::Isolate* isolate, Handle<Object> exc,
exc->Print();
// Extract stack frame from the exception.
auto stack = Handle<FixedArray>::cast(JSReceiver::GetDataProperty(
Handle<JSObject>::cast(exc), isolate->factory()->stack_trace_symbol()));
auto stack = isolate->GetSimpleStackTrace(Handle<JSObject>::cast(exc));
CHECK_EQ(N, stack->length());
for (int i = 0; i < N; ++i) {
......
This diff is collapsed.
This diff is collapsed.
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
const {session, contextGroup, Protocol} =
InspectorTest.start('Checks that Error.stackTraceLimit works correctly');
contextGroup.addScript(`
function recurse(f, n) {
if (n-- > 0) return recurse(f, n);
return f();
}
function foo() {
recurse(() => {
throw new Error('Thrown from foo!');
}, 20);
}
//# sourceURL=test.js
`);
InspectorTest.runAsyncTestSuite([
async function testErrorStackTraceLimitWithRuntimeDisabled() {
await Protocol.Runtime.evaluate({expression: 'Error.stackTraceLimit = 2'});
InspectorTest.logMessage(
await Protocol.Runtime.evaluate({expression: 'foo()'}));
},
async function testErrorStackTraceLimitWithRuntimeEnabled() {
await Protocol.Runtime.enable();
await Protocol.Runtime.evaluate({expression: 'Error.stackTraceLimit = 2'});
InspectorTest.logMessage(
await Protocol.Runtime.evaluate({expression: 'foo()'}));
for (let size = 0; size <= 10; size += 5) {
await Protocol.Runtime.evaluate(
{expression: `Error.stackTraceLimit = ${size}`});
await Protocol.Runtime.setMaxCallStackSizeToCapture({size});
InspectorTest.logMessage(
await Protocol.Runtime.evaluate({expression: 'foo()'}));
}
await Protocol.Runtime.disable();
},
async function testErrorStackTraceLimitNonNumber() {
await Protocol.Runtime.enable();
await Protocol.Runtime.evaluate(
{expression: 'Error.stackTraceLimit = "Invalid"'});
InspectorTest.logMessage(
await Protocol.Runtime.evaluate({expression: 'foo()'}));
await Protocol.Runtime.disable();
},
async function testErrorStackTraceLimitDeleted() {
await Protocol.Runtime.enable();
await Protocol.Runtime.evaluate(
{expression: 'delete Error.stackTraceLimit'});
InspectorTest.logMessage(
await Protocol.Runtime.evaluate({expression: 'foo()'}));
await Protocol.Runtime.disable();
}
]);
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
const {session, contextGroup, Protocol} =
InspectorTest.start('Checks that error.stack works correctly');
contextGroup.addScript(`
function recurse(f, n) {
if (n-- > 0) return recurse(f, n);
return f();
}
function foo() {
recurse(() => {
throw new Error('Thrown from foo!');
}, 20);
}
//# sourceURL=test.js
`);
InspectorTest.runAsyncTestSuite([
async function testErrorStackWithRuntimeDisabled() {
InspectorTest.logMessage(
await Protocol.Runtime.evaluate({expression: 'foo()'}));
},
async function testErrorStackWithRuntimeEnabled() {
await Protocol.Runtime.enable();
InspectorTest.logMessage(
await Protocol.Runtime.evaluate({expression: 'foo()'}));
for (let size = 0; size <= 10; size += 5) {
await Protocol.Runtime.setMaxCallStackSizeToCapture({size});
InspectorTest.logMessage(
await Protocol.Runtime.evaluate({expression: 'foo()'}));
}
await Protocol.Runtime.disable();
},
]);
This diff is collapsed.
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