trace_events: fix trace events JS API writing

The Trace Events JS API isn't functional if none of
--trace-events-enabled or --trace-event-categories is passed as a CLI
argument. This commit fixes that.

In addition, we currently don't test the trace_events JS API in the
casewhere no CLI args are provided. This commit adds that test.

Fixes https://github.com/nodejs/node/issues/24944

PR-URL: https://github.com/nodejs/node/pull/24945
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Ali Ijaz Sheikh <ofrobots@google.com>
This commit is contained in:
Kelvin Jin 2018-12-10 20:44:18 +00:00 committed by Ali Ijaz Sheikh
parent 7d60b6b21b
commit 582c0d5a09
4 changed files with 46 additions and 9 deletions

View File

@ -74,6 +74,9 @@ void NodeCategorySet::Enable(const FunctionCallbackInfo<Value>& args) {
CHECK_NOT_NULL(category_set); CHECK_NOT_NULL(category_set);
const auto& categories = category_set->GetCategories(); const auto& categories = category_set->GetCategories();
if (!category_set->enabled_ && !categories.empty()) { if (!category_set->enabled_ && !categories.empty()) {
// Starts the Tracing Agent if it wasn't started already (e.g. through
// a command line flag.)
StartTracingAgent();
GetTracingAgentWriter()->Enable(categories); GetTracingAgentWriter()->Enable(categories);
category_set->enabled_ = true; category_set->enabled_ = true;
} }

View File

@ -85,7 +85,11 @@ struct V8Platform {
tracing_agent_->GetTracingController(); tracing_agent_->GetTracingController();
trace_state_observer_.reset(new NodeTraceStateObserver(controller)); trace_state_observer_.reset(new NodeTraceStateObserver(controller));
controller->AddTraceStateObserver(trace_state_observer_.get()); controller->AddTraceStateObserver(trace_state_observer_.get());
StartTracingAgent(); tracing_file_writer_ = tracing_agent_->DefaultHandle();
// Only start the tracing agent if we enabled any tracing categories.
if (!per_process::cli_options->trace_event_categories.empty()) {
StartTracingAgent();
}
// Tracing must be initialized before platform threads are created. // Tracing must be initialized before platform threads are created.
platform_ = new NodePlatform(thread_pool_size, controller); platform_ = new NodePlatform(thread_pool_size, controller);
v8::V8::InitializePlatform(platform_); v8::V8::InitializePlatform(platform_);
@ -111,9 +115,9 @@ struct V8Platform {
} }
inline void StartTracingAgent() { inline void StartTracingAgent() {
if (per_process::cli_options->trace_event_categories.empty()) { // Attach a new NodeTraceWriter only if this function hasn't been called
tracing_file_writer_ = tracing_agent_->DefaultHandle(); // before.
} else { if (tracing_file_writer_.IsDefaultHandle()) {
std::vector<std::string> categories = std::vector<std::string> categories =
SplitString(per_process::cli_options->trace_event_categories, ','); SplitString(per_process::cli_options->trace_event_categories, ',');
@ -163,6 +167,10 @@ namespace per_process {
extern struct V8Platform v8_platform; extern struct V8Platform v8_platform;
} }
inline void StartTracingAgent() {
return per_process::v8_platform.StartTracingAgent();
}
inline tracing::AgentWriterHandle* GetTracingAgentWriter() { inline tracing::AgentWriterHandle* GetTracingAgentWriter() {
return per_process::v8_platform.GetTracingAgentWriter(); return per_process::v8_platform.GetTracingAgentWriter();
} }

View File

@ -59,6 +59,8 @@ class AgentWriterHandle {
inline void Enable(const std::set<std::string>& categories); inline void Enable(const std::set<std::string>& categories);
inline void Disable(const std::set<std::string>& categories); inline void Disable(const std::set<std::string>& categories);
inline bool IsDefaultHandle();
inline Agent* agent() { return agent_; } inline Agent* agent() { return agent_; }
inline v8::TracingController* GetTracingController(); inline v8::TracingController* GetTracingController();
@ -175,6 +177,10 @@ void AgentWriterHandle::Disable(const std::set<std::string>& categories) {
if (agent_ != nullptr) agent_->Disable(id_, categories); if (agent_ != nullptr) agent_->Disable(id_, categories);
} }
bool AgentWriterHandle::IsDefaultHandle() {
return agent_ != nullptr && id_ == Agent::kDefaultHandleId;
}
inline v8::TracingController* AgentWriterHandle::GetTracingController() { inline v8::TracingController* AgentWriterHandle::GetTracingController() {
return agent_ != nullptr ? agent_->GetTracingController() : nullptr; return agent_ != nullptr ? agent_->GetTracingController() : nullptr;
} }

View File

@ -17,8 +17,17 @@ const {
getEnabledCategories getEnabledCategories
} = require('trace_events'); } = require('trace_events');
function getEnabledCategoriesFromCommandLine() {
const indexOfCatFlag = process.execArgv.indexOf('--trace-event-categories');
if (indexOfCatFlag === -1) {
return undefined;
} else {
return process.execArgv[indexOfCatFlag + 1];
}
}
const isChild = process.argv[2] === 'child'; const isChild = process.argv[2] === 'child';
const enabledCategories = isChild ? 'foo' : undefined; const enabledCategories = getEnabledCategoriesFromCommandLine();
assert.strictEqual(getEnabledCategories(), enabledCategories); assert.strictEqual(getEnabledCategories(), enabledCategories);
[1, 'foo', true, false, null, undefined].forEach((i) => { [1, 'foo', true, false, null, undefined].forEach((i) => {
@ -51,7 +60,9 @@ tracing.enable(); // purposefully enable twice to test calling twice
assert.strictEqual(tracing.enabled, true); assert.strictEqual(tracing.enabled, true);
assert.strictEqual(getEnabledCategories(), assert.strictEqual(getEnabledCategories(),
isChild ? 'foo,node.perf' : 'node.perf'); [
...[enabledCategories].filter((_) => !!_), 'node.perf'
].join(','));
tracing.disable(); tracing.disable();
assert.strictEqual(tracing.enabled, false); assert.strictEqual(tracing.enabled, false);
@ -106,7 +117,15 @@ if (isChild) {
} }
} }
testApiInChildProcess([], () => {
testApiInChildProcess(['--trace-event-categories', 'foo']);
});
}
function testApiInChildProcess(execArgs, cb) {
tmpdir.refresh(); tmpdir.refresh();
// Save the current directory so we can chdir back to it later
const parentDir = process.cwd();
process.chdir(tmpdir.path); process.chdir(tmpdir.path);
const expectedMarks = ['A', 'B']; const expectedMarks = ['A', 'B'];
@ -121,15 +140,14 @@ if (isChild) {
const proc = cp.fork(__filename, const proc = cp.fork(__filename,
['child'], ['child'],
{ execArgv: [ '--expose-gc', { execArgv: [ '--expose-gc', ...execArgs ] });
'--trace-event-categories',
'foo' ] });
proc.once('exit', common.mustCall(() => { proc.once('exit', common.mustCall(() => {
const file = path.join(tmpdir.path, 'node_trace.1.log'); const file = path.join(tmpdir.path, 'node_trace.1.log');
assert(fs.existsSync(file)); assert(fs.existsSync(file));
fs.readFile(file, common.mustCall((err, data) => { fs.readFile(file, common.mustCall((err, data) => {
assert.ifError(err);
const traces = JSON.parse(data.toString()).traceEvents const traces = JSON.parse(data.toString()).traceEvents
.filter((trace) => trace.cat !== '__metadata'); .filter((trace) => trace.cat !== '__metadata');
assert.strictEqual(traces.length, assert.strictEqual(traces.length,
@ -160,6 +178,8 @@ if (isChild) {
assert.fail('Unexpected trace event phase'); assert.fail('Unexpected trace event phase');
} }
}); });
process.chdir(parentDir);
cb && process.nextTick(cb);
})); }));
})); }));
} }