blob: 3de58e7a61be58f89b063436267e2f27629bf835 [file] [log] [blame]
jbates@chromium.orge9b2d8b2012-01-13 06:41:54 +09001// Copyright (c) 2012 The Chromium Authors. All rights reserved.
jbates@chromium.orgf44a6882011-10-29 12:16:52 +09002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "base/test/trace_event_analyzer.h"
6
7#include <algorithm>
8#include <math.h>
jbates@chromium.org88295f92012-03-01 12:04:59 +09009#include <set>
jbates@chromium.orgf44a6882011-10-29 12:16:52 +090010
11#include "base/json/json_reader.h"
12#include "base/memory/scoped_ptr.h"
13#include "base/values.h"
14
15namespace trace_analyzer {
16
17// TraceEvent
18
19TraceEvent::TraceEvent()
20 : thread(0, 0),
21 timestamp(0),
wangxianzhu@chromium.orgf17afbd2013-10-21 12:51:13 +090022 duration(0),
jbates@chromium.org8bb0ee62011-12-03 02:17:38 +090023 phase(TRACE_EVENT_PHASE_BEGIN),
jbates@chromium.orgf44a6882011-10-29 12:16:52 +090024 other_event(NULL) {
25}
26
27TraceEvent::~TraceEvent() {
28}
29
30bool TraceEvent::SetFromJSON(const base::Value* event_value) {
jbates@chromium.org405fe4e2011-12-03 02:26:41 +090031 if (event_value->GetType() != base::Value::TYPE_DICTIONARY) {
32 LOG(ERROR) << "Value must be TYPE_DICTIONARY";
jbates@chromium.orgf44a6882011-10-29 12:16:52 +090033 return false;
jbates@chromium.org405fe4e2011-12-03 02:26:41 +090034 }
jbates@chromium.orgf44a6882011-10-29 12:16:52 +090035 const base::DictionaryValue* dictionary =
36 static_cast<const base::DictionaryValue*>(event_value);
37
38 std::string phase_str;
vabr@chromium.org74562432012-07-28 07:27:11 +090039 const base::DictionaryValue* args = NULL;
jbates@chromium.orgf44a6882011-10-29 12:16:52 +090040
jbates@chromium.org405fe4e2011-12-03 02:26:41 +090041 if (!dictionary->GetString("ph", &phase_str)) {
42 LOG(ERROR) << "ph is missing from TraceEvent JSON";
43 return false;
jbates@chromium.orgf44a6882011-10-29 12:16:52 +090044 }
45
jbates@chromium.org59c27602012-01-13 12:12:44 +090046 phase = *phase_str.data();
jbates@chromium.org405fe4e2011-12-03 02:26:41 +090047
wangxianzhu@chromium.orgf17afbd2013-10-21 12:51:13 +090048 bool may_have_duration = (phase == TRACE_EVENT_PHASE_COMPLETE);
jbates@chromium.org405fe4e2011-12-03 02:26:41 +090049 bool require_origin = (phase != TRACE_EVENT_PHASE_METADATA);
jbates@chromium.org88295f92012-03-01 12:04:59 +090050 bool require_id = (phase == TRACE_EVENT_PHASE_ASYNC_BEGIN ||
simonjam@chromium.org7611bfd2013-10-24 11:44:11 +090051 phase == TRACE_EVENT_PHASE_ASYNC_STEP_INTO ||
52 phase == TRACE_EVENT_PHASE_ASYNC_STEP_PAST ||
jbates@chromium.org88295f92012-03-01 12:04:59 +090053 phase == TRACE_EVENT_PHASE_ASYNC_END);
jbates@chromium.org405fe4e2011-12-03 02:26:41 +090054
55 if (require_origin && !dictionary->GetInteger("pid", &thread.process_id)) {
56 LOG(ERROR) << "pid is missing from TraceEvent JSON";
57 return false;
58 }
59 if (require_origin && !dictionary->GetInteger("tid", &thread.thread_id)) {
60 LOG(ERROR) << "tid is missing from TraceEvent JSON";
61 return false;
62 }
63 if (require_origin && !dictionary->GetDouble("ts", &timestamp)) {
64 LOG(ERROR) << "ts is missing from TraceEvent JSON";
65 return false;
66 }
wangxianzhu@chromium.orgf17afbd2013-10-21 12:51:13 +090067 if (may_have_duration) {
68 dictionary->GetDouble("dur", &duration);
69 }
jbates@chromium.org405fe4e2011-12-03 02:26:41 +090070 if (!dictionary->GetString("cat", &category)) {
71 LOG(ERROR) << "cat is missing from TraceEvent JSON";
72 return false;
73 }
74 if (!dictionary->GetString("name", &name)) {
75 LOG(ERROR) << "name is missing from TraceEvent JSON";
76 return false;
77 }
78 if (!dictionary->GetDictionary("args", &args)) {
79 LOG(ERROR) << "args is missing from TraceEvent JSON";
80 return false;
81 }
82 if (require_id && !dictionary->GetString("id", &id)) {
jbates@chromium.org88295f92012-03-01 12:04:59 +090083 LOG(ERROR) << "id is missing from ASYNC_BEGIN/ASYNC_END TraceEvent JSON";
jbates@chromium.org405fe4e2011-12-03 02:26:41 +090084 return false;
85 }
86
87 // For each argument, copy the type and create a trace_analyzer::TraceValue.
pneubeck@chromium.orga2fbefc2013-01-18 23:43:27 +090088 for (base::DictionaryValue::Iterator it(*args); !it.IsAtEnd();
89 it.Advance()) {
jbates@chromium.org405fe4e2011-12-03 02:26:41 +090090 std::string str;
91 bool boolean = false;
92 int int_num = 0;
93 double double_num = 0.0;
pkasting@chromium.org0f889132014-03-20 06:15:54 +090094 if (it.value().GetAsString(&str)) {
pneubeck@chromium.orga2fbefc2013-01-18 23:43:27 +090095 arg_strings[it.key()] = str;
pkasting@chromium.org0f889132014-03-20 06:15:54 +090096 } else if (it.value().GetAsInteger(&int_num)) {
pneubeck@chromium.orga2fbefc2013-01-18 23:43:27 +090097 arg_numbers[it.key()] = static_cast<double>(int_num);
pkasting@chromium.org0f889132014-03-20 06:15:54 +090098 } else if (it.value().GetAsBoolean(&boolean)) {
pneubeck@chromium.orga2fbefc2013-01-18 23:43:27 +090099 arg_numbers[it.key()] = static_cast<double>(boolean ? 1 : 0);
pkasting@chromium.org0f889132014-03-20 06:15:54 +0900100 } else if (it.value().GetAsDouble(&double_num)) {
pneubeck@chromium.orga2fbefc2013-01-18 23:43:27 +0900101 arg_numbers[it.key()] = double_num;
pkasting@chromium.org0f889132014-03-20 06:15:54 +0900102 } else {
ernstm@chromium.org896c50e2013-08-15 16:20:29 +0900103 LOG(WARNING) << "Value type of argument is not supported: " <<
pneubeck@chromium.orga2fbefc2013-01-18 23:43:27 +0900104 static_cast<int>(it.value().GetType());
ernstm@chromium.org896c50e2013-08-15 16:20:29 +0900105 continue; // Skip non-supported arguments.
jbates@chromium.org405fe4e2011-12-03 02:26:41 +0900106 }
107 }
108
109 return true;
jbates@chromium.orgf44a6882011-10-29 12:16:52 +0900110}
111
jbates@chromium.orgf8732bd2011-11-05 02:10:12 +0900112double TraceEvent::GetAbsTimeToOtherEvent() const {
113 return fabs(other_event->timestamp - timestamp);
jbates@chromium.orgf44a6882011-10-29 12:16:52 +0900114}
115
116bool TraceEvent::GetArgAsString(const std::string& name,
117 std::string* arg) const {
118 std::map<std::string, std::string>::const_iterator i = arg_strings.find(name);
119 if (i != arg_strings.end()) {
120 *arg = i->second;
121 return true;
122 }
123 return false;
124}
125
126bool TraceEvent::GetArgAsNumber(const std::string& name,
127 double* arg) const {
128 std::map<std::string, double>::const_iterator i = arg_numbers.find(name);
129 if (i != arg_numbers.end()) {
130 *arg = i->second;
131 return true;
132 }
133 return false;
134}
135
jbates@chromium.orgf8732bd2011-11-05 02:10:12 +0900136bool TraceEvent::HasStringArg(const std::string& name) const {
137 return (arg_strings.find(name) != arg_strings.end());
138}
139
140bool TraceEvent::HasNumberArg(const std::string& name) const {
141 return (arg_numbers.find(name) != arg_numbers.end());
142}
143
144std::string TraceEvent::GetKnownArgAsString(const std::string& name) const {
145 std::string arg_string;
pkasting@chromium.org0f889132014-03-20 06:15:54 +0900146 bool result = GetArgAsString(name, &arg_string);
147 DCHECK(result);
148 return arg_string;
jbates@chromium.orgf8732bd2011-11-05 02:10:12 +0900149}
150
151double TraceEvent::GetKnownArgAsDouble(const std::string& name) const {
pkasting@chromium.orgc117c652014-03-20 08:19:55 +0900152 double arg_double = 0;
pkasting@chromium.org0f889132014-03-20 06:15:54 +0900153 bool result = GetArgAsNumber(name, &arg_double);
154 DCHECK(result);
155 return arg_double;
jbates@chromium.orgf8732bd2011-11-05 02:10:12 +0900156}
157
158int TraceEvent::GetKnownArgAsInt(const std::string& name) const {
pkasting@chromium.orgc117c652014-03-20 08:19:55 +0900159 double arg_double = 0;
pkasting@chromium.org0f889132014-03-20 06:15:54 +0900160 bool result = GetArgAsNumber(name, &arg_double);
161 DCHECK(result);
162 return static_cast<int>(arg_double);
jbates@chromium.orgf8732bd2011-11-05 02:10:12 +0900163}
164
165bool TraceEvent::GetKnownArgAsBool(const std::string& name) const {
pkasting@chromium.orgc117c652014-03-20 08:19:55 +0900166 double arg_double = 0;
pkasting@chromium.org0f889132014-03-20 06:15:54 +0900167 bool result = GetArgAsNumber(name, &arg_double);
168 DCHECK(result);
169 return (arg_double != 0.0);
jbates@chromium.orgf8732bd2011-11-05 02:10:12 +0900170}
171
jbates@chromium.orgf44a6882011-10-29 12:16:52 +0900172// QueryNode
173
174QueryNode::QueryNode(const Query& query) : query_(query) {
175}
176
177QueryNode::~QueryNode() {
178}
179
180// Query
181
182Query::Query(TraceEventMember member)
183 : type_(QUERY_EVENT_MEMBER),
184 operator_(OP_INVALID),
185 member_(member),
186 number_(0),
187 is_pattern_(false) {
188}
189
190Query::Query(TraceEventMember member, const std::string& arg_name)
191 : type_(QUERY_EVENT_MEMBER),
192 operator_(OP_INVALID),
193 member_(member),
194 number_(0),
195 string_(arg_name),
196 is_pattern_(false) {
197}
198
199Query::Query(const Query& query)
200 : type_(query.type_),
201 operator_(query.operator_),
202 left_(query.left_),
203 right_(query.right_),
204 member_(query.member_),
205 number_(query.number_),
206 string_(query.string_),
207 is_pattern_(query.is_pattern_) {
208}
209
210Query::~Query() {
211}
212
213Query Query::String(const std::string& str) {
214 return Query(str);
215}
216
217Query Query::Double(double num) {
218 return Query(num);
219}
220
221Query Query::Int(int32 num) {
222 return Query(static_cast<double>(num));
223}
224
225Query Query::Uint(uint32 num) {
226 return Query(static_cast<double>(num));
227}
228
229Query Query::Bool(bool boolean) {
230 return Query(boolean ? 1.0 : 0.0);
231}
232
jbates@chromium.org59c27602012-01-13 12:12:44 +0900233Query Query::Phase(char phase) {
jbates@chromium.orgf44a6882011-10-29 12:16:52 +0900234 return Query(static_cast<double>(phase));
235}
236
237Query Query::Pattern(const std::string& pattern) {
238 Query query(pattern);
239 query.is_pattern_ = true;
240 return query;
241}
242
243bool Query::Evaluate(const TraceEvent& event) const {
244 // First check for values that can convert to bool.
245
246 // double is true if != 0:
247 double bool_value = 0.0;
248 bool is_bool = GetAsDouble(event, &bool_value);
249 if (is_bool)
250 return (bool_value != 0.0);
251
252 // string is true if it is non-empty:
253 std::string str_value;
254 bool is_str = GetAsString(event, &str_value);
255 if (is_str)
256 return !str_value.empty();
257
pkasting@chromium.org0f889132014-03-20 06:15:54 +0900258 DCHECK_EQ(QUERY_BOOLEAN_OPERATOR, type_)
jbates@chromium.orgf44a6882011-10-29 12:16:52 +0900259 << "Invalid query: missing boolean expression";
pkasting@chromium.org0f889132014-03-20 06:15:54 +0900260 DCHECK(left_.get());
261 DCHECK(right_.get() || is_unary_operator());
jbates@chromium.orgf44a6882011-10-29 12:16:52 +0900262
263 if (is_comparison_operator()) {
264 DCHECK(left().is_value() && right().is_value())
265 << "Invalid query: comparison operator used between event member and "
266 "value.";
267 bool compare_result = false;
268 if (CompareAsDouble(event, &compare_result))
269 return compare_result;
pkasting@chromium.org0f889132014-03-20 06:15:54 +0900270 if (CompareAsString(event, &compare_result))
jbates@chromium.orgf44a6882011-10-29 12:16:52 +0900271 return compare_result;
272 return false;
jbates@chromium.org59c27602012-01-13 12:12:44 +0900273 }
274 // It's a logical operator.
275 switch (operator_) {
276 case OP_AND:
277 return left().Evaluate(event) && right().Evaluate(event);
278 case OP_OR:
279 return left().Evaluate(event) || right().Evaluate(event);
280 case OP_NOT:
281 return !left().Evaluate(event);
282 default:
283 NOTREACHED();
pkasting@chromium.orgc117c652014-03-20 08:19:55 +0900284 return false;
jbates@chromium.orgf44a6882011-10-29 12:16:52 +0900285 }
jbates@chromium.orgf44a6882011-10-29 12:16:52 +0900286}
287
288bool Query::CompareAsDouble(const TraceEvent& event, bool* result) const {
289 double lhs, rhs;
290 if (!left().GetAsDouble(event, &lhs) || !right().GetAsDouble(event, &rhs))
291 return false;
292 switch (operator_) {
293 case OP_EQ:
294 *result = (lhs == rhs);
295 return true;
296 case OP_NE:
297 *result = (lhs != rhs);
298 return true;
299 case OP_LT:
300 *result = (lhs < rhs);
301 return true;
302 case OP_LE:
303 *result = (lhs <= rhs);
304 return true;
305 case OP_GT:
306 *result = (lhs > rhs);
307 return true;
308 case OP_GE:
309 *result = (lhs >= rhs);
310 return true;
311 default:
312 NOTREACHED();
313 return false;
314 }
jbates@chromium.orgf44a6882011-10-29 12:16:52 +0900315}
316
317bool Query::CompareAsString(const TraceEvent& event, bool* result) const {
318 std::string lhs, rhs;
319 if (!left().GetAsString(event, &lhs) || !right().GetAsString(event, &rhs))
320 return false;
321 switch (operator_) {
322 case OP_EQ:
323 if (right().is_pattern_)
324 *result = MatchPattern(lhs, rhs);
325 else if (left().is_pattern_)
326 *result = MatchPattern(rhs, lhs);
327 else
328 *result = (lhs == rhs);
329 return true;
330 case OP_NE:
331 if (right().is_pattern_)
332 *result = !MatchPattern(lhs, rhs);
333 else if (left().is_pattern_)
334 *result = !MatchPattern(rhs, lhs);
335 else
336 *result = (lhs != rhs);
337 return true;
338 case OP_LT:
339 *result = (lhs < rhs);
340 return true;
341 case OP_LE:
342 *result = (lhs <= rhs);
343 return true;
344 case OP_GT:
345 *result = (lhs > rhs);
346 return true;
347 case OP_GE:
348 *result = (lhs >= rhs);
349 return true;
350 default:
351 NOTREACHED();
352 return false;
353 }
jbates@chromium.orgf44a6882011-10-29 12:16:52 +0900354}
355
356bool Query::EvaluateArithmeticOperator(const TraceEvent& event,
357 double* num) const {
pkasting@chromium.org0f889132014-03-20 06:15:54 +0900358 DCHECK_EQ(QUERY_ARITHMETIC_OPERATOR, type_);
359 DCHECK(left_.get());
360 DCHECK(right_.get() || is_unary_operator());
jbates@chromium.orgf44a6882011-10-29 12:16:52 +0900361
362 double lhs = 0, rhs = 0;
363 if (!left().GetAsDouble(event, &lhs))
364 return false;
365 if (!is_unary_operator() && !right().GetAsDouble(event, &rhs))
366 return false;
367
368 switch (operator_) {
369 case OP_ADD:
370 *num = lhs + rhs;
371 return true;
372 case OP_SUB:
373 *num = lhs - rhs;
374 return true;
375 case OP_MUL:
376 *num = lhs * rhs;
377 return true;
378 case OP_DIV:
379 *num = lhs / rhs;
380 return true;
381 case OP_MOD:
382 *num = static_cast<double>(static_cast<int64>(lhs) %
383 static_cast<int64>(rhs));
384 return true;
385 case OP_NEGATE:
386 *num = -lhs;
387 return true;
388 default:
389 NOTREACHED();
390 return false;
391 }
392}
393
394bool Query::GetAsDouble(const TraceEvent& event, double* num) const {
jbates@chromium.orgf44a6882011-10-29 12:16:52 +0900395 switch (type_) {
396 case QUERY_ARITHMETIC_OPERATOR:
397 return EvaluateArithmeticOperator(event, num);
398 case QUERY_EVENT_MEMBER:
jbates@chromium.org59c27602012-01-13 12:12:44 +0900399 return GetMemberValueAsDouble(event, num);
jbates@chromium.orgf44a6882011-10-29 12:16:52 +0900400 case QUERY_NUMBER:
401 *num = number_;
402 return true;
403 default:
404 return false;
405 }
406}
407
408bool Query::GetAsString(const TraceEvent& event, std::string* str) const {
jbates@chromium.orgf44a6882011-10-29 12:16:52 +0900409 switch (type_) {
410 case QUERY_EVENT_MEMBER:
jbates@chromium.org59c27602012-01-13 12:12:44 +0900411 return GetMemberValueAsString(event, str);
jbates@chromium.orgf44a6882011-10-29 12:16:52 +0900412 case QUERY_STRING:
413 *str = string_;
414 return true;
415 default:
416 return false;
417 }
418}
419
jbates@chromium.org59c27602012-01-13 12:12:44 +0900420bool Query::GetMemberValueAsDouble(const TraceEvent& event,
421 double* num) const {
pkasting@chromium.org0f889132014-03-20 06:15:54 +0900422 DCHECK_EQ(QUERY_EVENT_MEMBER, type_);
jbates@chromium.orgf44a6882011-10-29 12:16:52 +0900423
424 // This could be a request for a member of |event| or a member of |event|'s
425 // associated event. Store the target event in the_event:
426 const TraceEvent* the_event = (member_ < OTHER_PID) ?
427 &event : event.other_event;
428
429 // Request for member of associated event, but there is no associated event.
430 if (!the_event)
jbates@chromium.org59c27602012-01-13 12:12:44 +0900431 return false;
jbates@chromium.orgf44a6882011-10-29 12:16:52 +0900432
433 switch (member_) {
434 case EVENT_PID:
435 case OTHER_PID:
jbates@chromium.org59c27602012-01-13 12:12:44 +0900436 *num = static_cast<double>(the_event->thread.process_id);
437 return true;
jbates@chromium.orgf44a6882011-10-29 12:16:52 +0900438 case EVENT_TID:
439 case OTHER_TID:
jbates@chromium.org59c27602012-01-13 12:12:44 +0900440 *num = static_cast<double>(the_event->thread.thread_id);
441 return true;
jbates@chromium.orgf44a6882011-10-29 12:16:52 +0900442 case EVENT_TIME:
443 case OTHER_TIME:
jbates@chromium.org59c27602012-01-13 12:12:44 +0900444 *num = the_event->timestamp;
445 return true;
jbates@chromium.orgf44a6882011-10-29 12:16:52 +0900446 case EVENT_DURATION:
pkasting@chromium.org0f889132014-03-20 06:15:54 +0900447 if (!the_event->has_other_event())
448 return false;
449 *num = the_event->GetAbsTimeToOtherEvent();
450 return true;
wangxianzhu@chromium.orgf17afbd2013-10-21 12:51:13 +0900451 case EVENT_COMPLETE_DURATION:
pkasting@chromium.org0f889132014-03-20 06:15:54 +0900452 if (the_event->phase != TRACE_EVENT_PHASE_COMPLETE)
453 return false;
454 *num = the_event->duration;
455 return true;
jbates@chromium.orgf44a6882011-10-29 12:16:52 +0900456 case EVENT_PHASE:
457 case OTHER_PHASE:
jbates@chromium.org59c27602012-01-13 12:12:44 +0900458 *num = static_cast<double>(the_event->phase);
459 return true;
jbates@chromium.orgf8732bd2011-11-05 02:10:12 +0900460 case EVENT_HAS_STRING_ARG:
461 case OTHER_HAS_STRING_ARG:
jbates@chromium.org59c27602012-01-13 12:12:44 +0900462 *num = (the_event->HasStringArg(string_) ? 1.0 : 0.0);
463 return true;
jbates@chromium.orgf8732bd2011-11-05 02:10:12 +0900464 case EVENT_HAS_NUMBER_ARG:
465 case OTHER_HAS_NUMBER_ARG:
jbates@chromium.org59c27602012-01-13 12:12:44 +0900466 *num = (the_event->HasNumberArg(string_) ? 1.0 : 0.0);
467 return true;
jbates@chromium.orgf44a6882011-10-29 12:16:52 +0900468 case EVENT_ARG:
jbates@chromium.org59c27602012-01-13 12:12:44 +0900469 case OTHER_ARG: {
470 // Search for the argument name and return its value if found.
471 std::map<std::string, double>::const_iterator num_i =
472 the_event->arg_numbers.find(string_);
473 if (num_i == the_event->arg_numbers.end())
474 return false;
475 *num = num_i->second;
476 return true;
477 }
jbates@chromium.orgf44a6882011-10-29 12:16:52 +0900478 case EVENT_HAS_OTHER:
jbates@chromium.org59c27602012-01-13 12:12:44 +0900479 // return 1.0 (true) if the other event exists
480 *num = event.other_event ? 1.0 : 0.0;
481 return true;
jbates@chromium.orgf44a6882011-10-29 12:16:52 +0900482 default:
jbates@chromium.org59c27602012-01-13 12:12:44 +0900483 return false;
484 }
485}
486
487bool Query::GetMemberValueAsString(const TraceEvent& event,
488 std::string* str) const {
pkasting@chromium.org0f889132014-03-20 06:15:54 +0900489 DCHECK_EQ(QUERY_EVENT_MEMBER, type_);
jbates@chromium.org59c27602012-01-13 12:12:44 +0900490
491 // This could be a request for a member of |event| or a member of |event|'s
492 // associated event. Store the target event in the_event:
493 const TraceEvent* the_event = (member_ < OTHER_PID) ?
494 &event : event.other_event;
495
496 // Request for member of associated event, but there is no associated event.
497 if (!the_event)
498 return false;
499
500 switch (member_) {
501 case EVENT_CATEGORY:
502 case OTHER_CATEGORY:
503 *str = the_event->category;
504 return true;
505 case EVENT_NAME:
506 case OTHER_NAME:
507 *str = the_event->name;
508 return true;
509 case EVENT_ID:
510 case OTHER_ID:
511 *str = the_event->id;
512 return true;
513 case EVENT_ARG:
514 case OTHER_ARG: {
515 // Search for the argument name and return its value if found.
516 std::map<std::string, std::string>::const_iterator str_i =
517 the_event->arg_strings.find(string_);
518 if (str_i == the_event->arg_strings.end())
519 return false;
520 *str = str_i->second;
521 return true;
522 }
523 default:
524 return false;
jbates@chromium.orgf44a6882011-10-29 12:16:52 +0900525 }
526}
527
528Query::Query(const std::string& str)
529 : type_(QUERY_STRING),
530 operator_(OP_INVALID),
531 member_(EVENT_INVALID),
532 number_(0),
533 string_(str),
534 is_pattern_(false) {
535}
536
537Query::Query(double num)
538 : type_(QUERY_NUMBER),
539 operator_(OP_INVALID),
540 member_(EVENT_INVALID),
541 number_(num),
542 is_pattern_(false) {
543}
544const Query& Query::left() const {
545 return left_->query();
546}
547
548const Query& Query::right() const {
549 return right_->query();
550}
551
552Query Query::operator==(const Query& rhs) const {
553 return Query(*this, rhs, OP_EQ);
554}
555
556Query Query::operator!=(const Query& rhs) const {
557 return Query(*this, rhs, OP_NE);
558}
559
560Query Query::operator<(const Query& rhs) const {
561 return Query(*this, rhs, OP_LT);
562}
563
564Query Query::operator<=(const Query& rhs) const {
565 return Query(*this, rhs, OP_LE);
566}
567
568Query Query::operator>(const Query& rhs) const {
569 return Query(*this, rhs, OP_GT);
570}
571
572Query Query::operator>=(const Query& rhs) const {
573 return Query(*this, rhs, OP_GE);
574}
575
576Query Query::operator&&(const Query& rhs) const {
577 return Query(*this, rhs, OP_AND);
578}
579
580Query Query::operator||(const Query& rhs) const {
581 return Query(*this, rhs, OP_OR);
582}
583
584Query Query::operator!() const {
585 return Query(*this, OP_NOT);
586}
587
588Query Query::operator+(const Query& rhs) const {
589 return Query(*this, rhs, OP_ADD);
590}
591
592Query Query::operator-(const Query& rhs) const {
593 return Query(*this, rhs, OP_SUB);
594}
595
596Query Query::operator*(const Query& rhs) const {
597 return Query(*this, rhs, OP_MUL);
598}
599
600Query Query::operator/(const Query& rhs) const {
601 return Query(*this, rhs, OP_DIV);
602}
603
604Query Query::operator%(const Query& rhs) const {
605 return Query(*this, rhs, OP_MOD);
606}
607
608Query Query::operator-() const {
609 return Query(*this, OP_NEGATE);
610}
611
612
613Query::Query(const Query& left, const Query& right, Operator binary_op)
614 : operator_(binary_op),
615 left_(new QueryNode(left)),
616 right_(new QueryNode(right)),
617 member_(EVENT_INVALID),
618 number_(0) {
619 type_ = (binary_op < OP_ADD ?
620 QUERY_BOOLEAN_OPERATOR : QUERY_ARITHMETIC_OPERATOR);
621}
622
623Query::Query(const Query& left, Operator unary_op)
624 : operator_(unary_op),
625 left_(new QueryNode(left)),
626 member_(EVENT_INVALID),
627 number_(0) {
628 type_ = (unary_op < OP_ADD ?
629 QUERY_BOOLEAN_OPERATOR : QUERY_ARITHMETIC_OPERATOR);
630}
631
632namespace {
633
634// Search |events| for |query| and add matches to |output|.
635size_t FindMatchingEvents(const std::vector<TraceEvent>& events,
636 const Query& query,
nduca@chromium.org78d54712014-05-24 11:25:20 +0900637 TraceEventVector* output,
638 bool ignore_metadata_events) {
jbates@chromium.orgf44a6882011-10-29 12:16:52 +0900639 for (size_t i = 0; i < events.size(); ++i) {
nduca@chromium.org78d54712014-05-24 11:25:20 +0900640 if (ignore_metadata_events && events[i].phase == TRACE_EVENT_PHASE_METADATA)
641 continue;
jbates@chromium.orgf44a6882011-10-29 12:16:52 +0900642 if (query.Evaluate(events[i]))
643 output->push_back(&events[i]);
644 }
645 return output->size();
646}
647
648bool ParseEventsFromJson(const std::string& json,
649 std::vector<TraceEvent>* output) {
650 scoped_ptr<base::Value> root;
rsesek@chromium.orgdc692f52012-04-11 09:15:41 +0900651 root.reset(base::JSONReader::Read(json));
jbates@chromium.orgf44a6882011-10-29 12:16:52 +0900652
brettw@chromium.org88aa6552013-06-15 02:56:08 +0900653 base::ListValue* root_list = NULL;
jbates@chromium.orgf44a6882011-10-29 12:16:52 +0900654 if (!root.get() || !root->GetAsList(&root_list))
655 return false;
656
657 for (size_t i = 0; i < root_list->GetSize(); ++i) {
brettw@chromium.org88aa6552013-06-15 02:56:08 +0900658 base::Value* item = NULL;
jbates@chromium.orgf44a6882011-10-29 12:16:52 +0900659 if (root_list->Get(i, &item)) {
660 TraceEvent event;
661 if (event.SetFromJSON(item))
662 output->push_back(event);
663 else
664 return false;
665 }
666 }
667
668 return true;
669}
670
671} // namespace
672
673// TraceAnalyzer
674
nduca@chromium.org78d54712014-05-24 11:25:20 +0900675TraceAnalyzer::TraceAnalyzer()
676 : ignore_metadata_events_(false),
677 allow_assocation_changes_(true) {}
jbates@chromium.orgf44a6882011-10-29 12:16:52 +0900678
679TraceAnalyzer::~TraceAnalyzer() {
680}
681
682// static
683TraceAnalyzer* TraceAnalyzer::Create(const std::string& json_events) {
684 scoped_ptr<TraceAnalyzer> analyzer(new TraceAnalyzer());
685 if (analyzer->SetEvents(json_events))
686 return analyzer.release();
687 return NULL;
688}
689
690bool TraceAnalyzer::SetEvents(const std::string& json_events) {
691 raw_events_.clear();
692 if (!ParseEventsFromJson(json_events, &raw_events_))
693 return false;
694 std::stable_sort(raw_events_.begin(), raw_events_.end());
695 ParseMetadata();
696 return true;
697}
698
699void TraceAnalyzer::AssociateBeginEndEvents() {
jbates@chromium.orge9b2d8b2012-01-13 06:41:54 +0900700 using trace_analyzer::Query;
jbates@chromium.orgf44a6882011-10-29 12:16:52 +0900701
jbates@chromium.orgd9354e42012-09-14 03:04:29 +0900702 Query begin(Query::EventPhaseIs(TRACE_EVENT_PHASE_BEGIN));
703 Query end(Query::EventPhaseIs(TRACE_EVENT_PHASE_END));
jbates@chromium.orge9b2d8b2012-01-13 06:41:54 +0900704 Query match(Query::EventName() == Query::OtherName() &&
705 Query::EventCategory() == Query::OtherCategory() &&
706 Query::EventTid() == Query::OtherTid() &&
707 Query::EventPid() == Query::OtherPid());
jbates@chromium.orgf44a6882011-10-29 12:16:52 +0900708
709 AssociateEvents(begin, end, match);
710}
711
jbates@chromium.org88295f92012-03-01 12:04:59 +0900712void TraceAnalyzer::AssociateAsyncBeginEndEvents() {
jbates@chromium.orge9b2d8b2012-01-13 06:41:54 +0900713 using trace_analyzer::Query;
jbates@chromium.org405fe4e2011-12-03 02:26:41 +0900714
jbates@chromium.org88295f92012-03-01 12:04:59 +0900715 Query begin(
jbates@chromium.orgd9354e42012-09-14 03:04:29 +0900716 Query::EventPhaseIs(TRACE_EVENT_PHASE_ASYNC_BEGIN) ||
simonjam@chromium.org7611bfd2013-10-24 11:44:11 +0900717 Query::EventPhaseIs(TRACE_EVENT_PHASE_ASYNC_STEP_INTO) ||
718 Query::EventPhaseIs(TRACE_EVENT_PHASE_ASYNC_STEP_PAST));
jbates@chromium.orgd9354e42012-09-14 03:04:29 +0900719 Query end(Query::EventPhaseIs(TRACE_EVENT_PHASE_ASYNC_END) ||
simonjam@chromium.org7611bfd2013-10-24 11:44:11 +0900720 Query::EventPhaseIs(TRACE_EVENT_PHASE_ASYNC_STEP_INTO) ||
721 Query::EventPhaseIs(TRACE_EVENT_PHASE_ASYNC_STEP_PAST));
jbates@chromium.orge9b2d8b2012-01-13 06:41:54 +0900722 Query match(Query::EventName() == Query::OtherName() &&
723 Query::EventCategory() == Query::OtherCategory() &&
724 Query::EventId() == Query::OtherId());
jbates@chromium.org405fe4e2011-12-03 02:26:41 +0900725
726 AssociateEvents(begin, end, match);
727}
728
jbates@chromium.orgf44a6882011-10-29 12:16:52 +0900729void TraceAnalyzer::AssociateEvents(const Query& first,
730 const Query& second,
731 const Query& match) {
pkasting@chromium.org0f889132014-03-20 06:15:54 +0900732 DCHECK(allow_assocation_changes_)
733 << "AssociateEvents not allowed after FindEvents";
jbates@chromium.orgf44a6882011-10-29 12:16:52 +0900734
735 // Search for matching begin/end event pairs. When a matching end is found,
736 // it is associated with the begin event.
737 std::vector<TraceEvent*> begin_stack;
738 for (size_t event_index = 0; event_index < raw_events_.size();
739 ++event_index) {
740
741 TraceEvent& this_event = raw_events_[event_index];
742
jbates@chromium.org88295f92012-03-01 12:04:59 +0900743 if (second.Evaluate(this_event)) {
jbates@chromium.orgf44a6882011-10-29 12:16:52 +0900744 // Search stack for matching begin, starting from end.
745 for (int stack_index = static_cast<int>(begin_stack.size()) - 1;
746 stack_index >= 0; --stack_index) {
747 TraceEvent& begin_event = *begin_stack[stack_index];
748
749 // Temporarily set other to test against the match query.
750 const TraceEvent* other_backup = begin_event.other_event;
751 begin_event.other_event = &this_event;
752 if (match.Evaluate(begin_event)) {
753 // Found a matching begin/end pair.
jbates@chromium.orgf44a6882011-10-29 12:16:52 +0900754 // Erase the matching begin event index from the stack.
755 begin_stack.erase(begin_stack.begin() + stack_index);
756 break;
757 }
758
759 // Not a match, restore original other and continue.
760 begin_event.other_event = other_backup;
761 }
762 }
jbates@chromium.org88295f92012-03-01 12:04:59 +0900763 // Even if this_event is a |second| event that has matched an earlier
764 // |first| event, it can still also be a |first| event and be associated
765 // with a later |second| event.
766 if (first.Evaluate(this_event)) {
767 begin_stack.push_back(&this_event);
768 }
jbates@chromium.orgf44a6882011-10-29 12:16:52 +0900769 }
770}
771
jbates@chromium.org33cb23c2011-12-15 07:21:38 +0900772void TraceAnalyzer::MergeAssociatedEventArgs() {
773 for (size_t i = 0; i < raw_events_.size(); ++i) {
jbates@chromium.org88295f92012-03-01 12:04:59 +0900774 // Merge all associated events with the first event.
775 const TraceEvent* other = raw_events_[i].other_event;
776 // Avoid looping by keeping set of encountered TraceEvents.
777 std::set<const TraceEvent*> encounters;
778 encounters.insert(&raw_events_[i]);
779 while (other && encounters.find(other) == encounters.end()) {
780 encounters.insert(other);
jbates@chromium.org33cb23c2011-12-15 07:21:38 +0900781 raw_events_[i].arg_numbers.insert(
jbates@chromium.org88295f92012-03-01 12:04:59 +0900782 other->arg_numbers.begin(),
783 other->arg_numbers.end());
jbates@chromium.org33cb23c2011-12-15 07:21:38 +0900784 raw_events_[i].arg_strings.insert(
jbates@chromium.org88295f92012-03-01 12:04:59 +0900785 other->arg_strings.begin(),
786 other->arg_strings.end());
787 other = other->other_event;
jbates@chromium.org33cb23c2011-12-15 07:21:38 +0900788 }
789 }
790}
791
jbates@chromium.orgf44a6882011-10-29 12:16:52 +0900792size_t TraceAnalyzer::FindEvents(const Query& query, TraceEventVector* output) {
793 allow_assocation_changes_ = false;
794 output->clear();
nduca@chromium.org78d54712014-05-24 11:25:20 +0900795 return FindMatchingEvents(
796 raw_events_, query, output, ignore_metadata_events_);
jbates@chromium.orgf44a6882011-10-29 12:16:52 +0900797}
798
jbates@chromium.orgd9354e42012-09-14 03:04:29 +0900799const TraceEvent* TraceAnalyzer::FindFirstOf(const Query& query) {
jbates@chromium.orgf44a6882011-10-29 12:16:52 +0900800 TraceEventVector output;
801 if (FindEvents(query, &output) > 0)
802 return output.front();
803 return NULL;
804}
805
jbates@chromium.orgd9354e42012-09-14 03:04:29 +0900806const TraceEvent* TraceAnalyzer::FindLastOf(const Query& query) {
807 TraceEventVector output;
808 if (FindEvents(query, &output) > 0)
809 return output.back();
810 return NULL;
811}
812
jbates@chromium.orgf44a6882011-10-29 12:16:52 +0900813const std::string& TraceAnalyzer::GetThreadName(
814 const TraceEvent::ProcessThreadID& thread) {
815 // If thread is not found, just add and return empty string.
816 return thread_names_[thread];
817}
818
jbates@chromium.org33cb23c2011-12-15 07:21:38 +0900819void TraceAnalyzer::ParseMetadata() {
820 for (size_t i = 0; i < raw_events_.size(); ++i) {
821 TraceEvent& this_event = raw_events_[i];
822 // Check for thread name metadata.
823 if (this_event.phase != TRACE_EVENT_PHASE_METADATA ||
824 this_event.name != "thread_name")
825 continue;
826 std::map<std::string, std::string>::const_iterator string_it =
827 this_event.arg_strings.find("name");
828 if (string_it != this_event.arg_strings.end())
829 thread_names_[this_event.thread] = string_it->second;
830 }
831}
832
833// TraceEventVector utility functions.
834
jbates@chromium.org9af00eb2012-03-10 05:43:12 +0900835bool GetRateStats(const TraceEventVector& events,
836 RateStats* stats,
837 const RateStatsOptions* options) {
pkasting@chromium.org0f889132014-03-20 06:15:54 +0900838 DCHECK(stats);
jbates@chromium.orgcda3b0f2011-12-01 09:39:23 +0900839 // Need at least 3 events to calculate rate stats.
jbates@chromium.org9af00eb2012-03-10 05:43:12 +0900840 const size_t kMinEvents = 3;
841 if (events.size() < kMinEvents) {
jbates@chromium.orgcda3b0f2011-12-01 09:39:23 +0900842 LOG(ERROR) << "Not enough events: " << events.size();
843 return false;
844 }
845
846 std::vector<double> deltas;
jbates@chromium.orgcda3b0f2011-12-01 09:39:23 +0900847 size_t num_deltas = events.size() - 1;
848 for (size_t i = 0; i < num_deltas; ++i) {
jbates@chromium.org33cb23c2011-12-15 07:21:38 +0900849 double delta = events.at(i + 1)->timestamp - events.at(i)->timestamp;
jbates@chromium.orgcda3b0f2011-12-01 09:39:23 +0900850 if (delta < 0.0) {
851 LOG(ERROR) << "Events are out of order";
852 return false;
853 }
854 deltas.push_back(delta);
jbates@chromium.orgcda3b0f2011-12-01 09:39:23 +0900855 }
856
jbates@chromium.org9af00eb2012-03-10 05:43:12 +0900857 std::sort(deltas.begin(), deltas.end());
858
859 if (options) {
860 if (options->trim_min + options->trim_max > events.size() - kMinEvents) {
861 LOG(ERROR) << "Attempt to trim too many events";
862 return false;
863 }
864 deltas.erase(deltas.begin(), deltas.begin() + options->trim_min);
865 deltas.erase(deltas.end() - options->trim_max, deltas.end());
866 }
867
868 num_deltas = deltas.size();
869 double delta_sum = 0.0;
870 for (size_t i = 0; i < num_deltas; ++i)
871 delta_sum += deltas[i];
872
jbates@chromium.orgcda3b0f2011-12-01 09:39:23 +0900873 stats->min_us = *std::min_element(deltas.begin(), deltas.end());
874 stats->max_us = *std::max_element(deltas.begin(), deltas.end());
875 stats->mean_us = delta_sum / static_cast<double>(num_deltas);
876
877 double sum_mean_offsets_squared = 0.0;
878 for (size_t i = 0; i < num_deltas; ++i) {
879 double offset = fabs(deltas[i] - stats->mean_us);
880 sum_mean_offsets_squared += offset * offset;
881 }
882 stats->standard_deviation_us =
hubbe@chromium.org4de80842013-08-20 18:56:02 +0900883 sqrt(sum_mean_offsets_squared / static_cast<double>(num_deltas - 1));
jbates@chromium.orgcda3b0f2011-12-01 09:39:23 +0900884
885 return true;
886}
887
jbates@chromium.org33cb23c2011-12-15 07:21:38 +0900888bool FindFirstOf(const TraceEventVector& events,
889 const Query& query,
890 size_t position,
891 size_t* return_index) {
pkasting@chromium.org0f889132014-03-20 06:15:54 +0900892 DCHECK(return_index);
jbates@chromium.org33cb23c2011-12-15 07:21:38 +0900893 for (size_t i = position; i < events.size(); ++i) {
pkasting@chromium.org0f889132014-03-20 06:15:54 +0900894 if (query.Evaluate(*events[i])) {
jbates@chromium.org33cb23c2011-12-15 07:21:38 +0900895 *return_index = i;
896 return true;
897 }
jbates@chromium.orgf44a6882011-10-29 12:16:52 +0900898 }
jbates@chromium.org33cb23c2011-12-15 07:21:38 +0900899 return false;
900}
901
902bool FindLastOf(const TraceEventVector& events,
903 const Query& query,
904 size_t position,
905 size_t* return_index) {
pkasting@chromium.org0f889132014-03-20 06:15:54 +0900906 DCHECK(return_index);
pkasting@chromium.orgc117c652014-03-20 08:19:55 +0900907 for (size_t i = std::min(position + 1, events.size()); i != 0; --i) {
908 if (query.Evaluate(*events[i - 1])) {
909 *return_index = i - 1;
jbates@chromium.org33cb23c2011-12-15 07:21:38 +0900910 return true;
911 }
jbates@chromium.org33cb23c2011-12-15 07:21:38 +0900912 }
913 return false;
914}
915
916bool FindClosest(const TraceEventVector& events,
917 const Query& query,
918 size_t position,
919 size_t* return_closest,
920 size_t* return_second_closest) {
pkasting@chromium.org0f889132014-03-20 06:15:54 +0900921 DCHECK(return_closest);
jbates@chromium.org33cb23c2011-12-15 07:21:38 +0900922 if (events.empty() || position >= events.size())
923 return false;
924 size_t closest = events.size();
925 size_t second_closest = events.size();
926 for (size_t i = 0; i < events.size(); ++i) {
927 if (!query.Evaluate(*events.at(i)))
928 continue;
929 if (closest == events.size()) {
930 closest = i;
931 continue;
932 }
933 if (fabs(events.at(i)->timestamp - events.at(position)->timestamp) <
934 fabs(events.at(closest)->timestamp - events.at(position)->timestamp)) {
935 second_closest = closest;
936 closest = i;
937 } else if (second_closest == events.size()) {
938 second_closest = i;
939 }
940 }
941
942 if (closest < events.size() &&
943 (!return_second_closest || second_closest < events.size())) {
944 *return_closest = closest;
945 if (return_second_closest)
946 *return_second_closest = second_closest;
947 return true;
948 }
949
950 return false;
951}
952
953size_t CountMatches(const TraceEventVector& events,
954 const Query& query,
955 size_t begin_position,
956 size_t end_position) {
957 if (begin_position >= events.size())
958 return 0u;
959 end_position = (end_position < events.size()) ? end_position : events.size();
960 size_t count = 0u;
961 for (size_t i = begin_position; i < end_position; ++i) {
962 if (query.Evaluate(*events.at(i)))
963 ++count;
964 }
965 return count;
jbates@chromium.orgf44a6882011-10-29 12:16:52 +0900966}
967
968} // namespace trace_analyzer