Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "base/debug/trace_event.h" | 5 #include "base/debug/trace_event.h" |
| 6 | 6 |
| 7 #if defined(OS_WIN) | |
| 8 #include "base/debug/trace_event_win.h" | |
| 9 #endif | |
| 7 #include "base/format_macros.h" | 10 #include "base/format_macros.h" |
| 8 #include "base/file_path.h" | |
| 9 #include "base/file_util.h" | |
| 10 #include "base/path_service.h" | |
| 11 #include "base/process_util.h" | 11 #include "base/process_util.h" |
| 12 #include "base/stringprintf.h" | 12 #include "base/stringprintf.h" |
| 13 #include "base/threading/platform_thread.h" | |
| 14 #include "base/utf_string_conversions.h" | 13 #include "base/utf_string_conversions.h" |
| 15 #include "base/time.h" | 14 #include "base/time.h" |
| 16 | 15 |
| 17 #define USE_UNRELIABLE_NOW | 16 #define USE_UNRELIABLE_NOW |
| 18 | 17 |
| 19 namespace base { | 18 namespace base { |
| 20 namespace debug { | 19 namespace debug { |
| 21 | 20 |
| 22 static const char* kEventTypeNames[] = { | 21 // Controls the number of trace events we will buffer in-memory |
| 23 "BEGIN", | 22 // before throwing them away. |
| 24 "END", | 23 const size_t kTraceEventBufferSize = 500000; |
| 25 "INSTANT" | 24 const size_t kTraceEventBatchSize = 1000; |
| 25 | |
| 26 #define TRACE_EVENT_MAX_CATEGORIES 42 | |
| 27 | |
| 28 static TraceCategory g_categories[TRACE_EVENT_MAX_CATEGORIES] = { | |
| 29 { "tracing already shutdown", false }, | |
| 30 { "tracing categories exhausted; must increase TRACE_EVENT_MAX_CATEGORIES", | |
| 31 false } | |
| 26 }; | 32 }; |
| 27 | 33 static const TraceCategory* const g_category_already_shutdown = |
| 28 static const FilePath::CharType* kLogFileName = | 34 &g_categories[0]; |
| 29 FILE_PATH_LITERAL("trace_%d.log"); | 35 static const TraceCategory* const g_category_categories_exhausted = |
| 36 &g_categories[1]; | |
| 37 static int g_category_index = 2; // skip initial 2 error categories | |
| 38 | |
| 39 //////////////////////////////////////////////////////////////////////////////// | |
| 40 // | |
| 41 // TraceValue | |
| 42 // | |
| 43 //////////////////////////////////////////////////////////////////////////////// | |
| 44 | |
| 45 void TraceValue::Destroy() { | |
| 46 if (type_ == TRACE_TYPE_STRING) { | |
| 47 value_.as_string_refptr->Release(); | |
| 48 value_.as_string_refptr = NULL; | |
| 49 } | |
| 50 type_ = TRACE_TYPE_UNDEFINED; | |
| 51 value_.as_uint = 0ull; | |
| 52 } | |
| 53 | |
| 54 TraceValue& TraceValue::operator=(const TraceValue& rhs) { | |
| 55 if (this == &rhs) | |
| 56 return *this; | |
| 57 | |
| 58 Destroy(); | |
| 59 | |
| 60 type_ = rhs.type_; | |
| 61 switch(type_) { | |
| 62 case TRACE_TYPE_UNDEFINED: | |
| 63 return *this; | |
| 64 case TRACE_TYPE_BOOL: | |
| 65 value_.as_bool = rhs.value_.as_bool; | |
| 66 return *this; | |
| 67 case TRACE_TYPE_UINT: | |
| 68 value_.as_uint = rhs.value_.as_uint; | |
| 69 return *this; | |
| 70 case TRACE_TYPE_INT: | |
| 71 value_.as_int = rhs.value_.as_int; | |
| 72 return *this; | |
| 73 case TRACE_TYPE_DOUBLE: | |
| 74 value_.as_double = rhs.value_.as_double; | |
| 75 return *this; | |
| 76 case TRACE_TYPE_POINTER: | |
| 77 value_.as_pointer = rhs.value_.as_pointer; | |
| 78 return *this; | |
| 79 case TRACE_TYPE_STRING: | |
| 80 value_.as_string_refptr = rhs.value_.as_string_refptr; | |
| 81 value_.as_string_refptr->AddRef(); | |
| 82 return *this; | |
| 83 default: | |
| 84 NOTREACHED(); | |
| 85 return *this; | |
| 86 } | |
| 87 } | |
| 88 | |
| 89 void TraceValue::AppendAsJSON(std::string* out) const { | |
| 90 char temp_string[128]; | |
| 91 std::string::size_type start_pos; | |
| 92 switch (type_) { | |
| 93 case TRACE_TYPE_BOOL: | |
| 94 *out += as_bool()? "true" : "false"; | |
| 95 break; | |
| 96 case TRACE_TYPE_UINT: | |
| 97 base::snprintf(temp_string, arraysize(temp_string), "%llu", | |
| 98 static_cast<unsigned long long>(as_uint())); | |
| 99 *out += temp_string; | |
| 100 break; | |
| 101 case TRACE_TYPE_INT: | |
| 102 base::snprintf(temp_string, arraysize(temp_string), "%lld", | |
| 103 static_cast<long long>(as_int())); | |
| 104 *out += temp_string; | |
| 105 break; | |
| 106 case TRACE_TYPE_DOUBLE: | |
| 107 base::snprintf(temp_string, arraysize(temp_string), "%f", as_double()); | |
| 108 *out += temp_string; | |
| 109 break; | |
| 110 case TRACE_TYPE_POINTER: | |
| 111 base::snprintf(temp_string, arraysize(temp_string), "%llu", | |
| 112 static_cast<unsigned long long>( | |
| 113 reinterpret_cast<intptr_t>( | |
| 114 as_pointer()))); | |
| 115 *out += temp_string; | |
| 116 break; | |
| 117 case TRACE_TYPE_STRING: | |
| 118 *out += "\""; | |
| 119 start_pos = out->size(); | |
| 120 *out += as_string(); | |
| 121 // replace " character with ' | |
| 122 while ((start_pos = out->find_first_of('\"', start_pos)) != | |
| 123 std::string::npos) | |
| 124 (*out)[start_pos] = '\''; | |
| 125 *out += "\""; | |
| 126 break; | |
| 127 default: | |
| 128 NOTREACHED() << "Don't know how to print this value"; | |
| 129 break; | |
| 130 } | |
| 131 } | |
| 132 | |
| 133 //////////////////////////////////////////////////////////////////////////////// | |
| 134 // | |
| 135 // TraceEvent | |
| 136 // | |
| 137 //////////////////////////////////////////////////////////////////////////////// | |
| 138 | |
| 139 namespace { | |
| 140 | |
| 141 const char* GetPhaseStr(TraceEventPhase phase) { | |
| 142 switch(phase) { | |
| 143 case TRACE_EVENT_PHASE_BEGIN: | |
| 144 return "B"; | |
| 145 case TRACE_EVENT_PHASE_INSTANT: | |
| 146 return "I"; | |
| 147 case TRACE_EVENT_PHASE_END: | |
| 148 return "E"; | |
| 149 default: | |
| 150 NOTREACHED() << "Invalid phase argument"; | |
| 151 return "?"; | |
| 152 } | |
| 153 } | |
| 154 | |
| 155 } // namespace | |
| 156 | |
| 157 TraceEvent::TraceEvent() | |
| 158 : process_id(0), | |
| 159 thread_id(0), | |
| 160 phase(TRACE_EVENT_PHASE_BEGIN), | |
| 161 category(NULL), | |
| 162 name(NULL) { | |
| 163 memset(arg_names, 0, sizeof(arg_names)); | |
| 164 } | |
| 165 | |
| 166 TraceEvent::~TraceEvent() { | |
| 167 } | |
| 168 | |
| 169 void TraceEvent::AppendEventsAsJSON(const std::vector<TraceEvent>& events, | |
| 170 size_t start, | |
| 171 size_t count, | |
| 172 std::string* out) { | |
| 173 *out += "["; | |
| 174 for (size_t i = 0; i < count && start + i < events.size(); ++i) { | |
| 175 if (i > 0) | |
| 176 *out += ","; | |
| 177 events[i + start].AppendAsJSON(out); | |
| 178 } | |
| 179 *out += "]"; | |
| 180 } | |
| 181 | |
| 182 void TraceEvent::AppendAsJSON(std::string* out) const { | |
| 183 const char* phase_str = GetPhaseStr(phase); | |
| 184 int64 time_int64 = timestamp.ToInternalValue(); | |
| 185 // Category name checked at category creation time. | |
| 186 DCHECK(!strchr(name, '"')); | |
| 187 StringAppendF(out, | |
| 188 "{\"cat\":\"%s\",\"pid\":%i,\"tid\":%i,\"ts\":%lld," | |
| 189 "\"ph\":\"%s\",\"name\":\"%s\",\"args\":{", | |
| 190 category->name, | |
| 191 static_cast<int>(process_id), | |
| 192 static_cast<int>(thread_id), | |
| 193 static_cast<long long>(time_int64), | |
| 194 phase_str, | |
| 195 name); | |
| 196 | |
| 197 // Output argument names and values, stop at first NULL argument name. | |
| 198 for (size_t i = 0; i < kTraceMaxNumArgs && arg_names[i]; ++i) { | |
| 199 if (i > 0) | |
| 200 *out += ","; | |
| 201 *out += "\""; | |
| 202 *out += arg_names[i]; | |
| 203 *out += "\":"; | |
| 204 arg_values[i].AppendAsJSON(out); | |
| 205 } | |
| 206 *out += "}}"; | |
| 207 } | |
| 208 | |
| 209 //////////////////////////////////////////////////////////////////////////////// | |
| 210 // | |
| 211 // TraceLog | |
| 212 // | |
| 213 //////////////////////////////////////////////////////////////////////////////// | |
| 30 | 214 |
| 31 // static | 215 // static |
| 32 TraceLog* TraceLog::GetInstance() { | 216 TraceLog* TraceLog::GetInstance() { |
| 33 return Singleton<TraceLog, DefaultSingletonTraits<TraceLog> >::get(); | 217 return Singleton<TraceLog, StaticMemorySingletonTraits<TraceLog> >::get(); |
| 34 } | 218 } |
| 35 | 219 |
| 36 // static | 220 TraceLog::TraceLog() |
| 37 bool TraceLog::IsTracing() { | 221 : enabled_(false) { |
| 38 return TraceLog::GetInstance()->enabled_; | 222 logged_events_.reserve(1024); |
| 39 } | 223 } |
| 40 | 224 |
| 41 // static | 225 TraceLog::~TraceLog() { |
| 42 bool TraceLog::StartTracing() { | 226 } |
| 43 return TraceLog::GetInstance()->Start(); | 227 |
| 44 } | 228 const TraceCategory* TraceLog::GetCategory(const char* name) { |
| 45 | 229 TraceLog* tracelog = GetInstance(); |
| 46 // static | 230 if (!tracelog){ |
| 47 void TraceLog::StopTracing() { | 231 CHECK(!g_category_already_shutdown->enabled); |
| 48 return TraceLog::GetInstance()->Stop(); | 232 return g_category_already_shutdown; |
| 49 } | 233 } |
| 50 | 234 return tracelog->GetCategoryInternal(name); |
| 51 void TraceLog::Trace(const std::string& name, | 235 } |
| 52 EventType type, | 236 |
| 53 const void* id, | 237 const TraceCategory* TraceLog::GetCategoryInternal(const char* name) { |
| 54 const std::wstring& extra, | 238 AutoLock lock(lock_); |
| 55 const char* file, | 239 CHECK(!strchr(name, '"')) << "Names may not contain double quote char"; |
|
Sigurður Ásgeirsson
2011/05/05 14:58:56
I'd make this a DCHECK, as we want to catch develo
scheib
2011/05/05 16:50:28
Done.
| |
| 56 int line) { | 240 |
| 57 if (!enabled_) | 241 // Search for pre-existing category matching this name |
| 242 for (int i = 0; i < g_category_index; i++) { | |
| 243 if (strcmp(g_categories[i].name, name) == 0) | |
| 244 return &g_categories[i]; | |
| 245 } | |
| 246 | |
| 247 // Create a new category | |
| 248 DCHECK(g_category_index < TRACE_EVENT_MAX_CATEGORIES) << | |
| 249 "must increase TRACE_EVENT_MAX_CATEGORIES"; | |
| 250 if (g_category_index < TRACE_EVENT_MAX_CATEGORIES) { | |
| 251 int new_index = g_category_index++; | |
| 252 g_categories[new_index].name = name; | |
| 253 DCHECK(!g_categories[new_index].enabled); | |
| 254 g_categories[new_index].enabled = enabled_; | |
| 255 return &g_categories[new_index]; | |
| 256 } else { | |
| 257 return g_category_categories_exhausted; | |
| 258 } | |
| 259 } | |
| 260 | |
| 261 void TraceLog::SetEnabled(bool enabled) { | |
| 262 { | |
| 263 AutoLock lock(lock_); | |
| 264 if (enabled == enabled_) | |
| 265 return; | |
| 266 enabled_ = enabled; | |
| 267 for (int i = 0; i < g_category_index; i++) { | |
| 268 //TODO(scheib): If changed to enable specific categories instead of all | |
| 269 // check GetCategoryInternal creation code that users TraceLog::enabled_ | |
| 270 g_categories[i].enabled = enabled; | |
| 271 } | |
| 272 } // release lock | |
| 273 if (!enabled) | |
| 274 Flush(); | |
| 275 } | |
| 276 | |
| 277 float TraceLog::GetBufferPercentFull() const { | |
| 278 return (float)((double)logged_events_.size()/(double)kTraceEventBufferSize); | |
| 279 } | |
| 280 | |
| 281 void TraceLog::SetOutputCallback(const TraceLog::OutputCallback& cb) { | |
| 282 Flush(); | |
| 283 AutoLock lock(lock_); | |
| 284 output_callback_ = cb; | |
| 285 } | |
| 286 | |
| 287 void TraceLog::SetBufferFullCallback(const TraceLog::BufferFullCallback& cb) { | |
| 288 AutoLock lock(lock_); | |
| 289 buffer_full_callback_ = cb; | |
| 290 } | |
| 291 | |
| 292 void TraceLog::Flush() { | |
| 293 std::vector<TraceEvent> previous_logged_events; | |
| 294 OutputCallback output_callback_copy; | |
| 295 { | |
| 296 AutoLock lock(lock_); | |
| 297 previous_logged_events.swap(logged_events_); | |
| 298 output_callback_copy = output_callback_; | |
| 299 } // release lock | |
| 300 | |
| 301 if (output_callback_copy.is_null()) | |
| 58 return; | 302 return; |
| 59 Trace(name, type, id, WideToUTF8(extra), file, line); | 303 |
| 60 } | 304 for (size_t i = 0; |
| 61 | 305 i < previous_logged_events.size(); |
| 62 void TraceLog::Trace(const std::string& name, | 306 i += kTraceEventBatchSize) { |
| 63 EventType type, | 307 scoped_refptr<RefCountedString> json_events_str_ptr = |
| 64 const void* id, | 308 new RefCountedString(); |
| 65 const std::string& extra, | 309 TraceEvent::AppendEventsAsJSON(previous_logged_events, |
| 66 const char* file, | 310 i, |
| 67 int line) { | 311 kTraceEventBatchSize, |
| 68 if (!enabled_) | 312 &(json_events_str_ptr->data)); |
| 69 return; | 313 output_callback_copy.Run(json_events_str_ptr.get()); |
| 70 | 314 } |
| 315 } | |
| 316 | |
| 317 void TraceLog::AddTraceEvent(TraceEventPhase phase, | |
| 318 const char* file, int line, | |
| 319 const TraceCategory* category, | |
| 320 const char* name, | |
| 321 const char* arg1_name, TraceValue arg1_val, | |
| 322 const char* arg2_name, TraceValue arg2_val) { | |
| 323 DCHECK(file && name); | |
| 71 #ifdef USE_UNRELIABLE_NOW | 324 #ifdef USE_UNRELIABLE_NOW |
| 72 TimeTicks tick = TimeTicks::HighResNow(); | 325 TimeTicks now = TimeTicks::HighResNow(); |
| 73 #else | 326 #else |
| 74 TimeTicks tick = TimeTicks::Now(); | 327 TimeTicks now = TimeTicks::Now(); |
| 75 #endif | 328 #endif |
| 76 TimeDelta delta = tick - trace_start_time_; | 329 BufferFullCallback buffer_full_callback_copy; |
| 77 int64 usec = delta.InMicroseconds(); | 330 { |
| 78 std::string msg = | 331 AutoLock lock(lock_); |
| 79 StringPrintf("{'pid':'0x%lx', 'tid':'0x%lx', 'type':'%s', " | 332 if (!enabled_ || !category->enabled) |
| 80 "'name':'%s', 'id':'%p', 'extra':'%s', 'file':'%s', " | 333 return; |
| 81 "'line_number':'%d', 'usec_begin': %" PRId64 "},\n", | 334 if (logged_events_.size() >= kTraceEventBufferSize) |
| 82 static_cast<unsigned long>(base::GetCurrentProcId()), | 335 return; |
| 83 static_cast<unsigned long>(PlatformThread::CurrentId()), | 336 logged_events_.push_back(TraceEvent()); |
| 84 kEventTypeNames[type], | 337 TraceEvent& event = logged_events_.back(); |
| 85 name.c_str(), | 338 event.process_id = static_cast<unsigned long>(base::GetCurrentProcId()); |
| 86 id, | 339 event.thread_id = PlatformThread::CurrentId(); |
| 87 extra.c_str(), | 340 event.timestamp = now; |
| 88 file, | 341 event.phase = phase; |
| 89 line, | 342 event.category = category; |
| 90 usec); | 343 event.name = name; |
| 91 | 344 event.arg_names[0] = arg1_name; |
| 92 Log(msg); | 345 event.arg_values[0] = arg1_val; |
| 93 } | 346 event.arg_names[1] = arg2_name; |
| 94 | 347 event.arg_values[1] = arg2_val; |
| 95 TraceLog::TraceLog() : enabled_(false), log_file_(NULL) { | 348 COMPILE_ASSERT(kTraceMaxNumArgs == 2, TraceEvent_arg_count_out_of_sync); |
| 96 base::ProcessHandle proc = base::GetCurrentProcessHandle(); | 349 |
| 97 #if !defined(OS_MACOSX) | 350 if (logged_events_.size() == kTraceEventBufferSize) { |
| 98 process_metrics_.reset(base::ProcessMetrics::CreateProcessMetrics(proc)); | 351 buffer_full_callback_copy = buffer_full_callback_; |
| 99 #else | 352 } |
| 100 // The default port provider is sufficient to get data for the current | 353 } // release lock |
| 101 // process. | 354 |
| 102 process_metrics_.reset(base::ProcessMetrics::CreateProcessMetrics(proc, | 355 if (!buffer_full_callback_copy.is_null()) |
| 103 NULL)); | 356 buffer_full_callback_copy.Run(); |
| 357 } | |
| 358 | |
| 359 void TraceLog::AddTraceEventEtw(TraceEventPhase phase, | |
| 360 const char* file, int line, | |
|
Sigurður Ásgeirsson
2011/05/05 14:58:56
re-nit: http://google-styleguide.googlecode.com/sv
scheib
2011/05/05 16:50:28
Done.
| |
| 361 const char* name, | |
| 362 const void* id, | |
| 363 const char* extra) { | |
| 364 // Legacy trace points on windows called to ETW | |
| 365 #if defined(OS_WIN) | |
| 366 TraceEventETWProvider::Trace(name, phase, id, extra); | |
| 104 #endif | 367 #endif |
| 105 } | 368 |
| 106 | 369 // Also add new trace event behavior |
| 107 TraceLog::~TraceLog() { | 370 static const TraceCategory* category = GetCategory("ETW Trace Event"); |
| 108 Stop(); | 371 if (category->enabled) { |
| 109 } | 372 TraceLog* tracelog = TraceLog::GetInstance(); |
| 110 | 373 if (!tracelog) |
| 111 bool TraceLog::OpenLogFile() { | 374 return; |
| 112 FilePath::StringType pid_filename = | 375 tracelog->AddTraceEvent(phase, file, line, category, name, |
| 113 StringPrintf(kLogFileName, base::GetCurrentProcId()); | 376 "id", id, |
| 114 FilePath log_file_path; | 377 "extra", extra ? extra : ""); |
| 115 if (!PathService::Get(base::DIR_EXE, &log_file_path)) | 378 } |
| 116 return false; | 379 } |
| 117 log_file_path = log_file_path.Append(pid_filename); | 380 |
| 118 log_file_ = file_util::OpenFile(log_file_path, "a"); | 381 void TraceLog::Resurrect() { |
| 119 if (!log_file_) { | 382 StaticMemorySingletonTraits<TraceLog>::Resurrect(); |
| 120 // try the current directory | |
| 121 log_file_ = file_util::OpenFile(FilePath(pid_filename), "a"); | |
| 122 if (!log_file_) { | |
| 123 return false; | |
| 124 } | |
| 125 } | |
| 126 return true; | |
| 127 } | |
| 128 | |
| 129 void TraceLog::CloseLogFile() { | |
| 130 if (log_file_) { | |
| 131 file_util::CloseFile(log_file_); | |
| 132 } | |
| 133 } | |
| 134 | |
| 135 bool TraceLog::Start() { | |
| 136 if (enabled_) | |
| 137 return true; | |
| 138 enabled_ = OpenLogFile(); | |
| 139 if (enabled_) { | |
| 140 Log("var raw_trace_events = [\n"); | |
| 141 trace_start_time_ = TimeTicks::Now(); | |
| 142 timer_.Start(TimeDelta::FromMilliseconds(250), this, &TraceLog::Heartbeat); | |
| 143 } | |
| 144 return enabled_; | |
| 145 } | |
| 146 | |
| 147 void TraceLog::Stop() { | |
| 148 if (enabled_) { | |
| 149 enabled_ = false; | |
| 150 Log("];\n"); | |
| 151 CloseLogFile(); | |
| 152 timer_.Stop(); | |
| 153 } | |
| 154 } | |
| 155 | |
| 156 void TraceLog::Heartbeat() { | |
| 157 std::string cpu = StringPrintf("%.0f", process_metrics_->GetCPUUsage()); | |
| 158 TRACE_EVENT_INSTANT("heartbeat.cpu", 0, cpu); | |
| 159 } | |
| 160 | |
| 161 void TraceLog::Log(const std::string& msg) { | |
| 162 AutoLock lock(file_lock_); | |
| 163 | |
| 164 fprintf(log_file_, "%s", msg.c_str()); | |
| 165 } | 383 } |
| 166 | 384 |
| 167 } // namespace debug | 385 } // namespace debug |
| 168 } // namespace base | 386 } // namespace base |
| OLD | NEW |