OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 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 | |
10 #include "base/format_macros.h" | 7 #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" |
13 #include "base/utf_string_conversions.h" | 14 #include "base/utf_string_conversions.h" |
14 #include "base/time.h" | 15 #include "base/time.h" |
15 | 16 |
16 #define USE_UNRELIABLE_NOW | 17 #define USE_UNRELIABLE_NOW |
17 | 18 |
18 namespace base { | 19 namespace base { |
19 namespace debug { | 20 namespace debug { |
20 | 21 |
21 // Controls the number of trace events we will buffer in-memory | 22 static const char* kEventTypeNames[] = { |
22 // before throwing them away. | 23 "BEGIN", |
23 const size_t kTraceEventBufferSize = 500000; | 24 "END", |
24 const size_t kTraceEventBatchSize = 1000; | 25 "INSTANT" |
| 26 }; |
25 | 27 |
26 #define TRACE_EVENT_MAX_CATEGORIES 42 | 28 static const FilePath::CharType* kLogFileName = |
| 29 FILE_PATH_LITERAL("trace_%d.log"); |
27 | 30 |
28 static TraceCategory g_categories[TRACE_EVENT_MAX_CATEGORIES] = { | 31 // static |
29 { "tracing already shutdown", false }, | 32 TraceLog* TraceLog::GetInstance() { |
30 { "tracing categories exhausted; must increase TRACE_EVENT_MAX_CATEGORIES", | 33 return Singleton<TraceLog, DefaultSingletonTraits<TraceLog> >::get(); |
31 false } | |
32 }; | |
33 static const TraceCategory* const g_category_already_shutdown = | |
34 &g_categories[0]; | |
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 } | 34 } |
53 | 35 |
54 TraceValue& TraceValue::operator=(const TraceValue& rhs) { | 36 // static |
55 if (this == &rhs) | 37 bool TraceLog::IsTracing() { |
56 return *this; | 38 return TraceLog::GetInstance()->enabled_; |
| 39 } |
57 | 40 |
58 Destroy(); | 41 // static |
| 42 bool TraceLog::StartTracing() { |
| 43 return TraceLog::GetInstance()->Start(); |
| 44 } |
59 | 45 |
60 type_ = rhs.type_; | 46 // static |
61 switch(type_) { | 47 void TraceLog::StopTracing() { |
62 case TRACE_TYPE_UNDEFINED: | 48 return TraceLog::GetInstance()->Stop(); |
63 return *this; | 49 } |
64 case TRACE_TYPE_BOOL: | 50 |
65 value_.as_bool = rhs.value_.as_bool; | 51 void TraceLog::Trace(const std::string& name, |
66 return *this; | 52 EventType type, |
67 case TRACE_TYPE_UINT: | 53 const void* id, |
68 value_.as_uint = rhs.value_.as_uint; | 54 const std::wstring& extra, |
69 return *this; | 55 const char* file, |
70 case TRACE_TYPE_INT: | 56 int line) { |
71 value_.as_int = rhs.value_.as_int; | 57 if (!enabled_) |
72 return *this; | 58 return; |
73 case TRACE_TYPE_DOUBLE: | 59 Trace(name, type, id, WideToUTF8(extra), file, line); |
74 value_.as_double = rhs.value_.as_double; | 60 } |
75 return *this; | 61 |
76 case TRACE_TYPE_POINTER: | 62 void TraceLog::Trace(const std::string& name, |
77 value_.as_pointer = rhs.value_.as_pointer; | 63 EventType type, |
78 return *this; | 64 const void* id, |
79 case TRACE_TYPE_STRING: | 65 const std::string& extra, |
80 value_.as_string_refptr = rhs.value_.as_string_refptr; | 66 const char* file, |
81 value_.as_string_refptr->AddRef(); | 67 int line) { |
82 return *this; | 68 if (!enabled_) |
83 default: | 69 return; |
84 NOTREACHED(); | 70 |
85 return *this; | 71 #ifdef USE_UNRELIABLE_NOW |
| 72 TimeTicks tick = TimeTicks::HighResNow(); |
| 73 #else |
| 74 TimeTicks tick = TimeTicks::Now(); |
| 75 #endif |
| 76 TimeDelta delta = tick - trace_start_time_; |
| 77 int64 usec = delta.InMicroseconds(); |
| 78 std::string msg = |
| 79 StringPrintf("{'pid':'0x%lx', 'tid':'0x%lx', 'type':'%s', " |
| 80 "'name':'%s', 'id':'%p', 'extra':'%s', 'file':'%s', " |
| 81 "'line_number':'%d', 'usec_begin': %" PRId64 "},\n", |
| 82 static_cast<unsigned long>(base::GetCurrentProcId()), |
| 83 static_cast<unsigned long>(PlatformThread::CurrentId()), |
| 84 kEventTypeNames[type], |
| 85 name.c_str(), |
| 86 id, |
| 87 extra.c_str(), |
| 88 file, |
| 89 line, |
| 90 usec); |
| 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_); |
86 } | 132 } |
87 } | 133 } |
88 | 134 |
89 void TraceValue::AppendAsJSON(std::string* out) const { | 135 bool TraceLog::Start() { |
90 char temp_string[128]; | 136 if (enabled_) |
91 std::string::size_type start_pos; | 137 return true; |
92 switch (type_) { | 138 enabled_ = OpenLogFile(); |
93 case TRACE_TYPE_BOOL: | 139 if (enabled_) { |
94 *out += as_bool()? "true" : "false"; | 140 Log("var raw_trace_events = [\n"); |
95 break; | 141 trace_start_time_ = TimeTicks::Now(); |
96 case TRACE_TYPE_UINT: | 142 timer_.Start(TimeDelta::FromMilliseconds(250), this, &TraceLog::Heartbeat); |
97 base::snprintf(temp_string, arraysize(temp_string), "%llu", | 143 } |
98 static_cast<unsigned long long>(as_uint())); | 144 return enabled_; |
99 *out += temp_string; | 145 } |
100 break; | 146 |
101 case TRACE_TYPE_INT: | 147 void TraceLog::Stop() { |
102 base::snprintf(temp_string, arraysize(temp_string), "%lld", | 148 if (enabled_) { |
103 static_cast<long long>(as_int())); | 149 enabled_ = false; |
104 *out += temp_string; | 150 Log("];\n"); |
105 break; | 151 CloseLogFile(); |
106 case TRACE_TYPE_DOUBLE: | 152 timer_.Stop(); |
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 } | 153 } |
131 } | 154 } |
132 | 155 |
133 //////////////////////////////////////////////////////////////////////////////// | 156 void TraceLog::Heartbeat() { |
134 // | 157 std::string cpu = StringPrintf("%.0f", process_metrics_->GetCPUUsage()); |
135 // TraceEvent | 158 TRACE_EVENT_INSTANT("heartbeat.cpu", 0, cpu); |
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 } | 159 } |
154 | 160 |
155 } // namespace | 161 void TraceLog::Log(const std::string& msg) { |
| 162 AutoLock lock(file_lock_); |
156 | 163 |
157 TraceEvent::TraceEvent() | 164 fprintf(log_file_, "%s", msg.c_str()); |
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 //////////////////////////////////////////////////////////////////////////////// | |
214 | |
215 // static | |
216 TraceLog* TraceLog::GetInstance() { | |
217 return Singleton<TraceLog, StaticMemorySingletonTraits<TraceLog> >::get(); | |
218 } | |
219 | |
220 TraceLog::TraceLog() | |
221 : enabled_(false) { | |
222 logged_events_.reserve(1024); | |
223 } | |
224 | |
225 TraceLog::~TraceLog() { | |
226 } | |
227 | |
228 const TraceCategory* TraceLog::GetCategory(const char* name) { | |
229 TraceLog* tracelog = GetInstance(); | |
230 if (!tracelog){ | |
231 CHECK(!g_category_already_shutdown->enabled); | |
232 return g_category_already_shutdown; | |
233 } | |
234 return tracelog->GetCategoryInternal(name); | |
235 } | |
236 | |
237 const TraceCategory* TraceLog::GetCategoryInternal(const char* name) { | |
238 AutoLock lock(lock_); | |
239 DCHECK(!strchr(name, '"')) << "Names may not contain double quote char"; | |
240 | |
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 AutoLock lock(lock_); | |
283 output_callback_ = cb; | |
284 logged_events_.clear(); | |
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()) | |
302 return; | |
303 | |
304 for (size_t i = 0; | |
305 i < previous_logged_events.size(); | |
306 i += kTraceEventBatchSize) { | |
307 scoped_refptr<RefCountedString> json_events_str_ptr = | |
308 new RefCountedString(); | |
309 TraceEvent::AppendEventsAsJSON(previous_logged_events, | |
310 i, | |
311 kTraceEventBatchSize, | |
312 &(json_events_str_ptr->data)); | |
313 output_callback_copy.Run(json_events_str_ptr.get()); | |
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); | |
324 #ifdef USE_UNRELIABLE_NOW | |
325 TimeTicks now = TimeTicks::HighResNow(); | |
326 #else | |
327 TimeTicks now = TimeTicks::Now(); | |
328 #endif | |
329 BufferFullCallback buffer_full_callback_copy; | |
330 { | |
331 AutoLock lock(lock_); | |
332 if (!enabled_ || !category->enabled) | |
333 return; | |
334 if (logged_events_.size() >= kTraceEventBufferSize) | |
335 return; | |
336 logged_events_.push_back(TraceEvent()); | |
337 TraceEvent& event = logged_events_.back(); | |
338 event.process_id = static_cast<unsigned long>(base::GetCurrentProcId()); | |
339 event.thread_id = PlatformThread::CurrentId(); | |
340 event.timestamp = now; | |
341 event.phase = phase; | |
342 event.category = category; | |
343 event.name = name; | |
344 event.arg_names[0] = arg1_name; | |
345 event.arg_values[0] = arg1_val; | |
346 event.arg_names[1] = arg2_name; | |
347 event.arg_values[1] = arg2_val; | |
348 COMPILE_ASSERT(kTraceMaxNumArgs == 2, TraceEvent_arg_count_out_of_sync); | |
349 | |
350 if (logged_events_.size() == kTraceEventBufferSize) { | |
351 buffer_full_callback_copy = buffer_full_callback_; | |
352 } | |
353 } // release lock | |
354 | |
355 if (!buffer_full_callback_copy.is_null()) | |
356 buffer_full_callback_copy.Run(); | |
357 } | |
358 | |
359 void TraceLog::AddTraceEventEtw(TraceEventPhase phase, | |
360 const char* file, int line, | |
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); | |
367 #endif | |
368 | |
369 // Also add new trace event behavior | |
370 static const TraceCategory* category = GetCategory("ETW Trace Event"); | |
371 if (category->enabled) { | |
372 TraceLog* tracelog = TraceLog::GetInstance(); | |
373 if (!tracelog) | |
374 return; | |
375 tracelog->AddTraceEvent(phase, file, line, category, name, | |
376 "id", id, | |
377 "extra", extra ? extra : ""); | |
378 } | |
379 } | |
380 | |
381 void TraceLog::Resurrect() { | |
382 StaticMemorySingletonTraits<TraceLog>::Resurrect(); | |
383 } | 165 } |
384 | 166 |
385 } // namespace debug | 167 } // namespace debug |
386 } // namespace base | 168 } // namespace base |
OLD | NEW |