diff options
Diffstat (limited to 'base/debug/trace_event.cc')
-rw-r--r-- | base/debug/trace_event.cc | 452 |
1 files changed, 335 insertions, 117 deletions
diff --git a/base/debug/trace_event.cc b/base/debug/trace_event.cc index c219cec..58ff82f 100644 --- a/base/debug/trace_event.cc +++ b/base/debug/trace_event.cc @@ -1,16 +1,15 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 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" +#if defined(OS_WIN) +#include "base/debug/trace_event_win.h" +#endif #include "base/format_macros.h" -#include "base/file_path.h" -#include "base/file_util.h" -#include "base/path_service.h" #include "base/process_util.h" #include "base/stringprintf.h" -#include "base/threading/platform_thread.h" #include "base/utf_string_conversions.h" #include "base/time.h" @@ -19,149 +18,368 @@ namespace base { namespace debug { -static const char* kEventTypeNames[] = { - "BEGIN", - "END", - "INSTANT" +// Controls the number of trace events we will buffer in-memory +// before throwing them away. +const size_t kTraceEventBufferSize = 500000; +const size_t kTraceEventBatchSize = 1000; + +#define TRACE_EVENT_MAX_CATEGORIES 42 + +static TraceCategory g_categories[TRACE_EVENT_MAX_CATEGORIES] = { + { "tracing already shutdown", false }, + { "tracing categories exhausted; must increase TRACE_EVENT_MAX_CATEGORIES", + false } }; +static const TraceCategory* const g_category_already_shutdown = + &g_categories[0]; +static const TraceCategory* const g_category_categories_exhausted = + &g_categories[1]; +static int g_category_index = 2; // skip initial 2 error categories -static const FilePath::CharType* kLogFileName = - FILE_PATH_LITERAL("trace_%d.log"); +//////////////////////////////////////////////////////////////////////////////// +// +// TraceValue +// +//////////////////////////////////////////////////////////////////////////////// -// static -TraceLog* TraceLog::GetInstance() { - return Singleton<TraceLog, DefaultSingletonTraits<TraceLog> >::get(); +void TraceValue::Destroy() { + if (type_ == TRACE_TYPE_STRING) { + value_.as_string_refptr->Release(); + value_.as_string_refptr = NULL; + } + type_ = TRACE_TYPE_UNDEFINED; + value_.as_uint = 0ull; } -// static -bool TraceLog::IsTracing() { - return TraceLog::GetInstance()->enabled_; +TraceValue& TraceValue::operator=(const TraceValue& rhs) { + if (this == &rhs) + return *this; + + Destroy(); + + type_ = rhs.type_; + switch(type_) { + case TRACE_TYPE_UNDEFINED: + return *this; + case TRACE_TYPE_BOOL: + value_.as_bool = rhs.value_.as_bool; + return *this; + case TRACE_TYPE_UINT: + value_.as_uint = rhs.value_.as_uint; + return *this; + case TRACE_TYPE_INT: + value_.as_int = rhs.value_.as_int; + return *this; + case TRACE_TYPE_DOUBLE: + value_.as_double = rhs.value_.as_double; + return *this; + case TRACE_TYPE_POINTER: + value_.as_pointer = rhs.value_.as_pointer; + return *this; + case TRACE_TYPE_STRING: + value_.as_string_refptr = rhs.value_.as_string_refptr; + value_.as_string_refptr->AddRef(); + return *this; + default: + NOTREACHED(); + return *this; + } } -// static -bool TraceLog::StartTracing() { - return TraceLog::GetInstance()->Start(); +void TraceValue::AppendAsJSON(std::string* out) const { + char temp_string[128]; + std::string::size_type start_pos; + switch (type_) { + case TRACE_TYPE_BOOL: + *out += as_bool()? "true" : "false"; + break; + case TRACE_TYPE_UINT: + base::snprintf(temp_string, arraysize(temp_string), "%llu", + static_cast<unsigned long long>(as_uint())); + *out += temp_string; + break; + case TRACE_TYPE_INT: + base::snprintf(temp_string, arraysize(temp_string), "%lld", + static_cast<long long>(as_int())); + *out += temp_string; + break; + case TRACE_TYPE_DOUBLE: + base::snprintf(temp_string, arraysize(temp_string), "%f", as_double()); + *out += temp_string; + break; + case TRACE_TYPE_POINTER: + base::snprintf(temp_string, arraysize(temp_string), "%llu", + static_cast<unsigned long long>( + reinterpret_cast<intptr_t>( + as_pointer()))); + *out += temp_string; + break; + case TRACE_TYPE_STRING: + *out += "\""; + start_pos = out->size(); + *out += as_string(); + // replace " character with ' + while ((start_pos = out->find_first_of('\"', start_pos)) != + std::string::npos) + (*out)[start_pos] = '\''; + *out += "\""; + break; + default: + NOTREACHED() << "Don't know how to print this value"; + break; + } } -// static -void TraceLog::StopTracing() { - return TraceLog::GetInstance()->Stop(); +//////////////////////////////////////////////////////////////////////////////// +// +// TraceEvent +// +//////////////////////////////////////////////////////////////////////////////// + +namespace { + +const char* GetPhaseStr(TraceEventPhase phase) { + switch(phase) { + case TRACE_EVENT_PHASE_BEGIN: + return "B"; + case TRACE_EVENT_PHASE_INSTANT: + return "I"; + case TRACE_EVENT_PHASE_END: + return "E"; + default: + NOTREACHED() << "Invalid phase argument"; + return "?"; + } } -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); +} // namespace + +TraceEvent::TraceEvent() + : process_id(0), + thread_id(0), + phase(TRACE_EVENT_PHASE_BEGIN), + category(NULL), + name(NULL) { + memset(arg_names, 0, sizeof(arg_names)); } -void TraceLog::Trace(const std::string& name, - EventType type, - const void* id, - const std::string& extra, - const char* file, - int line) { - if (!enabled_) - return; +TraceEvent::~TraceEvent() { +} -#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 +void TraceEvent::AppendEventsAsJSON(const std::vector<TraceEvent>& events, + size_t start, + size_t count, + std::string* out) { + *out += "["; + for (size_t i = 0; i < count && start + i < events.size(); ++i) { + if (i > 0) + *out += ","; + events[i + start].AppendAsJSON(out); + } + *out += "]"; } -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; - } +void TraceEvent::AppendAsJSON(std::string* out) const { + const char* phase_str = GetPhaseStr(phase); + int64 time_int64 = timestamp.ToInternalValue(); + // Category name checked at category creation time. + DCHECK(!strchr(name, '"')); + StringAppendF(out, + "{\"cat\":\"%s\",\"pid\":%i,\"tid\":%i,\"ts\":%lld," + "\"ph\":\"%s\",\"name\":\"%s\",\"args\":{", + category->name, + static_cast<int>(process_id), + static_cast<int>(thread_id), + static_cast<long long>(time_int64), + phase_str, + name); + + // Output argument names and values, stop at first NULL argument name. + for (size_t i = 0; i < kTraceMaxNumArgs && arg_names[i]; ++i) { + if (i > 0) + *out += ","; + *out += "\""; + *out += arg_names[i]; + *out += "\":"; + arg_values[i].AppendAsJSON(out); } - return true; + *out += "}}"; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// TraceLog +// +//////////////////////////////////////////////////////////////////////////////// + +// static +TraceLog* TraceLog::GetInstance() { + return Singleton<TraceLog, StaticMemorySingletonTraits<TraceLog> >::get(); +} + +TraceLog::TraceLog() + : enabled_(false) { + logged_events_.reserve(1024); } -void TraceLog::CloseLogFile() { - if (log_file_) { - file_util::CloseFile(log_file_); +TraceLog::~TraceLog() { +} + +const TraceCategory* TraceLog::GetCategory(const char* name) { + TraceLog* tracelog = GetInstance(); + if (!tracelog){ + CHECK(!g_category_already_shutdown->enabled); + return g_category_already_shutdown; } + return tracelog->GetCategoryInternal(name); } -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); +const TraceCategory* TraceLog::GetCategoryInternal(const char* name) { + AutoLock lock(lock_); + DCHECK(!strchr(name, '"')) << "Names may not contain double quote char"; + + // Search for pre-existing category matching this name + for (int i = 0; i < g_category_index; i++) { + if (strcmp(g_categories[i].name, name) == 0) + return &g_categories[i]; } - return enabled_; + + // Create a new category + DCHECK(g_category_index < TRACE_EVENT_MAX_CATEGORIES) << + "must increase TRACE_EVENT_MAX_CATEGORIES"; + if (g_category_index < TRACE_EVENT_MAX_CATEGORIES) { + int new_index = g_category_index++; + g_categories[new_index].name = name; + DCHECK(!g_categories[new_index].enabled); + g_categories[new_index].enabled = enabled_; + return &g_categories[new_index]; + } else { + return g_category_categories_exhausted; + } +} + +void TraceLog::SetEnabled(bool enabled) { + { + AutoLock lock(lock_); + if (enabled == enabled_) + return; + enabled_ = enabled; + for (int i = 0; i < g_category_index; i++) { + //TODO(scheib): If changed to enable specific categories instead of all + // check GetCategoryInternal creation code that users TraceLog::enabled_ + g_categories[i].enabled = enabled; + } + } // release lock + if (!enabled) + Flush(); +} + +float TraceLog::GetBufferPercentFull() const { + return (float)((double)logged_events_.size()/(double)kTraceEventBufferSize); +} + +void TraceLog::SetOutputCallback(const TraceLog::OutputCallback& cb) { + AutoLock lock(lock_); + output_callback_ = cb; + logged_events_.clear(); +} + +void TraceLog::SetBufferFullCallback(const TraceLog::BufferFullCallback& cb) { + AutoLock lock(lock_); + buffer_full_callback_ = cb; } -void TraceLog::Stop() { - if (enabled_) { - enabled_ = false; - Log("];\n"); - CloseLogFile(); - timer_.Stop(); +void TraceLog::Flush() { + std::vector<TraceEvent> previous_logged_events; + OutputCallback output_callback_copy; + { + AutoLock lock(lock_); + previous_logged_events.swap(logged_events_); + output_callback_copy = output_callback_; + } // release lock + + if (output_callback_copy.is_null()) + return; + + for (size_t i = 0; + i < previous_logged_events.size(); + i += kTraceEventBatchSize) { + scoped_refptr<RefCountedString> json_events_str_ptr = + new RefCountedString(); + TraceEvent::AppendEventsAsJSON(previous_logged_events, + i, + kTraceEventBatchSize, + &(json_events_str_ptr->data)); + output_callback_copy.Run(json_events_str_ptr.get()); } } -void TraceLog::Heartbeat() { - std::string cpu = StringPrintf("%.0f", process_metrics_->GetCPUUsage()); - TRACE_EVENT_INSTANT("heartbeat.cpu", 0, cpu); +void TraceLog::AddTraceEvent(TraceEventPhase phase, + const char* file, int line, + const TraceCategory* category, + const char* name, + const char* arg1_name, TraceValue arg1_val, + const char* arg2_name, TraceValue arg2_val) { + DCHECK(file && name); +#ifdef USE_UNRELIABLE_NOW + TimeTicks now = TimeTicks::HighResNow(); +#else + TimeTicks now = TimeTicks::Now(); +#endif + BufferFullCallback buffer_full_callback_copy; + { + AutoLock lock(lock_); + if (!enabled_ || !category->enabled) + return; + if (logged_events_.size() >= kTraceEventBufferSize) + return; + logged_events_.push_back(TraceEvent()); + TraceEvent& event = logged_events_.back(); + event.process_id = static_cast<unsigned long>(base::GetCurrentProcId()); + event.thread_id = PlatformThread::CurrentId(); + event.timestamp = now; + event.phase = phase; + event.category = category; + event.name = name; + event.arg_names[0] = arg1_name; + event.arg_values[0] = arg1_val; + event.arg_names[1] = arg2_name; + event.arg_values[1] = arg2_val; + COMPILE_ASSERT(kTraceMaxNumArgs == 2, TraceEvent_arg_count_out_of_sync); + + if (logged_events_.size() == kTraceEventBufferSize) { + buffer_full_callback_copy = buffer_full_callback_; + } + } // release lock + + if (!buffer_full_callback_copy.is_null()) + buffer_full_callback_copy.Run(); } -void TraceLog::Log(const std::string& msg) { - AutoLock lock(file_lock_); +void TraceLog::AddTraceEventEtw(TraceEventPhase phase, + const char* file, int line, + const char* name, + const void* id, + const char* extra) { + // Legacy trace points on windows called to ETW +#if defined(OS_WIN) + TraceEventETWProvider::Trace(name, phase, id, extra); +#endif + + // Also add new trace event behavior + static const TraceCategory* category = GetCategory("ETW Trace Event"); + if (category->enabled) { + TraceLog* tracelog = TraceLog::GetInstance(); + if (!tracelog) + return; + tracelog->AddTraceEvent(phase, file, line, category, name, + "id", id, + "extra", extra ? extra : ""); + } +} - fprintf(log_file_, "%s", msg.c_str()); +void TraceLog::Resurrect() { + StaticMemorySingletonTraits<TraceLog>::Resurrect(); } } // namespace debug |