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

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

Powered by Google App Engine
This is Rietveld 408576698