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

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

Powered by Google App Engine
This is Rietveld 408576698