[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: Ulan Degenbaev <ulan@chromium.org>
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#62522}
This commit is contained in:
Simon Zünd 2019-07-03 10:13:56 +02:00 committed by Commit Bot
parent dc5d7eddaf
commit db24e2000a
11 changed files with 287 additions and 279 deletions

View File

@ -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 {

View File

@ -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

View File

@ -2033,33 +2033,23 @@ Object Isolate::PromoteScheduledException() {
}
void Isolate::PrintCurrentStackTrace(FILE* out) {
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;
Handle<FixedArray> frames = Handle<FixedArray>::cast(
CaptureStackTrace(this, this->factory()->undefined_value(), options));
IncrementalStringBuilder builder(this);
for (StackTraceFrameIterator it(this); !it.done(); it.Advance()) {
if (!it.is_javascript()) continue;
for (int i = 0; i < frames->length(); ++i) {
Handle<StackTraceFrame> frame(StackTraceFrame::cast(frames->get(i)), this);
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());
}
// 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();

View File

@ -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

View File

@ -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;

View File

@ -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();
}

View File

@ -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

View File

@ -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

View File

@ -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());

View File

@ -105,7 +105,7 @@ Running test: testConsoleLog
callFrames : [
[0] : {
columnNumber : 8
functionName :
functionName : eval
lineNumber : 0
scriptId : <scriptId>
url :

View File

@ -96,7 +96,7 @@ Test runtime stack trace:
}
[1] : {
columnNumber : 0
functionName :
functionName : eval
lineNumber : 0
scriptId : <scriptId>
url : boo.js