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

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

Powered by Google App Engine
This is Rietveld 408576698