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]; |
Sigurður Ásgeirsson
2011/05/02 19:28:41
This will unfortunately not work, and will not pas
scheib
2011/05/04 02:14:15
Converted to POD.
| |
29 static int g_category_index = 0; | |
30 | |
31 //////////////////////////////////////////////////////////////////////////////// | |
32 // | |
33 // TraceLog::Category | |
34 // | |
35 //////////////////////////////////////////////////////////////////////////////// | |
36 TraceCategory::TraceCategory() | |
37 : enabled_(false), name_(NULL) { | |
38 } | |
39 | |
40 TraceCategory::~TraceCategory() { | |
41 enabled_ = false; | |
42 } | |
43 | |
44 //////////////////////////////////////////////////////////////////////////////// | |
45 // | |
46 // TraceValue | |
47 // | |
48 //////////////////////////////////////////////////////////////////////////////// | |
49 | |
50 void TraceValue::Destroy() { | |
51 if (type_ == TRACE_TYPE_STRING) { | |
52 free(value_.as_string); | |
Sigurður Ásgeirsson
2011/05/02 19:28:41
Per http://codesearch.google.com/codesearch/p?hl=e
scheib
2011/05/04 02:14:15
Done.
| |
53 value_.as_string = NULL; | |
54 } | |
55 type_ = TRACE_TYPE_UNDEFINED; | |
56 value_.as_uint = 0ull; | |
57 } | |
58 | |
59 TraceValue& TraceValue::operator=(const TraceValue& rhs) { | |
60 DCHECK(sizeof(value_) == sizeof(uint64)); | |
Sigurður Ásgeirsson
2011/05/02 19:28:41
COMPILE_ASSERT?
scheib
2011/05/04 02:14:15
Refactored, assert not needed.
| |
61 Destroy(); | |
62 type_ = rhs.type_; | |
63 if (rhs.type_ == TRACE_TYPE_STRING) { | |
64 value_.as_string = base::strdup(rhs.value_.as_string); | |
65 } else { | |
66 // Copy all 64 bits for all other types. | |
Sigurður Ásgeirsson
2011/05/02 19:28:41
Move the assert from above here, by way of documen
scheib
2011/05/04 02:14:15
Refactored, assert not needed.
| |
67 value_.as_uint = rhs.value_.as_uint; | |
68 } | |
69 return *this; | |
70 } | |
71 | |
72 bool TraceValue::operator==(const TraceValue& rhs) const { | |
73 if (type_ != rhs.type()) | |
74 return false; | |
75 if (rhs.type_ == TRACE_TYPE_STRING) { | |
76 return (strcmp(value_.as_string, rhs.value_.as_string) == 0); | |
77 } else { | |
78 // Compare all 64 bits for all other types. Unused bits are set to zero | |
79 // by the constructors of types that may be less than 64 bits. | |
80 return (value_.as_uint == rhs.value_.as_uint); | |
Sigurður Ásgeirsson
2011/05/02 19:28:41
mmmm, are you sure that doubles have a canonical r
scheib
2011/05/04 02:14:15
Refactored, assert not needed.
| |
81 } | |
82 } | |
83 | |
84 void TraceValue::AppendAsJSON(std::string* out) const { | |
85 char temp_string[128]; | |
86 std::string::size_type start_pos; | |
87 switch (type_) { | |
88 case TRACE_TYPE_BOOL: | |
89 *out += as_bool()? "true" : "false"; | |
90 break; | |
91 case TRACE_TYPE_UINT: | |
92 base::snprintf(temp_string, sizeof(temp_string), "%llu", | |
Sigurður Ásgeirsson
2011/05/02 19:28:41
nit: sizeof->arraysize
scheib
2011/05/04 02:14:15
Done.
| |
93 static_cast<unsigned long long>(as_uint())); | |
94 *out += temp_string; | |
95 break; | |
96 case TRACE_TYPE_INT: | |
97 base::snprintf(temp_string, sizeof(temp_string), "%lld", | |
98 static_cast<long long>(as_int())); | |
99 *out += temp_string; | |
100 break; | |
101 case TRACE_TYPE_DOUBLE: | |
102 base::snprintf(temp_string, sizeof(temp_string), "%f", as_double()); | |
103 *out += temp_string; | |
104 break; | |
105 case TRACE_TYPE_POINTER: | |
106 base::snprintf(temp_string, sizeof(temp_string), "%llu", | |
107 static_cast<unsigned long long>( | |
108 reinterpret_cast<intptr_t>( | |
109 as_pointer()))); | |
110 *out += temp_string; | |
111 break; | |
112 case TRACE_TYPE_STRING: | |
113 *out += "\""; | |
114 start_pos = out->size(); | |
115 *out += as_string(); | |
116 // replace " character with ' | |
117 while ((start_pos = out->find_first_of('\"', start_pos)) != | |
118 std::string::npos) | |
119 (*out)[start_pos] = '\''; | |
120 *out += "\""; | |
121 break; | |
122 default: | |
123 break; | |
124 } | |
125 } | |
126 | |
127 //////////////////////////////////////////////////////////////////////////////// | |
128 // | |
129 // TraceEvent | |
130 // | |
131 //////////////////////////////////////////////////////////////////////////////// | |
132 | |
133 namespace { | |
134 | |
135 const char* GetPhaseStr(TraceEventPhase phase) { | |
136 switch(phase) { | |
137 case TRACE_EVENT_PHASE_BEGIN: | |
Sigurður Ásgeirsson
2011/05/02 19:28:41
nit: see style guide for indentation: http://googl
scheib
2011/05/04 02:14:15
Done.
| |
138 return "B"; | |
139 case TRACE_EVENT_PHASE_INSTANT: | |
140 return "I"; | |
141 case TRACE_EVENT_PHASE_END: | |
142 return "E"; | |
143 default: | |
144 DCHECK(false); | |
Sigurður Ásgeirsson
2011/05/02 19:28:41
NOTREACHED() << "Some comment";?
scheib
2011/05/04 02:14:15
Done.
| |
145 return "?"; | |
146 } | |
147 } | |
148 | |
149 } // namespace | |
150 | |
151 TraceEvent::TraceEvent() | |
152 : process_id(0), | |
153 thread_id(0), | |
154 phase(TRACE_EVENT_PHASE_BEGIN), | |
155 category(NULL), | |
156 name(NULL) { | |
157 memset(&arg_names, 0, sizeof(arg_names)); | |
158 } | |
159 | |
160 TraceEvent::~TraceEvent() { | |
161 } | |
162 | |
Sigurður Ásgeirsson
2011/05/02 19:28:41
nit: spurious line.
scheib
2011/05/04 02:14:15
Done.
| |
163 | |
164 void TraceEvent::AppendEventsAsJSON(const std::vector<TraceEvent>& events, | |
165 size_t start, | |
Sigurður Ásgeirsson
2011/05/02 19:28:41
nit: align to bracket.
scheib
2011/05/04 02:14:15
Done.
| |
166 size_t count, | |
167 std::string* out) { | |
168 *out += "["; | |
169 for (size_t i = 0; i < count && start + i < events.size(); ++i) { | |
170 if (i > 0) | |
171 *out += ","; | |
172 events[i + start].AppendAsJSON(out); | |
173 } | |
174 *out += "]"; | |
175 } | |
176 | |
177 void TraceEvent::AppendAsJSON(std::string* out) const { | |
178 const char* phase_str = GetPhaseStr(phase); | |
179 int64 time_int64 = timestamp.ToInternalValue(); | |
180 StringAppendF(out, | |
181 "{\"cat\":\"%s\",\"pid\":%i,\"tid\":%i,\"ts\":%lld," | |
Sigurður Ásgeirsson
2011/05/02 19:28:41
nit: the JSON won't be well-formed if any of the s
scheib
2011/05/04 02:14:15
Done.
| |
182 "\"ph\":\"%s\",\"name\":\"%s\",\"args\":{", | |
183 category->name(), | |
184 static_cast<int>(process_id), | |
185 static_cast<int>(thread_id), | |
186 static_cast<long long>(time_int64), | |
187 phase_str, | |
188 name); | |
189 | |
190 // Output argument names and values, stop at first NULL argument name. | |
191 for (int i = 0; i < TRACE_MAX_NUM_ARGS && arg_names[i]; ++i) { | |
192 if (i > 0) | |
193 *out += ","; | |
194 *out += "\""; | |
195 *out += arg_names[i]; | |
196 *out += "\":"; | |
197 arg_values[i].AppendAsJSON(out); | |
198 } | |
199 *out += "}}"; | |
200 } | |
201 | |
202 //////////////////////////////////////////////////////////////////////////////// | |
203 // | |
204 // TraceLog | |
205 // | |
206 //////////////////////////////////////////////////////////////////////////////// | |
30 | 207 |
31 // static | 208 // static |
32 TraceLog* TraceLog::GetInstance() { | 209 TraceLog* TraceLog::GetInstance() { |
33 return Singleton<TraceLog, DefaultSingletonTraits<TraceLog> >::get(); | 210 return Singleton<TraceLog, StaticMemorySingletonTraits<TraceLog> >::get(); |
34 } | 211 } |
35 | 212 |
36 // static | 213 TraceLog::TraceLog() |
37 bool TraceLog::IsTracing() { | 214 : enabled_(false) { |
38 return TraceLog::GetInstance()->enabled_; | 215 logged_events_.reserve(1024); |
39 } | 216 } |
40 | 217 |
41 // static | 218 TraceLog::~TraceLog() { |
42 bool TraceLog::StartTracing() { | 219 } |
43 return TraceLog::GetInstance()->Start(); | 220 |
44 } | 221 TraceCategory* TraceLog::GetCategory(const char* name) { |
45 | 222 TraceLog* tracelog = GetInstance(); |
46 // static | 223 if (!tracelog){ |
47 void TraceLog::StopTracing() { | 224 static TraceCategory false_category_after_shutdown = TraceCategory(); |
48 return TraceLog::GetInstance()->Stop(); | 225 return &false_category_after_shutdown; |
49 } | 226 } |
50 | 227 return tracelog->GetCategoryInternal(name); |
51 void TraceLog::Trace(const std::string& name, | 228 } |
52 EventType type, | 229 |
53 const void* id, | 230 TraceCategory* TraceLog::GetCategoryInternal(const char* name) { |
54 const std::wstring& extra, | 231 AutoLock lock(lock_); |
55 const char* file, | 232 for (int i = 0; i < g_category_index; i++) { |
56 int line) { | 233 if (strcmp(g_categories[i].name(), name) == 0) |
57 if (!enabled_) | 234 return &g_categories[i]; |
235 } | |
236 CHECK(g_category_index < TRACE_EVENT_MAX_CATEGORIES) << | |
Sigurður Ásgeirsson
2011/05/02 19:28:41
This seems quite severe for release builds.
Assumi
scheib
2011/05/04 02:14:15
Done.
Two special case categories added for post
| |
237 "must increase TRACE_EVENT_MAX_CATEGORIES"; | |
238 int new_index = g_category_index++; | |
239 g_categories[new_index].set(name, enabled_); | |
240 return &g_categories[new_index]; | |
241 } | |
242 | |
243 void TraceLog::SetEnabled(bool enabled) { | |
244 { | |
245 AutoLock lock(lock_); | |
246 if (enabled == enabled_) | |
247 return; | |
248 enabled_ = enabled; | |
249 for (int i = 0; i < g_category_index; i++) { | |
250 g_categories[i].enabled_ = enabled; | |
251 } | |
252 } | |
253 if (!enabled) | |
254 Flush(); | |
255 } | |
256 | |
257 float TraceLog::GetBufferPercentFull() const { | |
258 return (float)((double)logged_events_.size()/(double)TRACE_EVENT_BUFFER_SIZE); | |
259 } | |
260 | |
261 void TraceLog::SetOutputCallback(TraceLog::OutputCallback* cb) { | |
262 Flush(); | |
263 AutoLock lock(lock_); | |
264 output_callback_.reset(cb); | |
265 } | |
266 | |
267 void TraceLog::SetBufferFullCallback(TraceLog::BufferFullCallback* cb) { | |
268 AutoLock lock(lock_); | |
269 buffer_full_callback_.reset(cb); | |
270 } | |
271 | |
272 void TraceLog::AddRemotelyCollectedData(const std::string& json_events) { | |
273 AutoLock lock(lock_); | |
274 if (output_callback_.get()) | |
275 output_callback_->Run(json_events); | |
276 } | |
277 | |
278 void TraceLog::Flush() { | |
279 std::vector<TraceEvent> previous_logged_events; | |
280 { | |
281 AutoLock lock(lock_); | |
282 previous_logged_events.swap(logged_events_); | |
283 } | |
284 | |
285 for (size_t i = 0; | |
286 i < previous_logged_events.size(); | |
287 i += TRACE_EVENT_BATCH_SIZE) { | |
288 std::string json_events; | |
289 TraceEvent::AppendEventsAsJSON(previous_logged_events, | |
290 i, | |
291 TRACE_EVENT_BATCH_SIZE, | |
292 &json_events); | |
293 { | |
294 AutoLock lock(lock_); | |
295 output_callback_->Run(json_events); | |
Sigurður Ásgeirsson
2011/05/02 19:28:41
You're still calling out of module here with the l
scheib
2011/05/04 02:14:15
This was an error and I've removed the lock. The c
| |
296 } | |
297 } | |
298 } | |
299 | |
300 void TraceLog::AddTraceEvent(TraceEventPhase phase, | |
301 const char* file, int line, | |
302 TraceCategory* category, | |
303 const char* name, | |
304 const char* arg1_name, TraceValue arg1_val, | |
305 const char* arg2_name, TraceValue arg2_val) { | |
306 DCHECK(file && name); | |
307 #ifdef USE_UNRELIABLE_NOW | |
308 TimeTicks now = TimeTicks::HighResNow(); | |
309 #else | |
310 TimeTicks now = TimeTicks::Now(); | |
311 #endif | |
312 AutoLock lock(lock_); | |
313 if (!enabled_ || !category->enabled_) | |
58 return; | 314 return; |
59 Trace(name, type, id, WideToUTF8(extra), file, line); | 315 if (logged_events_.size() >= TRACE_EVENT_BUFFER_SIZE) |
60 } | |
61 | |
62 void TraceLog::Trace(const std::string& name, | |
63 EventType type, | |
64 const void* id, | |
65 const std::string& extra, | |
66 const char* file, | |
67 int line) { | |
68 if (!enabled_) | |
69 return; | 316 return; |
70 | 317 logged_events_.push_back(TraceEvent()); |
71 #ifdef USE_UNRELIABLE_NOW | 318 TraceEvent& event = logged_events_.back(); |
72 TimeTicks tick = TimeTicks::HighResNow(); | 319 event.process_id = static_cast<unsigned long>(base::GetCurrentProcId()); |
73 #else | 320 event.thread_id = PlatformThread::CurrentId(); |
74 TimeTicks tick = TimeTicks::Now(); | 321 event.timestamp = now; |
322 event.phase = phase; | |
323 event.category = category; | |
324 event.name = name; | |
325 event.arg_names[0] = arg1_name; | |
326 event.arg_values[0] = arg1_val; | |
327 event.arg_names[1] = arg2_name; | |
328 event.arg_values[1] = arg2_val; | |
329 COMPILE_ASSERT(TRACE_MAX_NUM_ARGS == 2, TraceEvent_arc_count_out_of_sync); | |
330 if (logged_events_.size() == TRACE_EVENT_BUFFER_SIZE && | |
331 buffer_full_callback_.get()) | |
332 buffer_full_callback_->Run(); | |
Sigurður Ásgeirsson
2011/05/02 19:28:41
you're calling out of module here under a lock als
scheib
2011/05/04 02:14:15
Fixed.
| |
333 } | |
334 | |
335 void TraceLog::AddTraceEventEtw(TraceEventPhase phase, | |
336 const char* file, int line, | |
337 const char* name, | |
338 const void* id, | |
339 const char* extra) { | |
340 // Legacy trace points on windows called to ETW | |
341 #if defined(OS_WIN) | |
342 TraceEventETWProvider::Trace(name, phase, id, extra); | |
75 #endif | 343 #endif |
76 TimeDelta delta = tick - trace_start_time_; | 344 |
77 int64 usec = delta.InMicroseconds(); | 345 // Also add new trace event behavior |
78 std::string msg = | 346 static TraceCategory* category = GetCategory("ETW Trace Event"); |
79 StringPrintf("{'pid':'0x%lx', 'tid':'0x%lx', 'type':'%s', " | 347 if (category->enabled_) { |
80 "'name':'%s', 'id':'%p', 'extra':'%s', 'file':'%s', " | 348 TraceLog* tracelog = TraceLog::GetInstance(); |
81 "'line_number':'%d', 'usec_begin': %" PRId64 "},\n", | 349 if (!tracelog) |
82 static_cast<unsigned long>(base::GetCurrentProcId()), | 350 return; |
83 static_cast<unsigned long>(PlatformThread::CurrentId()), | 351 tracelog->AddTraceEvent(phase, file, line, category, name, |
84 kEventTypeNames[type], | 352 "id", StringPrintf("%p", id).c_str(), |
Sigurður Ásgeirsson
2011/05/02 19:28:41
nit: wonky indent.
scheib
2011/05/04 02:14:15
Done.
| |
85 name.c_str(), | 353 "extra", extra ? extra : ""); |
86 id, | 354 } |
87 extra.c_str(), | 355 } |
88 file, | 356 |
89 line, | 357 void TraceLog::Resurrect() { |
90 usec); | 358 StaticMemorySingletonTraits<TraceLog>::Resurrect(); |
91 | |
92 Log(msg); | |
93 } | |
94 | |
95 TraceLog::TraceLog() : enabled_(false), log_file_(NULL) { | |
96 base::ProcessHandle proc = base::GetCurrentProcessHandle(); | |
97 #if !defined(OS_MACOSX) | |
98 process_metrics_.reset(base::ProcessMetrics::CreateProcessMetrics(proc)); | |
99 #else | |
100 // The default port provider is sufficient to get data for the current | |
101 // process. | |
102 process_metrics_.reset(base::ProcessMetrics::CreateProcessMetrics(proc, | |
103 NULL)); | |
104 #endif | |
105 } | |
106 | |
107 TraceLog::~TraceLog() { | |
108 Stop(); | |
109 } | |
110 | |
111 bool TraceLog::OpenLogFile() { | |
112 FilePath::StringType pid_filename = | |
113 StringPrintf(kLogFileName, base::GetCurrentProcId()); | |
114 FilePath log_file_path; | |
115 if (!PathService::Get(base::DIR_EXE, &log_file_path)) | |
116 return false; | |
117 log_file_path = log_file_path.Append(pid_filename); | |
118 log_file_ = file_util::OpenFile(log_file_path, "a"); | |
119 if (!log_file_) { | |
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 } | 359 } |
166 | 360 |
167 } // namespace debug | 361 } // namespace debug |
168 } // namespace base | 362 } // namespace base |
OLD | NEW |