blob: 07dfd4db7b8da16d2ce9a4162544d13ff4ee643f [file] [log] [blame]
// Copyright 2014 The Chromium 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 "content/browser/tracing/etw_system_event_consumer_win.h"
#include <stdint.h>
#include "base/base64.h"
#include "base/json/json_string_value_serializer.h"
#include "base/lazy_instance.h"
#include "base/memory/singleton.h"
#include "base/strings/stringprintf.h"
#include "base/time/time.h"
#include "base/trace_event/trace_event_impl.h"
#include "content/public/browser/browser_thread.h"
namespace content {
namespace {
const char kETWTracingAgentName[] = "etw";
const char kETWTraceLabel[] = "systemTraceEvents";
const int kEtwBufferSizeInKBytes = 16;
const int kEtwBufferFlushTimeoutInSeconds = 1;
std::string GuidToString(const GUID& guid) {
return base::StringPrintf("%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X",
guid.Data1, guid.Data2, guid.Data3,
guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3],
guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]);
}
} // namespace
EtwSystemEventConsumer::EtwSystemEventConsumer()
: thread_("EtwConsumerThread") {
}
EtwSystemEventConsumer::~EtwSystemEventConsumer() {
}
std::string EtwSystemEventConsumer::GetTracingAgentName() {
return kETWTracingAgentName;
}
std::string EtwSystemEventConsumer::GetTraceEventLabel() {
return kETWTraceLabel;
}
bool EtwSystemEventConsumer::StartAgentTracing(
const base::trace_event::TraceConfig& trace_config) {
// Activate kernel tracing.
if (!StartKernelSessionTracing())
return false;
// Start the consumer thread and start consuming events.
thread_.Start();
thread_.message_loop()->PostTask(
FROM_HERE,
base::Bind(&EtwSystemEventConsumer::TraceAndConsumeOnThread,
base::Unretained(this)));
return true;
}
void EtwSystemEventConsumer::StopAgentTracing(
const StopAgentTracingCallback& callback) {
// Deactivate kernel tracing.
if (!StopKernelSessionTracing()) {
LOG(FATAL) << "Could not stop system tracing.";
}
// Stop consuming and flush events.
thread_.message_loop()->PostTask(FROM_HERE,
base::Bind(&EtwSystemEventConsumer::FlushOnThread,
base::Unretained(this),
callback));
}
void EtwSystemEventConsumer::OnStopSystemTracingDone(
const StopAgentTracingCallback& callback,
const scoped_refptr<base::RefCountedString>& result) {
// Stop the consumer thread.
thread_.Stop();
// Pass the serialized events.
callback.Run(GetTracingAgentName(), GetTraceEventLabel(), result);
}
bool EtwSystemEventConsumer::StartKernelSessionTracing() {
// Enabled flags (tracing facilities).
uint32_t enabled_flags = EVENT_TRACE_FLAG_IMAGE_LOAD |
EVENT_TRACE_FLAG_PROCESS | EVENT_TRACE_FLAG_THREAD |
EVENT_TRACE_FLAG_CSWITCH;
EVENT_TRACE_PROPERTIES& p = *properties_.get();
p.LogFileMode = EVENT_TRACE_REAL_TIME_MODE;
p.FlushTimer = kEtwBufferFlushTimeoutInSeconds;
p.BufferSize = kEtwBufferSizeInKBytes;
p.LogFileNameOffset = 0;
p.EnableFlags = enabled_flags;
p.Wnode.ClientContext = 1; // QPC timer accuracy.
HRESULT hr = base::win::EtwTraceController::Start(
KERNEL_LOGGER_NAME, &properties_, &session_handle_);
// It's possible that a previous tracing session has been orphaned. If so
// try stopping and restarting it.
if (hr == HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS)) {
VLOG(1) << "Session already exists, stopping and restarting it.";
hr = base::win::EtwTraceController::Stop(
KERNEL_LOGGER_NAME, &properties_);
if (FAILED(hr)) {
VLOG(1) << "EtwTraceController::Stop failed with " << hr << ".";
return false;
}
// The session was successfully shutdown so try to restart it.
hr = base::win::EtwTraceController::Start(
KERNEL_LOGGER_NAME, &properties_, &session_handle_);
}
if (FAILED(hr)) {
VLOG(1) << "EtwTraceController::Start failed with " << hr << ".";
return false;
}
return true;
}
bool EtwSystemEventConsumer::StopKernelSessionTracing() {
HRESULT hr = base::win::EtwTraceController::Stop(
KERNEL_LOGGER_NAME, &properties_);
return SUCCEEDED(hr);
}
// static
EtwSystemEventConsumer* EtwSystemEventConsumer::GetInstance() {
return base::Singleton<EtwSystemEventConsumer>::get();
}
// static
void EtwSystemEventConsumer::ProcessEvent(EVENT_TRACE* event) {
EtwSystemEventConsumer::GetInstance()->AppendEventToBuffer(event);
}
void EtwSystemEventConsumer::AddSyncEventToBuffer() {
// Sync the clocks.
base::Time walltime = base::Time::NowFromSystemTime();
base::TimeTicks now = base::TimeTicks::Now();
LARGE_INTEGER walltime_in_us;
walltime_in_us.QuadPart = walltime.ToInternalValue();
LARGE_INTEGER now_in_us;
now_in_us.QuadPart = now.ToInternalValue();
// Add fields to the event.
scoped_ptr<base::DictionaryValue> value(new base::DictionaryValue());
value->Set("guid", new base::StringValue("ClockSync"));
value->Set("walltime", new base::StringValue(
base::StringPrintf("%08X%08X",
walltime_in_us.HighPart,
walltime_in_us.LowPart)));
value->Set("tick", new base::StringValue(
base::StringPrintf("%08X%08X",
now_in_us.HighPart,
now_in_us.LowPart)));
// Append it to the events buffer.
events_->Append(value.release());
}
void EtwSystemEventConsumer::AppendEventToBuffer(EVENT_TRACE* event) {
using base::FundamentalValue;
scoped_ptr<base::DictionaryValue> value(new base::DictionaryValue());
// Add header fields to the event.
LARGE_INTEGER ts_us;
ts_us.QuadPart = event->Header.TimeStamp.QuadPart / 10;
value->Set("ts", new base::StringValue(
base::StringPrintf("%08X%08X", ts_us.HighPart, ts_us.LowPart)));
value->Set("guid", new base::StringValue(GuidToString(event->Header.Guid)));
value->Set("op", new FundamentalValue(event->Header.Class.Type));
value->Set("ver", new FundamentalValue(event->Header.Class.Version));
value->Set("pid",
new FundamentalValue(static_cast<int>(event->Header.ProcessId)));
value->Set("tid",
new FundamentalValue(static_cast<int>(event->Header.ThreadId)));
value->Set("cpu", new FundamentalValue(event->BufferContext.ProcessorNumber));
// Base64 encode the payload bytes.
base::StringPiece buffer(static_cast<const char*>(event->MofData),
event->MofLength);
std::string payload;
base::Base64Encode(buffer, &payload);
value->Set("payload", new base::StringValue(payload));
// Append it to the events buffer.
events_->Append(value.release());
}
void EtwSystemEventConsumer::TraceAndConsumeOnThread() {
// Create the events buffer.
events_.reset(new base::ListValue());
// Output a clock sync event.
AddSyncEventToBuffer();
HRESULT hr = OpenRealtimeSession(KERNEL_LOGGER_NAME);
if (FAILED(hr))
return;
Consume();
Close();
}
void EtwSystemEventConsumer::FlushOnThread(
const StopAgentTracingCallback& callback) {
// Add the header information to the stream.
scoped_ptr<base::DictionaryValue> header(new base::DictionaryValue());
header->Set("name", new base::StringValue("ETW"));
// Release and pass the events buffer.
header->Set("content", events_.release());
// Serialize the results as a JSon string.
std::string output;
JSONStringValueSerializer serializer(&output);
serializer.Serialize(*header.get());
// Pass the result to the UI Thread.
scoped_refptr<base::RefCountedString> result =
base::RefCountedString::TakeString(&output);
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::Bind(&EtwSystemEventConsumer::OnStopSystemTracingDone,
base::Unretained(this),
callback,
result));
}
} // namespace content