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()));