Getting empty function names from the "StackTrace::CurrentStackTrace" method

229 views
Skip to first unread message

Darin Dimitrov

unread,
Nov 18, 2019, 10:40:04 AM11/18/19
to v8-users
I am embedding V8 and trying to capture the current javascript stacktrace using the "v8::StackTrace::CurrentStackTrace":

Isolate* isolate = info.GetIsolate();

Local<StackTrace> stack = v8::StackTrace::CurrentStackTrace(isolate, 10, v8::StackTrace::StackTraceOptions::kDetailed);

for (int i = 0; i < stack->GetFrameCount(); i++) {

    Local<v8::StackFrame> frame = stack->GetFrame(isolate, i);

    Local<v8::String> funcName = frame->GetFunctionName();

    v8::String::Utf8Value str(isolate, funcName);

    const char* name = *str;

    printf("%s (%d:%d)\n", name, frame->GetLineNumber(), frame->GetColumn());

}


I have placed this code inside a custom function that I have registered to the global scope.

Before calling my custom function, I tried logging the stack property of an Error object (by using : console.log(new Error().stack)) and I obtained the following output:

Error
    at bar (file:///app/bundle.js:266:29)
    at test (file:///app/bundle.js:267:15)
    at Observable.onTap (file:///app/bundle.js:268:11)
    at Button.notify (file:///app/vendor.js:3620:32)
    at Button._emit (file:///app/vendor.js:3640:18)
    at TapHandlerImpl.tap (file:///app/vendor.js:15540:19)

With the "v8::StackTrace::CurrentStackTrace" method I get:

bar (266:29)
test (267:15)
(null) (268:11)
(null) (3620:32)
(null) (3640:18)
(null) (15540:19)

I am getting empty function names from some frames, while all the other information is present (script name, line, column numbers, ...).

Do you know what might be the reason for getting empty function names?

Simon Zünd

unread,
Nov 19, 2019, 12:58:10 AM11/19/19
to v8-users
This is a bug in our API implementation. Or better, there is a mismatch between API and what we do internally. "Method names" and "class names" are stored separately from function names. This can be seen here, when we collect all the information for a single frame: https://cs.chromium.org/chromium/src/v8/src/heap/factory.cc?rcl=e1eb815647f334a8cf970439343a8febfa9f6d11&l=3732

There is really no reason why this information shouldn't be made available in the API. The only tricky thing is to come up with a better API, as some of the getters change meaning depending whether its a JS or WASM frame and I am not really happy with the current solution.

--
--
v8-users mailing list
v8-u...@googlegroups.com
http://groups.google.com/group/v8-users
---
You received this message because you are subscribed to the Google Groups "v8-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to v8-users+u...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/v8-users/64d9e4ca-b271-4d83-aace-753c28e193f4%40googlegroups.com.

Darin Dimitrov

unread,
Nov 19, 2019, 2:30:07 AM11/19/19
to v8-users
Simon, thanks for the clarification. Does this mean that there's no reliable way to get this information with the current V8 public API?

I went ahead and wrote a simple example illustrating the issue and tried running it on different platforms. And the results were surprising.

Here's the sample code I wrote:

    Isolate::CreateParams create_params;
    create_params.array_buffer_allocator = &allocator_;
    Isolate* isolate = Isolate::New(create_params);
    Isolate::Scope isolate_scope(isolate);
    HandleScope handle_scope(isolate);
    Local<ObjectTemplate> globalTemplate = ObjectTemplate::New(isolate);
    Local<Context> context = Context::New(isolate, nullptr, globalTemplate);
    context->Enter();

    Local<v8::Function> captureFunc = v8::Function::New(context, [](const FunctionCallbackInfo<Value>& info) {
        Isolate* isolate = info.GetIsolate();
        Local<Context> context = isolate->GetCurrentContext();
        Local<Object> exception = Exception::Error(v8::String::NewFromUtf8(isolate, "", NewStringType::kNormal, 0).ToLocalChecked()).As<Object>();
        Local<Value> stackProperty = exception->Get(context, v8::String::NewFromUtf8(isolate, "stack", NewStringType::kNormal, 5).ToLocalChecked()).ToLocalChecked();
        v8::String::Utf8Value stackStr(isolate, stackProperty);
        printf("%s\n\n", *stackStr);
        
        Local<StackTrace> stack = v8::StackTrace::CurrentStackTrace(isolate, 10, v8::StackTrace::StackTraceOptions::kDetailed);

        for (int i = 0; i < stack->GetFrameCount(); i++) {
            Local<v8::StackFrame> frame = stack->GetFrame(isolate, i);
            Local<v8::String> funcName = frame->GetFunctionName();
            v8::String::Utf8Value str(isolate, funcName);
            const char* name = *str;
            printf("%s (%d:%d)\n", name, frame->GetLineNumber(), frame->GetColumn());
        }
    }).ToLocalChecked();
    assert(context->Global()->Set(context, v8::String::NewFromUtf8(isolate, "captureTrace", NewStringType::kNormal, 12).ToLocalChecked(), captureFunc).FromMaybe(false));

    std::string src =
        "(function test() {"
        "    const viewModel = new Object();"
        "    viewModel.onTap = () => { captureTrace(); };"
        "    viewModel.onTap.apply(viewModel, []);"
        "})();";
    Local<Script> script = Script::Compile(context, v8::String::NewFromUtf8(isolate, src.c_str(), NewStringType::kNormal, (int)src.length()).ToLocalChecked()).ToLocalChecked();
    Local<Value> result;
    assert(script->Run(context).ToLocal(&result));

On V8 8.0.0 (https://chromium.googlesource.com/v8/v8.git/+/50031fae736fac7b2b309369df492bfd0edd7553), running in --jitless on an arm64 iOS device, I get the following output:

Error
    at Object.onTap (<anonymous>:1:84)
    at test (<anonymous>:1:122)
    at <anonymous>:1:145

(null) (1:84)
test (1:122)
(null) (1:145)

and on V8 7.7.299.11 (https://chromium.googlesource.com/v8/v8.git/+/027689dbfcb2a9bbc8ceec4db2631c558e879633), running on an arm64 Android device, I get the following output:

Error
        at Object.viewModel.onTap (<anonymous>:1:84)
        at test (<anonymous>:1:122)
        at <anonymous>:1:145

viewModel.onTap (1:84)
test (1:122)
(null) (1:145)

Do you know what might explain the discrepancy between those two platforms?

On Tuesday, November 19, 2019 at 7:58:10 AM UTC+2, Simon Zünd wrote:
This is a bug in our API implementation. Or better, there is a mismatch between API and what we do internally. "Method names" and "class names" are stored separately from function names. This can be seen here, when we collect all the information for a single frame: https://cs.chromium.org/chromium/src/v8/src/heap/factory.cc?rcl=e1eb815647f334a8cf970439343a8febfa9f6d11&l=3732

There is really no reason why this information shouldn't be made available in the API. The only tricky thing is to come up with a better API, as some of the getters change meaning depending whether its a JS or WASM frame and I am not really happy with the current solution.

To unsubscribe from this group and stop receiving emails from it, send an email to v8-u...@googlegroups.com.

Darin Dimitrov

unread,
Nov 19, 2019, 2:37:45 AM11/19/19
to v8-users
Further narrowed it down to the "--no-lazy" runtime flag:

Working:

        std::string flags = "--expose_gc --jitless";
        V8::SetFlagsFromString(flags.c_str(), flags.size());

NOT working:

        std::string flags = "--expose_gc --jitless --no-lazy";
        V8::SetFlagsFromString(flags.c_str(), flags.size());

Any idea why would the --no-lazy flag affect function names in stack frames? I need the --no-lazy flag because I noticed that it speeds up code cached scripts (v8::ScriptCompiler::CreateCodeCache API).

Simon Zünd

unread,
Nov 19, 2019, 2:50:33 AM11/19/19
to v8-users, Leszek Swirski
+Leszek Swirski 

I am unsure what impact lazy compilation has here. There might be a weird interaction with the stack frame cache. Does the flag "--optimize-for-size" cause any change in the result? That flag disables the stack frame cache (among other things).

To unsubscribe from this group and stop receiving emails from it, send an email to v8-users+u...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/v8-users/358d6517-c279-459b-a675-9ad4d712638d%40googlegroups.com.

Darin Dimitrov

unread,
Nov 19, 2019, 3:05:19 AM11/19/19
to v8-users
The --optimize-for-size flag doesn't seem to have any effect on the result.


On Tuesday, November 19, 2019 at 9:50:33 AM UTC+2, Simon Zünd wrote:
+Leszek Swirski 

I am unsure what impact lazy compilation has here. There might be a weird interaction with the stack frame cache. Does the flag "--optimize-for-size" cause any change in the result? That flag disables the stack frame cache (among other things).

Reply all
Reply to author
Forward
0 new messages