Commit db24e200 authored by Simon Zünd's avatar Simon Zünd Committed by Commit Bot

[stack-trace] Separate stack-trace symbolization and serialization

This CL moves the code responsible for serializing a stack trace frame into
a string, out of messages.cc and into stack-frame-info.cc. Instead of
symbolizing the stack trace frame while serializing, the code is changed to
work on top of StackTraceFrame and StackFrameInfo objects.

The result is that the serialization code no longer cares when a stack trace
frame is symbolized. Symbolization could happen eagerly during capturing, or
lazily the first time any of StackFrameInfo fields are accessed.

Drive-by: Existing users of StackFrameBase::ToString are adapted to the
new SerializeStackTraceFrame API. This includes Isolate::PrintCurrentStackTrace,
which is changed to re-use the existing capturing and serializing mechanism.

Bug: v8:8742
Change-Id: Ic7fd80668c9d993e99d586ef7fe022850104c34f
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1631414
Commit-Queue: Simon Zünd <szuend@chromium.org>
Reviewed-by: 's avatarUlan Degenbaev <ulan@chromium.org>
Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#62522}
parent dc5d7edd
......@@ -2945,11 +2945,11 @@ Local<StackTrace> StackTrace::CurrentStackTrace(Isolate* isolate,
// --- S t a c k F r a m e ---
int StackFrame::GetLineNumber() const {
return i::StackTraceFrame::GetLineNumber(Utils::OpenHandle(this));
return i::StackTraceFrame::GetOneBasedLineNumber(Utils::OpenHandle(this));
}
int StackFrame::GetColumn() const {
return i::StackTraceFrame::GetColumnNumber(Utils::OpenHandle(this));
return i::StackTraceFrame::GetOneBasedColumnNumber(Utils::OpenHandle(this));
}
int StackFrame::GetScriptId() const {
......
......@@ -8,6 +8,7 @@
#include "src/logging/counters.h"
#include "src/objects/frame-array-inl.h"
#include "src/objects/objects-inl.h"
#include "src/objects/stack-frame-info.h"
namespace v8 {
namespace internal {
......@@ -203,9 +204,9 @@ BUILTIN(CallSitePrototypeIsToplevel) {
BUILTIN(CallSitePrototypeToString) {
HandleScope scope(isolate);
CHECK_CALLSITE(recv, "toString");
FrameArrayIterator it(isolate, GetFrameArray(isolate, recv),
GetFrameIndex(isolate, recv));
RETURN_RESULT_OR_FAILURE(isolate, it.Frame()->ToString());
Handle<StackTraceFrame> frame = isolate->factory()->NewStackTraceFrame(
GetFrameArray(isolate, recv), GetFrameIndex(isolate, recv));
RETURN_RESULT_OR_FAILURE(isolate, SerializeStackTraceFrame(isolate, frame));
}
#undef CHECK_CALLSITE
......
......@@ -2033,33 +2033,23 @@ Object Isolate::PromoteScheduledException() {
}
void Isolate::PrintCurrentStackTrace(FILE* out) {
IncrementalStringBuilder builder(this);
for (StackTraceFrameIterator it(this); !it.done(); it.Advance()) {
if (!it.is_javascript()) continue;
CaptureStackTraceOptions options;
options.limit = 0;
options.skip_mode = SKIP_NONE;
options.capture_builtin_exit_frames = true;
options.async_stack_trace = FLAG_async_stack_traces;
options.filter_mode = FrameArrayBuilder::CURRENT_SECURITY_CONTEXT;
options.capture_only_frames_subject_to_debugging = false;
options.enable_frame_caching = false;
HandleScope scope(this);
JavaScriptFrame* frame = it.javascript_frame();
Handle<Object> receiver(frame->receiver(), this);
Handle<JSFunction> function(frame->function(), this);
Handle<AbstractCode> code;
int offset;
if (frame->is_interpreted()) {
InterpretedFrame* interpreted_frame = InterpretedFrame::cast(frame);
code = handle(AbstractCode::cast(interpreted_frame->GetBytecodeArray()),
this);
offset = interpreted_frame->GetBytecodeOffset();
} else {
code = handle(AbstractCode::cast(frame->LookupCode()), this);
offset = static_cast<int>(frame->pc() - code->InstructionStart());
}
Handle<FixedArray> frames = Handle<FixedArray>::cast(
CaptureStackTrace(this, this->factory()->undefined_value(), options));
IncrementalStringBuilder builder(this);
for (int i = 0; i < frames->length(); ++i) {
Handle<StackTraceFrame> frame(StackTraceFrame::cast(frames->get(i)), this);
// To preserve backwards compatiblity, only append a newline when
// the current stringified frame actually has characters.
const int old_length = builder.Length();
JSStackFrame site(this, receiver, function, code, offset);
site.ToString(builder);
if (old_length != builder.Length()) builder.AppendCharacter('\n');
SerializeStackTraceFrame(this, frame, builder);
}
Handle<String> stack_trace = builder.Finish().ToHandleChecked();
......
......@@ -322,12 +322,6 @@ bool StackFrameBase::IsEval() {
GetScript()->compilation_type() == Script::COMPILATION_TYPE_EVAL;
}
MaybeHandle<String> StackFrameBase::ToString() {
IncrementalStringBuilder builder(isolate_);
ToString(builder);
return builder.Finish();
}
void JSStackFrame::FromFrameArray(Isolate* isolate, Handle<FrameArray> array,
int frame_ix) {
DCHECK(!array->IsWasmFrame(frame_ix));
......@@ -366,7 +360,7 @@ Handle<Object> JSStackFrame::GetFileName() {
}
Handle<Object> JSStackFrame::GetFunctionName() {
Handle<String> result = JSFunction::GetName(function_);
Handle<String> result = JSFunction::GetDebugName(function_);
if (result->length() != 0) return result;
if (HasScript() &&
......@@ -515,171 +509,6 @@ bool JSStackFrame::IsToplevel() {
return receiver_->IsJSGlobalProxy() || receiver_->IsNullOrUndefined(isolate_);
}
namespace {
bool IsNonEmptyString(Handle<Object> object) {
return (object->IsString() && String::cast(*object).length() > 0);
}
void AppendFileLocation(Isolate* isolate, StackFrameBase* call_site,
IncrementalStringBuilder* builder) {
if (call_site->IsNative()) {
builder->AppendCString("native");
return;
}
Handle<Object> file_name = call_site->GetScriptNameOrSourceUrl();
if (!file_name->IsString() && call_site->IsEval()) {
Handle<Object> eval_origin = call_site->GetEvalOrigin();
DCHECK(eval_origin->IsString());
builder->AppendString(Handle<String>::cast(eval_origin));
builder->AppendCString(", "); // Expecting source position to follow.
}
if (IsNonEmptyString(file_name)) {
builder->AppendString(Handle<String>::cast(file_name));
} else {
// Source code does not originate from a file and is not native, but we
// can still get the source position inside the source string, e.g. in
// an eval string.
builder->AppendCString("<anonymous>");
}
int line_number = call_site->GetLineNumber();
if (line_number != StackFrameBase::kNone) {
builder->AppendCharacter(':');
builder->AppendInt(line_number);
int column_number = call_site->GetColumnNumber();
if (column_number != StackFrameBase::kNone) {
builder->AppendCharacter(':');
builder->AppendInt(column_number);
}
}
}
int StringIndexOf(Isolate* isolate, Handle<String> subject,
Handle<String> pattern) {
if (pattern->length() > subject->length()) return -1;
return String::IndexOf(isolate, subject, pattern, 0);
}
// Returns true iff
// 1. the subject ends with '.' + pattern, or
// 2. subject == pattern.
bool StringEndsWithMethodName(Isolate* isolate, Handle<String> subject,
Handle<String> pattern) {
if (String::Equals(isolate, subject, pattern)) return true;
FlatStringReader subject_reader(isolate, String::Flatten(isolate, subject));
FlatStringReader pattern_reader(isolate, String::Flatten(isolate, pattern));
int pattern_index = pattern_reader.length() - 1;
int subject_index = subject_reader.length() - 1;
for (int i = 0; i <= pattern_reader.length(); i++) { // Iterate over len + 1.
if (subject_index < 0) {
return false;
}
const uc32 subject_char = subject_reader.Get(subject_index);
if (i == pattern_reader.length()) {
if (subject_char != '.') return false;
} else if (subject_char != pattern_reader.Get(pattern_index)) {
return false;
}
pattern_index--;
subject_index--;
}
return true;
}
void AppendMethodCall(Isolate* isolate, JSStackFrame* call_site,
IncrementalStringBuilder* builder) {
Handle<Object> type_name = call_site->GetTypeName();
Handle<Object> method_name = call_site->GetMethodName();
Handle<Object> function_name = call_site->GetFunctionName();
if (IsNonEmptyString(function_name)) {
Handle<String> function_string = Handle<String>::cast(function_name);
if (IsNonEmptyString(type_name)) {
Handle<String> type_string = Handle<String>::cast(type_name);
bool starts_with_type_name =
(StringIndexOf(isolate, function_string, type_string) == 0);
if (!starts_with_type_name) {
builder->AppendString(type_string);
builder->AppendCharacter('.');
}
}
builder->AppendString(function_string);
if (IsNonEmptyString(method_name)) {
Handle<String> method_string = Handle<String>::cast(method_name);
if (!StringEndsWithMethodName(isolate, function_string, method_string)) {
builder->AppendCString(" [as ");
builder->AppendString(method_string);
builder->AppendCharacter(']');
}
}
} else {
if (IsNonEmptyString(type_name)) {
builder->AppendString(Handle<String>::cast(type_name));
builder->AppendCharacter('.');
}
if (IsNonEmptyString(method_name)) {
builder->AppendString(Handle<String>::cast(method_name));
} else {
builder->AppendCString("<anonymous>");
}
}
}
} // namespace
void JSStackFrame::ToString(IncrementalStringBuilder& builder) {
Handle<Object> function_name = GetFunctionName();
const bool is_toplevel = IsToplevel();
const bool is_async = IsAsync();
const bool is_promise_all = IsPromiseAll();
const bool is_constructor = IsConstructor();
const bool is_method_call = !(is_toplevel || is_constructor);
if (is_async) {
builder.AppendCString("async ");
}
if (is_promise_all) {
// For `Promise.all(iterable)` frames we interpret the {offset_}
// as the element index into `iterable` where the error occurred.
builder.AppendCString("Promise.all (index ");
builder.AppendInt(offset_);
builder.AppendCString(")");
return;
}
if (is_method_call) {
AppendMethodCall(isolate_, this, &builder);
} else if (is_constructor) {
builder.AppendCString("new ");
if (IsNonEmptyString(function_name)) {
builder.AppendString(Handle<String>::cast(function_name));
} else {
builder.AppendCString("<anonymous>");
}
} else if (IsNonEmptyString(function_name)) {
builder.AppendString(Handle<String>::cast(function_name));
} else {
AppendFileLocation(isolate_, this, &builder);
return;
}
builder.AppendCString(" (");
AppendFileLocation(isolate_, this, &builder);
builder.AppendCString(")");
return;
}
int JSStackFrame::GetPosition() const {
Handle<SharedFunctionInfo> shared = handle(function_->shared(), isolate_);
SharedFunctionInfo::EnsureSourcePositionsAvailable(isolate_, shared);
......@@ -744,41 +573,6 @@ Handle<Object> WasmStackFrame::GetWasmModuleName() {
return module_name;
}
void WasmStackFrame::ToString(IncrementalStringBuilder& builder) {
Handle<WasmModuleObject> module_object(wasm_instance_->module_object(),
isolate_);
MaybeHandle<String> module_name =
WasmModuleObject::GetModuleNameOrNull(isolate_, module_object);
MaybeHandle<String> function_name = WasmModuleObject::GetFunctionNameOrNull(
isolate_, module_object, wasm_func_index_);
bool has_name = !module_name.is_null() || !function_name.is_null();
if (has_name) {
if (module_name.is_null()) {
builder.AppendString(function_name.ToHandleChecked());
} else {
builder.AppendString(module_name.ToHandleChecked());
if (!function_name.is_null()) {
builder.AppendCString(".");
builder.AppendString(function_name.ToHandleChecked());
}
}
builder.AppendCString(" (");
}
builder.AppendCString("wasm-function[");
DCHECK(wasm_func_index_ <= kMaxInt);
builder.AppendInt(static_cast<int>(wasm_func_index_));
builder.AppendCString("]:");
char buffer[16];
SNPrintF(ArrayVector(buffer), "0x%x", GetModuleOffset());
builder.AppendCString(buffer);
if (has_name) builder.AppendCString(")");
return;
}
int WasmStackFrame::GetPosition() const {
return IsInterpreted()
? offset_
......@@ -861,24 +655,6 @@ int AsmJsWasmStackFrame::GetColumnNumber() {
return Script::GetColumnNumber(script, GetPosition()) + 1;
}
void AsmJsWasmStackFrame::ToString(IncrementalStringBuilder& builder) {
// The string should look exactly as the respective javascript frame string.
// Keep this method in line to
// JSStackFrame::ToString(IncrementalStringBuilder&).
Handle<Object> function_name = GetFunctionName();
if (IsNonEmptyString(function_name)) {
builder.AppendString(Handle<String>::cast(function_name));
builder.AppendCString(" (");
}
AppendFileLocation(isolate_, this, &builder);
if (IsNonEmptyString(function_name)) builder.AppendCString(")");
return;
}
FrameArrayIterator::FrameArrayIterator(Isolate* isolate,
Handle<FrameArray> array, int frame_ix)
: isolate_(isolate), array_(array), frame_ix_(frame_ix) {}
......@@ -1094,13 +870,13 @@ MaybeHandle<Object> ErrorUtils::FormatStackTrace(Isolate* isolate,
wasm::WasmCodeRefScope wasm_code_ref_scope;
Handle<FrameArray> frame_array = GetFrameArrayFromStackTrace(isolate, elems);
for (FrameArrayIterator it(isolate, frame_array); it.HasFrame();
it.Advance()) {
for (int i = 0; i < elems->length(); ++i) {
builder.AppendCString("\n at ");
StackFrameBase* frame = it.Frame();
frame->ToString(builder);
Handle<StackTraceFrame> frame(StackTraceFrame::cast(elems->get(i)),
isolate);
SerializeStackTraceFrame(isolate, frame, builder);
if (isolate->has_pending_exception()) {
// CallSite.toString threw. Parts of the current frame might have been
// stringified already regardless. Still, try to append a string
......
......@@ -24,7 +24,6 @@ class WasmCode;
// Forward declarations.
class AbstractCode;
class FrameArray;
class IncrementalStringBuilder;
class JSMessageObject;
class LookupIterator;
class SharedFunctionInfo;
......@@ -94,9 +93,6 @@ class StackFrameBase {
virtual bool IsConstructor() = 0;
virtual bool IsStrict() const = 0;
MaybeHandle<String> ToString();
virtual void ToString(IncrementalStringBuilder& builder) = 0;
// Used to signal that the requested field is unknown.
static const int kNone = -1;
......@@ -139,8 +135,6 @@ class JSStackFrame : public StackFrameBase {
bool IsConstructor() override { return is_constructor_; }
bool IsStrict() const override { return is_strict_; }
void ToString(IncrementalStringBuilder& builder) override;
private:
JSStackFrame() = default;
void FromFrameArray(Isolate* isolate, Handle<FrameArray> array, int frame_ix);
......@@ -189,8 +183,6 @@ class WasmStackFrame : public StackFrameBase {
bool IsStrict() const override { return false; }
bool IsInterpreted() const { return code_ == nullptr; }
void ToString(IncrementalStringBuilder& builder) override;
protected:
Handle<Object> Null() const;
......@@ -226,8 +218,6 @@ class AsmJsWasmStackFrame : public WasmStackFrame {
int GetLineNumber() override;
int GetColumnNumber() override;
void ToString(IncrementalStringBuilder& builder) override;
private:
friend class FrameArrayIterator;
AsmJsWasmStackFrame() = default;
......
......@@ -3689,13 +3689,8 @@ Handle<StackFrameInfo> Factory::NewStackFrameInfo(
const bool is_wasm = frame_array->IsAnyWasmFrame(index);
// Line numbers are 1-based, for Wasm we need to adjust.
int line = it.Frame()->GetLineNumber();
if (is_wasm && line >= 0) line++;
// Column numbers are 1-based, for Wasm we need to adjust.
int column = it.Frame()->GetColumnNumber();
if (is_wasm && column >= 0) column++;
const int script_id = it.Frame()->GetScriptId();
......@@ -3711,7 +3706,6 @@ Handle<StackFrameInfo> Factory::NewStackFrameInfo(
Handle<Object> function = it.Frame()->GetFunction();
if (function->IsJSFunction()) {
Handle<JSFunction> fun = Handle<JSFunction>::cast(function);
function_name = JSFunction::GetDebugName(fun);
is_user_java_script = fun->shared().IsUserJavaScript();
}
......
......@@ -5,6 +5,7 @@
#include "src/objects/stack-frame-info.h"
#include "src/objects/stack-frame-info-inl.h"
#include "src/strings/string-builder-inl.h"
namespace v8 {
namespace internal {
......@@ -15,12 +16,30 @@ int StackTraceFrame::GetLineNumber(Handle<StackTraceFrame> frame) {
return line != StackFrameBase::kNone ? line : Message::kNoLineNumberInfo;
}
// static
int StackTraceFrame::GetOneBasedLineNumber(Handle<StackTraceFrame> frame) {
// JavaScript line numbers are already 1-based. Wasm line numbers need
// to be adjusted.
int line = StackTraceFrame::GetLineNumber(frame);
if (StackTraceFrame::IsWasm(frame) && line >= 0) line++;
return line;
}
// static
int StackTraceFrame::GetColumnNumber(Handle<StackTraceFrame> frame) {
int column = GetFrameInfo(frame)->column_number();
return column != StackFrameBase::kNone ? column : Message::kNoColumnInfo;
}
// static
int StackTraceFrame::GetOneBasedColumnNumber(Handle<StackTraceFrame> frame) {
// JavaScript colun numbers are already 1-based. Wasm column numbers need
// to be adjusted.
int column = StackTraceFrame::GetColumnNumber(frame);
if (StackTraceFrame::IsWasm(frame) && column >= 0) column++;
return column;
}
// static
int StackTraceFrame::GetScriptId(Handle<StackTraceFrame> frame) {
int id = GetFrameInfo(frame)->script_id();
......@@ -152,5 +171,233 @@ Handle<FrameArray> GetFrameArrayFromStackTrace(Isolate* isolate,
return handle(FrameArray::cast(frame->frame_array()), isolate);
}
namespace {
bool IsNonEmptyString(Handle<Object> object) {
return (object->IsString() && String::cast(*object).length() > 0);
}
void AppendFileLocation(Isolate* isolate, Handle<StackTraceFrame> frame,
IncrementalStringBuilder* builder) {
Handle<Object> file_name = StackTraceFrame::GetScriptNameOrSourceUrl(frame);
if (!file_name->IsString() && StackTraceFrame::IsEval(frame)) {
Handle<Object> eval_origin = StackTraceFrame::GetEvalOrigin(frame);
DCHECK(eval_origin->IsString());
builder->AppendString(Handle<String>::cast(eval_origin));
builder->AppendCString(", "); // Expecting source position to follow.
}
if (IsNonEmptyString(file_name)) {
builder->AppendString(Handle<String>::cast(file_name));
} else {
// Source code does not originate from a file and is not native, but we
// can still get the source position inside the source string, e.g. in
// an eval string.
builder->AppendCString("<anonymous>");
}
int line_number = StackTraceFrame::GetLineNumber(frame);
if (line_number != Message::kNoLineNumberInfo) {
builder->AppendCharacter(':');
builder->AppendInt(line_number);
int column_number = StackTraceFrame::GetColumnNumber(frame);
if (column_number != Message::kNoColumnInfo) {
builder->AppendCharacter(':');
builder->AppendInt(column_number);
}
}
}
int StringIndexOf(Isolate* isolate, Handle<String> subject,
Handle<String> pattern) {
if (pattern->length() > subject->length()) return -1;
return String::IndexOf(isolate, subject, pattern, 0);
}
// Returns true iff
// 1. the subject ends with '.' + pattern, or
// 2. subject == pattern.
bool StringEndsWithMethodName(Isolate* isolate, Handle<String> subject,
Handle<String> pattern) {
if (String::Equals(isolate, subject, pattern)) return true;
FlatStringReader subject_reader(isolate, String::Flatten(isolate, subject));
FlatStringReader pattern_reader(isolate, String::Flatten(isolate, pattern));
int pattern_index = pattern_reader.length() - 1;
int subject_index = subject_reader.length() - 1;
for (int i = 0; i <= pattern_reader.length(); i++) { // Iterate over len + 1.
if (subject_index < 0) {
return false;
}
const uc32 subject_char = subject_reader.Get(subject_index);
if (i == pattern_reader.length()) {
if (subject_char != '.') return false;
} else if (subject_char != pattern_reader.Get(pattern_index)) {
return false;
}
pattern_index--;
subject_index--;
}
return true;
}
void AppendMethodCall(Isolate* isolate, Handle<StackTraceFrame> frame,
IncrementalStringBuilder* builder) {
Handle<Object> type_name = StackTraceFrame::GetTypeName(frame);
Handle<Object> method_name = StackTraceFrame::GetMethodName(frame);
Handle<Object> function_name = StackTraceFrame::GetFunctionName(frame);
if (IsNonEmptyString(function_name)) {
Handle<String> function_string = Handle<String>::cast(function_name);
if (IsNonEmptyString(type_name)) {
Handle<String> type_string = Handle<String>::cast(type_name);
bool starts_with_type_name =
(StringIndexOf(isolate, function_string, type_string) == 0);
if (!starts_with_type_name) {
builder->AppendString(type_string);
builder->AppendCharacter('.');
}
}
builder->AppendString(function_string);
if (IsNonEmptyString(method_name)) {
Handle<String> method_string = Handle<String>::cast(method_name);
if (!StringEndsWithMethodName(isolate, function_string, method_string)) {
builder->AppendCString(" [as ");
builder->AppendString(method_string);
builder->AppendCharacter(']');
}
}
} else {
if (IsNonEmptyString(type_name)) {
builder->AppendString(Handle<String>::cast(type_name));
builder->AppendCharacter('.');
}
if (IsNonEmptyString(method_name)) {
builder->AppendString(Handle<String>::cast(method_name));
} else {
builder->AppendCString("<anonymous>");
}
}
}
void SerializeJSStackFrame(Isolate* isolate, Handle<StackTraceFrame> frame,
IncrementalStringBuilder& builder) {
Handle<Object> function_name = StackTraceFrame::GetFunctionName(frame);
const bool is_toplevel = StackTraceFrame::IsToplevel(frame);
const bool is_async = StackTraceFrame::IsAsync(frame);
const bool is_promise_all = StackTraceFrame::IsPromiseAll(frame);
const bool is_constructor = StackTraceFrame::IsConstructor(frame);
const bool is_method_call = !(is_toplevel || is_constructor);
if (is_async) {
builder.AppendCString("async ");
}
if (is_promise_all) {
builder.AppendCString("Promise.all (index ");
builder.AppendInt(StackTraceFrame::GetPromiseAllIndex(frame));
builder.AppendCString(")");
return;
}
if (is_method_call) {
AppendMethodCall(isolate, frame, &builder);
} else if (is_constructor) {
builder.AppendCString("new ");
if (IsNonEmptyString(function_name)) {
builder.AppendString(Handle<String>::cast(function_name));
} else {
builder.AppendCString("<anonymous>");
}
} else if (IsNonEmptyString(function_name)) {
builder.AppendString(Handle<String>::cast(function_name));
} else {
AppendFileLocation(isolate, frame, &builder);
return;
}
builder.AppendCString(" (");
AppendFileLocation(isolate, frame, &builder);
builder.AppendCString(")");
}
void SerializeAsmJsWasmStackFrame(Isolate* isolate,
Handle<StackTraceFrame> frame,
IncrementalStringBuilder& builder) {
// The string should look exactly as the respective javascript frame string.
// Keep this method in line to
// JSStackFrame::ToString(IncrementalStringBuilder&).
Handle<Object> function_name = StackTraceFrame::GetFunctionName(frame);
if (IsNonEmptyString(function_name)) {
builder.AppendString(Handle<String>::cast(function_name));
builder.AppendCString(" (");
}
AppendFileLocation(isolate, frame, &builder);
if (IsNonEmptyString(function_name)) builder.AppendCString(")");
return;
}
void SerializeWasmStackFrame(Isolate* isolate, Handle<StackTraceFrame> frame,
IncrementalStringBuilder& builder) {
Handle<Object> module_name = StackTraceFrame::GetWasmModuleName(frame);
Handle<Object> function_name = StackTraceFrame::GetFunctionName(frame);
const bool has_name = !module_name->IsNull() || !function_name->IsNull();
if (has_name) {
if (module_name->IsNull()) {
builder.AppendString(Handle<String>::cast(function_name));
} else {
builder.AppendString(Handle<String>::cast(module_name));
if (!function_name->IsNull()) {
builder.AppendCString(".");
builder.AppendString(Handle<String>::cast(function_name));
}
}
builder.AppendCString(" (");
}
const int wasm_func_index = StackTraceFrame::GetLineNumber(frame);
builder.AppendCString("wasm-function[");
builder.AppendInt(wasm_func_index);
builder.AppendCString("]:");
char buffer[16];
SNPrintF(ArrayVector(buffer), "0x%x",
StackTraceFrame::GetColumnNumber(frame));
builder.AppendCString(buffer);
if (has_name) builder.AppendCString(")");
}
} // namespace
void SerializeStackTraceFrame(Isolate* isolate, Handle<StackTraceFrame> frame,
IncrementalStringBuilder& builder) {
// Ordering here is important, as AsmJs frames are also marked as Wasm.
if (StackTraceFrame::IsAsmJsWasm(frame)) {
SerializeAsmJsWasmStackFrame(isolate, frame, builder);
} else if (StackTraceFrame::IsWasm(frame)) {
SerializeWasmStackFrame(isolate, frame, builder);
} else {
SerializeJSStackFrame(isolate, frame, builder);
}
}
MaybeHandle<String> SerializeStackTraceFrame(Isolate* isolate,
Handle<StackTraceFrame> frame) {
IncrementalStringBuilder builder(isolate);
SerializeStackTraceFrame(isolate, frame, builder);
return builder.Finish();
}
} // namespace internal
} // namespace v8
......@@ -85,7 +85,9 @@ class StackTraceFrame : public Struct {
TORQUE_GENERATED_STACK_TRACE_FRAME_FIELDS)
static int GetLineNumber(Handle<StackTraceFrame> frame);
static int GetOneBasedLineNumber(Handle<StackTraceFrame> frame);
static int GetColumnNumber(Handle<StackTraceFrame> frame);
static int GetOneBasedColumnNumber(Handle<StackTraceFrame> frame);
static int GetScriptId(Handle<StackTraceFrame> frame);
static int GetPromiseAllIndex(Handle<StackTraceFrame> frame);
......@@ -121,6 +123,12 @@ V8_EXPORT_PRIVATE
Handle<FrameArray> GetFrameArrayFromStackTrace(Isolate* isolate,
Handle<FixedArray> stack_trace);
class IncrementalStringBuilder;
void SerializeStackTraceFrame(Isolate* isolate, Handle<StackTraceFrame> frame,
IncrementalStringBuilder& builder);
MaybeHandle<String> SerializeStackTraceFrame(Isolate* isolate,
Handle<StackTraceFrame> frame);
} // namespace internal
} // namespace v8
......
......@@ -190,7 +190,9 @@ static void checkStackFrame(const char* expected_script_name,
} else {
CHECK_NOT_NULL(strstr(*script_name, expected_script_name));
}
CHECK_NOT_NULL(strstr(*func_name, expected_func_name));
if (!frame->GetFunctionName().IsEmpty()) {
CHECK_NOT_NULL(strstr(*func_name, expected_func_name));
}
CHECK_EQ(expected_line_number, frame->GetLineNumber());
CHECK_EQ(expected_column, frame->GetColumn());
CHECK_EQ(is_eval, frame->IsEval());
......
......@@ -105,7 +105,7 @@ Running test: testConsoleLog
callFrames : [
[0] : {
columnNumber : 8
functionName :
functionName : eval
lineNumber : 0
scriptId : <scriptId>
url :
......
......@@ -96,7 +96,7 @@ Test runtime stack trace:
}
[1] : {
columnNumber : 0
functionName :
functionName : eval
lineNumber : 0
scriptId : <scriptId>
url : boo.js
......
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