diff --git a/benchmark/misc/trace.js b/benchmark/misc/trace.js new file mode 100644 index 00000000000000..75c87d363a6b03 --- /dev/null +++ b/benchmark/misc/trace.js @@ -0,0 +1,75 @@ +'use strict'; + +const common = require('../common.js'); + +const bench = common.createBenchmark(main, { + n: [100000], + method: ['trace', 'emit', 'isTraceCategoryEnabled', 'categoryGroupEnabled'] +}, { + flags: ['--expose-internals', '--trace-event-categories', 'foo'] +}); + +const { + trace, + isTraceCategoryEnabled, + emit, + categoryGroupEnabled +} = process.binding('trace_events'); + +const { + TRACE_EVENT_PHASE_NESTABLE_ASYNC_BEGIN: kBeforeEvent +} = process.binding('constants').trace; + +function doEmit(n) { + bench.start(); + for (var i = 0; i < n; i++) { + emit(kBeforeEvent, 'foo', 'test', 0, 'arg1', 1); + } + bench.end(n); +} + +function doTrace(n) { + bench.start(); + for (var i = 0; i < n; i++) { + trace(kBeforeEvent, 'foo', 'test', 0, 'test'); + } + bench.end(n); +} + +function doIsTraceCategoryEnabled(n) { + bench.start(); + for (var i = 0; i < n; i++) { + isTraceCategoryEnabled('foo'); + isTraceCategoryEnabled('bar'); + } + bench.end(n); +} + +function doCategoryGroupEnabled(n) { + bench.start(); + for (var i = 0; i < n; i++) { + categoryGroupEnabled('foo'); + categoryGroupEnabled('bar'); + } + bench.end(n); +} + +function main({ n, method }) { + switch (method) { + case '': + case 'trace': + doTrace(n); + break; + case 'emit': + doEmit(n); + break; + case 'isTraceCategoryEnabled': + doIsTraceCategoryEnabled(n); + break; + case 'categoryGroupEnabled': + doCategoryGroupEnabled(n); + break; + default: + throw new Error(`Unexpected method "${method}"`); + } +} diff --git a/common.gypi b/common.gypi index 447ff992b46745..14fd67e411bcb8 100644 --- a/common.gypi +++ b/common.gypi @@ -28,7 +28,7 @@ # Reset this number to 0 on major V8 upgrades. # Increment by one for each non-official patch applied to deps/v8. - 'v8_embedder_string': '-node.17', + 'v8_embedder_string': '-node.18', # Enable disassembler for `--print-code` v8 options 'v8_enable_disassembler': 1, diff --git a/deps/v8/AUTHORS b/deps/v8/AUTHORS index 4b5163961d282e..d1df5403e9c7a0 100644 --- a/deps/v8/AUTHORS +++ b/deps/v8/AUTHORS @@ -82,6 +82,7 @@ Jan de Mooij Jan Krems Jay Freeman James Pike +James M Snell Jianghua Yang Joel Stanley Johan Bergström diff --git a/deps/v8/BUILD.gn b/deps/v8/BUILD.gn index 4b48f7d6874df9..1d42461ba73128 100644 --- a/deps/v8/BUILD.gn +++ b/deps/v8/BUILD.gn @@ -1420,6 +1420,7 @@ v8_source_set("v8_base") { "src/builtins/builtins-sharedarraybuffer.cc", "src/builtins/builtins-string.cc", "src/builtins/builtins-symbol.cc", + "src/builtins/builtins-trace.cc", "src/builtins/builtins-typedarray.cc", "src/builtins/builtins-utils.h", "src/builtins/builtins.cc", diff --git a/deps/v8/gypfiles/v8.gyp b/deps/v8/gypfiles/v8.gyp index 80ce748d1fecab..99f868d1d38077 100644 --- a/deps/v8/gypfiles/v8.gyp +++ b/deps/v8/gypfiles/v8.gyp @@ -626,6 +626,7 @@ '../src/builtins/builtins-intl.cc', '../src/builtins/builtins-intl.h', '../src/builtins/builtins-symbol.cc', + '../src/builtins/builtins-trace.cc', '../src/builtins/builtins-typedarray.cc', '../src/builtins/builtins-utils.h', '../src/builtins/builtins.cc', diff --git a/deps/v8/src/bootstrapper.cc b/deps/v8/src/bootstrapper.cc index 2138b9c73d7db7..bbb374918a966a 100644 --- a/deps/v8/src/bootstrapper.cc +++ b/deps/v8/src/bootstrapper.cc @@ -4947,6 +4947,15 @@ bool Genesis::InstallExtraNatives() { Handle extras_binding = factory()->NewJSObject(isolate()->object_function()); + + // binding.isTraceCategoryenabled(category) + SimpleInstallFunction(extras_binding, "isTraceCategoryEnabled", + Builtins::kIsTraceCategoryEnabled, 1, true); + + // binding.trace(phase, category, name, id, data) + SimpleInstallFunction(extras_binding, "trace", Builtins::kTrace, 5, + true); + native_context()->set_extras_binding_object(*extras_binding); for (int i = ExtraNatives::GetDebuggerCount(); diff --git a/deps/v8/src/builtins/builtins-definitions.h b/deps/v8/src/builtins/builtins-definitions.h index 5f06abeceb0d76..6ef4878fa189b1 100644 --- a/deps/v8/src/builtins/builtins-definitions.h +++ b/deps/v8/src/builtins/builtins-definitions.h @@ -1247,7 +1247,11 @@ namespace internal { /* #sec-%asyncfromsynciteratorprototype%.return */ \ TFJ(AsyncFromSyncIteratorPrototypeReturn, 1, kValue) \ /* #sec-async-iterator-value-unwrap-functions */ \ - TFJ(AsyncIteratorValueUnwrap, 1, kValue) + TFJ(AsyncIteratorValueUnwrap, 1, kValue) \ + \ + /* Trace */ \ + CPP(IsTraceCategoryEnabled) \ + CPP(Trace) #ifdef V8_INTL_SUPPORT #define BUILTIN_LIST(CPP, API, TFJ, TFC, TFS, TFH, ASM) \ diff --git a/deps/v8/src/builtins/builtins-trace.cc b/deps/v8/src/builtins/builtins-trace.cc new file mode 100644 index 00000000000000..a10c1363387db8 --- /dev/null +++ b/deps/v8/src/builtins/builtins-trace.cc @@ -0,0 +1,191 @@ +// Copyright 2018 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "src/api.h" +#include "src/builtins/builtins-utils.h" +#include "src/builtins/builtins.h" +#include "src/counters.h" +#include "src/json-stringifier.h" +#include "src/objects-inl.h" + +namespace v8 { +namespace internal { + +namespace { + +using v8::tracing::TracedValue; + +#define MAX_STACK_LENGTH 100 + +class MaybeUtf8 { + public: + explicit MaybeUtf8(Isolate* isolate, Handle string) : buf_(data_) { + string = String::Flatten(string); + int len; + if (string->IsOneByteRepresentation()) { + // Technically this allows unescaped latin1 characters but the trace + // events mechanism currently does the same and the current consuming + // tools are tolerant of it. A more correct approach here would be to + // escape non-ascii characters but this is easier and faster. + len = string->length(); + AllocateSufficientSpace(len); + if (len > 0) { + // Why copy? Well, the trace event mechanism requires null-terminated + // strings, the bytes we get from SeqOneByteString are not. buf_ is + // guaranteed to be null terminated. + memcpy(buf_, Handle::cast(string)->GetChars(), len); + } + } else { + Local local = Utils::ToLocal(string); + len = local->Utf8Length(); + AllocateSufficientSpace(len); + if (len > 0) { + local->WriteUtf8(reinterpret_cast(buf_)); + } + } + buf_[len] = 0; + } + const char* operator*() const { return reinterpret_cast(buf_); } + + private: + void AllocateSufficientSpace(int len) { + if (len + 1 > MAX_STACK_LENGTH) { + allocated_.reset(new uint8_t[len + 1]); + buf_ = allocated_.get(); + } + } + + // In the most common cases, the buffer here will be stack allocated. + // A heap allocation will only occur if the data is more than MAX_STACK_LENGTH + // Given that this is used primarily for trace event categories and names, + // the MAX_STACK_LENGTH should be more than enough. + uint8_t* buf_; + uint8_t data_[MAX_STACK_LENGTH]; + std::unique_ptr allocated_; +}; + +class JsonTraceValue : public ConvertableToTraceFormat { + public: + explicit JsonTraceValue(Isolate* isolate, Handle object) { + // object is a JSON string serialized using JSON.stringify() from within + // the BUILTIN(Trace) method. This may (likely) contain UTF8 values so + // to grab the appropriate buffer data we have to serialize it out. We + // hold on to the bits until the AppendAsTraceFormat method is called. + MaybeUtf8 data(isolate, object); + data_ = *data; + } + + void AppendAsTraceFormat(std::string* out) const override { *out += data_; } + + private: + std::string data_; +}; + +const uint8_t* GetCategoryGroupEnabled(Isolate* isolate, + Handle string) { + MaybeUtf8 category(isolate, string); + return TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(*category); +} + +#undef MAX_STACK_LENGTH + +} // namespace + +// Builins::kIsTraceCategoryEnabled(category) : bool +BUILTIN(IsTraceCategoryEnabled) { + HandleScope scope(isolate); + Handle category = args.atOrUndefined(isolate, 1); + if (!category->IsString()) { + THROW_NEW_ERROR_RETURN_FAILURE( + isolate, NewTypeError(MessageTemplate::kTraceEventCategoryError)); + } + return isolate->heap()->ToBoolean( + *GetCategoryGroupEnabled(isolate, Handle::cast(category))); +} + +// Builtins::kTrace(phase, category, name, id, data) : bool +BUILTIN(Trace) { + HandleScope handle_scope(isolate); + + Handle phase_arg = args.atOrUndefined(isolate, 1); + Handle category = args.atOrUndefined(isolate, 2); + Handle name_arg = args.atOrUndefined(isolate, 3); + Handle id_arg = args.atOrUndefined(isolate, 4); + Handle data_arg = args.atOrUndefined(isolate, 5); + + const uint8_t* category_group_enabled = + GetCategoryGroupEnabled(isolate, Handle::cast(category)); + + // Exit early if the category group is not enabled. + if (!*category_group_enabled) { + return isolate->heap()->false_value(); + } + + if (!phase_arg->IsNumber()) { + THROW_NEW_ERROR_RETURN_FAILURE( + isolate, NewTypeError(MessageTemplate::kTraceEventPhaseError)); + } + if (!category->IsString()) { + THROW_NEW_ERROR_RETURN_FAILURE( + isolate, NewTypeError(MessageTemplate::kTraceEventCategoryError)); + } + if (!name_arg->IsString()) { + THROW_NEW_ERROR_RETURN_FAILURE( + isolate, NewTypeError(MessageTemplate::kTraceEventNameError)); + } + + uint32_t flags = TRACE_EVENT_FLAG_COPY; + int32_t id = 0; + if (!id_arg->IsNullOrUndefined(isolate)) { + if (!id_arg->IsNumber()) { + THROW_NEW_ERROR_RETURN_FAILURE( + isolate, NewTypeError(MessageTemplate::kTraceEventIDError)); + } + flags |= TRACE_EVENT_FLAG_HAS_ID; + id = DoubleToInt32(id_arg->Number()); + } + + Handle name_str = Handle::cast(name_arg); + if (name_str->length() == 0) { + THROW_NEW_ERROR_RETURN_FAILURE( + isolate, NewTypeError(MessageTemplate::kTraceEventNameLengthError)); + } + MaybeUtf8 name(isolate, name_str); + + // We support passing one additional trace event argument with the + // name "data". Any JSON serializable value may be passed. + static const char* arg_name = "data"; + int32_t num_args = 0; + uint8_t arg_type; + uint64_t arg_value; + + if (!data_arg->IsUndefined(isolate)) { + // Serializes the data argument as a JSON string, which is then + // copied into an object. This eliminates duplicated code but + // could have perf costs. It is also subject to all the same + // limitations as JSON.stringify() as it relates to circular + // references and value limitations (e.g. BigInt is not supported). + JsonStringifier stringifier(isolate); + Handle result; + ASSIGN_RETURN_FAILURE_ON_EXCEPTION( + isolate, result, + stringifier.Stringify(data_arg, isolate->factory()->undefined_value(), + isolate->factory()->undefined_value())); + std::unique_ptr traced_value; + traced_value.reset( + new JsonTraceValue(isolate, Handle::cast(result))); + tracing::SetTraceValue(std::move(traced_value), &arg_type, &arg_value); + num_args++; + } + + TRACE_EVENT_API_ADD_TRACE_EVENT( + static_cast(DoubleToInt32(phase_arg->Number())), + category_group_enabled, *name, tracing::kGlobalScope, id, tracing::kNoId, + num_args, &arg_name, &arg_type, &arg_value, flags); + + return isolate->heap()->true_value(); +} + +} // namespace internal +} // namespace v8 diff --git a/deps/v8/src/debug/debug-evaluate.cc b/deps/v8/src/debug/debug-evaluate.cc index be456678299f6a..8efb23962cdec0 100644 --- a/deps/v8/src/debug/debug-evaluate.cc +++ b/deps/v8/src/debug/debug-evaluate.cc @@ -621,6 +621,9 @@ bool BuiltinHasNoSideEffect(Builtins::Name id) { case Builtins::kArrayMap: case Builtins::kArrayReduce: case Builtins::kArrayReduceRight: + // Trace builtins + case Builtins::kIsTraceCategoryEnabled: + case Builtins::kTrace: // TypedArray builtins. case Builtins::kTypedArrayConstructor: case Builtins::kTypedArrayPrototypeBuffer: diff --git a/deps/v8/src/messages.h b/deps/v8/src/messages.h index 187bd4d308a010..b2ce5dfa875325 100644 --- a/deps/v8/src/messages.h +++ b/deps/v8/src/messages.h @@ -755,7 +755,14 @@ class ErrorUtils : public AllStatic { T(DataCloneDeserializationError, "Unable to deserialize cloned data.") \ T(DataCloneDeserializationVersionError, \ "Unable to deserialize cloned data due to invalid or unsupported " \ - "version.") + "version.") \ + /* Builtins-Trace Errors */ \ + T(TraceEventCategoryError, "Trace event category must be a string.") \ + T(TraceEventNameError, "Trace event name must be a string.") \ + T(TraceEventNameLengthError, \ + "Trace event name must not be an empty string.") \ + T(TraceEventPhaseError, "Trace event phase must be a number.") \ + T(TraceEventIDError, "Trace event id must be a number.") class MessageTemplate { public: diff --git a/deps/v8/test/cctest/test-trace-event.cc b/deps/v8/test/cctest/test-trace-event.cc index 471619062a02d3..d2f8b69aac3ef5 100644 --- a/deps/v8/test/cctest/test-trace-event.cc +++ b/deps/v8/test/cctest/test-trace-event.cc @@ -75,7 +75,7 @@ class MockTracingController : public v8::TracingController { const char* name, uint64_t handle) override {} const uint8_t* GetCategoryGroupEnabled(const char* name) override { - if (strcmp(name, "v8-cat")) { + if (strncmp(name, "v8-cat", 6)) { static uint8_t no = 0; return &no; } else { @@ -274,3 +274,135 @@ TEST(TestEventWithTimestamp) { CHECK_EQ(13832, GET_TRACE_OBJECT(2)->timestamp); CHECK_EQ(2, GET_TRACE_OBJECT(2)->num_args); } + +TEST(BuiltinsIsTraceCategoryEnabled) { + CcTest::InitializeVM(); + MockTracingPlatform platform; + + v8::Isolate* isolate = CcTest::isolate(); + v8::HandleScope handle_scope(isolate); + LocalContext env; + + v8::Local binding = env->GetExtrasBindingObject(); + CHECK(!binding.IsEmpty()); + + auto undefined = v8::Undefined(isolate); + auto isTraceCategoryEnabled = + binding->Get(env.local(), v8_str("isTraceCategoryEnabled")) + .ToLocalChecked() + .As(); + + { + // Test with an enabled category + v8::Local argv[] = {v8_str("v8-cat")}; + auto result = isTraceCategoryEnabled->Call(env.local(), undefined, 1, argv) + .ToLocalChecked() + .As(); + + CHECK(result->BooleanValue()); + } + + { + // Test with a disabled category + v8::Local argv[] = {v8_str("cat")}; + auto result = isTraceCategoryEnabled->Call(env.local(), undefined, 1, argv) + .ToLocalChecked() + .As(); + + CHECK(!result->BooleanValue()); + } + + { + // Test with an enabled utf8 category + v8::Local argv[] = {v8_str("v8-cat\u20ac")}; + auto result = isTraceCategoryEnabled->Call(env.local(), undefined, 1, argv) + .ToLocalChecked() + .As(); + + CHECK(result->BooleanValue()); + } +} + +TEST(BuiltinsTrace) { + CcTest::InitializeVM(); + MockTracingPlatform platform; + + v8::Isolate* isolate = CcTest::isolate(); + v8::HandleScope handle_scope(isolate); + LocalContext env; + + v8::Local binding = env->GetExtrasBindingObject(); + CHECK(!binding.IsEmpty()); + + auto undefined = v8::Undefined(isolate); + auto trace = binding->Get(env.local(), v8_str("trace")) + .ToLocalChecked() + .As(); + + // Test with disabled category + { + v8::Local category = v8_str("cat"); + v8::Local name = v8_str("name"); + v8::Local argv[] = { + v8::Integer::New(isolate, 'b'), // phase + category, name, v8::Integer::New(isolate, 0), // id + undefined // data + }; + auto result = trace->Call(env.local(), undefined, 5, argv) + .ToLocalChecked() + .As(); + + CHECK(!result->BooleanValue()); + CHECK_EQ(0, GET_TRACE_OBJECTS_LIST->size()); + } + + // Test with enabled category + { + v8::Local category = v8_str("v8-cat"); + v8::Local name = v8_str("name"); + v8::Local context = isolate->GetCurrentContext(); + v8::Local data = v8::Object::New(isolate); + data->Set(context, v8_str("foo"), v8_str("bar")).FromJust(); + v8::Local argv[] = { + v8::Integer::New(isolate, 'b'), // phase + category, name, v8::Integer::New(isolate, 123), // id + data // data arg + }; + auto result = trace->Call(env.local(), undefined, 5, argv) + .ToLocalChecked() + .As(); + + CHECK(result->BooleanValue()); + CHECK_EQ(1, GET_TRACE_OBJECTS_LIST->size()); + + CHECK_EQ(123, GET_TRACE_OBJECT(0)->id); + CHECK_EQ('b', GET_TRACE_OBJECT(0)->phase); + CHECK_EQ("name", GET_TRACE_OBJECT(0)->name); + CHECK_EQ(1, GET_TRACE_OBJECT(0)->num_args); + } + + // Test with enabled utf8 category + { + v8::Local category = v8_str("v8-cat\u20ac"); + v8::Local name = v8_str("name\u20ac"); + v8::Local context = isolate->GetCurrentContext(); + v8::Local data = v8::Object::New(isolate); + data->Set(context, v8_str("foo"), v8_str("bar")).FromJust(); + v8::Local argv[] = { + v8::Integer::New(isolate, 'b'), // phase + category, name, v8::Integer::New(isolate, 123), // id + data // data arg + }; + auto result = trace->Call(env.local(), undefined, 5, argv) + .ToLocalChecked() + .As(); + + CHECK(result->BooleanValue()); + CHECK_EQ(2, GET_TRACE_OBJECTS_LIST->size()); + + CHECK_EQ(123, GET_TRACE_OBJECT(1)->id); + CHECK_EQ('b', GET_TRACE_OBJECT(1)->phase); + CHECK_EQ("name\u20ac", GET_TRACE_OBJECT(1)->name); + CHECK_EQ(1, GET_TRACE_OBJECT(1)->num_args); + } +} diff --git a/src/node_constants.cc b/src/node_constants.cc index c1e39244b359b4..61aa42a8efb6b2 100644 --- a/src/node_constants.cc +++ b/src/node_constants.cc @@ -1282,6 +1282,35 @@ void DefineDLOpenConstants(Local target) { #endif } +void DefineTraceConstants(Local target) { + NODE_DEFINE_CONSTANT(target, TRACE_EVENT_PHASE_BEGIN); + NODE_DEFINE_CONSTANT(target, TRACE_EVENT_PHASE_END); + NODE_DEFINE_CONSTANT(target, TRACE_EVENT_PHASE_COMPLETE); + NODE_DEFINE_CONSTANT(target, TRACE_EVENT_PHASE_INSTANT); + NODE_DEFINE_CONSTANT(target, TRACE_EVENT_PHASE_ASYNC_BEGIN); + NODE_DEFINE_CONSTANT(target, TRACE_EVENT_PHASE_ASYNC_STEP_INTO); + NODE_DEFINE_CONSTANT(target, TRACE_EVENT_PHASE_ASYNC_STEP_PAST); + NODE_DEFINE_CONSTANT(target, TRACE_EVENT_PHASE_ASYNC_END); + NODE_DEFINE_CONSTANT(target, TRACE_EVENT_PHASE_NESTABLE_ASYNC_BEGIN); + NODE_DEFINE_CONSTANT(target, TRACE_EVENT_PHASE_NESTABLE_ASYNC_END); + NODE_DEFINE_CONSTANT(target, TRACE_EVENT_PHASE_NESTABLE_ASYNC_INSTANT); + NODE_DEFINE_CONSTANT(target, TRACE_EVENT_PHASE_FLOW_BEGIN); + NODE_DEFINE_CONSTANT(target, TRACE_EVENT_PHASE_FLOW_STEP); + NODE_DEFINE_CONSTANT(target, TRACE_EVENT_PHASE_FLOW_END); + NODE_DEFINE_CONSTANT(target, TRACE_EVENT_PHASE_METADATA); + NODE_DEFINE_CONSTANT(target, TRACE_EVENT_PHASE_COUNTER); + NODE_DEFINE_CONSTANT(target, TRACE_EVENT_PHASE_SAMPLE); + NODE_DEFINE_CONSTANT(target, TRACE_EVENT_PHASE_CREATE_OBJECT); + NODE_DEFINE_CONSTANT(target, TRACE_EVENT_PHASE_SNAPSHOT_OBJECT); + NODE_DEFINE_CONSTANT(target, TRACE_EVENT_PHASE_DELETE_OBJECT); + NODE_DEFINE_CONSTANT(target, TRACE_EVENT_PHASE_MEMORY_DUMP); + NODE_DEFINE_CONSTANT(target, TRACE_EVENT_PHASE_MARK); + NODE_DEFINE_CONSTANT(target, TRACE_EVENT_PHASE_CLOCK_SYNC); + NODE_DEFINE_CONSTANT(target, TRACE_EVENT_PHASE_ENTER_CONTEXT); + NODE_DEFINE_CONSTANT(target, TRACE_EVENT_PHASE_LEAVE_CONTEXT); + NODE_DEFINE_CONSTANT(target, TRACE_EVENT_PHASE_LINK_IDS); +} + } // anonymous namespace void DefineConstants(v8::Isolate* isolate, Local target) { @@ -1315,6 +1344,10 @@ void DefineConstants(v8::Isolate* isolate, Local target) { CHECK(dlopen_constants->SetPrototype(env->context(), Null(env->isolate())).FromJust()); + Local trace_constants = Object::New(isolate); + CHECK(trace_constants->SetPrototype(env->context(), + Null(env->isolate())).FromJust()); + DefineErrnoConstants(err_constants); DefineWindowsErrorConstants(err_constants); DefineSignalConstants(sig_constants); @@ -1323,6 +1356,7 @@ void DefineConstants(v8::Isolate* isolate, Local target) { DefineCryptoConstants(crypto_constants); DefineZlibConstants(zlib_constants); DefineDLOpenConstants(dlopen_constants); + DefineTraceConstants(trace_constants); // Define libuv constants. NODE_DEFINE_CONSTANT(os_constants, UV_UDP_REUSEADDR); @@ -1334,6 +1368,7 @@ void DefineConstants(v8::Isolate* isolate, Local target) { target->Set(OneByteString(isolate, "fs"), fs_constants); target->Set(OneByteString(isolate, "crypto"), crypto_constants); target->Set(OneByteString(isolate, "zlib"), zlib_constants); + target->Set(OneByteString(isolate, "trace"), trace_constants); } } // namespace node diff --git a/src/node_trace_events.cc b/src/node_trace_events.cc index 0904311dad865b..a2f42249483cd7 100644 --- a/src/node_trace_events.cc +++ b/src/node_trace_events.cc @@ -232,6 +232,19 @@ void Initialize(Local target, target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "CategorySet"), category_set->GetFunction()); + + Local isTraceCategoryEnabled = + FIXED_ONE_BYTE_STRING(env->isolate(), "isTraceCategoryEnabled"); + Local trace = FIXED_ONE_BYTE_STRING(env->isolate(), "trace"); + + // Grab the trace and isTraceCategoryEnabled intrinsics from the binding + // object and expose those to our binding layer. + Local binding = context->GetExtrasBindingObject(); + target->Set(context, isTraceCategoryEnabled, + binding->Get(context, isTraceCategoryEnabled).ToLocalChecked()) + .FromJust(); + target->Set(context, trace, + binding->Get(context, trace).ToLocalChecked()).FromJust(); } } // namespace node diff --git a/test/parallel/test-binding-constants.js b/test/parallel/test-binding-constants.js index aaf0ebde5230a8..b8e852b3525ab8 100644 --- a/test/parallel/test-binding-constants.js +++ b/test/parallel/test-binding-constants.js @@ -5,7 +5,7 @@ const constants = process.binding('constants'); const assert = require('assert'); assert.deepStrictEqual( - Object.keys(constants).sort(), ['crypto', 'fs', 'os', 'zlib'] + Object.keys(constants).sort(), ['crypto', 'fs', 'os', 'trace', 'zlib'] ); assert.deepStrictEqual( @@ -26,6 +26,6 @@ function test(obj) { } [ - constants, constants.crypto, constants.fs, constants.os, constants.zlib, - constants.os.dlopen, constants.os.errno, constants.os.signals + constants, constants.crypto, constants.fs, constants.os, constants.trace, + constants.zlib, constants.os.dlopen, constants.os.errno, constants.os.signals ].forEach(test);