-
Notifications
You must be signed in to change notification settings - Fork 29.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[WIP] trace_events: V8 Intrinsic (do not land) #20618
Conversation
This is awesome. How is the |
Yeah, the extra data is serialized like...
the extra "values" is there because of a limitation in the current implementation of |
Ok, now that it's working, I'm working on making it faster. Current perf compared to the existing methods in
|
Unfortunately, in extremely hot paths the intrinsics are still a bottleneck. In a local experiment, adding trace events to Granted, the |
it might make sense to use something besides the extras object as v8 wants to deprecate it, and why not upstream all these changes to v8? otherwise this looks pretty cool. |
Using the extras object was @hashseed's suggestion :-) Also, these will be upstreamed to v8... I opened this work-in-progress PR to put the work in context and solicit review. This PR should not land in core as is. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking good so far. First round of comments.
namespace v8 { | ||
namespace internal { | ||
|
||
enum Result { NODATA, SUCCESS, FAILED }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As per chat discussion: all these helpers should be inside an anonymous namespace.
@@ -0,0 +1,430 @@ | |||
// Copyright 2016 the V8 project authors. All rights reserved. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Copyright years.
HandleScope scope(isolate); | ||
Handle<Object> category = args.atOrUndefined(isolate, 1); | ||
Handle<String> string; | ||
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, string, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ToString comes with potential side effects, which are usually unwanted. As per chat discussion: Let's just throw here, and below if the category is not already a string.
|
||
const uint8_t* GetCategoryGroupEnabled(Handle<String> string) { | ||
const uint8_t* category_group_enabled; | ||
if (string->IsOneByteRepresentation()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should definitely use one of the existing abstractions instead of peaking into the string directly. But that can be done when we upstream to V8.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I could do it now, but either way works. Any pointer on which of the existing abstractions to use ;-)
Handle<JSValue> object, | ||
uint8_t* arg_type, | ||
uint64_t* arg_value) { | ||
Object* raw = object->value(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about just ToPrimitive for JSValues?
Ok... current numbers are looking even better...
One side note (/cc @AndreasMadsen and @mafintosh) ... moving to this would change the argument syntax for things like the async hooks trace events emitted by |
Why is it the array is the most performant? And could we not implement flat objects with something like: trace(phase, category, name, id, keys, values);
trace('b', 'node.async_hooks', 'example', 0, ['executeAsyncId', 'triggerAsyncId'], [1, 2]); Which would output:
The |
Not really a fan of the I just added a commit that adds basic support for an object passed in as trace('e'.charCodeAt(0), 'foo', 'name', 0, {a: 12, b: "hello"}) Would produce:
Not as flat, but better ergonomically than just the Array option. |
It was more from the consideration that I would be more type consistent and thus perhaps easier to optimize. Making a friendly API on top would then be trivial. However, if a classical object is not an issue that is preferable. |
Yep, that's definitely a consideration. @bmeurer ... which pattern would make this easiest to optimize, or will it matter? The fast path here keys off whether category is enabled or not. |
Cleaned up the code a bit and added the ability to get the trace('e'.charCodeAt(0), 'foo', 'name', 0, () => "construct expensive trace data here."); |
@jasnell I'd highly recommend to stick to objects for ergonomic reasons. I doubt that the performance impact is significant. |
This commit will be dropped once this lands upstream. It will be replaced with a backport commit from the upstream
Finally had a chance to take look at the V8 part of this change. In general this looks pretty good. |
->GetChars()); | ||
} else { | ||
data_ = nullptr; | ||
Local<v8::String> local = Utils::ToLocal(string); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would not do this round trip through the V8 API. Also one-byte strings can include Latin1 characters, which are not UTF8 encoding?
Awesome, thank you @hashseed ... I was going to open the v8 pr on monday this week but had some other bits come up. I'll be trying to get that opened by end of the day tomorrow and we can work through whatever code changes are necessary on the v8 side. |
Status update on this... the PR in V8 is getting closer to landing (https://chromium-review.googlesource.com/c/v8/v8/+/1103294) ... once it does, I will update this PR with a backport of the commits that land there and the additional pieces on top. |
Superseded by #21899 |
WIP: Do not land
@ofrobots @eugeneo @hashseed @bmeurer @mcollina @nodejs/diagnostics @nodejs/chakracore ...
Here is the current work in progress prototype for a V8 builtin for emitting trace events. This is the first working iteration so it likely has much room for improvement.
There are two builtins installed on
extras_binding
:IsTraceCategoryEnabled
andTrace
. These are access by Node.js usingcontext->GetExtrasBindingObject()
and exposed to the JS side viaprocess.binding('trace_events')
.IsTraceCategoryEnabled(category)
is simple enough... it takes a category group string and returns a booleantrue
orfalse
.Trace(phase, category, name, id, data)
emits the actual trace event. Ifdata
isnull
orundefined
, no additional data is emitted with the trace event. Otherwisedata
must either be a primitive value or an Array of primitive values, which are then serialized out as part of the trace event. ATypeError
is thrown if non-primitive values are passed.A benchmark compares the performance difference between this approach and the existing
emit
function innode_trace_events.cc
. The intrinsics are faster and more flexible than the current functions innode_trace_events.cc
but there are some issues to look at.This would need to be upstreamed into V8 before it can land here. I'm opening this PR only to show the current status and to solicit input on the implementation. Once it's done, this will be my first V8 PR so I'm quite certain there is lots of room for improvement here.
Example use:
Checklist
make -j4 test
(UNIX), orvcbuild test
(Windows) passes