// Copyright 2015 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 #include #include "include/v8-function.h" #include "src/init/v8.h" #include "src/tracing/trace-event.h" #include "test/cctest/cctest.h" namespace { struct MockTraceObject { char phase; std::string name; uint64_t id; uint64_t bind_id; int num_args; unsigned int flags; int64_t timestamp; MockTraceObject(char phase, std::string name, uint64_t id, uint64_t bind_id, int num_args, int flags, int64_t timestamp) : phase(phase), name(name), id(id), bind_id(bind_id), num_args(num_args), flags(flags), timestamp(timestamp) {} }; class MockTracingController : public v8::TracingController { public: MockTracingController() = default; MockTracingController(const MockTracingController&) = delete; MockTracingController& operator=(const MockTracingController&) = delete; uint64_t AddTraceEvent( char phase, const uint8_t* category_enabled_flag, const char* name, const char* scope, uint64_t id, uint64_t bind_id, int num_args, const char** arg_names, const uint8_t* arg_types, const uint64_t* arg_values, std::unique_ptr* arg_convertables, unsigned int flags) override { return AddTraceEventWithTimestamp( phase, category_enabled_flag, name, scope, id, bind_id, num_args, arg_names, arg_types, arg_values, arg_convertables, flags, 0); } uint64_t AddTraceEventWithTimestamp( char phase, const uint8_t* category_enabled_flag, const char* name, const char* scope, uint64_t id, uint64_t bind_id, int num_args, const char** arg_names, const uint8_t* arg_types, const uint64_t* arg_values, std::unique_ptr* arg_convertables, unsigned int flags, int64_t timestamp) override { std::unique_ptr to = std::make_unique( phase, std::string(name), id, bind_id, num_args, flags, timestamp); trace_objects_.push_back(std::move(to)); return 0; } void UpdateTraceEventDuration(const uint8_t* category_enabled_flag, const char* name, uint64_t handle) override {} const uint8_t* GetCategoryGroupEnabled(const char* name) override { if (strncmp(name, "v8-cat", 6)) { static uint8_t no = 0; return &no; } else { static uint8_t yes = 0x7; return &yes; } } const std::vector>& GetMockTraceObjects() const { return trace_objects_; } private: std::vector> trace_objects_; }; class MockTracingPlatform : public TestPlatform { public: MockTracingPlatform() { // Now that it's completely constructed, make this the current platform. i::V8::SetPlatformForTesting(this); } ~MockTracingPlatform() override = default; v8::TracingController* GetTracingController() override { return &tracing_controller_; } size_t NumberOfTraceObjects() { return tracing_controller_.GetMockTraceObjects().size(); } MockTraceObject* GetTraceObject(size_t index) { return tracing_controller_.GetMockTraceObjects().at(index).get(); } private: MockTracingController tracing_controller_; }; } // namespace TEST(TraceEventDisabledCategory) { MockTracingPlatform platform; // Disabled category, will not add events. TRACE_EVENT_BEGIN0("cat", "e1"); TRACE_EVENT_END0("cat", "e1"); CHECK_EQ(0, platform.NumberOfTraceObjects()); } TEST(TraceEventNoArgs) { MockTracingPlatform platform; // Enabled category will add 2 events. TRACE_EVENT_BEGIN0("v8-cat", "e1"); TRACE_EVENT_END0("v8-cat", "e1"); CHECK_EQ(2, platform.NumberOfTraceObjects()); CHECK_EQ('B', platform.GetTraceObject(0)->phase); CHECK_EQ("e1", platform.GetTraceObject(0)->name); CHECK_EQ(0, platform.GetTraceObject(0)->num_args); CHECK_EQ('E', platform.GetTraceObject(1)->phase); CHECK_EQ("e1", platform.GetTraceObject(1)->name); CHECK_EQ(0, platform.GetTraceObject(1)->num_args); } TEST(TraceEventWithOneArg) { MockTracingPlatform platform; TRACE_EVENT_BEGIN1("v8-cat", "e1", "arg1", 42); TRACE_EVENT_END1("v8-cat", "e1", "arg1", 42); TRACE_EVENT_BEGIN1("v8-cat", "e2", "arg1", "abc"); TRACE_EVENT_END1("v8-cat", "e2", "arg1", "abc"); CHECK_EQ(4, platform.NumberOfTraceObjects()); CHECK_EQ(1, platform.GetTraceObject(0)->num_args); CHECK_EQ(1, platform.GetTraceObject(1)->num_args); CHECK_EQ(1, platform.GetTraceObject(2)->num_args); CHECK_EQ(1, platform.GetTraceObject(3)->num_args); } TEST(TraceEventWithTwoArgs) { MockTracingPlatform platform; TRACE_EVENT_BEGIN2("v8-cat", "e1", "arg1", 42, "arg2", "abc"); TRACE_EVENT_END2("v8-cat", "e1", "arg1", 42, "arg2", "abc"); TRACE_EVENT_BEGIN2("v8-cat", "e2", "arg1", "abc", "arg2", 43); TRACE_EVENT_END2("v8-cat", "e2", "arg1", "abc", "arg2", 43); CHECK_EQ(4, platform.NumberOfTraceObjects()); CHECK_EQ(2, platform.GetTraceObject(0)->num_args); CHECK_EQ(2, platform.GetTraceObject(1)->num_args); CHECK_EQ(2, platform.GetTraceObject(2)->num_args); CHECK_EQ(2, platform.GetTraceObject(3)->num_args); } TEST(ScopedTraceEvent) { MockTracingPlatform platform; { TRACE_EVENT0("v8-cat", "e"); } CHECK_EQ(1, platform.NumberOfTraceObjects()); CHECK_EQ(0, platform.GetTraceObject(0)->num_args); { TRACE_EVENT1("v8-cat", "e1", "arg1", "abc"); } CHECK_EQ(2, platform.NumberOfTraceObjects()); CHECK_EQ(1, platform.GetTraceObject(1)->num_args); { TRACE_EVENT2("v8-cat", "e1", "arg1", "abc", "arg2", 42); } CHECK_EQ(3, platform.NumberOfTraceObjects()); CHECK_EQ(2, platform.GetTraceObject(2)->num_args); } TEST(TestEventWithFlow) { MockTracingPlatform platform; static uint64_t bind_id = 21; { TRACE_EVENT_WITH_FLOW0("v8-cat", "f1", bind_id, TRACE_EVENT_FLAG_FLOW_OUT); } { TRACE_EVENT_WITH_FLOW0( "v8-cat", "f2", bind_id, TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT); } { TRACE_EVENT_WITH_FLOW0("v8-cat", "f3", bind_id, TRACE_EVENT_FLAG_FLOW_IN); } CHECK_EQ(3, platform.NumberOfTraceObjects()); CHECK_EQ(bind_id, platform.GetTraceObject(0)->bind_id); CHECK_EQ(TRACE_EVENT_FLAG_FLOW_OUT, platform.GetTraceObject(0)->flags); CHECK_EQ(bind_id, platform.GetTraceObject(1)->bind_id); CHECK_EQ(TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, platform.GetTraceObject(1)->flags); CHECK_EQ(bind_id, platform.GetTraceObject(2)->bind_id); CHECK_EQ(TRACE_EVENT_FLAG_FLOW_IN, platform.GetTraceObject(2)->flags); } TEST(TestEventWithId) { MockTracingPlatform platform; static uint64_t event_id = 21; TRACE_EVENT_ASYNC_BEGIN0("v8-cat", "a1", event_id); TRACE_EVENT_ASYNC_END0("v8-cat", "a1", event_id); CHECK_EQ(2, platform.NumberOfTraceObjects()); CHECK_EQ(TRACE_EVENT_PHASE_ASYNC_BEGIN, platform.GetTraceObject(0)->phase); CHECK_EQ(event_id, platform.GetTraceObject(0)->id); CHECK_EQ(TRACE_EVENT_PHASE_ASYNC_END, platform.GetTraceObject(1)->phase); CHECK_EQ(event_id, platform.GetTraceObject(1)->id); } TEST(TestEventWithTimestamp) { MockTracingPlatform platform; TRACE_EVENT_INSTANT_WITH_TIMESTAMP0("v8-cat", "0arg", TRACE_EVENT_SCOPE_GLOBAL, 1729); TRACE_EVENT_INSTANT_WITH_TIMESTAMP1("v8-cat", "1arg", TRACE_EVENT_SCOPE_GLOBAL, 4104, "val", 1); TRACE_EVENT_MARK_WITH_TIMESTAMP2("v8-cat", "mark", 13832, "a", 1, "b", 2); TRACE_EVENT_COPY_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP0("v8-cat", "begin", 5, 20683); TRACE_EVENT_COPY_NESTABLE_ASYNC_END_WITH_TIMESTAMP0("v8-cat", "end", 5, 32832); CHECK_EQ(5, platform.NumberOfTraceObjects()); CHECK_EQ(1729, platform.GetTraceObject(0)->timestamp); CHECK_EQ(0, platform.GetTraceObject(0)->num_args); CHECK_EQ(4104, platform.GetTraceObject(1)->timestamp); CHECK_EQ(1, platform.GetTraceObject(1)->num_args); CHECK_EQ(13832, platform.GetTraceObject(2)->timestamp); CHECK_EQ(2, platform.GetTraceObject(2)->num_args); CHECK_EQ(20683, platform.GetTraceObject(3)->timestamp); CHECK_EQ(32832, platform.GetTraceObject(4)->timestamp); } 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(isolate)); } { // 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(isolate)); } { // 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(isolate)); } } TEST(BuiltinsTrace) { CcTest::InitializeVM(); MockTracingPlatform platform; v8::Isolate* isolate = CcTest::isolate(); v8::HandleScope handle_scope(isolate); v8::Local context = isolate->GetCurrentContext(); 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(isolate)); CHECK_EQ(0, platform.NumberOfTraceObjects()); } // Test with enabled category { v8::Local category = v8_str("v8-cat"); v8::Local name = v8_str("name"); 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(isolate)); CHECK_EQ(1, platform.NumberOfTraceObjects()); CHECK_EQ(123, platform.GetTraceObject(0)->id); CHECK_EQ('b', platform.GetTraceObject(0)->phase); CHECK_EQ("name", platform.GetTraceObject(0)->name); CHECK_EQ(1, platform.GetTraceObject(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 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(isolate)); CHECK_EQ(2, platform.NumberOfTraceObjects()); CHECK_EQ(123, platform.GetTraceObject(1)->id); CHECK_EQ('b', platform.GetTraceObject(1)->phase); CHECK_EQ("name\u20ac", platform.GetTraceObject(1)->name); CHECK_EQ(1, platform.GetTraceObject(1)->num_args); } }