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

Side by Side Diff: chrome/test/base/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: cleanup 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 "chrome/test/base/trace_event_analyzer.h"
Paweł Hajdan Jr. 2011/10/13 19:50:43 Why is this in chrome and not base or maybe conten
jbates 2011/10/13 19:56:01 It used to be in base/debug. It probably makes sen
jbates 2011/10/14 03:41:20 Moved to base/test.
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(const base::Value* event_value)
25 : pid_tid(0, 0),
26 timestamp(0),
27 phase(base::debug::TRACE_EVENT_PHASE_BEGIN),
28 other_event(NULL) {
29 if (event_value->GetType() != base::Value::TYPE_DICTIONARY)
30 return;
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", &pid_tid.pid) &&
38 dictionary->GetInteger("tid", &pid_tid.tid) &&
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 base::debug::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 }
65 }
66 }
67 }
68
69 TraceEvent::~TraceEvent() {
70 }
71
72 bool TraceEvent::GetDuration(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(float num) {
149 *this = Query(static_cast<double>(num));
150 }
151
152 Query::Query(int num) {
153 *this = Query(static_cast<double>(num));
154 }
155
156 Query::Query(uint32 num) {
157 *this = Query(static_cast<double>(num));
158 }
159
160 Query::Query(bool boolean) {
161 *this = Query(static_cast<double>(boolean ? 1 : 0));
162 }
163
164 Query::Query(base::debug::TraceEventPhase phase) {
165 *this = Query(static_cast<double>(phase));
166 }
167
168 Query::Query(const Query& query)
169 : type_(query.type_),
170 operator_(query.operator_),
171 left_(query.left_),
172 right_(query.right_),
173 member_(query.member_),
174 number_(query.number_),
175 string_(query.string_),
176 is_pattern_(query.is_pattern_) {
177 }
178
179 Query::~Query() {
180 }
181
182 // static
183 Query Query::Pattern(const std::string& pattern) {
184 Query query(pattern);
185 query.is_pattern_ = true;
186 return query;
187 }
188
189 bool Query::Evaluate(const TraceEvent& event) const {
190 // First check for values that can convert to bool.
191
192 // double is true if != 0:
193 double bool_value = 0.0;
194 bool is_bool = GetAsDouble(event, &bool_value);
195 if (is_bool)
196 return (bool_value != 0.0);
197
198 // string is true if it is non-empty:
199 std::string str_value;
200 bool is_str = GetAsString(event, &str_value);
201 if (is_str)
202 return !str_value.empty();
203
204 CHECK(type_ == QUERY_BOOLEAN_OPERATOR)
205 << "Invalid query: missing boolean expression";
206 CHECK(left_.get() && (right_.get() || is_unary_operator()));
207
208 if (operator_ < OP_AND) {
209 CHECK(left().is_value() && right().is_value())
210 << "Invalid query: comparison operator used between event member and "
211 "value.";
212 bool compare_result = false;
213 if (CompareAsDouble(event, &compare_result))
214 return compare_result;
215 else if (CompareAsString(event, &compare_result))
216 return compare_result;
217 return false;
218 } else {
219 switch (operator_) {
220 case OP_AND:
221 return left().Evaluate(event) && right().Evaluate(event);
222 case OP_OR:
223 return left().Evaluate(event) || right().Evaluate(event);
224 case OP_NOT:
225 return !left().Evaluate(event);
226 default:
227 NOTREACHED();
228 }
229 }
230
231 NOTREACHED();
232 return false;
233 }
234
235 bool Query::CompareAsDouble(const TraceEvent& event, bool* result) const {
236 double lhs, rhs;
237 if (!left().GetAsDouble(event, &lhs) || !right().GetAsDouble(event, &rhs))
238 return false;
239 switch (operator_) {
240 case OP_EQ:
241 *result = (lhs == rhs);
242 return true;
243 case OP_NE:
244 *result = (lhs != rhs);
245 return true;
246 case OP_LT:
247 *result = (lhs < rhs);
248 return true;
249 case OP_LE:
250 *result = (lhs <= rhs);
251 return true;
252 case OP_GT:
253 *result = (lhs > rhs);
254 return true;
255 case OP_GE:
256 *result = (lhs >= rhs);
257 return true;
258 default:
259 NOTREACHED();
260 return false;
261 }
262 return true;
263 }
264
265 bool Query::CompareAsString(const TraceEvent& event, bool* result) const {
266 std::string lhs, rhs;
267 if (!left().GetAsString(event, &lhs) || !right().GetAsString(event, &rhs))
268 return false;
269 switch (operator_) {
270 case OP_EQ:
271 if (right().is_pattern_)
272 *result = MatchPattern(lhs, rhs);
273 else if (left().is_pattern_)
274 *result = MatchPattern(rhs, lhs);
275 else
276 *result = (lhs == rhs);
277 return true;
278 case OP_NE:
279 if (right().is_pattern_)
280 *result = !MatchPattern(lhs, rhs);
281 else if (left().is_pattern_)
282 *result = !MatchPattern(rhs, lhs);
283 else
284 *result = (lhs != rhs);
285 return true;
286 case OP_LT:
287 *result = (lhs < rhs);
288 return true;
289 case OP_LE:
290 *result = (lhs <= rhs);
291 return true;
292 case OP_GT:
293 *result = (lhs > rhs);
294 return true;
295 case OP_GE:
296 *result = (lhs >= rhs);
297 return true;
298 default:
299 NOTREACHED();
300 return false;
301 }
302 return true;
303 }
304
305 bool Query::EvaluateArithmeticOperator(const TraceEvent& event,
306 double* num) const {
307 CHECK(type_ == QUERY_ARITHMETIC_OPERATOR);
308 CHECK(left_.get() && (right_.get() || is_unary_operator()));
309
310 double lhs = 0, rhs = 0;
311 if (!left().GetAsDouble(event, &lhs))
312 return false;
313 if (!is_unary_operator() && !right().GetAsDouble(event, &rhs))
314 return false;
315
316 switch (operator_) {
317 case OP_ADD:
318 *num = lhs + rhs;
319 break;
320 case OP_SUB:
321 *num = lhs - rhs;
322 break;
323 case OP_MUL:
324 *num = lhs * rhs;
325 break;
326 case OP_DIV:
327 *num = lhs / rhs;
328 break;
329 case OP_MOD:
330 *num = static_cast<double>(static_cast<int64>(lhs) %
331 static_cast<int64>(rhs));
332 break;
333 case OP_NEGATE:
334 *num = -lhs;
335 break;
336 default:
337 NOTREACHED();
338 return false;
339 }
340
341 return true;
342 }
343
344 bool Query::GetAsDouble(const TraceEvent& event, double* num) const {
345 base::debug::TraceValue value;
346 switch (type_) {
347 case QUERY_ARITHMETIC_OPERATOR:
348 return EvaluateArithmeticOperator(event, num);
349 case QUERY_EVENT_MEMBER:
350 value = GetMemberValue(event);
351 if (value.type() == base::debug::TraceValue::TRACE_TYPE_DOUBLE) {
352 *num = value.as_double();
353 return true;
354 }
355 return false;
356 case QUERY_NUMBER:
357 *num = number_;
358 return true;
359 default:
360 return false;
361 }
362 }
363
364 bool Query::GetAsString(const TraceEvent& event, std::string* str) const {
365 base::debug::TraceValue value;
366 switch (type_) {
367 case QUERY_EVENT_MEMBER:
368 value = GetMemberValue(event);
369 if (value.is_string()) {
370 *str = value.as_string();
371 return true;
372 }
373 return false;
374 case QUERY_STRING:
375 *str = string_;
376 return true;
377 default:
378 return false;
379 }
380 }
381
382 base::debug::TraceValue Query::GetMemberValue(const TraceEvent& event) const {
383 CHECK(type_ == QUERY_EVENT_MEMBER);
384
385 // This could be a request for a member of |event| or a member of |event|'s
386 // associated event. Store the target event in the_event:
387 const TraceEvent* the_event = (member_ < OTHER_PID) ?
388 &event : event.other_event;
389
390 // Request for member of associated event, but there is no associated event.
391 if (!the_event)
392 return base::debug::TraceValue();
393
394 switch (member_) {
395 case EVENT_PID:
396 case OTHER_PID:
397 return static_cast<double>(the_event->pid_tid.pid);
398 case EVENT_TID:
399 case OTHER_TID:
400 return static_cast<double>(the_event->pid_tid.tid);
401 case EVENT_TIME:
402 case OTHER_TIME:
403 return the_event->timestamp;
404 case EVENT_DURATION:
405 {
406 double duration;
407 if (the_event->GetDuration(&duration))
408 return duration;
409 else
410 return base::debug::TraceValue();
411 }
412 case EVENT_PHASE:
413 case OTHER_PHASE:
414 return static_cast<double>(the_event->phase);
415 case EVENT_CATEGORY:
416 case OTHER_CATEGORY:
417 return the_event->category;
418 case EVENT_NAME:
419 case OTHER_NAME:
420 return the_event->name;
421 case EVENT_HAS_ARG:
422 case OTHER_HAS_ARG:
423 // Search for the argument name and return true if found.
424 return static_cast<double>((the_event->arg_strings.find(string_) !=
425 the_event->arg_strings.end()) ||
426 (the_event->arg_numbers.find(string_) !=
427 the_event->arg_numbers.end()) ? 1 : 0);
428 case EVENT_ARG:
429 case OTHER_ARG:
430 {
431 // Search for the argument name and return its value if found.
432
433 std::map<std::string, std::string>::const_iterator str_i =
434 the_event->arg_strings.find(string_);
435 if (str_i != the_event->arg_strings.end())
436 return str_i->second;
437
438 std::map<std::string, double>::const_iterator num_i =
439 the_event->arg_numbers.find(string_);
440 if (num_i != the_event->arg_numbers.end())
441 return num_i->second;
442
443 return base::debug::TraceValue();
444 }
445 case EVENT_HAS_OTHER:
446 {
447 // return 1.0 (true) if the other event exists
448 double result = event.other_event ? 1.0 : 0.0;
449 return result;
450 }
451 default:
452 NOTREACHED();
453 return base::debug::TraceValue();
454 }
455 }
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 output->push_back(TraceEvent(item));
571 }
572
573 return true;
574 }
575
576 } // namespace
577
578 // TraceAnalyzer
579
580 TraceAnalyzer::TraceAnalyzer(const std::string& json_events)
581 : allow_assocation_changes_(true) {
582 bool success = ParseEventsFromJson(json_events, &raw_events_);
583 CHECK(success) << "Invalid JSON string";
Paweł Hajdan Jr. 2011/10/13 19:50:43 Is that really better than returning a bool parame
jbates 2011/10/13 19:56:01 This should never happen. It simplifies the API a
jbates 2011/10/14 03:41:20 Done.
584 std::stable_sort(raw_events_.begin(), raw_events_.end());
585 ParseMetadata();
586 }
587
588 TraceAnalyzer::~TraceAnalyzer() {
589 }
590
591 void TraceAnalyzer::AssociateBeginEndEvents() {
592 using namespace trace_analyzer;
593
594 Query begin(Query(EVENT_PHASE) == base::debug::TRACE_EVENT_PHASE_BEGIN);
595 Query end(Query(EVENT_PHASE) == base::debug::TRACE_EVENT_PHASE_END);
596 Query match(Query(EVENT_NAME) == Query(OTHER_NAME) &&
597 Query(EVENT_CATEGORY) == Query(OTHER_CATEGORY) &&
598 Query(EVENT_TID) == Query(OTHER_TID) &&
599 Query(EVENT_PID) == Query(OTHER_PID));
600
601 AssociateEvents(begin, end, match);
602 }
603
604 void TraceAnalyzer::AssociateEvents(const Query& begin,
605 const Query& end,
606 const Query& match) {
607 CHECK(allow_assocation_changes_) << "AssociateEvents not allowed after "
608 "FindEvents";
609
610 // Search for matching begin/end event pairs. When a matching end is found,
611 // it is associated with the begin event.
612 std::vector<TraceEvent*> begin_stack;
613 for (size_t input_i = 0; input_i < raw_events_.size(); ++input_i) {
614
615 TraceEvent& this_event = raw_events_[input_i];
616
617 if (begin.Evaluate(this_event)) {
618 begin_stack.push_back(&this_event);
619 } else if (end.Evaluate(this_event)) {
620 // Search stack for matching begin, starting from end.
621 for (int si = static_cast<int>(begin_stack.size()) - 1; si >= 0; --si) {
622 TraceEvent& begin_event = *begin_stack[si];
623
624 // Temporarily set other to test against the match query.
625 const TraceEvent* other_backup = begin_event.other_event;
626 begin_event.other_event = &this_event;
627 if (match.Evaluate(begin_event)) {
628 // Found a matching begin/end pair.
629 // Set event association:
630 this_event.other_event = &begin_event;
631 // Erase the matching begin event index from the stack.
632 begin_stack.erase(begin_stack.begin() + si);
633 break;
634 }
635
636 // Not a match, restore original other and continue.
637 begin_event.other_event = other_backup;
638 }
639 }
640 }
641 }
642
643 size_t TraceAnalyzer::FindEvents(const Query& query, TraceEventVector* output) {
644 allow_assocation_changes_ = false;
645 output->clear();
646 return FindMatchingEvents(raw_events_, query, output);
647 }
648
649 const TraceEvent* TraceAnalyzer::FindOneEvent(const Query& query) {
650 TraceEventVector output;
651 if (FindEvents(query, &output) > 0)
652 return output.front();
653 return NULL;
654 }
655
656 const std::string& TraceAnalyzer::GetThreadName(
657 const TraceEvent::PidTid& pid_tid) {
658 // If pid_tid is not found, just add and return empty string.
659 return thread_names_[pid_tid];
660 }
661
662 void TraceAnalyzer::ParseMetadata() {
663 for (size_t i = 0; i < raw_events_.size(); ++i) {
664 TraceEvent& this_event = raw_events_[i];
665 if (this_event.phase == base::debug::TRACE_EVENT_PHASE_METADATA) {
666 // Check for thread name metadata.
667 if (this_event.name == "thread_name") {
668 std::map<std::string, std::string>::const_iterator string_i =
669 this_event.arg_strings.find("name");
670 if (string_i != this_event.arg_strings.end())
671 thread_names_[this_event.pid_tid] = string_i->second;
672 }
673 }
674 }
675 }
676
677 } // namespace trace_analyzer
678
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698