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.
5 #include "tools/gn/trace.h"
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"
25 events_.reserve(16384);
27 // Trace items leaked intentionally.
29 void Add(TraceItem* item) {
30 base::AutoLock lock(lock_);
31 events_.push_back(item);
34 // Returns a copy for threadsafety.
35 std::vector<TraceItem*> events() const { return events_; }
40 std::vector<TraceItem*> events_;
42 DISALLOW_COPY_AND_ASSIGN(TraceLog);
45 TraceLog* trace_log = NULL;
48 Coalesced() : name_ptr(NULL), total_duration(0.0), count(0) {}
50 const std::string* name_ptr; // Pointer to a string with the name in it.
51 double total_duration;
55 bool DurationGreater(const TraceItem* a, const TraceItem* b) {
56 return a->delta() > b->delta();
59 bool CoalescedDurationGreater(const Coalesced& a, const Coalesced& b) {
60 return a.total_duration > b.total_duration;
63 void SummarizeParses(std::vector<const TraceItem*>& loads,
65 out << "File parse times: (time in ms, name)\n";
67 std::sort(loads.begin(), loads.end(), &DurationGreater);
69 for (size_t i = 0; i < loads.size(); i++) {
70 out << base::StringPrintf(" %8.2f ",
71 loads[i]->delta().InMillisecondsF());
72 out << loads[i]->name() << std::endl;
76 void SummarizeCoalesced(std::vector<const TraceItem*>& items,
78 // Group by file name.
79 std::map<std::string, Coalesced> coalesced;
80 for (size_t i = 0; i < items.size(); i++) {
81 Coalesced& c = coalesced[items[i]->name()];
82 c.name_ptr = &items[i]->name();
83 c.total_duration += items[i]->delta().InMillisecondsF();
88 std::vector<Coalesced> sorted;
89 for (std::map<std::string, Coalesced>::iterator iter = coalesced.begin();
90 iter != coalesced.end(); ++iter)
91 sorted.push_back(iter->second);
92 std::sort(sorted.begin(), sorted.end(), &CoalescedDurationGreater);
94 for (size_t i = 0; i < sorted.size(); i++) {
95 out << base::StringPrintf(" %8.2f %d ",
96 sorted[i].total_duration, sorted[i].count);
97 out << *sorted[i].name_ptr << std::endl;
101 void SummarizeFileExecs(std::vector<const TraceItem*>& execs,
103 out << "File execute times: (total time in ms, # executions, name)\n";
104 SummarizeCoalesced(execs, out);
107 void SummarizeScriptExecs(std::vector<const TraceItem*>& execs,
109 out << "Script execute times: (total time in ms, # executions, name)\n";
110 SummarizeCoalesced(execs, out);
115 TraceItem::TraceItem(Type type,
116 const std::string& name,
117 base::PlatformThreadId thread_id)
120 thread_id_(thread_id) {
123 TraceItem::~TraceItem() {
126 ScopedTrace::ScopedTrace(TraceItem::Type t, const std::string& name)
130 item_ = new TraceItem(t, name, base::PlatformThread::CurrentId());
131 item_->set_begin(base::TimeTicks::HighResNow());
135 ScopedTrace::ScopedTrace(TraceItem::Type t, const Label& label)
139 item_ = new TraceItem(t, label.GetUserVisibleName(false),
140 base::PlatformThread::CurrentId());
141 item_->set_begin(base::TimeTicks::HighResNow());
145 ScopedTrace::~ScopedTrace() {
149 void ScopedTrace::SetToolchain(const Label& label) {
151 item_->set_toolchain(label.GetUserVisibleName(false));
154 void ScopedTrace::SetCommandLine(const CommandLine& cmdline) {
156 item_->set_cmdline(FilePathToUTF8(cmdline.GetArgumentsString()));
159 void ScopedTrace::Done() {
163 item_->set_end(base::TimeTicks::HighResNow());
169 void EnableTracing() {
171 trace_log = new TraceLog;
174 void AddTrace(TraceItem* item) {
175 trace_log->Add(item);
178 std::string SummarizeTraces() {
180 return std::string();
182 std::vector<TraceItem*> events = trace_log->events();
184 // Classify all events.
185 std::vector<const TraceItem*> parses;
186 std::vector<const TraceItem*> file_execs;
187 std::vector<const TraceItem*> script_execs;
188 std::vector<const TraceItem*> check_headers;
189 int headers_checked = 0;
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]);
195 case TraceItem::TRACE_FILE_EXECUTE:
196 file_execs.push_back(events[i]);
198 case TraceItem::TRACE_SCRIPT_EXECUTE:
199 script_execs.push_back(events[i]);
201 case TraceItem::TRACE_CHECK_HEADERS:
202 check_headers.push_back(events[i]);
204 case TraceItem::TRACE_CHECK_HEADER:
207 case TraceItem::TRACE_SETUP:
208 case TraceItem::TRACE_FILE_LOAD:
209 case TraceItem::TRACE_FILE_WRITE:
210 case TraceItem::TRACE_DEFINE_TARGET:
211 break; // Ignore these for the summary.
215 std::ostringstream out;
216 SummarizeParses(parses, out);
218 SummarizeFileExecs(file_execs, out);
220 SummarizeScriptExecs(script_execs, out);
223 // Generally there will only be one header check, but it's theoretically
224 // possible for more than one to run if more than one build is going in
225 // parallel. Just report the total of all of them.
226 if (!check_headers.empty()) {
227 float check_headers_time = 0;
228 for (size_t i = 0; i < check_headers.size(); i++)
229 check_headers_time += check_headers[i]->delta().InMillisecondsF();
231 out << "Header check time: (total time in ms, files checked)\n";
232 out << base::StringPrintf(" %8.2f %d\n",
233 check_headers_time, headers_checked);
239 void SaveTraces(const base::FilePath& file_name) {
240 std::ostringstream out;
242 out << "{\"traceEvents\":[";
244 std::string quote_buffer; // Allocate outside loop to prevent reallocationg.
246 // Write main thread metadata (assume this is being written on the main
248 out << "{\"pid\":0,\"tid\":" << base::PlatformThread::CurrentId();
249 out << ",\"ts\":0,\"ph\":\"M\",";
250 out << "\"name\":\"thread_name\",\"args\":{\"name\":\"Main thread\"}},";
252 std::vector<TraceItem*> events = trace_log->events();
253 for (size_t i = 0; i < events.size(); i++) {
254 const TraceItem& item = *events[i];
258 out << "{\"pid\":0,\"tid\":" << item.thread_id();
259 out << ",\"ts\":" << item.begin().ToInternalValue();
260 out << ",\"ph\":\"X\""; // "X" = complete event with begin & duration.
261 out << ",\"dur\":" << item.delta().InMicroseconds();
263 quote_buffer.resize(0);
264 base::EscapeJSONString(item.name(), true, "e_buffer);
265 out << ",\"name\":" << quote_buffer;
268 switch (item.type()) {
269 case TraceItem::TRACE_SETUP:
272 case TraceItem::TRACE_FILE_LOAD:
275 case TraceItem::TRACE_FILE_PARSE:
278 case TraceItem::TRACE_FILE_EXECUTE:
279 out << "\"file_exec\"";
281 case TraceItem::TRACE_FILE_WRITE:
282 out << "\"file_write\"";
284 case TraceItem::TRACE_SCRIPT_EXECUTE:
285 out << "\"script_exec\"";
287 case TraceItem::TRACE_DEFINE_TARGET:
290 case TraceItem::TRACE_CHECK_HEADER:
293 case TraceItem::TRACE_CHECK_HEADERS:
294 out << "\"header_check\"";
298 if (!item.toolchain().empty() || !item.cmdline().empty()) {
299 out << ",\"args\":{";
300 bool needs_comma = false;
301 if (!item.toolchain().empty()) {
302 quote_buffer.resize(0);
303 base::EscapeJSONString(item.toolchain(), true, "e_buffer);
304 out << "\"toolchain\":" << quote_buffer;
307 if (!item.cmdline().empty()) {
308 quote_buffer.resize(0);
309 base::EscapeJSONString(item.cmdline(), true, "e_buffer);
312 out << "\"cmdline\":" << quote_buffer;
322 std::string out_str = out.str();
323 base::WriteFile(file_name, out_str.data(),
324 static_cast<int>(out_str.size()));