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

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

Powered by Google App Engine
This is Rietveld 408576698