1 // Copyright (c) 2012 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 "base/test/trace_event_analyzer.h"
6
7 #include <math.h>
8
9 #include <algorithm>
10 #include <set>
11
12 #include "base/json/json_reader.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "base/strings/pattern.h"
15 #include "base/values.h"
16
17 namespace trace_analyzer {
18
19 // TraceEvent
20
TraceEvent()21 TraceEvent::TraceEvent()
22 : thread(0, 0),
23 timestamp(0),
24 duration(0),
25 phase(TRACE_EVENT_PHASE_BEGIN),
26 other_event(NULL) {
27 }
28
~TraceEvent()29 TraceEvent::~TraceEvent() {
30 }
31
SetFromJSON(const base::Value * event_value)32 bool TraceEvent::SetFromJSON(const base::Value* event_value) {
33 if (event_value->GetType() != base::Value::TYPE_DICTIONARY) {
34 LOG(ERROR) << "Value must be TYPE_DICTIONARY";
35 return false;
36 }
37 const base::DictionaryValue* dictionary =
38 static_cast<const base::DictionaryValue*>(event_value);
39
40 std::string phase_str;
41 const base::DictionaryValue* args = NULL;
42
43 if (!dictionary->GetString("ph", &phase_str)) {
44 LOG(ERROR) << "ph is missing from TraceEvent JSON";
45 return false;
46 }
47
48 phase = *phase_str.data();
49
50 bool may_have_duration = (phase == TRACE_EVENT_PHASE_COMPLETE);
51 bool require_origin = (phase != TRACE_EVENT_PHASE_METADATA);
52 bool require_id = (phase == TRACE_EVENT_PHASE_ASYNC_BEGIN ||
53 phase == TRACE_EVENT_PHASE_ASYNC_STEP_INTO ||
54 phase == TRACE_EVENT_PHASE_ASYNC_STEP_PAST ||
55 phase == TRACE_EVENT_PHASE_ASYNC_END);
56
57 if (require_origin && !dictionary->GetInteger("pid", &thread.process_id)) {
58 LOG(ERROR) << "pid is missing from TraceEvent JSON";
59 return false;
60 }
61 if (require_origin && !dictionary->GetInteger("tid", &thread.thread_id)) {
62 LOG(ERROR) << "tid is missing from TraceEvent JSON";
63 return false;
64 }
65 if (require_origin && !dictionary->GetDouble("ts", ×tamp)) {
66 LOG(ERROR) << "ts is missing from TraceEvent JSON";
67 return false;
68 }
69 if (may_have_duration) {
70 dictionary->GetDouble("dur", &duration);
71 }
72 if (!dictionary->GetString("cat", &category)) {
73 LOG(ERROR) << "cat is missing from TraceEvent JSON";
74 return false;
75 }
76 if (!dictionary->GetString("name", &name)) {
77 LOG(ERROR) << "name is missing from TraceEvent JSON";
78 return false;
79 }
80 if (!dictionary->GetDictionary("args", &args)) {
81 LOG(ERROR) << "args is missing from TraceEvent JSON";
82 return false;
83 }
84 if (require_id && !dictionary->GetString("id", &id)) {
85 LOG(ERROR) << "id is missing from ASYNC_BEGIN/ASYNC_END TraceEvent JSON";
86 return false;
87 }
88
89 // For each argument, copy the type and create a trace_analyzer::TraceValue.
90 for (base::DictionaryValue::Iterator it(*args); !it.IsAtEnd();
91 it.Advance()) {
92 std::string str;
93 bool boolean = false;
94 int int_num = 0;
95 double double_num = 0.0;
96 if (it.value().GetAsString(&str)) {
97 arg_strings[it.key()] = str;
98 } else if (it.value().GetAsInteger(&int_num)) {
99 arg_numbers[it.key()] = static_cast<double>(int_num);
100 } else if (it.value().GetAsBoolean(&boolean)) {
101 arg_numbers[it.key()] = static_cast<double>(boolean ? 1 : 0);
102 } else if (it.value().GetAsDouble(&double_num)) {
103 arg_numbers[it.key()] = double_num;
104 } else {
105 LOG(WARNING) << "Value type of argument is not supported: " <<
106 static_cast<int>(it.value().GetType());
107 continue; // Skip non-supported arguments.
108 }
109 }
110
111 return true;
112 }
113
GetAbsTimeToOtherEvent() const114 double TraceEvent::GetAbsTimeToOtherEvent() const {
115 return fabs(other_event->timestamp - timestamp);
116 }
117
GetArgAsString(const std::string & name,std::string * arg) const118 bool TraceEvent::GetArgAsString(const std::string& name,
119 std::string* arg) const {
120 std::map<std::string, std::string>::const_iterator i = arg_strings.find(name);
121 if (i != arg_strings.end()) {
122 *arg = i->second;
123 return true;
124 }
125 return false;
126 }
127
GetArgAsNumber(const std::string & name,double * arg) const128 bool TraceEvent::GetArgAsNumber(const std::string& name,
129 double* arg) const {
130 std::map<std::string, double>::const_iterator i = arg_numbers.find(name);
131 if (i != arg_numbers.end()) {
132 *arg = i->second;
133 return true;
134 }
135 return false;
136 }
137
HasStringArg(const std::string & name) const138 bool TraceEvent::HasStringArg(const std::string& name) const {
139 return (arg_strings.find(name) != arg_strings.end());
140 }
141
HasNumberArg(const std::string & name) const142 bool TraceEvent::HasNumberArg(const std::string& name) const {
143 return (arg_numbers.find(name) != arg_numbers.end());
144 }
145
GetKnownArgAsString(const std::string & name) const146 std::string TraceEvent::GetKnownArgAsString(const std::string& name) const {
147 std::string arg_string;
148 bool result = GetArgAsString(name, &arg_string);
149 DCHECK(result);
150 return arg_string;
151 }
152
GetKnownArgAsDouble(const std::string & name) const153 double TraceEvent::GetKnownArgAsDouble(const std::string& name) const {
154 double arg_double = 0;
155 bool result = GetArgAsNumber(name, &arg_double);
156 DCHECK(result);
157 return arg_double;
158 }
159
GetKnownArgAsInt(const std::string & name) const160 int TraceEvent::GetKnownArgAsInt(const std::string& name) const {
161 double arg_double = 0;
162 bool result = GetArgAsNumber(name, &arg_double);
163 DCHECK(result);
164 return static_cast<int>(arg_double);
165 }
166
GetKnownArgAsBool(const std::string & name) const167 bool TraceEvent::GetKnownArgAsBool(const std::string& name) const {
168 double arg_double = 0;
169 bool result = GetArgAsNumber(name, &arg_double);
170 DCHECK(result);
171 return (arg_double != 0.0);
172 }
173
174 // QueryNode
175
QueryNode(const Query & query)176 QueryNode::QueryNode(const Query& query) : query_(query) {
177 }
178
~QueryNode()179 QueryNode::~QueryNode() {
180 }
181
182 // Query
183
Query(TraceEventMember member)184 Query::Query(TraceEventMember member)
185 : type_(QUERY_EVENT_MEMBER),
186 operator_(OP_INVALID),
187 member_(member),
188 number_(0),
189 is_pattern_(false) {
190 }
191
Query(TraceEventMember member,const std::string & arg_name)192 Query::Query(TraceEventMember member, const std::string& arg_name)
193 : type_(QUERY_EVENT_MEMBER),
194 operator_(OP_INVALID),
195 member_(member),
196 number_(0),
197 string_(arg_name),
198 is_pattern_(false) {
199 }
200
Query(const Query & query)201 Query::Query(const Query& query)
202 : type_(query.type_),
203 operator_(query.operator_),
204 left_(query.left_),
205 right_(query.right_),
206 member_(query.member_),
207 number_(query.number_),
208 string_(query.string_),
209 is_pattern_(query.is_pattern_) {
210 }
211
~Query()212 Query::~Query() {
213 }
214
String(const std::string & str)215 Query Query::String(const std::string& str) {
216 return Query(str);
217 }
218
Double(double num)219 Query Query::Double(double num) {
220 return Query(num);
221 }
222
Int(int32_t num)223 Query Query::Int(int32_t num) {
224 return Query(static_cast<double>(num));
225 }
226
Uint(uint32_t num)227 Query Query::Uint(uint32_t num) {
228 return Query(static_cast<double>(num));
229 }
230
Bool(bool boolean)231 Query Query::Bool(bool boolean) {
232 return Query(boolean ? 1.0 : 0.0);
233 }
234
Phase(char phase)235 Query Query::Phase(char phase) {
236 return Query(static_cast<double>(phase));
237 }
238
Pattern(const std::string & pattern)239 Query Query::Pattern(const std::string& pattern) {
240 Query query(pattern);
241 query.is_pattern_ = true;
242 return query;
243 }
244
Evaluate(const TraceEvent & event) const245 bool Query::Evaluate(const TraceEvent& event) const {
246 // First check for values that can convert to bool.
247
248 // double is true if != 0:
249 double bool_value = 0.0;
250 bool is_bool = GetAsDouble(event, &bool_value);
251 if (is_bool)
252 return (bool_value != 0.0);
253
254 // string is true if it is non-empty:
255 std::string str_value;
256 bool is_str = GetAsString(event, &str_value);
257 if (is_str)
258 return !str_value.empty();
259
260 DCHECK_EQ(QUERY_BOOLEAN_OPERATOR, type_)
261 << "Invalid query: missing boolean expression";
262 DCHECK(left_.get());
263 DCHECK(right_.get() || is_unary_operator());
264
265 if (is_comparison_operator()) {
266 DCHECK(left().is_value() && right().is_value())
267 << "Invalid query: comparison operator used between event member and "
268 "value.";
269 bool compare_result = false;
270 if (CompareAsDouble(event, &compare_result))
271 return compare_result;
272 if (CompareAsString(event, &compare_result))
273 return compare_result;
274 return false;
275 }
276 // It's a logical operator.
277 switch (operator_) {
278 case OP_AND:
279 return left().Evaluate(event) && right().Evaluate(event);
280 case OP_OR:
281 return left().Evaluate(event) || right().Evaluate(event);
282 case OP_NOT:
283 return !left().Evaluate(event);
284 default:
285 NOTREACHED();
286 return false;
287 }
288 }
289
CompareAsDouble(const TraceEvent & event,bool * result) const290 bool Query::CompareAsDouble(const TraceEvent& event, bool* result) const {
291 double lhs, rhs;
292 if (!left().GetAsDouble(event, &lhs) || !right().GetAsDouble(event, &rhs))
293 return false;
294 switch (operator_) {
295 case OP_EQ:
296 *result = (lhs == rhs);
297 return true;
298 case OP_NE:
299 *result = (lhs != rhs);
300 return true;
301 case OP_LT:
302 *result = (lhs < rhs);
303 return true;
304 case OP_LE:
305 *result = (lhs <= rhs);
306 return true;
307 case OP_GT:
308 *result = (lhs > rhs);
309 return true;
310 case OP_GE:
311 *result = (lhs >= rhs);
312 return true;
313 default:
314 NOTREACHED();
315 return false;
316 }
317 }
318
CompareAsString(const TraceEvent & event,bool * result) const319 bool Query::CompareAsString(const TraceEvent& event, bool* result) const {
320 std::string lhs, rhs;
321 if (!left().GetAsString(event, &lhs) || !right().GetAsString(event, &rhs))
322 return false;
323 switch (operator_) {
324 case OP_EQ:
325 if (right().is_pattern_)
326 *result = base::MatchPattern(lhs, rhs);
327 else if (left().is_pattern_)
328 *result = base::MatchPattern(rhs, lhs);
329 else
330 *result = (lhs == rhs);
331 return true;
332 case OP_NE:
333 if (right().is_pattern_)
334 *result = !base::MatchPattern(lhs, rhs);
335 else if (left().is_pattern_)
336 *result = !base::MatchPattern(rhs, lhs);
337 else
338 *result = (lhs != rhs);
339 return true;
340 case OP_LT:
341 *result = (lhs < rhs);
342 return true;
343 case OP_LE:
344 *result = (lhs <= rhs);
345 return true;
346 case OP_GT:
347 *result = (lhs > rhs);
348 return true;
349 case OP_GE:
350 *result = (lhs >= rhs);
351 return true;
352 default:
353 NOTREACHED();
354 return false;
355 }
356 }
357
EvaluateArithmeticOperator(const TraceEvent & event,double * num) const358 bool Query::EvaluateArithmeticOperator(const TraceEvent& event,
359 double* num) const {
360 DCHECK_EQ(QUERY_ARITHMETIC_OPERATOR, type_);
361 DCHECK(left_.get());
362 DCHECK(right_.get() || is_unary_operator());
363
364 double lhs = 0, rhs = 0;
365 if (!left().GetAsDouble(event, &lhs))
366 return false;
367 if (!is_unary_operator() && !right().GetAsDouble(event, &rhs))
368 return false;
369
370 switch (operator_) {
371 case OP_ADD:
372 *num = lhs + rhs;
373 return true;
374 case OP_SUB:
375 *num = lhs - rhs;
376 return true;
377 case OP_MUL:
378 *num = lhs * rhs;
379 return true;
380 case OP_DIV:
381 *num = lhs / rhs;
382 return true;
383 case OP_MOD:
384 *num = static_cast<double>(static_cast<int64_t>(lhs) %
385 static_cast<int64_t>(rhs));
386 return true;
387 case OP_NEGATE:
388 *num = -lhs;
389 return true;
390 default:
391 NOTREACHED();
392 return false;
393 }
394 }
395
GetAsDouble(const TraceEvent & event,double * num) const396 bool Query::GetAsDouble(const TraceEvent& event, double* num) const {
397 switch (type_) {
398 case QUERY_ARITHMETIC_OPERATOR:
399 return EvaluateArithmeticOperator(event, num);
400 case QUERY_EVENT_MEMBER:
401 return GetMemberValueAsDouble(event, num);
402 case QUERY_NUMBER:
403 *num = number_;
404 return true;
405 default:
406 return false;
407 }
408 }
409
GetAsString(const TraceEvent & event,std::string * str) const410 bool Query::GetAsString(const TraceEvent& event, std::string* str) const {
411 switch (type_) {
412 case QUERY_EVENT_MEMBER:
413 return GetMemberValueAsString(event, str);
414 case QUERY_STRING:
415 *str = string_;
416 return true;
417 default:
418 return false;
419 }
420 }
421
GetMemberValueAsDouble(const TraceEvent & event,double * num) const422 bool Query::GetMemberValueAsDouble(const TraceEvent& event,
423 double* num) const {
424 DCHECK_EQ(QUERY_EVENT_MEMBER, type_);
425
426 // This could be a request for a member of |event| or a member of |event|'s
427 // associated event. Store the target event in the_event:
428 const TraceEvent* the_event = (member_ < OTHER_PID) ?
429 &event : event.other_event;
430
431 // Request for member of associated event, but there is no associated event.
432 if (!the_event)
433 return false;
434
435 switch (member_) {
436 case EVENT_PID:
437 case OTHER_PID:
438 *num = static_cast<double>(the_event->thread.process_id);
439 return true;
440 case EVENT_TID:
441 case OTHER_TID:
442 *num = static_cast<double>(the_event->thread.thread_id);
443 return true;
444 case EVENT_TIME:
445 case OTHER_TIME:
446 *num = the_event->timestamp;
447 return true;
448 case EVENT_DURATION:
449 if (!the_event->has_other_event())
450 return false;
451 *num = the_event->GetAbsTimeToOtherEvent();
452 return true;
453 case EVENT_COMPLETE_DURATION:
454 if (the_event->phase != TRACE_EVENT_PHASE_COMPLETE)
455 return false;
456 *num = the_event->duration;
457 return true;
458 case EVENT_PHASE:
459 case OTHER_PHASE:
460 *num = static_cast<double>(the_event->phase);
461 return true;
462 case EVENT_HAS_STRING_ARG:
463 case OTHER_HAS_STRING_ARG:
464 *num = (the_event->HasStringArg(string_) ? 1.0 : 0.0);
465 return true;
466 case EVENT_HAS_NUMBER_ARG:
467 case OTHER_HAS_NUMBER_ARG:
468 *num = (the_event->HasNumberArg(string_) ? 1.0 : 0.0);
469 return true;
470 case EVENT_ARG:
471 case OTHER_ARG: {
472 // Search for the argument name and return its value if found.
473 std::map<std::string, double>::const_iterator num_i =
474 the_event->arg_numbers.find(string_);
475 if (num_i == the_event->arg_numbers.end())
476 return false;
477 *num = num_i->second;
478 return true;
479 }
480 case EVENT_HAS_OTHER:
481 // return 1.0 (true) if the other event exists
482 *num = event.other_event ? 1.0 : 0.0;
483 return true;
484 default:
485 return false;
486 }
487 }
488
GetMemberValueAsString(const TraceEvent & event,std::string * str) const489 bool Query::GetMemberValueAsString(const TraceEvent& event,
490 std::string* str) const {
491 DCHECK_EQ(QUERY_EVENT_MEMBER, type_);
492
493 // This could be a request for a member of |event| or a member of |event|'s
494 // associated event. Store the target event in the_event:
495 const TraceEvent* the_event = (member_ < OTHER_PID) ?
496 &event : event.other_event;
497
498 // Request for member of associated event, but there is no associated event.
499 if (!the_event)
500 return false;
501
502 switch (member_) {
503 case EVENT_CATEGORY:
504 case OTHER_CATEGORY:
505 *str = the_event->category;
506 return true;
507 case EVENT_NAME:
508 case OTHER_NAME:
509 *str = the_event->name;
510 return true;
511 case EVENT_ID:
512 case OTHER_ID:
513 *str = the_event->id;
514 return true;
515 case EVENT_ARG:
516 case OTHER_ARG: {
517 // Search for the argument name and return its value if found.
518 std::map<std::string, std::string>::const_iterator str_i =
519 the_event->arg_strings.find(string_);
520 if (str_i == the_event->arg_strings.end())
521 return false;
522 *str = str_i->second;
523 return true;
524 }
525 default:
526 return false;
527 }
528 }
529
Query(const std::string & str)530 Query::Query(const std::string& str)
531 : type_(QUERY_STRING),
532 operator_(OP_INVALID),
533 member_(EVENT_INVALID),
534 number_(0),
535 string_(str),
536 is_pattern_(false) {
537 }
538
Query(double num)539 Query::Query(double num)
540 : type_(QUERY_NUMBER),
541 operator_(OP_INVALID),
542 member_(EVENT_INVALID),
543 number_(num),
544 is_pattern_(false) {
545 }
left() const546 const Query& Query::left() const {
547 return left_->query();
548 }
549
right() const550 const Query& Query::right() const {
551 return right_->query();
552 }
553
operator ==(const Query & rhs) const554 Query Query::operator==(const Query& rhs) const {
555 return Query(*this, rhs, OP_EQ);
556 }
557
operator !=(const Query & rhs) const558 Query Query::operator!=(const Query& rhs) const {
559 return Query(*this, rhs, OP_NE);
560 }
561
operator <(const Query & rhs) const562 Query Query::operator<(const Query& rhs) const {
563 return Query(*this, rhs, OP_LT);
564 }
565
operator <=(const Query & rhs) const566 Query Query::operator<=(const Query& rhs) const {
567 return Query(*this, rhs, OP_LE);
568 }
569
operator >(const Query & rhs) const570 Query Query::operator>(const Query& rhs) const {
571 return Query(*this, rhs, OP_GT);
572 }
573
operator >=(const Query & rhs) const574 Query Query::operator>=(const Query& rhs) const {
575 return Query(*this, rhs, OP_GE);
576 }
577
operator &&(const Query & rhs) const578 Query Query::operator&&(const Query& rhs) const {
579 return Query(*this, rhs, OP_AND);
580 }
581
operator ||(const Query & rhs) const582 Query Query::operator||(const Query& rhs) const {
583 return Query(*this, rhs, OP_OR);
584 }
585
operator !() const586 Query Query::operator!() const {
587 return Query(*this, OP_NOT);
588 }
589
operator +(const Query & rhs) const590 Query Query::operator+(const Query& rhs) const {
591 return Query(*this, rhs, OP_ADD);
592 }
593
operator -(const Query & rhs) const594 Query Query::operator-(const Query& rhs) const {
595 return Query(*this, rhs, OP_SUB);
596 }
597
operator *(const Query & rhs) const598 Query Query::operator*(const Query& rhs) const {
599 return Query(*this, rhs, OP_MUL);
600 }
601
operator /(const Query & rhs) const602 Query Query::operator/(const Query& rhs) const {
603 return Query(*this, rhs, OP_DIV);
604 }
605
operator %(const Query & rhs) const606 Query Query::operator%(const Query& rhs) const {
607 return Query(*this, rhs, OP_MOD);
608 }
609
operator -() const610 Query Query::operator-() const {
611 return Query(*this, OP_NEGATE);
612 }
613
614
Query(const Query & left,const Query & right,Operator binary_op)615 Query::Query(const Query& left, const Query& right, Operator binary_op)
616 : operator_(binary_op),
617 left_(new QueryNode(left)),
618 right_(new QueryNode(right)),
619 member_(EVENT_INVALID),
620 number_(0) {
621 type_ = (binary_op < OP_ADD ?
622 QUERY_BOOLEAN_OPERATOR : QUERY_ARITHMETIC_OPERATOR);
623 }
624
Query(const Query & left,Operator unary_op)625 Query::Query(const Query& left, Operator unary_op)
626 : operator_(unary_op),
627 left_(new QueryNode(left)),
628 member_(EVENT_INVALID),
629 number_(0) {
630 type_ = (unary_op < OP_ADD ?
631 QUERY_BOOLEAN_OPERATOR : QUERY_ARITHMETIC_OPERATOR);
632 }
633
634 namespace {
635
636 // Search |events| for |query| and add matches to |output|.
FindMatchingEvents(const std::vector<TraceEvent> & events,const Query & query,TraceEventVector * output,bool ignore_metadata_events)637 size_t FindMatchingEvents(const std::vector<TraceEvent>& events,
638 const Query& query,
639 TraceEventVector* output,
640 bool ignore_metadata_events) {
641 for (size_t i = 0; i < events.size(); ++i) {
642 if (ignore_metadata_events && events[i].phase == TRACE_EVENT_PHASE_METADATA)
643 continue;
644 if (query.Evaluate(events[i]))
645 output->push_back(&events[i]);
646 }
647 return output->size();
648 }
649
ParseEventsFromJson(const std::string & json,std::vector<TraceEvent> * output)650 bool ParseEventsFromJson(const std::string& json,
651 std::vector<TraceEvent>* output) {
652 scoped_ptr<base::Value> root = base::JSONReader::Read(json);
653
654 base::ListValue* root_list = NULL;
655 if (!root.get() || !root->GetAsList(&root_list))
656 return false;
657
658 for (size_t i = 0; i < root_list->GetSize(); ++i) {
659 base::Value* item = NULL;
660 if (root_list->Get(i, &item)) {
661 TraceEvent event;
662 if (event.SetFromJSON(item))
663 output->push_back(event);
664 else
665 return false;
666 }
667 }
668
669 return true;
670 }
671
672 } // namespace
673
674 // TraceAnalyzer
675
TraceAnalyzer()676 TraceAnalyzer::TraceAnalyzer()
677 : ignore_metadata_events_(false),
678 allow_assocation_changes_(true) {}
679
~TraceAnalyzer()680 TraceAnalyzer::~TraceAnalyzer() {
681 }
682
683 // static
Create(const std::string & json_events)684 TraceAnalyzer* TraceAnalyzer::Create(const std::string& json_events) {
685 scoped_ptr<TraceAnalyzer> analyzer(new TraceAnalyzer());
686 if (analyzer->SetEvents(json_events))
687 return analyzer.release();
688 return NULL;
689 }
690
SetEvents(const std::string & json_events)691 bool TraceAnalyzer::SetEvents(const std::string& json_events) {
692 raw_events_.clear();
693 if (!ParseEventsFromJson(json_events, &raw_events_))
694 return false;
695 std::stable_sort(raw_events_.begin(), raw_events_.end());
696 ParseMetadata();
697 return true;
698 }
699
AssociateBeginEndEvents()700 void TraceAnalyzer::AssociateBeginEndEvents() {
701 using trace_analyzer::Query;
702
703 Query begin(Query::EventPhaseIs(TRACE_EVENT_PHASE_BEGIN));
704 Query end(Query::EventPhaseIs(TRACE_EVENT_PHASE_END));
705 Query match(Query::EventName() == Query::OtherName() &&
706 Query::EventCategory() == Query::OtherCategory() &&
707 Query::EventTid() == Query::OtherTid() &&
708 Query::EventPid() == Query::OtherPid());
709
710 AssociateEvents(begin, end, match);
711 }
712
AssociateAsyncBeginEndEvents()713 void TraceAnalyzer::AssociateAsyncBeginEndEvents() {
714 using trace_analyzer::Query;
715
716 Query begin(
717 Query::EventPhaseIs(TRACE_EVENT_PHASE_ASYNC_BEGIN) ||
718 Query::EventPhaseIs(TRACE_EVENT_PHASE_ASYNC_STEP_INTO) ||
719 Query::EventPhaseIs(TRACE_EVENT_PHASE_ASYNC_STEP_PAST));
720 Query end(Query::EventPhaseIs(TRACE_EVENT_PHASE_ASYNC_END) ||
721 Query::EventPhaseIs(TRACE_EVENT_PHASE_ASYNC_STEP_INTO) ||
722 Query::EventPhaseIs(TRACE_EVENT_PHASE_ASYNC_STEP_PAST));
723 Query match(Query::EventName() == Query::OtherName() &&
724 Query::EventCategory() == Query::OtherCategory() &&
725 Query::EventId() == Query::OtherId());
726
727 AssociateEvents(begin, end, match);
728 }
729
AssociateEvents(const Query & first,const Query & second,const Query & match)730 void TraceAnalyzer::AssociateEvents(const Query& first,
731 const Query& second,
732 const Query& match) {
733 DCHECK(allow_assocation_changes_)
734 << "AssociateEvents not allowed after FindEvents";
735
736 // Search for matching begin/end event pairs. When a matching end is found,
737 // it is associated with the begin event.
738 std::vector<TraceEvent*> begin_stack;
739 for (size_t event_index = 0; event_index < raw_events_.size();
740 ++event_index) {
741
742 TraceEvent& this_event = raw_events_[event_index];
743
744 if (second.Evaluate(this_event)) {
745 // Search stack for matching begin, starting from end.
746 for (int stack_index = static_cast<int>(begin_stack.size()) - 1;
747 stack_index >= 0; --stack_index) {
748 TraceEvent& begin_event = *begin_stack[stack_index];
749
750 // Temporarily set other to test against the match query.
751 const TraceEvent* other_backup = begin_event.other_event;
752 begin_event.other_event = &this_event;
753 if (match.Evaluate(begin_event)) {
754 // Found a matching begin/end pair.
755 // Erase the matching begin event index from the stack.
756 begin_stack.erase(begin_stack.begin() + stack_index);
757 break;
758 }
759
760 // Not a match, restore original other and continue.
761 begin_event.other_event = other_backup;
762 }
763 }
764 // Even if this_event is a |second| event that has matched an earlier
765 // |first| event, it can still also be a |first| event and be associated
766 // with a later |second| event.
767 if (first.Evaluate(this_event)) {
768 begin_stack.push_back(&this_event);
769 }
770 }
771 }
772
MergeAssociatedEventArgs()773 void TraceAnalyzer::MergeAssociatedEventArgs() {
774 for (size_t i = 0; i < raw_events_.size(); ++i) {
775 // Merge all associated events with the first event.
776 const TraceEvent* other = raw_events_[i].other_event;
777 // Avoid looping by keeping set of encountered TraceEvents.
778 std::set<const TraceEvent*> encounters;
779 encounters.insert(&raw_events_[i]);
780 while (other && encounters.find(other) == encounters.end()) {
781 encounters.insert(other);
782 raw_events_[i].arg_numbers.insert(
783 other->arg_numbers.begin(),
784 other->arg_numbers.end());
785 raw_events_[i].arg_strings.insert(
786 other->arg_strings.begin(),
787 other->arg_strings.end());
788 other = other->other_event;
789 }
790 }
791 }
792
FindEvents(const Query & query,TraceEventVector * output)793 size_t TraceAnalyzer::FindEvents(const Query& query, TraceEventVector* output) {
794 allow_assocation_changes_ = false;
795 output->clear();
796 return FindMatchingEvents(
797 raw_events_, query, output, ignore_metadata_events_);
798 }
799
FindFirstOf(const Query & query)800 const TraceEvent* TraceAnalyzer::FindFirstOf(const Query& query) {
801 TraceEventVector output;
802 if (FindEvents(query, &output) > 0)
803 return output.front();
804 return NULL;
805 }
806
FindLastOf(const Query & query)807 const TraceEvent* TraceAnalyzer::FindLastOf(const Query& query) {
808 TraceEventVector output;
809 if (FindEvents(query, &output) > 0)
810 return output.back();
811 return NULL;
812 }
813
GetThreadName(const TraceEvent::ProcessThreadID & thread)814 const std::string& TraceAnalyzer::GetThreadName(
815 const TraceEvent::ProcessThreadID& thread) {
816 // If thread is not found, just add and return empty string.
817 return thread_names_[thread];
818 }
819
ParseMetadata()820 void TraceAnalyzer::ParseMetadata() {
821 for (size_t i = 0; i < raw_events_.size(); ++i) {
822 TraceEvent& this_event = raw_events_[i];
823 // Check for thread name metadata.
824 if (this_event.phase != TRACE_EVENT_PHASE_METADATA ||
825 this_event.name != "thread_name")
826 continue;
827 std::map<std::string, std::string>::const_iterator string_it =
828 this_event.arg_strings.find("name");
829 if (string_it != this_event.arg_strings.end())
830 thread_names_[this_event.thread] = string_it->second;
831 }
832 }
833
834 // TraceEventVector utility functions.
835
GetRateStats(const TraceEventVector & events,RateStats * stats,const RateStatsOptions * options)836 bool GetRateStats(const TraceEventVector& events,
837 RateStats* stats,
838 const RateStatsOptions* options) {
839 DCHECK(stats);
840 // Need at least 3 events to calculate rate stats.
841 const size_t kMinEvents = 3;
842 if (events.size() < kMinEvents) {
843 LOG(ERROR) << "Not enough events: " << events.size();
844 return false;
845 }
846
847 std::vector<double> deltas;
848 size_t num_deltas = events.size() - 1;
849 for (size_t i = 0; i < num_deltas; ++i) {
850 double delta = events.at(i + 1)->timestamp - events.at(i)->timestamp;
851 if (delta < 0.0) {
852 LOG(ERROR) << "Events are out of order";
853 return false;
854 }
855 deltas.push_back(delta);
856 }
857
858 std::sort(deltas.begin(), deltas.end());
859
860 if (options) {
861 if (options->trim_min + options->trim_max > events.size() - kMinEvents) {
862 LOG(ERROR) << "Attempt to trim too many events";
863 return false;
864 }
865 deltas.erase(deltas.begin(), deltas.begin() + options->trim_min);
866 deltas.erase(deltas.end() - options->trim_max, deltas.end());
867 }
868
869 num_deltas = deltas.size();
870 double delta_sum = 0.0;
871 for (size_t i = 0; i < num_deltas; ++i)
872 delta_sum += deltas[i];
873
874 stats->min_us = *std::min_element(deltas.begin(), deltas.end());
875 stats->max_us = *std::max_element(deltas.begin(), deltas.end());
876 stats->mean_us = delta_sum / static_cast<double>(num_deltas);
877
878 double sum_mean_offsets_squared = 0.0;
879 for (size_t i = 0; i < num_deltas; ++i) {
880 double offset = fabs(deltas[i] - stats->mean_us);
881 sum_mean_offsets_squared += offset * offset;
882 }
883 stats->standard_deviation_us =
884 sqrt(sum_mean_offsets_squared / static_cast<double>(num_deltas - 1));
885
886 return true;
887 }
888
FindFirstOf(const TraceEventVector & events,const Query & query,size_t position,size_t * return_index)889 bool FindFirstOf(const TraceEventVector& events,
890 const Query& query,
891 size_t position,
892 size_t* return_index) {
893 DCHECK(return_index);
894 for (size_t i = position; i < events.size(); ++i) {
895 if (query.Evaluate(*events[i])) {
896 *return_index = i;
897 return true;
898 }
899 }
900 return false;
901 }
902
FindLastOf(const TraceEventVector & events,const Query & query,size_t position,size_t * return_index)903 bool FindLastOf(const TraceEventVector& events,
904 const Query& query,
905 size_t position,
906 size_t* return_index) {
907 DCHECK(return_index);
908 for (size_t i = std::min(position + 1, events.size()); i != 0; --i) {
909 if (query.Evaluate(*events[i - 1])) {
910 *return_index = i - 1;
911 return true;
912 }
913 }
914 return false;
915 }
916
FindClosest(const TraceEventVector & events,const Query & query,size_t position,size_t * return_closest,size_t * return_second_closest)917 bool FindClosest(const TraceEventVector& events,
918 const Query& query,
919 size_t position,
920 size_t* return_closest,
921 size_t* return_second_closest) {
922 DCHECK(return_closest);
923 if (events.empty() || position >= events.size())
924 return false;
925 size_t closest = events.size();
926 size_t second_closest = events.size();
927 for (size_t i = 0; i < events.size(); ++i) {
928 if (!query.Evaluate(*events.at(i)))
929 continue;
930 if (closest == events.size()) {
931 closest = i;
932 continue;
933 }
934 if (fabs(events.at(i)->timestamp - events.at(position)->timestamp) <
935 fabs(events.at(closest)->timestamp - events.at(position)->timestamp)) {
936 second_closest = closest;
937 closest = i;
938 } else if (second_closest == events.size()) {
939 second_closest = i;
940 }
941 }
942
943 if (closest < events.size() &&
944 (!return_second_closest || second_closest < events.size())) {
945 *return_closest = closest;
946 if (return_second_closest)
947 *return_second_closest = second_closest;
948 return true;
949 }
950
951 return false;
952 }
953
CountMatches(const TraceEventVector & events,const Query & query,size_t begin_position,size_t end_position)954 size_t CountMatches(const TraceEventVector& events,
955 const Query& query,
956 size_t begin_position,
957 size_t end_position) {
958 if (begin_position >= events.size())
959 return 0u;
960 end_position = (end_position < events.size()) ? end_position : events.size();
961 size_t count = 0u;
962 for (size_t i = begin_position; i < end_position; ++i) {
963 if (query.Evaluate(*events.at(i)))
964 ++count;
965 }
966 return count;
967 }
968
969 } // namespace trace_analyzer
970