Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2010 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 | |
| 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 //////////////////////////////////////////////////////////////////////////////// |
| 28 static const FilePath::CharType* kLogFileName = | 27 // |
| 29 FILE_PATH_LITERAL("trace_%d.log"); | 28 // TraceLog::Category |
| 29 // | |
| 30 //////////////////////////////////////////////////////////////////////////////// | |
| 31 TraceCategory::TraceCategory(const char* name, bool enabled) | |
| 32 : name_(name) { | |
| 33 base::subtle::NoBarrier_Store(&enabled_, | |
| 34 static_cast<base::subtle::Atomic32>(enabled)); | |
| 35 } | |
| 36 | |
| 37 TraceCategory::~TraceCategory() { | |
| 38 base::subtle::NoBarrier_Store(&enabled_, | |
| 39 static_cast<base::subtle::Atomic32>(0)); | |
| 40 } | |
| 41 | |
| 42 //////////////////////////////////////////////////////////////////////////////// | |
| 43 // | |
| 44 // TraceEvent | |
| 45 // | |
| 46 //////////////////////////////////////////////////////////////////////////////// | |
| 47 | |
| 48 namespace { | |
| 49 const char* GetPhaseStr(TraceEventPhase phase) { | |
|
Sigurður Ásgeirsson
2011/04/15 17:41:45
nit: one blank line after opening namespace.
scheib
2011/04/27 21:20:10
Done.
| |
| 50 if (phase== TRACE_EVENT_PHASE_BEGIN) { | |
|
Sigurður Ásgeirsson
2011/04/15 17:41:45
nit: phase ==
Sigurður Ásgeirsson
2011/04/15 17:41:45
perhaps this is better phrased as a switch?
scheib
2011/04/27 21:20:10
Done.
scheib
2011/04/27 21:20:10
Done.
scheib
2011/04/27 21:20:10
Done.
| |
| 51 return "B"; | |
| 52 } else if (phase == TRACE_EVENT_PHASE_INSTANT) { | |
| 53 return "I"; | |
| 54 } else if (phase == TRACE_EVENT_PHASE_END) { | |
| 55 return "E"; | |
| 56 } else { | |
| 57 DCHECK(false); | |
| 58 return "?"; | |
| 59 } | |
| 60 } | |
| 61 } | |
|
Sigurður Ásgeirsson
2011/04/15 17:41:45
nit: one blank line before closing namespace
} //
scheib
2011/04/27 21:20:10
Done.
| |
| 62 | |
| 63 TraceEvent::TraceEvent() | |
| 64 : processId(0), | |
| 65 threadId(0), | |
| 66 phase(TRACE_EVENT_PHASE_BEGIN), | |
| 67 category(NULL), | |
| 68 name(NULL) { | |
| 69 memset(&argNames, 0, sizeof(argNames)); | |
| 70 } | |
| 71 | |
| 72 TraceEvent::~TraceEvent() { | |
| 73 } | |
| 74 | |
| 75 void TraceEvent::AppendAsJSON(std::string* out, | |
|
Sigurður Ásgeirsson
2011/04/15 17:41:45
First input args, then output args:
http://google
scheib
2011/04/27 21:20:10
Done.
| |
| 76 const std::vector<TraceEvent>& events, | |
| 77 size_t start, | |
| 78 size_t count) { | |
| 79 *out += "["; | |
| 80 for (size_t i = 0; i < count && start + i < events.size(); ++i) { | |
| 81 if (i > 0) | |
| 82 *out += ","; | |
| 83 events[i + start].AppendAsJSON(out); | |
| 84 } | |
| 85 *out += "]"; | |
| 86 } | |
| 87 | |
| 88 void TraceEvent::AppendAsJSON(std::string* out) const { | |
| 89 int nargs = 0; | |
|
Sigurður Ásgeirsson
2011/04/15 17:41:45
some running commentary would make this function e
scheib
2011/04/27 21:20:10
Done.
| |
| 90 for (int i = 0; i < TRACE_MAX_NUM_ARGS; ++i) { | |
| 91 if (argNames[i] == NULL) | |
| 92 break; | |
| 93 nargs += 1; | |
| 94 } | |
| 95 | |
| 96 const char* phaseStr = GetPhaseStr(phase); | |
|
Sigurður Ásgeirsson
2011/04/15 17:41:45
phaseStr->phase_str
scheib
2011/04/27 21:20:10
Done.
| |
| 97 int64 time_int64 = timestamp.ToInternalValue(); | |
| 98 long long unsigned int time_llui = | |
| 99 static_cast<long long unsigned int>(time_int64); | |
| 100 StringAppendF(out, | |
| 101 "{cat:'%s',pid:%i,tid:%i,ts:0x%llx,ph:'%s',name:'%s',args:{", | |
| 102 category->name(), | |
| 103 static_cast<int>(processId), | |
| 104 static_cast<int>(threadId), | |
| 105 time_llui, | |
| 106 phaseStr, | |
| 107 name); | |
| 108 for (int i = 0; i < nargs; ++i) { | |
| 109 if (i > 0) | |
| 110 *out += ","; | |
| 111 *out += argNames[i]; | |
| 112 *out += ":'"; | |
| 113 *out += argValues[i]; | |
| 114 *out += "'"; | |
| 115 } | |
| 116 *out += "}}"; | |
| 117 } | |
| 118 | |
| 119 //////////////////////////////////////////////////////////////////////////////// | |
| 120 // | |
| 121 // TraceLog | |
| 122 // | |
| 123 //////////////////////////////////////////////////////////////////////////////// | |
| 30 | 124 |
| 31 // static | 125 // static |
| 32 TraceLog* TraceLog::GetInstance() { | 126 TraceLog* TraceLog::GetInstance() { |
| 33 return Singleton<TraceLog, DefaultSingletonTraits<TraceLog> >::get(); | 127 return Singleton<TraceLog, StaticMemorySingletonTraits<TraceLog> >::get(); |
| 34 } | 128 } |
| 35 | 129 |
| 36 // static | 130 TraceLog::TraceLog() |
| 37 bool TraceLog::IsTracing() { | 131 : enabled_(false) |
| 38 return TraceLog::GetInstance()->enabled_; | 132 { |
|
Sigurður Ásgeirsson
2011/04/15 17:41:45
nit: bracket placement.
scheib
2011/04/27 21:20:10
Done.
| |
| 39 } | 133 logged_events_.reserve(1024); |
| 40 | 134 } |
| 41 // static | 135 |
| 42 bool TraceLog::StartTracing() { | 136 TraceLog::~TraceLog() { |
| 43 return TraceLog::GetInstance()->Start(); | 137 } |
| 44 } | 138 |
| 45 | 139 TraceCategory* TraceLog::GetCategory(const char* name) { |
| 46 // static | 140 AutoLock lock(lock_); |
| 47 void TraceLog::StopTracing() { | 141 // TODO(nduca): replace with a hash_map. |
|
Sigurður Ásgeirsson
2011/04/15 17:41:45
Yes, suggest adding an efficient name lookup befor
scheib
2011/04/27 21:20:10
Categories moved to static array. Categories are c
| |
| 48 return TraceLog::GetInstance()->Stop(); | 142 for (int i = static_cast<int>(categories_.size()) - 1; i >= 0; i-- ) { |
| 49 } | 143 if (strcmp(categories_[i]->name(), name) == 0) |
| 50 | 144 return categories_[i]; |
| 51 void TraceLog::Trace(const std::string& name, | 145 } |
| 52 EventType type, | 146 TraceCategory* category = new TraceCategory(name, enabled_); |
| 53 const void* id, | 147 categories_.push_back(category); |
| 54 const std::wstring& extra, | 148 return category; |
| 55 const char* file, | 149 } |
| 56 int line) { | 150 |
| 57 if (!enabled_) | 151 void TraceLog::SetEnabled(bool enabled) { |
| 152 AutoLock lock(lock_); | |
| 153 if (enabled == enabled_) | |
| 58 return; | 154 return; |
| 59 Trace(name, type, id, WideToUTF8(extra), file, line); | 155 if (enabled) { |
| 60 } | 156 // Enable all categories |
| 61 | 157 enabled_ = true; |
| 62 void TraceLog::Trace(const std::string& name, | 158 for (size_t i = 0; i < categories_.size(); i++) { |
| 63 EventType type, | 159 base::subtle::NoBarrier_Store(&categories_[i]->enabled_, |
|
Sigurður Ásgeirsson
2011/04/15 17:41:45
better to encapsulate the flag in inline accessors
scheib
2011/04/27 21:20:10
Removed, changed to just a bool.
| |
| 64 const void* id, | 160 static_cast<base::subtle::Atomic32>(1)); |
| 65 const std::string& extra, | 161 } |
| 66 const char* file, | 162 } else { |
| 67 int line) { | 163 // Disable all categories. |
| 68 if (!enabled_) | 164 for (size_t i = 0; i < categories_.size(); i++) { |
|
Sigurður Ásgeirsson
2011/04/15 17:41:45
copy paste loop, better to copy the test and do pr
scheib
2011/04/27 21:20:10
Done.
| |
| 165 base::subtle::NoBarrier_Store(&categories_[i]->enabled_, | |
| 166 static_cast<base::subtle::Atomic32>(0)); | |
| 167 } | |
| 168 enabled_ = false; | |
| 169 FlushWithLockAlreadyHeld(); | |
| 170 } | |
| 171 } | |
| 172 | |
| 173 void TraceLog::SetOutputCallback(TraceLog::OutputCallback* cb) { | |
| 174 AutoLock lock(lock_); | |
| 175 if (enabled_) { | |
|
Sigurður Ásgeirsson
2011/04/15 17:41:45
nit: no brackets for an uncomplicated one-line com
scheib
2011/04/27 21:20:10
Done.
| |
| 176 FlushWithLockAlreadyHeld(); | |
| 177 } | |
| 178 output_callback_.reset(cb); | |
| 179 } | |
| 180 | |
| 181 void TraceLog::AddRemotelyCollectedData(const std::string& json_events) { | |
| 182 AutoLock lock(lock_); | |
| 183 if (output_callback_.get()) | |
| 184 output_callback_->Run(json_events); | |
| 185 } | |
| 186 | |
| 187 void TraceLog::Flush() { | |
| 188 AutoLock lock(lock_); | |
| 189 FlushWithLockAlreadyHeld(); | |
| 190 } | |
| 191 | |
| 192 void TraceLog::FlushWithLockAlreadyHeld() { | |
|
Sigurður Ásgeirsson
2011/04/15 17:41:45
lock_.AssertAcquired()?
scheib
2011/04/27 21:20:10
Done.
| |
| 193 if (output_callback_.get() && logged_events_.size()) { | |
| 194 for (size_t i = 0; i < logged_events_.size(); i += TRACE_EVENT_BATCH_SIZE) { | |
| 195 std::string json_events; | |
| 196 TraceEvent::AppendAsJSON(&json_events, logged_events_, | |
| 197 i, TRACE_EVENT_BATCH_SIZE); | |
| 198 output_callback_->Run(json_events); | |
|
Sigurður Ásgeirsson
2011/04/15 17:41:45
I really don't like this - e.g. calling out of mod
scheib
2011/04/27 21:20:10
Done.
| |
| 199 } | |
| 200 } | |
| 201 logged_events_.erase(logged_events_.begin(), logged_events_.end()); | |
| 202 } | |
| 203 | |
| 204 void TraceLog::AddTraceEvent(TraceEventPhase phase, | |
| 205 const char* file, int line, | |
| 206 TraceCategory* category, | |
| 207 const char* name, | |
| 208 const char* arg1name, const char* arg1val, | |
|
Sigurður Ásgeirsson
2011/04/15 17:41:45
arg1_name, arg1_val, etc
scheib
2011/04/27 21:20:10
Done.
| |
| 209 const char* arg2name, const char* arg2val) { | |
| 210 DCHECK(file && name); | |
| 211 #ifdef USE_UNRELIABLE_NOW | |
| 212 TimeTicks now = TimeTicks::HighResNow(); | |
| 213 #else | |
| 214 TimeTicks now = TimeTicks::Now(); | |
| 215 #endif | |
| 216 //static_cast<unsigned long>(base::GetCurrentProcId()), | |
|
Sigurður Ásgeirsson
2011/04/15 17:41:45
?
scheib
2011/04/27 21:20:10
Done.
| |
| 217 AutoLock lock(lock_); | |
| 218 if (logged_events_.size() >= TRACE_EVENT_BUFFER_SIZE) | |
| 69 return; | 219 return; |
| 70 | 220 logged_events_.push_back(TraceEvent()); |
| 71 #ifdef USE_UNRELIABLE_NOW | 221 TraceEvent& event = logged_events_.back(); |
| 72 TimeTicks tick = TimeTicks::HighResNow(); | 222 event.processId = static_cast<unsigned long>(base::GetCurrentProcId()); |
| 73 #else | 223 event.threadId = PlatformThread::CurrentId(); |
| 74 TimeTicks tick = TimeTicks::Now(); | 224 event.timestamp = now; |
| 225 event.phase = phase; | |
| 226 event.category = category; | |
| 227 event.name = name; | |
| 228 event.argNames[0] = arg1name; | |
| 229 event.argValues[0] = arg1name ? arg1val : ""; | |
| 230 event.argNames[1] = arg2name; | |
| 231 event.argValues[1] = arg2name ? arg2val : ""; | |
| 232 COMPILE_ASSERT(TRACE_MAX_NUM_ARGS == 2, TraceEvent_arc_count_out_of_sync); | |
| 233 } | |
| 234 | |
| 235 void TraceLog::AddTraceEventEtw(TraceEventPhase phase, | |
| 236 const char* file, int line, | |
| 237 const char* name, | |
| 238 const void* id, | |
| 239 const char* extra) { | |
| 240 // Legacy trace points on windows called to ETW | |
| 241 #if defined(OS_WIN) | |
| 242 TraceEventETWProvider::Trace(name, phase, id, extra); | |
| 75 #endif | 243 #endif |
| 76 TimeDelta delta = tick - trace_start_time_; | 244 |
| 77 int64 usec = delta.InMicroseconds(); | 245 // Also add new trace event behavior |
| 78 std::string msg = | 246 static TraceCategory* category = GetCategory("ETW Trace Event"); |
| 79 StringPrintf("{'pid':'0x%lx', 'tid':'0x%lx', 'type':'%s', " | 247 if (category->enabled_) { |
| 80 "'name':'%s', 'id':'%p', 'extra':'%s', 'file':'%s', " | 248 AddTraceEvent(phase, file, line, category, name, |
| 81 "'line_number':'%d', 'usec_begin': %" PRId64 "},\n", | 249 "id", StringPrintf("%p", id).c_str(), |
| 82 static_cast<unsigned long>(base::GetCurrentProcId()), | 250 "extra", extra ? extra : ""); |
| 83 static_cast<unsigned long>(PlatformThread::CurrentId()), | 251 } |
| 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_); | |
| 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 } | 252 } |
| 166 | 253 |
| 167 } // namespace debug | 254 } // namespace debug |
| 168 } // namespace base | 255 } // namespace base |
| OLD | NEW |