diff --git a/benchmark/misc/trace.js b/benchmark/misc/trace.js new file mode 100644 index 00000000000000..228aaac4e6d2c4 --- /dev/null +++ b/benchmark/misc/trace.js @@ -0,0 +1,72 @@ +'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 'trace': + doTrace(n); + break; + case 'emit': + doEmit(n); + break; + case 'isTraceCategoryEnabled': + doIsTraceCategoryEnabled(n); + break; + case 'categoryGroupEnabled': + doCategoryGroupEnabled(n); + break; + } +} diff --git a/deps/v8/BUILD.gn b/deps/v8/BUILD.gn index 72a19b2ca450fa..b24c626829c962 100644 --- a/deps/v8/BUILD.gn +++ b/deps/v8/BUILD.gn @@ -1320,6 +1320,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 601b8403cc4378..2931b2cb06cbbc 100644 --- a/deps/v8/gypfiles/v8.gyp +++ b/deps/v8/gypfiles/v8.gyp @@ -627,6 +627,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 ff4beae7fdf3b9..393213c1fe29b3 100644 --- a/deps/v8/src/bootstrapper.cc +++ b/deps/v8/src/bootstrapper.cc @@ -4931,6 +4931,14 @@ bool Genesis::InstallExtraNatives() { if (!Bootstrapper::CompileExtraBuiltin(isolate(), i)) return false; } + // 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); + return true; } diff --git a/deps/v8/src/builtins/builtins-definitions.h b/deps/v8/src/builtins/builtins-definitions.h index bf5b9086aaf967..9bb79191e864fd 100644 --- a/deps/v8/src/builtins/builtins-definitions.h +++ b/deps/v8/src/builtins/builtins-definitions.h @@ -1076,6 +1076,10 @@ namespace internal { /* ES6 #sec-symbol.prototype.valueof */ \ TFJ(SymbolPrototypeValueOf, 0) \ \ + /* Trace */ \ + CPP(IsTraceCategoryEnabled) \ + CPP(Trace) \ + \ /* TypedArray */ \ TFS(IterableToList, kIterable, kIteratorFn) \ TFS(TypedArrayInitialize, kHolder, kLength, kElementSize, kInitialize) \ diff --git a/deps/v8/src/builtins/builtins-trace.cc b/deps/v8/src/builtins/builtins-trace.cc new file mode 100644 index 00000000000000..e070bb14f98379 --- /dev/null +++ b/deps/v8/src/builtins/builtins-trace.cc @@ -0,0 +1,701 @@ +// 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/builtins/builtins-utils.h" +#include "src/builtins/builtins.h" +#include "src/counters.h" +#include "src/api.h" +#include "src/objects-inl.h" + +namespace v8 { +namespace internal { + +namespace { + +using v8::tracing::TracedValue; + +enum Result { NODATA, SUCCESS, FAILED }; + +class MaybeUtf8 { + public: + MaybeUtf8(Handle string) { + string = String::Flatten(string); + one_byte_ = string->IsOneByteRepresentation(); + if (one_byte_) { + data_ = string->length() == 0 ? "" : + reinterpret_cast(Handle::cast(string) + ->GetChars()); + } else { + data_ = nullptr; + Local local = Utils::ToLocal(string); + allocated_.reset(new char[local->Utf8Length() + 1]); + local->WriteUtf8(allocated_.get()); + } + } + const char* operator*() const { + return allocated_ ? const_cast(allocated_.get()) : data_; + } + private: + const char* data_; + std::unique_ptr allocated_; + bool one_byte_; +}; + +class ScopedSetTraceValue { + public: + ScopedSetTraceValue(std::unique_ptr traced_value, + uint8_t* arg_type, uint64_t* arg_value) + : traced_value_(std::move(traced_value)), + arg_type_(arg_type), + arg_value_(arg_value) {} + + ~ScopedSetTraceValue() { + tracing::SetTraceValue(std::move(traced_value_), arg_type_, arg_value_); + } + + TracedValue* operator*() const { return traced_value_.get(); } + TracedValue* operator->() const { return traced_value_.get(); } + private: + std::unique_ptr traced_value_; + uint8_t* arg_type_; + uint64_t* arg_value_; +}; + +const uint8_t* GetCategoryGroupEnabled(Handle string) { + MaybeUtf8 category(string); + return TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(*category); +} + +Result ProcessSmi(Smi* smi, uint8_t* arg_type, uint64_t* arg_value) { + tracing::SetTraceValue(smi->value(), arg_type, arg_value); + return SUCCESS; +} + +Result SetSmi(Handle key, Smi* smi, TracedValue* traced_value) { + MaybeUtf8 name(key); + traced_value->SetInteger(*name, smi->value()); + return SUCCESS; +} + +Result ProcessDouble(Handle object, + uint8_t* arg_type, + uint64_t* arg_value) { + double number = Handle::cast(object)->value(); + if (std::isinf(number) || std::isnan(number)) { + return NODATA; + } + tracing::SetTraceValue(number, arg_type, arg_value); + return SUCCESS; +} + +Result AppendDouble(Handle object, TracedValue* traced_value) { + double number = Handle::cast(object)->value(); + if (std::isinf(number) || std::isnan(number)) { + traced_value->AppendNull(); + } else { + traced_value->AppendDouble(number); + } + return SUCCESS; +} + +Result SetDouble(Handle key, + Handle object, + TracedValue* traced_value) { + MaybeUtf8 name(key); + double number = Handle::cast(object)->value(); + if (std::isinf(number) || std::isnan(number)) { + traced_value->SetNull(*name); + } else { + traced_value->SetDouble(*name, number); + } + return SUCCESS; +} + +Result ProcessString(Isolate* isolate, + Handle string, + uint8_t* arg_type, + uint64_t* arg_value) { + MaybeUtf8 data(string); + tracing::SetTraceValue(TRACE_STR_COPY(*data), arg_type, arg_value); + return SUCCESS; +} + +Result AppendString(Isolate* isolate, + Handle string, + TracedValue* traced_value) { + MaybeUtf8 data(string); + traced_value->AppendString(*data); + return SUCCESS; +} + +Result SetString(Isolate* isolate, + Handle key, + Handle string, + TracedValue* traced_value) { + MaybeUtf8 name(key); + MaybeUtf8 data(string); + traced_value->SetString(*name, *data); + return SUCCESS; +} + +Result ProcessOddball(Oddball* oddball, + uint8_t* arg_type, + uint64_t* arg_value) { + switch (oddball->kind()) { + case Oddball::kFalse: + tracing::SetTraceValue(false, arg_type, arg_value); + return SUCCESS; + case Oddball::kTrue: + tracing::SetTraceValue(true, arg_type, arg_value); + return SUCCESS; + default: + return NODATA; + } + UNREACHABLE(); +} + +Result AppendOddball(Oddball* oddball, TracedValue* traced_value) { + switch (oddball->kind()) { + case Oddball::kFalse: + traced_value->AppendBoolean(false); + break; + case Oddball::kTrue: + traced_value->AppendBoolean(true); + break; + default: + traced_value->AppendNull(); + } + return SUCCESS; +} + +Result SetOddball(Handle key, + Oddball* oddball, + TracedValue* traced_value) { + MaybeUtf8 name(key); + switch (oddball->kind()) { + case Oddball::kFalse: + traced_value->SetBoolean(*name, false); + break; + case Oddball::kTrue: + traced_value->SetBoolean(*name, true); + break; + default: + traced_value->SetNull(*name); + } + return SUCCESS; +} + +Result ProcessJSValue(Isolate* isolate, + Handle object, + uint8_t* arg_type, + uint64_t* arg_value) { + Handle value; + ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, value, + Object::ToPrimitive(object), + FAILED); + if (value->IsString()) { + return ProcessString(isolate, + Handle::cast(value), + arg_type, arg_value); + } else if (value->IsNumber()) { + if (value->IsSmi()) { + return ProcessSmi(Smi::cast(*value), arg_type, arg_value); + } + return ProcessDouble(Handle::cast(value), arg_type, arg_value); + } else if (value->IsBoolean()) { + tracing::SetTraceValue(value->IsTrue(isolate) ? true : false, + arg_type, arg_value); + return SUCCESS; + } else { + return NODATA; + } + + UNREACHABLE(); +} + +Result AppendJSElement(Isolate* isolate, + Handle object, + TracedValue* traced_value) { + Handle value; + ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, value, + Object::ToPrimitive(object), + FAILED); + if (value->IsString()) { + return AppendString(isolate, + Handle::cast(value), + traced_value); + } else if (value->IsNumber()) { + if (value->IsSmi()) { + traced_value->AppendInteger(Smi::cast(*value)->value()); + return SUCCESS; + } + return AppendDouble(Handle::cast(value), traced_value); + } else if (value->IsBoolean()) { + traced_value->AppendBoolean(value->IsTrue(isolate) ? true : false); + } else { + traced_value->AppendNull(); + } + return SUCCESS; +} + +Result SetJSElement(Isolate* isolate, + Handle key, + Handle object, + TracedValue* traced_value) { + Handle value; + ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, value, + Object::ToPrimitive(object), + FAILED); + if (value->IsString()) { + return SetString(isolate, + key, Handle::cast(value), + traced_value); + } else if (value->IsNumber()) { + if (value->IsSmi()) { + return SetSmi(key, Smi::cast(*value), traced_value); + } + return SetDouble(key, Handle::cast(value), traced_value); + } else { + MaybeUtf8 name(key); + if (value->IsBoolean()) { + traced_value->SetBoolean(*name, value->IsTrue(isolate) ? true : false); + } else { + traced_value->SetNull(*name); + } + return SUCCESS; + } + + UNREACHABLE(); +} + +Result AppendJSValue(Isolate* isolate, + Handle object, + TracedValue* traced_value) { + + if (!object->IsNullOrUndefined(isolate)) { + if (object->IsSmi()) { + traced_value->AppendInteger(Smi::cast(*object)->value()); + return SUCCESS; + } + switch (HeapObject::cast(*object)->map()->instance_type()) { + case HEAP_NUMBER_TYPE: + case MUTABLE_HEAP_NUMBER_TYPE: + return AppendDouble(Handle::cast(object), traced_value); + case ODDBALL_TYPE: + return AppendOddball(Oddball::cast(*object), traced_value); + case JS_VALUE_TYPE: + return AppendJSElement(isolate, + Handle::cast(object), + traced_value); + default: + if (object->IsString()) { + return AppendString(isolate, + Handle::cast(object), + traced_value); + } + return FAILED; + } + + UNREACHABLE(); + } else { + traced_value->AppendNull(); + } + + return SUCCESS; +} + +Result SetJSValue(Isolate* isolate, + Handle key, + Handle object, + TracedValue* traced_value) { + if (!object->IsNullOrUndefined(isolate)) { + if (object->IsSmi()) { + return SetSmi(key, Smi::cast(*object), traced_value); + } + switch (HeapObject::cast(*object)->map()->instance_type()) { + case HEAP_NUMBER_TYPE: + case MUTABLE_HEAP_NUMBER_TYPE: + return SetDouble(key, Handle::cast(object), traced_value); + case ODDBALL_TYPE: + return SetOddball(key, Oddball::cast(*object), traced_value); + case JS_VALUE_TYPE: + return SetJSElement(isolate, + key, Handle::cast(object), + traced_value); + default: + if (object->IsString()) { + return SetString(isolate, + key, Handle::cast(object), + traced_value); + } + return FAILED; + } + + UNREACHABLE(); + } else { + MaybeUtf8 name(key); + traced_value->SetNull(*name); + } + + return SUCCESS; +} + +Result ProcessArrayLikeSlow(Isolate* isolate, + Handle object, + uint32_t start, + uint32_t length, + TracedValue* traced_value) { + for (uint32_t i = start; i < length; i++) { + Handle element; + ASSIGN_RETURN_ON_EXCEPTION_VALUE( + isolate, element, JSReceiver::GetElement(isolate, object, i), + FAILED); + Result result = AppendJSValue(isolate, element, traced_value); + if (result == SUCCESS) continue; + if (result == NODATA) { + traced_value->AppendNull(); + } else { + return result; + } + } + return SUCCESS; +} + +Result ProcessJSArray(Isolate* isolate, + Handle object, + uint8_t* arg_type, + uint64_t* arg_value) { + ScopedSetTraceValue traced_value(TracedValue::CreateArray(), + arg_type, arg_value); + + uint32_t length = 0; + uint32_t i = 0; + CHECK(object->length()->ToArrayLength(&length)); + DCHECK(!object->IsAccessCheckNeeded()); + + switch (object->GetElementsKind()) { + case PACKED_SMI_ELEMENTS: { + Handle elements(FixedArray::cast(object->elements()), + isolate); + while (i < length) { + traced_value->AppendInteger(Smi::cast(elements->get(i))->value()); + i++; + } + break; + } + case PACKED_DOUBLE_ELEMENTS: { + if (length == 0) break; + Handle elements( + FixedDoubleArray::cast(object->elements()), isolate); + while (i < length) { + traced_value->AppendDouble(elements->get_scalar(i)); + i++; + } + break; + } + case PACKED_ELEMENTS: { + Handle old_length(object->length(), isolate); + while (i < length) { + if (object->length() != *old_length || + object->GetElementsKind() != PACKED_ELEMENTS) { + // Fall back to slow path. + break; + } + Result result = AppendJSValue( + isolate, + Handle(FixedArray::cast(object->elements())->get(i), + isolate), *traced_value); + if (result == NODATA) { + traced_value->AppendNull(); + } else if (result == FAILED) { + return FAILED; + } + i++; + } + break; + } + default: + break; + } + if (i < length && + ProcessArrayLikeSlow(isolate, object, i, length, + *traced_value) == FAILED) { + return FAILED; + } + return SUCCESS; +} + +Result ProcessJSReceiverSlow(Isolate* isolate, + Handle object, + TracedValue* traced_value) { + Handle contents; + ASSIGN_RETURN_ON_EXCEPTION_VALUE( + isolate, contents, + KeyAccumulator::GetKeys(object, KeyCollectionMode::kOwnOnly, + ENUMERABLE_STRINGS, + GetKeysConversion::kConvertToString), + FAILED); + for (int i = 0; i < contents->length(); i++) { + Handle key(String::cast(contents->get(i)), isolate); + Handle property; + ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, property, + Object::GetPropertyOrElement(object, key), + FAILED); + if (SetJSValue(isolate, key, property, traced_value) == FAILED) + return FAILED; + } + return SUCCESS; +} + +Result ProcessObject(Isolate* isolate, + Handle object, + uint8_t* arg_type, + uint64_t* arg_value) { + ScopedSetTraceValue traced_value(TracedValue::Create(), arg_type, arg_value); + HandleScope handle_scope(isolate); + + if (object->map()->instance_type() > LAST_CUSTOM_ELEMENTS_RECEIVER && + object->HasFastProperties() && + Handle::cast(object)->elements()->length() == 0) { + DCHECK(object->IsJSObject()); + DCHECK(!object->IsJSGlobalProxy()); + Handle js_obj = Handle::cast(object); + DCHECK(!js_obj->HasIndexedInterceptor()); + DCHECK(!js_obj->HasNamedInterceptor()); + Handle map(js_obj->map()); + + for (int i = 0; i < map->NumberOfOwnDescriptors(); i++) { + Handle name(map->instance_descriptors()->GetKey(i), isolate); + if (!name->IsString()) continue; + Handle key = Handle::cast(name); + PropertyDetails details = map->instance_descriptors()->GetDetails(i); + if (details.IsDontEnum()) continue; + Handle property; + if (details.location() == kField && *map == js_obj->map()) { + DCHECK_EQ(kData, details.kind()); + FieldIndex field_index = FieldIndex::ForDescriptor(*map, i); + property = JSObject::FastPropertyAt(js_obj, details.representation(), + field_index); + } else { + ASSIGN_RETURN_ON_EXCEPTION_VALUE( + isolate, property, Object::GetPropertyOrElement(js_obj, key), + FAILED); + } + if (SetJSValue(isolate, key, property, *traced_value) == FAILED) + return FAILED; + } + } else { + if (ProcessJSReceiverSlow(isolate, object, *traced_value) == FAILED) + return FAILED; + } + + return SUCCESS; +} + +Result ProcessJSProxy(Isolate* isolate, + Handle object, + int32_t* num_args, + uint8_t* arg_type, + uint64_t* arg_value) { + HandleScope scope(isolate); + Maybe is_array = Object::IsArray(object); + if (is_array.IsNothing()) return FAILED; + if (is_array.FromJust()) { + Handle length_object; + ASSIGN_RETURN_ON_EXCEPTION_VALUE( + isolate, length_object, + Object::GetLengthFromArrayLike(isolate, object), FAILED); + *num_args = 1; + uint32_t length; + if (!length_object->ToUint32(&length)) + return FAILED; + + ScopedSetTraceValue traced_value(TracedValue::CreateArray(), + arg_type, arg_value); + + *num_args = 1; + if (ProcessArrayLikeSlow(isolate, object, 0, length, + *traced_value) == FAILED) { + return FAILED; + } + } else { + *num_args = 1; + ScopedSetTraceValue traced_value(TracedValue::Create(), + arg_type, arg_value); + if (ProcessJSReceiverSlow(isolate, object, *traced_value) == FAILED) { + return FAILED; + } + } + return SUCCESS; +} + +Result ProcessData(Isolate* isolate, + Handle object, + int32_t* num_args, + uint8_t* arg_type, + uint64_t* arg_value); + +Result ProcessCallable(Isolate* isolate, + Handle callable, + int32_t* num_args, + uint8_t* arg_type, + uint64_t* arg_value) { + HandleScope scope(isolate); + DCHECK(callback->IsCallable()); + Handle argv[] = {}; + Handle value; + ASSIGN_RETURN_ON_EXCEPTION_VALUE( + isolate, value, + Execution::Call(isolate, callable, isolate->global_proxy(), + 0, argv), FAILED); + if (isolate->has_pending_exception()) + return FAILED; + return ProcessData(isolate, scope.CloseAndEscape(value), + num_args, arg_type, arg_value); +} + +Result ProcessData(Isolate* isolate, + Handle object, + int32_t* num_args, + uint8_t* arg_type, + uint64_t* arg_value) { + // Only supports primitives, objects with primitive own values, + // arrays with primitives, and functions that return primitives. + if (!object->IsNullOrUndefined(isolate)) { + if (object->IsSmi()) { + return ProcessSmi(Smi::cast(*object), arg_type, arg_value); + } + switch (HeapObject::cast(*object)->map()->instance_type()) { + case HEAP_NUMBER_TYPE: + case MUTABLE_HEAP_NUMBER_TYPE: + *num_args = 1; + return ProcessDouble(Handle::cast(object), + arg_type, arg_value); + case ODDBALL_TYPE: + *num_args = 1; + return ProcessOddball(Oddball::cast(*object), arg_type, arg_value); + case JS_VALUE_TYPE: + *num_args = 1; + return ProcessJSValue(isolate, + Handle::cast(object), + arg_type, arg_value); + case JS_ARRAY_TYPE: + *num_args = 1; + return ProcessJSArray(isolate, + Handle::cast(object), + arg_type, arg_value); + default: + if (object->IsString()) { + *num_args = 1; + return ProcessString(isolate, + Handle::cast(object), + arg_type, arg_value); + } else { + DCHECK(object->IsJSReceiver()); + if (object->IsCallable()) { + return ProcessCallable(isolate, Handle::cast(object), + num_args, arg_type, arg_value); + } + if (object->IsJSProxy()) { + return ProcessJSProxy(isolate, Handle::cast(object), + num_args, arg_type, arg_value); + } + *num_args = 1; + return ProcessObject(isolate, Handle::cast(object), + arg_type, arg_value); + } + } + + UNREACHABLE(); + } + + return NODATA; +} + +} // namespace + +// Builins::kIsTraceCategoryEnabled(category) : bool +BUILTIN(IsTraceCategoryEnabled) { + HandleScope scope(isolate); + Handle category = args.atOrUndefined(isolate, 1); + if (!category->IsString()) { + return isolate->Throw(*isolate->factory()->NewTypeError( + MessageTemplate::kTraceEventCategory)); + } + return isolate->heap()->ToBoolean( + *GetCategoryGroupEnabled(Handle::cast(category))); +} + +// Builtins::kTrace(phase, category, name, id, data) : bool +BUILTIN(Trace) { + static const char* arg_name = "data"; + 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); + + bool result = false; + + if (!phase_arg->IsNumber()) { + return isolate->Throw(*isolate->factory()->NewTypeError( + MessageTemplate::kTraceEventPhase)); + } + if (!category->IsString()) { + return isolate->Throw(*isolate->factory()->NewTypeError( + MessageTemplate::kTraceEventCategory)); + } + if (!name_arg->IsString()) { + return isolate->Throw(*isolate->factory()->NewTypeError( + MessageTemplate::kTraceEventName)); + } + if (!id_arg->IsNumber()) { + return isolate->Throw(*isolate->factory()->NewTypeError( + MessageTemplate::kTraceEventID)); + } + + const uint8_t* category_group_enabled = + GetCategoryGroupEnabled(Handle::cast(category)); + + if (*category_group_enabled) { + int32_t num_args = 0; + uint8_t arg_type; + uint64_t arg_value; + int32_t id = 0; + uint32_t flags = TRACE_EVENT_FLAG_COPY; + if (!id_arg->IsNullOrUndefined(isolate)) { + flags |= TRACE_EVENT_FLAG_HAS_ID; + id = DoubleToInt32(id_arg->Number()); + } + + char phase = static_cast(DoubleToInt32(phase_arg->Number())); + + if (ProcessData(isolate, data_arg, &num_args, + &arg_type, &arg_value) == FAILED) { + + if (isolate->has_pending_exception()) + return isolate->heap()->exception(); + + return isolate->Throw(*isolate->factory()->NewTypeError( + MessageTemplate::kTraceEventData)); + } + + Handle name_str = Handle::cast(name_arg); + if (name_str->length() == 0) { + return isolate->Throw(*isolate->factory()->NewTypeError( + MessageTemplate::kTraceEventNameLength)); + } + MaybeUtf8 name(name_str); + TRACE_EVENT_API_ADD_TRACE_EVENT( + phase, category_group_enabled, *name, tracing::kGlobalScope, id, + tracing::kNoId, num_args, &arg_name, &arg_type, &arg_value, flags); + result = true; + } + return isolate->heap()->ToBoolean(result); +} + +} // namespace internal +} // namespace v8 diff --git a/deps/v8/src/debug/debug-evaluate.cc b/deps/v8/src/debug/debug-evaluate.cc index 6052149b812f83..4c2137fdbaaebe 100644 --- a/deps/v8/src/debug/debug-evaluate.cc +++ b/deps/v8/src/debug/debug-evaluate.cc @@ -570,6 +570,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 1a1a5b29ff446c..8b436f0f9f7237 100644 --- a/deps/v8/src/messages.h +++ b/deps/v8/src/messages.h @@ -745,7 +745,13 @@ 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.") \ + T(TraceEventData, "Trace data does not support deeply nested objects") \ + T(TraceEventCategory, "Trace event category must be a string.") \ + T(TraceEventName, "Trace event name must be a string.") \ + T(TraceEventNameLength, "Trace event name must not be an empty string.") \ + T(TraceEventPhase, "Trace event phase must be a number.") \ + T(TraceEventID, "Trace event id must be a number.") class MessageTemplate { public: diff --git a/deps/v8/src/tracing/traced-value.cc b/deps/v8/src/tracing/traced-value.cc index de9382e65b1038..a10f489782e0c0 100644 --- a/deps/v8/src/tracing/traced-value.cc +++ b/deps/v8/src/tracing/traced-value.cc @@ -62,8 +62,13 @@ std::unique_ptr TracedValue::Create() { return std::unique_ptr(new TracedValue()); } -TracedValue::TracedValue() : first_item_(true) { - DEBUG_PUSH_CONTAINER(kStackTypeDict); +std::unique_ptr TracedValue::CreateArray() { + return std::unique_ptr(new TracedValue(true)); +} + +TracedValue::TracedValue(bool root_is_array) : first_item_(true), + root_is_array_(root_is_array) { + DEBUG_PUSH_CONTAINER(root_is_array ? kStackTypeDict : kStackTypeArray); } TracedValue::~TracedValue() { @@ -97,6 +102,12 @@ void TracedValue::SetString(const char* name, const char* value) { EscapeAndAppendString(value, &data_); } +void TracedValue::SetNull(const char* name) { + DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict); + WriteName(name); + data_ += "null"; +} + void TracedValue::BeginDictionary(const char* name) { DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict); DEBUG_PUSH_CONTAINER(kStackTypeDict); @@ -138,6 +149,12 @@ void TracedValue::AppendString(const char* value) { EscapeAndAppendString(value, &data_); } +void TracedValue::AppendNull() { + DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray); + WriteComma(); + data_ += "null"; +} + void TracedValue::BeginDictionary() { DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray); DEBUG_PUSH_CONTAINER(kStackTypeDict); @@ -184,9 +201,9 @@ void TracedValue::WriteName(const char* name) { } void TracedValue::AppendAsTraceFormat(std::string* out) const { - *out += '{'; + *out += root_is_array_ ? '[' : '{'; *out += data_; - *out += '}'; + *out += root_is_array_ ? ']' : '}'; } } // namespace tracing diff --git a/deps/v8/src/tracing/traced-value.h b/deps/v8/src/tracing/traced-value.h index 7de4c234a24205..68a4f98ad4f09f 100644 --- a/deps/v8/src/tracing/traced-value.h +++ b/deps/v8/src/tracing/traced-value.h @@ -21,6 +21,7 @@ class TracedValue : public ConvertableToTraceFormat { ~TracedValue() override; static std::unique_ptr Create(); + static std::unique_ptr CreateArray(); void EndDictionary(); void EndArray(); @@ -29,6 +30,7 @@ class TracedValue : public ConvertableToTraceFormat { void SetInteger(const char* name, int value); void SetDouble(const char* name, double value); void SetBoolean(const char* name, bool value); + void SetNull(const char* name); void SetString(const char* name, const char* value); void SetString(const char* name, const std::string& value) { SetString(name, value.c_str()); @@ -39,6 +41,7 @@ class TracedValue : public ConvertableToTraceFormat { void AppendInteger(int); void AppendDouble(double); void AppendBoolean(bool); + void AppendNull(); void AppendString(const char*); void AppendString(const std::string& value) { AppendString(value.c_str()); } void BeginArray(); @@ -48,7 +51,7 @@ class TracedValue : public ConvertableToTraceFormat { void AppendAsTraceFormat(std::string* out) const override; private: - TracedValue(); + TracedValue(bool root_is_array = false); void WriteComma(); void WriteName(const char* name); @@ -60,6 +63,7 @@ class TracedValue : public ConvertableToTraceFormat { std::string data_; bool first_item_; + bool root_is_array_; DISALLOW_COPY_AND_ASSIGN(TracedValue); }; diff --git a/src/node_constants.cc b/src/node_constants.cc index 68339c0032804b..1c4f14e30e832d 100644 --- a/src/node_constants.cc +++ b/src/node_constants.cc @@ -1261,6 +1261,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) { @@ -1294,6 +1323,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); @@ -1302,6 +1335,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); @@ -1316,6 +1350,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 363d046de1e947..5ab3fcf9c56d84 100644 --- a/src/node_trace_events.cc +++ b/src/node_trace_events.cc @@ -228,6 +228,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