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