Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2)

Side by Side Diff: base/test/trace_event_analyzer.cc

Issue 7981004: add classes trace_analyzer::Query and TraceAnalyzer to make it easy to search through trace data (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: comments updated Created 9 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2011 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 <algorithm>
6 #include <math.h>
7
8 #include "base/test/trace_event_analyzer.h"
jar (doing other things) 2011/10/26 19:35:20 nit: alphabetize
jbates 2011/10/26 21:51:54 Done.
9 #include "base/json/json_reader.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "base/values.h"
12
13 namespace trace_analyzer {
14
15 // TraceEvent
16
17 TraceEvent::TraceEvent()
18 : pid_tid(0, 0),
19 timestamp(0),
20 phase(base::debug::TRACE_EVENT_PHASE_BEGIN),
21 other_event(NULL) {
22 }
23
24 TraceEvent::~TraceEvent() {
25 }
26
27 bool TraceEvent::SetFromJSON(const base::Value* event_value) {
28 if (event_value->GetType() != base::Value::TYPE_DICTIONARY)
29 return false;
30 const base::DictionaryValue* dictionary =
31 static_cast<const base::DictionaryValue*>(event_value);
32
33 std::string phase_str;
34 base::DictionaryValue* args = NULL;
35
36 if (dictionary->GetInteger("pid", &pid_tid.pid) &&
37 dictionary->GetInteger("tid", &pid_tid.tid) &&
38 dictionary->GetDouble("ts", &timestamp) &&
39 dictionary->GetString("cat", &category) &&
40 dictionary->GetString("name", &name) &&
41 dictionary->GetString("ph", &phase_str) &&
42 dictionary->GetDictionary("args", &args)) {
43
44 phase = base::debug::TraceEvent::GetPhase(phase_str.c_str());
45
46 // For each argument, copy the type and create a trace_analyzer::TraceValue.
47 base::DictionaryValue::key_iterator keyi = args->begin_keys();
48 for (; keyi != args->end_keys(); ++keyi) {
49 std::string str;
50 bool boolean = false;
51 int int_num = 0;
52 double double_num = 0.0;
53 Value* value = NULL;
54 if (args->GetWithoutPathExpansion(*keyi, &value)) {
55 if (value->GetAsString(&str))
56 arg_strings[*keyi] = str;
57 else if (value->GetAsInteger(&int_num))
58 arg_numbers[*keyi] = static_cast<double>(int_num);
59 else if (value->GetAsBoolean(&boolean))
60 arg_numbers[*keyi] = static_cast<double>(boolean ? 1 : 0);
61 else if (value->GetAsDouble(&double_num))
62 arg_numbers[*keyi] = double_num;
63 else
64 return false; // Invalid trace event JSON format.
65 }
66 }
67
68 return true;
69 }
70
71 return false;
72 }
73
74 bool TraceEvent::GetAbsTimeToOtherEvent(double* duration) const {
75 if (!other_event)
76 return false;
77
78 *duration = fabs(other_event->timestamp - timestamp);
79 return true;
80 }
81
82 bool TraceEvent::GetArgAsString(const std::string& name,
83 std::string* arg) const {
84 std::map<std::string, std::string>::const_iterator i = arg_strings.find(name);
85 if (i != arg_strings.end()) {
86 *arg = i->second;
87 return true;
88 }
89 return false;
90 }
91
92 bool TraceEvent::GetArgAsNumber(const std::string& name,
93 double* arg) const {
94 std::map<std::string, double>::const_iterator i = arg_numbers.find(name);
95 if (i != arg_numbers.end()) {
96 *arg = i->second;
97 return true;
98 }
99 return false;
100 }
101
102 // QueryNode
103
104 QueryNode::QueryNode(const Query& query) : query_(query) {
105 }
106
107 QueryNode::~QueryNode() {
108 }
109
110 // Query
111
112 Query::Query(TraceEventMember member)
113 : type_(QUERY_EVENT_MEMBER),
114 operator_(OP_INVALID),
115 member_(member),
116 number_(0),
117 is_pattern_(false) {
118 }
119
120 Query::Query(TraceEventMember member, const std::string& arg_name)
121 : type_(QUERY_EVENT_MEMBER),
122 operator_(OP_INVALID),
123 member_(member),
124 number_(0),
125 string_(arg_name),
126 is_pattern_(false) {
127 }
128
129 Query::Query(const Query& query)
130 : type_(query.type_),
131 operator_(query.operator_),
132 left_(query.left_),
133 right_(query.right_),
134 member_(query.member_),
135 number_(query.number_),
136 string_(query.string_),
137 is_pattern_(query.is_pattern_) {
138 }
139
140 Query::~Query() {
141 }
142
143 Query Query::String(const std::string& str) {
144 return Query(str);
145 }
146
147 Query Query::Double(double num) {
148 return Query(num);
149 }
150
151 Query Query::Int(int32 num) {
152 return Query(static_cast<double>(num));
153 }
154
155 Query Query::Uint(uint32 num) {
156 return Query(static_cast<double>(num));
157 }
158
159 Query Query::Bool(bool boolean) {
160 return Query(boolean ? 1.0 : 0.0);
161 }
162
163 Query Query::Phase(base::debug::TraceEventPhase phase) {
164 return Query(static_cast<double>(phase));
165 }
166
167 Query Query::Pattern(const std::string& pattern) {
168 Query query(pattern);
169 query.is_pattern_ = true;
170 return query;
171 }
172
173 bool Query::Evaluate(const TraceEvent& event) const {
174 // First check for values that can convert to bool.
175
176 // double is true if != 0:
177 double bool_value = 0.0;
178 bool is_bool = GetAsDouble(event, &bool_value);
179 if (is_bool)
180 return (bool_value != 0.0);
181
182 // string is true if it is non-empty:
183 std::string str_value;
184 bool is_str = GetAsString(event, &str_value);
185 if (is_str)
186 return !str_value.empty();
187
188 CHECK(type_ == QUERY_BOOLEAN_OPERATOR)
jar (doing other things) 2011/10/26 19:35:20 Checks in production code like this are bad. Brett
jbates 2011/10/26 21:51:54 Done. This is base/test, so shouldn't end up in pr
jar (doing other things) 2011/10/28 20:47:53 DCHECK is debug only (I think) CHECK is Release an
jbates 2011/10/28 21:54:28 Since --enable-dcheck works for Release builds, DC
189 << "Invalid query: missing boolean expression";
190 CHECK(left_.get() && (right_.get() || is_unary_operator()));
191
192 if (operator_ < OP_AND) {
jar (doing other things) 2011/10/26 19:35:20 This was hard to read, as I had to go back to the
jbates 2011/10/26 21:51:54 Done.
193 CHECK(left().is_value() && right().is_value())
194 << "Invalid query: comparison operator used between event member and "
195 "value.";
196 bool compare_result = false;
197 if (CompareAsDouble(event, &compare_result))
198 return compare_result;
199 else if (CompareAsString(event, &compare_result))
200 return compare_result;
201 return false;
202 } else {
203 switch (operator_) {
204 case OP_AND:
205 return left().Evaluate(event) && right().Evaluate(event);
206 case OP_OR:
207 return left().Evaluate(event) || right().Evaluate(event);
208 case OP_NOT:
209 return !left().Evaluate(event);
210 default:
211 NOTREACHED();
212 }
213 }
214
215 NOTREACHED();
216 return false;
217 }
218
219 bool Query::CompareAsDouble(const TraceEvent& event, bool* result) const {
220 double lhs, rhs;
221 if (!left().GetAsDouble(event, &lhs) || !right().GetAsDouble(event, &rhs))
222 return false;
223 switch (operator_) {
224 case OP_EQ:
225 *result = (lhs == rhs);
226 return true;
227 case OP_NE:
228 *result = (lhs != rhs);
229 return true;
230 case OP_LT:
231 *result = (lhs < rhs);
232 return true;
233 case OP_LE:
234 *result = (lhs <= rhs);
235 return true;
236 case OP_GT:
237 *result = (lhs > rhs);
238 return true;
239 case OP_GE:
240 *result = (lhs >= rhs);
241 return true;
242 default:
243 NOTREACHED();
244 return false;
245 }
246 return true;
247 }
248
249 bool Query::CompareAsString(const TraceEvent& event, bool* result) const {
250 std::string lhs, rhs;
251 if (!left().GetAsString(event, &lhs) || !right().GetAsString(event, &rhs))
252 return false;
253 switch (operator_) {
254 case OP_EQ:
255 if (right().is_pattern_)
256 *result = MatchPattern(lhs, rhs);
257 else if (left().is_pattern_)
258 *result = MatchPattern(rhs, lhs);
259 else
260 *result = (lhs == rhs);
261 return true;
262 case OP_NE:
263 if (right().is_pattern_)
264 *result = !MatchPattern(lhs, rhs);
265 else if (left().is_pattern_)
266 *result = !MatchPattern(rhs, lhs);
267 else
268 *result = (lhs != rhs);
269 return true;
270 case OP_LT:
271 *result = (lhs < rhs);
272 return true;
273 case OP_LE:
274 *result = (lhs <= rhs);
275 return true;
276 case OP_GT:
277 *result = (lhs > rhs);
278 return true;
279 case OP_GE:
280 *result = (lhs >= rhs);
281 return true;
282 default:
283 NOTREACHED();
284 return false;
285 }
286 return true;
287 }
288
289 bool Query::EvaluateArithmeticOperator(const TraceEvent& event,
290 double* num) const {
291 CHECK(type_ == QUERY_ARITHMETIC_OPERATOR);
292 CHECK(left_.get() && (right_.get() || is_unary_operator()));
293
294 double lhs = 0, rhs = 0;
295 if (!left().GetAsDouble(event, &lhs))
296 return false;
297 if (!is_unary_operator() && !right().GetAsDouble(event, &rhs))
298 return false;
299
300 switch (operator_) {
301 case OP_ADD:
302 *num = lhs + rhs;
303 break;
jar (doing other things) 2011/10/26 19:35:20 personal style nit: Early return is nicer, so that
jbates 2011/10/26 21:51:54 Done.
304 case OP_SUB:
305 *num = lhs - rhs;
306 break;
307 case OP_MUL:
308 *num = lhs * rhs;
309 break;
310 case OP_DIV:
311 *num = lhs / rhs;
312 break;
313 case OP_MOD:
314 *num = static_cast<double>(static_cast<int64>(lhs) %
315 static_cast<int64>(rhs));
316 break;
317 case OP_NEGATE:
318 *num = -lhs;
319 break;
320 default:
321 NOTREACHED();
322 return false;
323 }
324
325 return true;
326 }
327
328 bool Query::GetAsDouble(const TraceEvent& event, double* num) const {
329 base::debug::TraceValue value;
330 switch (type_) {
331 case QUERY_ARITHMETIC_OPERATOR:
332 return EvaluateArithmeticOperator(event, num);
333 case QUERY_EVENT_MEMBER:
334 value = GetMemberValue(event);
335 if (value.type() == base::debug::TraceValue::TRACE_TYPE_DOUBLE) {
336 *num = value.as_double();
337 return true;
338 }
339 return false;
340 case QUERY_NUMBER:
341 *num = number_;
342 return true;
343 default:
344 return false;
345 }
346 }
347
348 bool Query::GetAsString(const TraceEvent& event, std::string* str) const {
349 base::debug::TraceValue value;
350 switch (type_) {
351 case QUERY_EVENT_MEMBER:
352 value = GetMemberValue(event);
353 if (value.is_string()) {
354 *str = value.as_string();
355 return true;
356 }
357 return false;
358 case QUERY_STRING:
359 *str = string_;
360 return true;
361 default:
362 return false;
363 }
364 }
365
366 base::debug::TraceValue Query::GetMemberValue(const TraceEvent& event) const {
367 CHECK(type_ == QUERY_EVENT_MEMBER);
368
369 // This could be a request for a member of |event| or a member of |event|'s
370 // associated event. Store the target event in the_event:
371 const TraceEvent* the_event = (member_ < OTHER_PID) ?
372 &event : event.other_event;
373
374 // Request for member of associated event, but there is no associated event.
375 if (!the_event)
376 return base::debug::TraceValue();
377
378 switch (member_) {
379 case EVENT_PID:
380 case OTHER_PID:
381 return static_cast<double>(the_event->pid_tid.pid);
382 case EVENT_TID:
383 case OTHER_TID:
384 return static_cast<double>(the_event->pid_tid.tid);
385 case EVENT_TIME:
386 case OTHER_TIME:
387 return the_event->timestamp;
388 case EVENT_DURATION:
389 {
390 double duration;
391 if (the_event->GetAbsTimeToOtherEvent(&duration))
392 return duration;
393 else
394 return base::debug::TraceValue();
395 }
396 case EVENT_PHASE:
397 case OTHER_PHASE:
398 return static_cast<double>(the_event->phase);
399 case EVENT_CATEGORY:
400 case OTHER_CATEGORY:
401 return the_event->category;
402 case EVENT_NAME:
403 case OTHER_NAME:
404 return the_event->name;
405 case EVENT_HAS_ARG:
406 case OTHER_HAS_ARG:
407 // Search for the argument name and return true if found.
408 return static_cast<double>((the_event->arg_strings.find(string_) !=
409 the_event->arg_strings.end()) ||
410 (the_event->arg_numbers.find(string_) !=
411 the_event->arg_numbers.end()) ? 1 : 0);
412 case EVENT_ARG:
413 case OTHER_ARG:
414 {
415 // Search for the argument name and return its value if found.
416
417 std::map<std::string, std::string>::const_iterator str_i =
418 the_event->arg_strings.find(string_);
419 if (str_i != the_event->arg_strings.end())
420 return str_i->second;
421
422 std::map<std::string, double>::const_iterator num_i =
423 the_event->arg_numbers.find(string_);
424 if (num_i != the_event->arg_numbers.end())
425 return num_i->second;
426
427 return base::debug::TraceValue();
428 }
429 case EVENT_HAS_OTHER:
430 {
431 // return 1.0 (true) if the other event exists
432 double result = event.other_event ? 1.0 : 0.0;
433 return result;
434 }
435 default:
436 NOTREACHED();
437 return base::debug::TraceValue();
438 }
439 }
440
441 Query::Query(const std::string& str)
442 : type_(QUERY_STRING),
443 operator_(OP_INVALID),
444 member_(EVENT_INVALID),
445 number_(0),
446 string_(str),
447 is_pattern_(false) {
448 }
449
450 Query::Query(double num)
451 : type_(QUERY_NUMBER),
452 operator_(OP_INVALID),
453 member_(EVENT_INVALID),
454 number_(num),
455 is_pattern_(false) {
456 }
457 const Query& Query::left() const {
458 return left_->query();
459 }
460
461 const Query& Query::right() const {
462 return right_->query();
463 }
464
465 Query Query::operator==(const Query& rhs) const {
466 return Query(*this, rhs, OP_EQ);
467 }
468
469 Query Query::operator!=(const Query& rhs) const {
470 return Query(*this, rhs, OP_NE);
471 }
472
473 Query Query::operator<(const Query& rhs) const {
474 return Query(*this, rhs, OP_LT);
475 }
476
477 Query Query::operator<=(const Query& rhs) const {
478 return Query(*this, rhs, OP_LE);
479 }
480
481 Query Query::operator>(const Query& rhs) const {
482 return Query(*this, rhs, OP_GT);
483 }
484
485 Query Query::operator>=(const Query& rhs) const {
486 return Query(*this, rhs, OP_GE);
487 }
488
489 Query Query::operator&&(const Query& rhs) const {
490 return Query(*this, rhs, OP_AND);
491 }
492
493 Query Query::operator||(const Query& rhs) const {
494 return Query(*this, rhs, OP_OR);
495 }
496
497 Query Query::operator!() const {
498 return Query(*this, OP_NOT);
499 }
500
501 Query Query::operator+(const Query& rhs) const {
502 return Query(*this, rhs, OP_ADD);
503 }
504
505 Query Query::operator-(const Query& rhs) const {
506 return Query(*this, rhs, OP_SUB);
507 }
508
509 Query Query::operator*(const Query& rhs) const {
510 return Query(*this, rhs, OP_MUL);
511 }
512
513 Query Query::operator/(const Query& rhs) const {
514 return Query(*this, rhs, OP_DIV);
515 }
516
517 Query Query::operator%(const Query& rhs) const {
518 return Query(*this, rhs, OP_MOD);
519 }
520
521 Query Query::operator-() const {
522 return Query(*this, OP_NEGATE);
523 }
524
525
526 Query::Query(const Query& left, const Query& right, Operator binary_op)
527 : operator_(binary_op),
528 left_(new QueryNode(left)),
529 right_(new QueryNode(right)),
530 member_(EVENT_INVALID),
531 number_(0) {
532 type_ = (binary_op < OP_ADD ?
533 QUERY_BOOLEAN_OPERATOR : QUERY_ARITHMETIC_OPERATOR);
534 }
535
536 Query::Query(const Query& left, Operator unary_op)
537 : operator_(unary_op),
538 left_(new QueryNode(left)),
539 member_(EVENT_INVALID),
540 number_(0) {
541 type_ = (unary_op < OP_ADD ?
542 QUERY_BOOLEAN_OPERATOR : QUERY_ARITHMETIC_OPERATOR);
543 }
544
545 namespace {
546
547 // Search |events| for |query| and add matches to |output|.
548 size_t FindMatchingEvents(const std::vector<TraceEvent>& events,
549 const Query& query,
550 TraceAnalyzer::TraceEventVector* output) {
551 for (size_t i = 0; i < events.size(); ++i) {
552 if (query.Evaluate(events[i]))
553 output->push_back(&events[i]);
554 }
555 return output->size();
556 }
557
558 bool ParseEventsFromJson(const std::string& json,
559 std::vector<TraceEvent>* output) {
560 scoped_ptr<base::Value> root;
561 root.reset(base::JSONReader::Read(json, false));
562
563 ListValue* root_list = NULL;
564 if (!root.get() || !root->GetAsList(&root_list))
565 return false;
566
567 for (size_t i = 0; i < root_list->GetSize(); ++i) {
568 Value* item = NULL;
569 if (root_list->Get(i, &item)) {
570 TraceEvent event;
571 if (event.SetFromJSON(item))
572 output->push_back(event);
573 else
574 return false;
575 }
576 }
577
578 return true;
579 }
580
581 } // namespace
582
583 // TraceAnalyzer
584
585 TraceAnalyzer::TraceAnalyzer() : allow_assocation_changes_(true) {
586 }
587
588 TraceAnalyzer::~TraceAnalyzer() {
589 }
590
591 // static
592 TraceAnalyzer* TraceAnalyzer::Create(const std::string& json_events) {
593 scoped_ptr<TraceAnalyzer> analyzer(new TraceAnalyzer());
594 if (analyzer->SetEvents(json_events))
595 return analyzer.release();
596 return NULL;
597 }
598
599 bool TraceAnalyzer::SetEvents(const std::string& json_events) {
600 raw_events_.clear();
601 if (!ParseEventsFromJson(json_events, &raw_events_))
602 return false;
603 std::stable_sort(raw_events_.begin(), raw_events_.end());
604 ParseMetadata();
605 return true;
606 }
607
608 void TraceAnalyzer::AssociateBeginEndEvents() {
609 using namespace trace_analyzer;
610
611 Query begin(Query(EVENT_PHASE) ==
612 Query::Phase(base::debug::TRACE_EVENT_PHASE_BEGIN));
613 Query end(Query(EVENT_PHASE) ==
614 Query::Phase(base::debug::TRACE_EVENT_PHASE_END));
615 Query match(Query(EVENT_NAME) == Query(OTHER_NAME) &&
616 Query(EVENT_CATEGORY) == Query(OTHER_CATEGORY) &&
617 Query(EVENT_TID) == Query(OTHER_TID) &&
618 Query(EVENT_PID) == Query(OTHER_PID));
619
620 AssociateEvents(begin, end, match);
621 }
622
623 void TraceAnalyzer::AssociateEvents(const Query& first,
624 const Query& second,
625 const Query& match) {
626 CHECK(allow_assocation_changes_) << "AssociateEvents not allowed after "
627 "FindEvents";
628
629 // Search for matching begin/end event pairs. When a matching end is found,
630 // it is associated with the begin event.
631 std::vector<TraceEvent*> begin_stack;
632 for (size_t input_i = 0; input_i < raw_events_.size(); ++input_i) {
jar (doing other things) 2011/10/26 19:35:20 nit: please use either i and j for integer index,
jbates 2011/10/26 21:51:54 Done.
633
634 TraceEvent& this_event = raw_events_[input_i];
635
636 if (first.Evaluate(this_event)) {
637 begin_stack.push_back(&this_event);
638 } else if (second.Evaluate(this_event)) {
639 // Search stack for matching begin, starting from end.
640 for (int si = static_cast<int>(begin_stack.size()) - 1; si >= 0; --si) {
jar (doing other things) 2011/10/26 19:35:20 nit: variable name |si| had me looking all over.
jbates 2011/10/26 21:51:54 Done.
641 TraceEvent& begin_event = *begin_stack[si];
642
643 // Temporarily set other to test against the match query.
644 const TraceEvent* other_backup = begin_event.other_event;
645 begin_event.other_event = &this_event;
646 if (match.Evaluate(begin_event)) {
647 // Found a matching begin/end pair.
648 // Set event association:
649 this_event.other_event = &begin_event;
650 // Erase the matching begin event index from the stack.
651 begin_stack.erase(begin_stack.begin() + si);
652 break;
653 }
654
655 // Not a match, restore original other and continue.
656 begin_event.other_event = other_backup;
657 }
658 }
659 }
660 }
661
662 size_t TraceAnalyzer::FindEvents(const Query& query, TraceEventVector* output) {
663 allow_assocation_changes_ = false;
664 output->clear();
665 return FindMatchingEvents(raw_events_, query, output);
666 }
667
668 const TraceEvent* TraceAnalyzer::FindOneEvent(const Query& query) {
669 TraceEventVector output;
670 if (FindEvents(query, &output) > 0)
671 return output.front();
672 return NULL;
673 }
674
675 const std::string& TraceAnalyzer::GetThreadName(
676 const TraceEvent::PidTid& pid_tid) {
677 // If pid_tid is not found, just add and return empty string.
678 return thread_names_[pid_tid];
679 }
680
681 void TraceAnalyzer::ParseMetadata() {
682 for (size_t i = 0; i < raw_events_.size(); ++i) {
683 TraceEvent& this_event = raw_events_[i];
684 if (this_event.phase == base::debug::TRACE_EVENT_PHASE_METADATA) {
jar (doing other things) 2011/10/26 19:35:20 nit: for readability, nested ifs are hard to grok.
jbates 2011/10/26 21:51:54 Done.
685 // Check for thread name metadata.
686 if (this_event.name == "thread_name") {
687 std::map<std::string, std::string>::const_iterator string_i =
688 this_event.arg_strings.find("name");
689 if (string_i != this_event.arg_strings.end())
jar (doing other things) 2011/10/26 19:35:20 How do you deal with naming of worker threads?
jbates 2011/10/26 21:51:54 They show as "WorkerPool/3478" in about:tracing wi
690 thread_names_[this_event.pid_tid] = string_i->second;
691 }
692 }
693 }
694 }
695
696 } // namespace trace_analyzer
697
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698