// Copyright (c) 2010 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 "base/debug/trace_event.h"
#include "base/format_macros.h"
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/path_service.h"
#include "base/platform_thread.h"
#include "base/process_util.h"
#include "base/stringprintf.h"
#include "base/utf_string_conversions.h"
#include "base/time.h"
#define USE_UNRELIABLE_NOW
namespace base {
namespace debug {
static const char* kEventTypeNames[] = {
"BEGIN",
"END",
"INSTANT"
};
static const FilePath::CharType* kLogFileName =
FILE_PATH_LITERAL("trace_%d.log");
// static
TraceLog* TraceLog::GetInstance() {
return Singleton<TraceLog, DefaultSingletonTraits<TraceLog> >::get();
}
// static
bool TraceLog::IsTracing() {
return TraceLog::GetInstance()->enabled_;
}
// static
bool TraceLog::StartTracing() {
return TraceLog::GetInstance()->Start();
}
// static
void TraceLog::StopTracing() {
return TraceLog::GetInstance()->Stop();
}
void TraceLog::Trace(const std::string& name,
EventType type,
const void* id,
const std::wstring& extra,
const char* file,
int line) {
if (!enabled_)
return;
Trace(name, type, id, WideToUTF8(extra), file, line);
}
void TraceLog::Trace(const std::string& name,
EventType type,
const void* id,
const std::string& extra,
const char* file,
int line) {
if (!enabled_)
return;
#ifdef USE_UNRELIABLE_NOW
TimeTicks tick = TimeTicks::HighResNow();
#else
TimeTicks tick = TimeTicks::Now();
#endif
TimeDelta delta = tick - trace_start_time_;
int64 usec = delta.InMicroseconds();
std::string msg =
StringPrintf("{'pid':'0x%lx', 'tid':'0x%lx', 'type':'%s', "
"'name':'%s', 'id':'%p', 'extra':'%s', 'file':'%s', "
"'line_number':'%d', 'usec_begin': %" PRId64 "},\n",
static_cast<unsigned long>(base::GetCurrentProcId()),
static_cast<unsigned long>(PlatformThread::CurrentId()),
kEventTypeNames[type],
name.c_str(),
id,
extra.c_str(),
file,
line,
usec);
Log(msg);
}
TraceLog::TraceLog() : enabled_(false), log_file_(NULL) {
base::ProcessHandle proc = base::GetCurrentProcessHandle();
#if !defined(OS_MACOSX)
process_metrics_.reset(base::ProcessMetrics::CreateProcessMetrics(proc));
#else
// The default port provider is sufficient to get data for the current
// process.
process_metrics_.reset(base::ProcessMetrics::CreateProcessMetrics(proc,
NULL));
#endif
}
TraceLog::~TraceLog() {
Stop();
}
bool TraceLog::OpenLogFile() {
FilePath::StringType pid_filename =
StringPrintf(kLogFileName, base::GetCurrentProcId());
FilePath log_file_path;
if (!PathService::Get(base::DIR_EXE, &log_file_path))
return false;
log_file_path = log_file_path.Append(pid_filename);
log_file_ = file_util::OpenFile(log_file_path, "a");
if (!log_file_) {
// try the current directory
log_file_ = file_util::OpenFile(FilePath(pid_filename), "a");
if (!log_file_) {
return false;
}
}
return true;
}
void TraceLog::CloseLogFile() {
if (log_file_) {
file_util::CloseFile(log_file_);
}
}
bool TraceLog::Start() {
if (enabled_)
return true;
enabled_ = OpenLogFile();
if (enabled_) {
Log("var raw_trace_events = [\n");
trace_start_time_ = TimeTicks::Now();
timer_.Start(TimeDelta::FromMilliseconds(250), this, &TraceLog::Heartbeat);
}
return enabled_;
}
void TraceLog::Stop() {
if (enabled_) {
enabled_ = false;
Log("];\n");
CloseLogFile();
timer_.Stop();
}
}
void TraceLog::Heartbeat() {
std::string cpu = StringPrintf("%.0f", process_metrics_->GetCPUUsage());
TRACE_EVENT_INSTANT("heartbeat.cpu", 0, cpu);
}
void TraceLog::Log(const std::string& msg) {
AutoLock lock(file_lock_);
fprintf(log_file_, "%s", msg.c_str());
}
} // namespace debug
} // namespace base