Commit 6daff990 authored by Benedikt Meurer's avatar Benedikt Meurer Committed by Commit Bot

[stack-traces] Implement fast-path for JSFunction::GetDebugName().

For stack traces, especially all stack traces exposed via the Inspector
(i.e. for the purpose of async stack traces), JSFunction::GetDebugName()
is still a bottleneck, even after the removal of "displayName" support.
As outlined in https://bit.ly/devtools-function-displayName-removal a
follow-up optimization here would be to improve the performance of the
"name" lookup. Previously, it'd always use the LookupIterator combined
with JSReceiver::GetDataProperty(), which in the common case would find
the "name" property and the return undefined, since it doesn't invoke
getters on AccessorInfos, and eventually fall through to the actual
logic in SharedFunctionInfo::DebugName().

Now we had a similar situation with Function.prototype.bind(), which
also needs to lookup "name" on regular function objects quite often, and
what we implemented there is to just look into the DescriptorArray of
the incoming function object and see if the entry for the "name"
descriptor is still untouched (key is "name" and value is an
AccessorInfo), and if so completely bypass the slow-path lookup via the
LookupIterator.

With this CL (and the optimization in https://crrev.com/c/2695386), the
cost of symbolization is now significantly lower than the cost of the
actual stack trace capturing, for the async stack traces in the example
from https://crbug.com/1077657 as indicated by the perf profile below:

```
- 26.03% v8_inspector::AsyncStackTrace::capture
   + 17.34% v8::StackTrace::CurrentStackTrace
   - 7.27% v8_inspector::(anonymous namespace)::toFramesVector
      - 7.18% v8_inspector::V8Debugger::symbolize
         - 6.27% v8_inspector::StackFrame::StackFrame
            + 2.52% v8_inspector::toProtocolString
            + 1.88% v8::internal::StackFrameInfo::GetLineNumber
           0.78% operator new[]
     0.55% operator new[]
```

Bug: chromium:1077657, v8:8742, chromium:1069425, chromium:1177685
Change-Id: I38f23816295f4381f5109cc78e4856dc0b67b097
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2695593
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Auto-Submit: Benedikt Meurer <bmeurer@chromium.org>
Reviewed-by: 's avatarYang Guo <yangguo@chromium.org>
Cr-Commit-Position: refs/heads/master@{#72758}
parent 9a4e4d82
......@@ -812,11 +812,48 @@ void JSFunction::PrintName(FILE* out) {
PrintF(out, "%s", shared().DebugNameCStr().get());
}
namespace {
bool UseFastFunctionNameLookup(Isolate* isolate, Map map) {
DCHECK(map.IsJSFunctionMap());
if (map.NumberOfOwnDescriptors() < JSFunction::kMinDescriptorsForFastBind) {
return false;
}
DCHECK(!map.is_dictionary_map());
HeapObject value;
ReadOnlyRoots roots(isolate);
auto descriptors = map.instance_descriptors(kRelaxedLoad);
InternalIndex kNameIndex{JSFunction::kNameDescriptorIndex};
if (descriptors.GetKey(kNameIndex) != roots.name_string() ||
!descriptors.GetValue(kNameIndex)
.GetHeapObjectIfStrong(isolate, &value)) {
return false;
}
return value.IsAccessorInfo();
}
} // namespace
Handle<String> JSFunction::GetDebugName(Handle<JSFunction> function) {
// Below we use the same fast-path that we already established for
// Function.prototype.bind(), where we avoid a slow "name" property
// lookup if the DescriptorArray for the |function| still has the
// "name" property at the original spot and that property is still
// implemented via an AccessorInfo (which effectively means that
// it must be the FunctionNameGetter).
Isolate* isolate = function->GetIsolate();
Handle<Object> name =
JSReceiver::GetDataProperty(function, isolate->factory()->name_string());
if (name->IsString()) return Handle<String>::cast(name);
if (!UseFastFunctionNameLookup(isolate, function->map())) {
// Normally there should be an else case for the fast-path check
// above, which should invoke JSFunction::GetName(), since that's
// what the FunctionNameGetter does, however GetDataProperty() has
// never invoked accessors and thus always returned undefined for
// JSFunction where the "name" property is untouched, so we retain
// that exact behavior and go with SharedFunctionInfo::DebugName()
// in case of the fast-path.
Handle<Object> name =
GetDataProperty(function, isolate->factory()->name_string());
if (name->IsString()) return Handle<String>::cast(name);
}
return SharedFunctionInfo::DebugName(handle(function->shared(), isolate));
}
......
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