1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "tools/gn/trace.h" 6 7 #include <algorithm> 8 #include <map> 9 #include <sstream> 10 #include <vector> 11 12 #include "base/file_util.h" 13 #include "base/json/string_escape.h" 14 #include "base/logging.h" 15 #include "base/strings/stringprintf.h" 16 #include "base/synchronization/lock.h" 17 #include "tools/gn/filesystem_utils.h" 18 #include "tools/gn/label.h" 19 20 namespace { 21 22 class TraceLog { 23 public: 24 TraceLog() { 25 events_.reserve(16384); 26 } 27 ~TraceLog() { 28 // Trace items leanked intentionally. 29 } 30 31 void Add(TraceItem* item) { 32 base::AutoLock lock(lock_); 33 events_.push_back(item); 34 } 35 36 // Returns a copy for threadsafety. 37 std::vector<TraceItem*> events() const { return events_; } 38 39 private: 40 base::Lock lock_; 41 42 std::vector<TraceItem*> events_; 43 44 DISALLOW_COPY_AND_ASSIGN(TraceLog); 45 }; 46 47 TraceLog* trace_log = NULL; 48 49 struct Coalesced { 50 Coalesced() : name_ptr(NULL), total_duration(0.0), count(0) {} 51 52 const std::string* name_ptr; // Pointer to a string with the name in it. 53 double total_duration; 54 int count; 55 }; 56 57 bool DurationGreater(const TraceItem* a, const TraceItem* b) { 58 return a->delta() > b->delta(); 59 } 60 61 bool CoalescedDurationGreater(const Coalesced& a, const Coalesced& b) { 62 return a.total_duration > b.total_duration; 63 } 64 65 void SummarizeParses(std::vector<const TraceItem*>& loads, 66 std::ostream& out) { 67 out << "File parse times: (time in ms, name)\n"; 68 69 std::sort(loads.begin(), loads.end(), &DurationGreater); 70 71 for (size_t i = 0; i < loads.size(); i++) { 72 out << base::StringPrintf(" %8.2f ", 73 loads[i]->delta().InMillisecondsF()); 74 out << loads[i]->name() << std::endl; 75 } 76 } 77 78 void SummarizeCoalesced(std::vector<const TraceItem*>& items, 79 std::ostream& out) { 80 // Group by file name. 81 std::map<std::string, Coalesced> coalesced; 82 for (size_t i = 0; i < items.size(); i++) { 83 Coalesced& c = coalesced[items[i]->name()]; 84 c.name_ptr = &items[i]->name(); 85 c.total_duration += items[i]->delta().InMillisecondsF(); 86 c.count++; 87 } 88 89 // Sort by duration. 90 std::vector<Coalesced> sorted; 91 for (std::map<std::string, Coalesced>::iterator iter = coalesced.begin(); 92 iter != coalesced.end(); ++iter) 93 sorted.push_back(iter->second); 94 std::sort(sorted.begin(), sorted.end(), &CoalescedDurationGreater); 95 96 for (size_t i = 0; i < sorted.size(); i++) { 97 out << base::StringPrintf(" %8.2f %d ", 98 sorted[i].total_duration, sorted[i].count); 99 out << *sorted[i].name_ptr << std::endl; 100 } 101 } 102 103 void SummarizeFileExecs(std::vector<const TraceItem*>& execs, 104 std::ostream& out) { 105 out << "File execute times: (total time in ms, # executions, name)\n"; 106 SummarizeCoalesced(execs, out); 107 } 108 109 void SummarizeScriptExecs(std::vector<const TraceItem*>& execs, 110 std::ostream& out) { 111 out << "Script execute times: (total time in ms, # executions, name)\n"; 112 SummarizeCoalesced(execs, out); 113 } 114 115 } // namespace 116 117 TraceItem::TraceItem(Type type, 118 const std::string& name, 119 base::PlatformThreadId thread_id) 120 : type_(type), 121 name_(name), 122 thread_id_(thread_id) { 123 } 124 125 TraceItem::~TraceItem() { 126 } 127 128 ScopedTrace::ScopedTrace(TraceItem::Type t, const std::string& name) 129 : item_(NULL), 130 done_(false) { 131 if (trace_log) { 132 item_ = new TraceItem(t, name, base::PlatformThread::CurrentId()); 133 item_->set_begin(base::TimeTicks::HighResNow()); 134 } 135 } 136 137 ScopedTrace::ScopedTrace(TraceItem::Type t, const Label& label) 138 : item_(NULL), 139 done_(false) { 140 if (trace_log) { 141 item_ = new TraceItem(t, label.GetUserVisibleName(false), 142 base::PlatformThread::CurrentId()); 143 item_->set_begin(base::TimeTicks::HighResNow()); 144 } 145 } 146 147 ScopedTrace::~ScopedTrace() { 148 Done(); 149 } 150 151 void ScopedTrace::SetToolchain(const Label& label) { 152 if (item_) 153 item_->set_toolchain(label.GetUserVisibleName(false)); 154 } 155 156 void ScopedTrace::SetCommandLine(const CommandLine& cmdline) { 157 if (item_) 158 item_->set_cmdline(FilePathToUTF8(cmdline.GetArgumentsString())); 159 } 160 161 void ScopedTrace::Done() { 162 if (!done_) { 163 done_ = true; 164 if (trace_log) { 165 item_->set_end(base::TimeTicks::HighResNow()); 166 AddTrace(item_); 167 } 168 } 169 } 170 171 void EnableTracing() { 172 CHECK(!trace_log); 173 trace_log = new TraceLog; 174 } 175 176 void AddTrace(TraceItem* item) { 177 trace_log->Add(item); 178 } 179 180 std::string SummarizeTraces() { 181 if (!trace_log) 182 return std::string(); 183 184 std::vector<TraceItem*> events = trace_log->events(); 185 186 // Classify all events. 187 std::vector<const TraceItem*> parses; 188 std::vector<const TraceItem*> file_execs; 189 std::vector<const TraceItem*> script_execs; 190 for (size_t i = 0; i < events.size(); i++) { 191 switch (events[i]->type()) { 192 case TraceItem::TRACE_FILE_PARSE: 193 parses.push_back(events[i]); 194 break; 195 case TraceItem::TRACE_FILE_EXECUTE: 196 file_execs.push_back(events[i]); 197 break; 198 case TraceItem::TRACE_SCRIPT_EXECUTE: 199 script_execs.push_back(events[i]); 200 break; 201 case TraceItem::TRACE_FILE_LOAD: 202 case TraceItem::TRACE_FILE_WRITE: 203 case TraceItem::TRACE_DEFINE_TARGET: 204 break; // Ignore these for the summary. 205 } 206 } 207 208 std::ostringstream out; 209 SummarizeParses(parses, out); 210 out << std::endl; 211 SummarizeFileExecs(file_execs, out); 212 out << std::endl; 213 SummarizeScriptExecs(script_execs, out); 214 out << std::endl; 215 216 return out.str(); 217 } 218 219 void SaveTraces(const base::FilePath& file_name) { 220 std::ostringstream out; 221 222 out << "{\"traceEvents\":["; 223 224 std::string quote_buffer; // Allocate outside loop to prevent reallocationg. 225 226 // Write main thread metadata (assume this is being written on the main 227 // thread). 228 out << "{\"pid\":0,\"tid\":" << base::PlatformThread::CurrentId(); 229 out << ",\"ts\":0,\"ph\":\"M\","; 230 out << "\"name\":\"thread_name\",\"args\":{\"name\":\"Main thread\"}},"; 231 232 std::vector<TraceItem*> events = trace_log->events(); 233 for (size_t i = 0; i < events.size(); i++) { 234 const TraceItem& item = *events[i]; 235 236 if (i != 0) 237 out << ","; 238 out << "{\"pid\":0,\"tid\":" << item.thread_id(); 239 out << ",\"ts\":" << item.begin().ToInternalValue(); 240 out << ",\"ph\":\"X\""; // "X" = complete event with begin & duration. 241 out << ",\"dur\":" << item.delta().InMicroseconds(); 242 243 quote_buffer.resize(0); 244 base::EscapeJSONString(item.name(), true, "e_buffer); 245 out << ",\"name\":" << quote_buffer; 246 247 out << ",\"cat\":"; 248 switch (item.type()) { 249 case TraceItem::TRACE_FILE_LOAD: 250 out << "\"load\""; 251 break; 252 case TraceItem::TRACE_FILE_PARSE: 253 out << "\"parse\""; 254 break; 255 case TraceItem::TRACE_FILE_EXECUTE: 256 out << "\"file_exec\""; 257 break; 258 case TraceItem::TRACE_FILE_WRITE: 259 out << "\"file_write\""; 260 break; 261 case TraceItem::TRACE_SCRIPT_EXECUTE: 262 out << "\"script_exec\""; 263 break; 264 case TraceItem::TRACE_DEFINE_TARGET: 265 out << "\"define\""; 266 } 267 268 if (!item.toolchain().empty() || !item.cmdline().empty()) { 269 out << ",\"args\":{"; 270 bool needs_comma = false; 271 if (!item.toolchain().empty()) { 272 quote_buffer.resize(0); 273 base::EscapeJSONString(item.toolchain(), true, "e_buffer); 274 out << "\"toolchain\":" << quote_buffer; 275 needs_comma = true; 276 } 277 if (!item.cmdline().empty()) { 278 quote_buffer.resize(0); 279 base::EscapeJSONString(item.cmdline(), true, "e_buffer); 280 if (needs_comma) 281 out << ","; 282 out << "\"cmdline\":" << quote_buffer; 283 needs_comma = true; 284 } 285 out << "}"; 286 } 287 out << "}"; 288 } 289 290 out << "]}"; 291 292 std::string out_str = out.str(); 293 file_util::WriteFile(file_name, out_str.data(), 294 static_cast<int>(out_str.size())); 295 } 296