1 // Copyright 2012 The Chromium Authors
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 <math.h>
8
9 #include <algorithm>
10 #include <optional>
11 #include <set>
12
13 #include "base/functional/bind.h"
14 #include "base/json/json_reader.h"
15 #include "base/logging.h"
16 #include "base/memory/ptr_util.h"
17 #include "base/memory/ref_counted_memory.h"
18 #include "base/ranges/algorithm.h"
19 #include "base/run_loop.h"
20 #include "base/strings/pattern.h"
21 #include "base/trace_event/trace_buffer.h"
22 #include "base/trace_event/trace_config.h"
23 #include "base/trace_event/trace_log.h"
24 #include "base/values.h"
25
26 namespace {
OnTraceDataCollected(base::OnceClosure quit_closure,base::trace_event::TraceResultBuffer * buffer,const scoped_refptr<base::RefCountedString> & json,bool has_more_events)27 void OnTraceDataCollected(base::OnceClosure quit_closure,
28 base::trace_event::TraceResultBuffer* buffer,
29 const scoped_refptr<base::RefCountedString>& json,
30 bool has_more_events) {
31 buffer->AddFragment(json->data());
32 if (!has_more_events)
33 std::move(quit_closure).Run();
34 }
35 } // namespace
36
37 namespace trace_analyzer {
38
39 // TraceEvent
40
TraceEvent()41 TraceEvent::TraceEvent() : thread(0, 0) {}
42
43 TraceEvent::TraceEvent(TraceEvent&& other) = default;
44
45 TraceEvent::~TraceEvent() = default;
46
47 TraceEvent& TraceEvent::operator=(TraceEvent&& rhs) = default;
48
SetFromJSON(const base::Value * event_value)49 bool TraceEvent::SetFromJSON(const base::Value* event_value) {
50 if (!event_value->is_dict()) {
51 LOG(ERROR) << "Value must be Type::DICT";
52 return false;
53 }
54
55 const base::Value::Dict& event_dict = event_value->GetDict();
56 const std::string* maybe_phase = event_dict.FindString("ph");
57 if (!maybe_phase) {
58 LOG(ERROR) << "ph is missing from TraceEvent JSON";
59 return false;
60 }
61
62 phase = *maybe_phase->data();
63
64 bool may_have_duration = (phase == TRACE_EVENT_PHASE_COMPLETE);
65 bool require_origin = (phase != TRACE_EVENT_PHASE_METADATA);
66 bool require_id = (phase == TRACE_EVENT_PHASE_ASYNC_BEGIN ||
67 phase == TRACE_EVENT_PHASE_ASYNC_STEP_INTO ||
68 phase == TRACE_EVENT_PHASE_ASYNC_STEP_PAST ||
69 phase == TRACE_EVENT_PHASE_MEMORY_DUMP ||
70 phase == TRACE_EVENT_PHASE_CREATE_OBJECT ||
71 phase == TRACE_EVENT_PHASE_DELETE_OBJECT ||
72 phase == TRACE_EVENT_PHASE_SNAPSHOT_OBJECT ||
73 phase == TRACE_EVENT_PHASE_ASYNC_END ||
74 phase == TRACE_EVENT_PHASE_NESTABLE_ASYNC_BEGIN ||
75 phase == TRACE_EVENT_PHASE_NESTABLE_ASYNC_END);
76
77 if (require_origin) {
78 std::optional<int> maybe_process_id = event_dict.FindInt("pid");
79 if (!maybe_process_id) {
80 LOG(ERROR) << "pid is missing from TraceEvent JSON";
81 return false;
82 }
83 thread.process_id = *maybe_process_id;
84
85 std::optional<int> maybe_thread_id = event_dict.FindInt("tid");
86 if (!maybe_thread_id) {
87 LOG(ERROR) << "tid is missing from TraceEvent JSON";
88 return false;
89 }
90 thread.thread_id = *maybe_thread_id;
91
92 std::optional<double> maybe_timestamp = event_dict.FindDouble("ts");
93 if (!maybe_timestamp) {
94 LOG(ERROR) << "ts is missing from TraceEvent JSON";
95 return false;
96 }
97 timestamp = *maybe_timestamp;
98 }
99 if (may_have_duration) {
100 std::optional<double> maybe_duration = event_dict.FindDouble("dur");
101 if (maybe_duration)
102 duration = *maybe_duration;
103 }
104 const std::string* maybe_category = event_dict.FindString("cat");
105 if (!maybe_category) {
106 LOG(ERROR) << "cat is missing from TraceEvent JSON";
107 return false;
108 }
109 category = *maybe_category;
110 const std::string* maybe_name = event_dict.FindString("name");
111 if (!maybe_name) {
112 LOG(ERROR) << "name is missing from TraceEvent JSON";
113 return false;
114 }
115 name = *maybe_name;
116 const base::Value::Dict* maybe_args = event_dict.FindDict("args");
117 if (!maybe_args) {
118 // If argument filter is enabled, the arguments field contains a string
119 // value.
120 const std::string* maybe_stripped_args = event_dict.FindString("args");
121 if (!maybe_stripped_args || *maybe_stripped_args != "__stripped__") {
122 LOG(ERROR) << "args is missing from TraceEvent JSON";
123 return false;
124 }
125 }
126 const base::Value::Dict* maybe_id2 = nullptr;
127 if (require_id) {
128 const std::string* maybe_id = event_dict.FindString("id");
129 maybe_id2 = event_dict.FindDict("id2");
130 if (!maybe_id && !maybe_id2) {
131 LOG(ERROR)
132 << "id/id2 is missing from ASYNC_BEGIN/ASYNC_END TraceEvent JSON";
133 return false;
134 }
135 if (maybe_id)
136 id = *maybe_id;
137 }
138
139 std::optional<double> maybe_thread_duration = event_dict.FindDouble("tdur");
140 if (maybe_thread_duration) {
141 thread_duration = *maybe_thread_duration;
142 }
143 std::optional<double> maybe_thread_timestamp = event_dict.FindDouble("tts");
144 if (maybe_thread_timestamp) {
145 thread_timestamp = *maybe_thread_timestamp;
146 }
147 const std::string* maybe_scope = event_dict.FindString("scope");
148 if (maybe_scope) {
149 scope = *maybe_scope;
150 }
151 const std::string* maybe_bind_id = event_dict.FindString("bind_id");
152 if (maybe_bind_id) {
153 bind_id = *maybe_bind_id;
154 }
155 std::optional<bool> maybe_flow_out = event_dict.FindBool("flow_out");
156 if (maybe_flow_out) {
157 flow_out = *maybe_flow_out;
158 }
159 std::optional<bool> maybe_flow_in = event_dict.FindBool("flow_in");
160 if (maybe_flow_in) {
161 flow_in = *maybe_flow_in;
162 }
163
164 if (maybe_id2) {
165 const std::string* maybe_global_id2 = maybe_id2->FindString("global");
166 if (maybe_global_id2) {
167 global_id2 = *maybe_global_id2;
168 }
169 const std::string* maybe_local_id2 = maybe_id2->FindString("local");
170 if (maybe_local_id2) {
171 local_id2 = *maybe_local_id2;
172 }
173 }
174
175 // For each argument, copy the type and create a trace_analyzer::TraceValue.
176 // TODO(crbug.com/1303874): Add BINARY and LIST arg types if needed.
177 if (maybe_args) {
178 for (auto pair : *maybe_args) {
179 switch (pair.second.type()) {
180 case base::Value::Type::STRING:
181 arg_strings[pair.first] = pair.second.GetString();
182 break;
183
184 case base::Value::Type::INTEGER:
185 arg_numbers[pair.first] = static_cast<double>(pair.second.GetInt());
186 break;
187
188 case base::Value::Type::BOOLEAN:
189 arg_numbers[pair.first] = pair.second.GetBool() ? 1.0 : 0.0;
190 break;
191
192 case base::Value::Type::DOUBLE:
193 arg_numbers[pair.first] = pair.second.GetDouble();
194 break;
195
196 case base::Value::Type::DICT:
197 arg_dicts[pair.first] = pair.second.GetDict().Clone();
198 break;
199
200 default:
201 break;
202 }
203 }
204 }
205
206 return true;
207 }
208
GetAbsTimeToOtherEvent() const209 double TraceEvent::GetAbsTimeToOtherEvent() const {
210 return fabs(other_event->timestamp - timestamp);
211 }
212
GetArgAsString(const std::string & arg_name,std::string * arg) const213 bool TraceEvent::GetArgAsString(const std::string& arg_name,
214 std::string* arg) const {
215 const auto it = arg_strings.find(arg_name);
216 if (it != arg_strings.end()) {
217 *arg = it->second;
218 return true;
219 }
220 return false;
221 }
222
GetArgAsNumber(const std::string & arg_name,double * arg) const223 bool TraceEvent::GetArgAsNumber(const std::string& arg_name,
224 double* arg) const {
225 const auto it = arg_numbers.find(arg_name);
226 if (it != arg_numbers.end()) {
227 *arg = it->second;
228 return true;
229 }
230 return false;
231 }
232
GetArgAsDict(const std::string & arg_name,base::Value::Dict * arg) const233 bool TraceEvent::GetArgAsDict(const std::string& arg_name,
234 base::Value::Dict* arg) const {
235 const auto it = arg_dicts.find(arg_name);
236 if (it != arg_dicts.end()) {
237 *arg = it->second.Clone();
238 return true;
239 }
240 return false;
241 }
242
HasStringArg(const std::string & arg_name) const243 bool TraceEvent::HasStringArg(const std::string& arg_name) const {
244 return (arg_strings.find(arg_name) != arg_strings.end());
245 }
246
HasNumberArg(const std::string & arg_name) const247 bool TraceEvent::HasNumberArg(const std::string& arg_name) const {
248 return (arg_numbers.find(arg_name) != arg_numbers.end());
249 }
250
HasDictArg(const std::string & arg_name) const251 bool TraceEvent::HasDictArg(const std::string& arg_name) const {
252 return (arg_dicts.find(arg_name) != arg_dicts.end());
253 }
254
GetKnownArgAsString(const std::string & arg_name) const255 std::string TraceEvent::GetKnownArgAsString(const std::string& arg_name) const {
256 std::string arg_string;
257 bool result = GetArgAsString(arg_name, &arg_string);
258 DCHECK(result);
259 return arg_string;
260 }
261
GetKnownArgAsDouble(const std::string & arg_name) const262 double TraceEvent::GetKnownArgAsDouble(const std::string& arg_name) const {
263 double arg_double = 0;
264 bool result = GetArgAsNumber(arg_name, &arg_double);
265 DCHECK(result);
266 return arg_double;
267 }
268
GetKnownArgAsInt(const std::string & arg_name) const269 int TraceEvent::GetKnownArgAsInt(const std::string& arg_name) const {
270 double arg_double = 0;
271 bool result = GetArgAsNumber(arg_name, &arg_double);
272 DCHECK(result);
273 return static_cast<int>(arg_double);
274 }
275
GetKnownArgAsBool(const std::string & arg_name) const276 bool TraceEvent::GetKnownArgAsBool(const std::string& arg_name) const {
277 double arg_double = 0;
278 bool result = GetArgAsNumber(arg_name, &arg_double);
279 DCHECK(result);
280 return (arg_double != 0.0);
281 }
282
GetKnownArgAsDict(const std::string & arg_name) const283 base::Value::Dict TraceEvent::GetKnownArgAsDict(
284 const std::string& arg_name) const {
285 base::Value::Dict arg_dict;
286 bool result = GetArgAsDict(arg_name, &arg_dict);
287 DCHECK(result);
288 return arg_dict;
289 }
290
291 // QueryNode
292
QueryNode(const Query & query)293 QueryNode::QueryNode(const Query& query) : query_(query) {
294 }
295
296 QueryNode::~QueryNode() = default;
297
298 // Query
299
Query(TraceEventMember member)300 Query::Query(TraceEventMember member)
301 : type_(QUERY_EVENT_MEMBER),
302 operator_(OP_INVALID),
303 member_(member),
304 number_(0),
305 is_pattern_(false) {
306 }
307
Query(TraceEventMember member,const std::string & arg_name)308 Query::Query(TraceEventMember member, const std::string& arg_name)
309 : type_(QUERY_EVENT_MEMBER),
310 operator_(OP_INVALID),
311 member_(member),
312 number_(0),
313 string_(arg_name),
314 is_pattern_(false) {
315 }
316
317 Query::Query(const Query& query) = default;
318
319 Query::~Query() = default;
320
String(const std::string & str)321 Query Query::String(const std::string& str) {
322 return Query(str);
323 }
324
Double(double num)325 Query Query::Double(double num) {
326 return Query(num);
327 }
328
Int(int32_t num)329 Query Query::Int(int32_t num) {
330 return Query(static_cast<double>(num));
331 }
332
Uint(uint32_t num)333 Query Query::Uint(uint32_t num) {
334 return Query(static_cast<double>(num));
335 }
336
Bool(bool boolean)337 Query Query::Bool(bool boolean) {
338 return Query(boolean ? 1.0 : 0.0);
339 }
340
Phase(char phase)341 Query Query::Phase(char phase) {
342 return Query(static_cast<double>(phase));
343 }
344
Pattern(const std::string & pattern)345 Query Query::Pattern(const std::string& pattern) {
346 Query query(pattern);
347 query.is_pattern_ = true;
348 return query;
349 }
350
Evaluate(const TraceEvent & event) const351 bool Query::Evaluate(const TraceEvent& event) const {
352 // First check for values that can convert to bool.
353
354 // double is true if != 0:
355 double bool_value = 0.0;
356 bool is_bool = GetAsDouble(event, &bool_value);
357 if (is_bool)
358 return (bool_value != 0.0);
359
360 // string is true if it is non-empty:
361 std::string str_value;
362 bool is_str = GetAsString(event, &str_value);
363 if (is_str)
364 return !str_value.empty();
365
366 DCHECK_EQ(QUERY_BOOLEAN_OPERATOR, type_)
367 << "Invalid query: missing boolean expression";
368 DCHECK(left_.get());
369 DCHECK(right_.get() || is_unary_operator());
370
371 if (is_comparison_operator()) {
372 DCHECK(left().is_value() && right().is_value())
373 << "Invalid query: comparison operator used between event member and "
374 "value.";
375 bool compare_result = false;
376 if (CompareAsDouble(event, &compare_result))
377 return compare_result;
378 if (CompareAsString(event, &compare_result))
379 return compare_result;
380 return false;
381 }
382 // It's a logical operator.
383 switch (operator_) {
384 case OP_AND:
385 return left().Evaluate(event) && right().Evaluate(event);
386 case OP_OR:
387 return left().Evaluate(event) || right().Evaluate(event);
388 case OP_NOT:
389 return !left().Evaluate(event);
390 default:
391 NOTREACHED();
392 return false;
393 }
394 }
395
CompareAsDouble(const TraceEvent & event,bool * result) const396 bool Query::CompareAsDouble(const TraceEvent& event, bool* result) const {
397 double lhs, rhs;
398 if (!left().GetAsDouble(event, &lhs) || !right().GetAsDouble(event, &rhs))
399 return false;
400 switch (operator_) {
401 case OP_EQ:
402 *result = (lhs == rhs);
403 return true;
404 case OP_NE:
405 *result = (lhs != rhs);
406 return true;
407 case OP_LT:
408 *result = (lhs < rhs);
409 return true;
410 case OP_LE:
411 *result = (lhs <= rhs);
412 return true;
413 case OP_GT:
414 *result = (lhs > rhs);
415 return true;
416 case OP_GE:
417 *result = (lhs >= rhs);
418 return true;
419 default:
420 NOTREACHED();
421 return false;
422 }
423 }
424
CompareAsString(const TraceEvent & event,bool * result) const425 bool Query::CompareAsString(const TraceEvent& event, bool* result) const {
426 std::string lhs, rhs;
427 if (!left().GetAsString(event, &lhs) || !right().GetAsString(event, &rhs))
428 return false;
429 switch (operator_) {
430 case OP_EQ:
431 if (right().is_pattern_)
432 *result = base::MatchPattern(lhs, rhs);
433 else if (left().is_pattern_)
434 *result = base::MatchPattern(rhs, lhs);
435 else
436 *result = (lhs == rhs);
437 return true;
438 case OP_NE:
439 if (right().is_pattern_)
440 *result = !base::MatchPattern(lhs, rhs);
441 else if (left().is_pattern_)
442 *result = !base::MatchPattern(rhs, lhs);
443 else
444 *result = (lhs != rhs);
445 return true;
446 case OP_LT:
447 *result = (lhs < rhs);
448 return true;
449 case OP_LE:
450 *result = (lhs <= rhs);
451 return true;
452 case OP_GT:
453 *result = (lhs > rhs);
454 return true;
455 case OP_GE:
456 *result = (lhs >= rhs);
457 return true;
458 default:
459 NOTREACHED();
460 return false;
461 }
462 }
463
EvaluateArithmeticOperator(const TraceEvent & event,double * num) const464 bool Query::EvaluateArithmeticOperator(const TraceEvent& event,
465 double* num) const {
466 DCHECK_EQ(QUERY_ARITHMETIC_OPERATOR, type_);
467 DCHECK(left_.get());
468 DCHECK(right_.get() || is_unary_operator());
469
470 double lhs = 0, rhs = 0;
471 if (!left().GetAsDouble(event, &lhs))
472 return false;
473 if (!is_unary_operator() && !right().GetAsDouble(event, &rhs))
474 return false;
475
476 switch (operator_) {
477 case OP_ADD:
478 *num = lhs + rhs;
479 return true;
480 case OP_SUB:
481 *num = lhs - rhs;
482 return true;
483 case OP_MUL:
484 *num = lhs * rhs;
485 return true;
486 case OP_DIV:
487 *num = lhs / rhs;
488 return true;
489 case OP_MOD:
490 *num = static_cast<double>(static_cast<int64_t>(lhs) %
491 static_cast<int64_t>(rhs));
492 return true;
493 case OP_NEGATE:
494 *num = -lhs;
495 return true;
496 default:
497 NOTREACHED();
498 return false;
499 }
500 }
501
GetAsDouble(const TraceEvent & event,double * num) const502 bool Query::GetAsDouble(const TraceEvent& event, double* num) const {
503 switch (type_) {
504 case QUERY_ARITHMETIC_OPERATOR:
505 return EvaluateArithmeticOperator(event, num);
506 case QUERY_EVENT_MEMBER:
507 return GetMemberValueAsDouble(event, num);
508 case QUERY_NUMBER:
509 *num = number_;
510 return true;
511 default:
512 return false;
513 }
514 }
515
GetAsString(const TraceEvent & event,std::string * str) const516 bool Query::GetAsString(const TraceEvent& event, std::string* str) const {
517 switch (type_) {
518 case QUERY_EVENT_MEMBER:
519 return GetMemberValueAsString(event, str);
520 case QUERY_STRING:
521 *str = string_;
522 return true;
523 default:
524 return false;
525 }
526 }
527
SelectTargetEvent(const TraceEvent * event,TraceEventMember member)528 const TraceEvent* Query::SelectTargetEvent(const TraceEvent* event,
529 TraceEventMember member) {
530 if (member >= OTHER_FIRST_MEMBER && member <= OTHER_LAST_MEMBER)
531 return event->other_event;
532 if (member >= PREV_FIRST_MEMBER && member <= PREV_LAST_MEMBER)
533 return event->prev_event;
534 return event;
535 }
536
GetMemberValueAsDouble(const TraceEvent & event,double * num) const537 bool Query::GetMemberValueAsDouble(const TraceEvent& event,
538 double* num) const {
539 DCHECK_EQ(QUERY_EVENT_MEMBER, type_);
540
541 // This could be a request for a member of |event| or a member of |event|'s
542 // associated previous or next event. Store the target event in the_event:
543 const TraceEvent* the_event = SelectTargetEvent(&event, member_);
544
545 // Request for member of associated event, but there is no associated event.
546 if (!the_event)
547 return false;
548
549 switch (member_) {
550 case EVENT_PID:
551 case OTHER_PID:
552 case PREV_PID:
553 *num = static_cast<double>(the_event->thread.process_id);
554 return true;
555 case EVENT_TID:
556 case OTHER_TID:
557 case PREV_TID:
558 *num = static_cast<double>(the_event->thread.thread_id);
559 return true;
560 case EVENT_TIME:
561 case OTHER_TIME:
562 case PREV_TIME:
563 *num = the_event->timestamp;
564 return true;
565 case EVENT_DURATION:
566 if (!the_event->has_other_event())
567 return false;
568 *num = the_event->GetAbsTimeToOtherEvent();
569 return true;
570 case EVENT_COMPLETE_DURATION:
571 if (the_event->phase != TRACE_EVENT_PHASE_COMPLETE)
572 return false;
573 *num = the_event->duration;
574 return true;
575 case EVENT_PHASE:
576 case OTHER_PHASE:
577 case PREV_PHASE:
578 *num = static_cast<double>(the_event->phase);
579 return true;
580 case EVENT_HAS_STRING_ARG:
581 case OTHER_HAS_STRING_ARG:
582 case PREV_HAS_STRING_ARG:
583 *num = (the_event->HasStringArg(string_) ? 1.0 : 0.0);
584 return true;
585 case EVENT_HAS_NUMBER_ARG:
586 case OTHER_HAS_NUMBER_ARG:
587 case PREV_HAS_NUMBER_ARG:
588 *num = (the_event->HasNumberArg(string_) ? 1.0 : 0.0);
589 return true;
590 case EVENT_ARG:
591 case OTHER_ARG:
592 case PREV_ARG: {
593 // Search for the argument name and return its value if found.
594 auto num_i = the_event->arg_numbers.find(string_);
595 if (num_i == the_event->arg_numbers.end())
596 return false;
597 *num = num_i->second;
598 return true;
599 }
600 case EVENT_HAS_OTHER:
601 // return 1.0 (true) if the other event exists
602 *num = event.other_event ? 1.0 : 0.0;
603 return true;
604 case EVENT_HAS_PREV:
605 *num = event.prev_event ? 1.0 : 0.0;
606 return true;
607 default:
608 return false;
609 }
610 }
611
GetMemberValueAsString(const TraceEvent & event,std::string * str) const612 bool Query::GetMemberValueAsString(const TraceEvent& event,
613 std::string* str) const {
614 DCHECK_EQ(QUERY_EVENT_MEMBER, type_);
615
616 // This could be a request for a member of |event| or a member of |event|'s
617 // associated previous or next event. Store the target event in the_event:
618 const TraceEvent* the_event = SelectTargetEvent(&event, member_);
619
620 // Request for member of associated event, but there is no associated event.
621 if (!the_event)
622 return false;
623
624 switch (member_) {
625 case EVENT_CATEGORY:
626 case OTHER_CATEGORY:
627 case PREV_CATEGORY:
628 *str = the_event->category;
629 return true;
630 case EVENT_NAME:
631 case OTHER_NAME:
632 case PREV_NAME:
633 *str = the_event->name;
634 return true;
635 case EVENT_ID:
636 case OTHER_ID:
637 case PREV_ID:
638 *str = the_event->id;
639 return true;
640 case EVENT_ARG:
641 case OTHER_ARG:
642 case PREV_ARG: {
643 // Search for the argument name and return its value if found.
644 auto str_i = the_event->arg_strings.find(string_);
645 if (str_i == the_event->arg_strings.end())
646 return false;
647 *str = str_i->second;
648 return true;
649 }
650 default:
651 return false;
652 }
653 }
654
Query(const std::string & str)655 Query::Query(const std::string& str)
656 : type_(QUERY_STRING),
657 operator_(OP_INVALID),
658 member_(EVENT_INVALID),
659 number_(0),
660 string_(str),
661 is_pattern_(false) {
662 }
663
Query(double num)664 Query::Query(double num)
665 : type_(QUERY_NUMBER),
666 operator_(OP_INVALID),
667 member_(EVENT_INVALID),
668 number_(num),
669 is_pattern_(false) {
670 }
left() const671 const Query& Query::left() const {
672 return left_->query();
673 }
674
right() const675 const Query& Query::right() const {
676 return right_->query();
677 }
678
operator ==(const Query & rhs) const679 Query Query::operator==(const Query& rhs) const {
680 return Query(*this, rhs, OP_EQ);
681 }
682
operator !=(const Query & rhs) const683 Query Query::operator!=(const Query& rhs) const {
684 return Query(*this, rhs, OP_NE);
685 }
686
operator <(const Query & rhs) const687 Query Query::operator<(const Query& rhs) const {
688 return Query(*this, rhs, OP_LT);
689 }
690
operator <=(const Query & rhs) const691 Query Query::operator<=(const Query& rhs) const {
692 return Query(*this, rhs, OP_LE);
693 }
694
operator >(const Query & rhs) const695 Query Query::operator>(const Query& rhs) const {
696 return Query(*this, rhs, OP_GT);
697 }
698
operator >=(const Query & rhs) const699 Query Query::operator>=(const Query& rhs) const {
700 return Query(*this, rhs, OP_GE);
701 }
702
operator &&(const Query & rhs) const703 Query Query::operator&&(const Query& rhs) const {
704 return Query(*this, rhs, OP_AND);
705 }
706
operator ||(const Query & rhs) const707 Query Query::operator||(const Query& rhs) const {
708 return Query(*this, rhs, OP_OR);
709 }
710
operator !() const711 Query Query::operator!() const {
712 return Query(*this, OP_NOT);
713 }
714
operator +(const Query & rhs) const715 Query Query::operator+(const Query& rhs) const {
716 return Query(*this, rhs, OP_ADD);
717 }
718
operator -(const Query & rhs) const719 Query Query::operator-(const Query& rhs) const {
720 return Query(*this, rhs, OP_SUB);
721 }
722
operator *(const Query & rhs) const723 Query Query::operator*(const Query& rhs) const {
724 return Query(*this, rhs, OP_MUL);
725 }
726
operator /(const Query & rhs) const727 Query Query::operator/(const Query& rhs) const {
728 return Query(*this, rhs, OP_DIV);
729 }
730
operator %(const Query & rhs) const731 Query Query::operator%(const Query& rhs) const {
732 return Query(*this, rhs, OP_MOD);
733 }
734
operator -() const735 Query Query::operator-() const {
736 return Query(*this, OP_NEGATE);
737 }
738
739
Query(const Query & left,const Query & right,Operator binary_op)740 Query::Query(const Query& left, const Query& right, Operator binary_op)
741 : operator_(binary_op),
742 left_(new QueryNode(left)),
743 right_(new QueryNode(right)),
744 member_(EVENT_INVALID),
745 number_(0) {
746 type_ = (binary_op < OP_ADD ?
747 QUERY_BOOLEAN_OPERATOR : QUERY_ARITHMETIC_OPERATOR);
748 }
749
Query(const Query & left,Operator unary_op)750 Query::Query(const Query& left, Operator unary_op)
751 : operator_(unary_op),
752 left_(new QueryNode(left)),
753 member_(EVENT_INVALID),
754 number_(0) {
755 type_ = (unary_op < OP_ADD ?
756 QUERY_BOOLEAN_OPERATOR : QUERY_ARITHMETIC_OPERATOR);
757 }
758
759 namespace {
760
761 // Search |events| for |query| and add matches to |output|.
FindMatchingEvents(const std::vector<TraceEvent> & events,const Query & query,TraceEventVector * output,bool ignore_metadata_events)762 size_t FindMatchingEvents(const std::vector<TraceEvent>& events,
763 const Query& query,
764 TraceEventVector* output,
765 bool ignore_metadata_events) {
766 for (const auto& i : events) {
767 if (ignore_metadata_events && i.phase == TRACE_EVENT_PHASE_METADATA)
768 continue;
769 if (query.Evaluate(i))
770 output->push_back(&i);
771 }
772 return output->size();
773 }
774
ParseEventsFromJson(const std::string & json,std::vector<TraceEvent> * output)775 bool ParseEventsFromJson(const std::string& json,
776 std::vector<TraceEvent>* output) {
777 std::optional<base::Value> root = base::JSONReader::Read(json);
778
779 if (!root)
780 return false;
781
782 base::Value::List* list = nullptr;
783 if (root->is_list()) {
784 list = &root->GetList();
785 } else if (root->is_dict()) {
786 list = root->GetDict().FindList("traceEvents");
787 }
788 if (!list)
789 return false;
790
791 for (const auto& item : *list) {
792 TraceEvent event;
793 if (!event.SetFromJSON(&item))
794 return false;
795 output->push_back(std::move(event));
796 }
797
798 return true;
799 }
800
801 } // namespace
802
803 // TraceAnalyzer
804
TraceAnalyzer()805 TraceAnalyzer::TraceAnalyzer()
806 : ignore_metadata_events_(false), allow_association_changes_(true) {}
807
808 TraceAnalyzer::~TraceAnalyzer() = default;
809
810 // static
Create(const std::string & json_events)811 std::unique_ptr<TraceAnalyzer> TraceAnalyzer::Create(
812 const std::string& json_events) {
813 std::unique_ptr<TraceAnalyzer> analyzer(new TraceAnalyzer());
814 if (analyzer->SetEvents(json_events))
815 return analyzer;
816 return nullptr;
817 }
818
SetEvents(const std::string & json_events)819 bool TraceAnalyzer::SetEvents(const std::string& json_events) {
820 raw_events_.clear();
821 if (!ParseEventsFromJson(json_events, &raw_events_))
822 return false;
823 base::ranges::stable_sort(raw_events_);
824 ParseMetadata();
825 return true;
826 }
827
AssociateBeginEndEvents()828 void TraceAnalyzer::AssociateBeginEndEvents() {
829 using trace_analyzer::Query;
830
831 Query begin(Query::EventPhaseIs(TRACE_EVENT_PHASE_BEGIN));
832 Query end(Query::EventPhaseIs(TRACE_EVENT_PHASE_END));
833 Query match(Query::EventName() == Query::OtherName() &&
834 Query::EventCategory() == Query::OtherCategory() &&
835 Query::EventTid() == Query::OtherTid() &&
836 Query::EventPid() == Query::OtherPid());
837
838 AssociateEvents(begin, end, match);
839 }
840
AssociateAsyncBeginEndEvents(bool match_pid)841 void TraceAnalyzer::AssociateAsyncBeginEndEvents(bool match_pid) {
842 using trace_analyzer::Query;
843
844 Query begin(Query::EventPhaseIs(TRACE_EVENT_PHASE_ASYNC_BEGIN) ||
845 Query::EventPhaseIs(TRACE_EVENT_PHASE_ASYNC_STEP_INTO) ||
846 Query::EventPhaseIs(TRACE_EVENT_PHASE_ASYNC_STEP_PAST) ||
847 Query::EventPhaseIs(TRACE_EVENT_PHASE_NESTABLE_ASYNC_BEGIN));
848 Query end(Query::EventPhaseIs(TRACE_EVENT_PHASE_ASYNC_END) ||
849 Query::EventPhaseIs(TRACE_EVENT_PHASE_ASYNC_STEP_INTO) ||
850 Query::EventPhaseIs(TRACE_EVENT_PHASE_ASYNC_STEP_PAST) ||
851 Query::EventPhaseIs(TRACE_EVENT_PHASE_NESTABLE_ASYNC_END));
852 Query match(Query::EventCategory() == Query::OtherCategory() &&
853 Query::EventId() == Query::OtherId());
854
855 if (match_pid) {
856 match = match && Query::EventPid() == Query::OtherPid();
857 }
858
859 AssociateEvents(begin, end, match);
860 }
861
AssociateEvents(const Query & first,const Query & second,const Query & match)862 void TraceAnalyzer::AssociateEvents(const Query& first,
863 const Query& second,
864 const Query& match) {
865 DCHECK(allow_association_changes_)
866 << "AssociateEvents not allowed after FindEvents";
867
868 // Search for matching begin/end event pairs. When a matching end is found,
869 // it is associated with the begin event.
870 std::vector<TraceEvent*> begin_stack;
871 for (auto& this_event : raw_events_) {
872 if (second.Evaluate(this_event)) {
873 // Search stack for matching begin, starting from end.
874 for (int stack_index = static_cast<int>(begin_stack.size()) - 1;
875 stack_index >= 0; --stack_index) {
876 TraceEvent& begin_event = *begin_stack[stack_index];
877
878 // Temporarily set other to test against the match query.
879 const TraceEvent* other_backup = begin_event.other_event;
880 begin_event.other_event = &this_event;
881 if (match.Evaluate(begin_event)) {
882 // Found a matching begin/end pair.
883 // Set the associated previous event
884 this_event.prev_event = &begin_event;
885 // Erase the matching begin event index from the stack.
886 begin_stack.erase(begin_stack.begin() + stack_index);
887 break;
888 }
889
890 // Not a match, restore original other and continue.
891 begin_event.other_event = other_backup;
892 }
893 }
894 // Even if this_event is a |second| event that has matched an earlier
895 // |first| event, it can still also be a |first| event and be associated
896 // with a later |second| event.
897 if (first.Evaluate(this_event)) {
898 begin_stack.push_back(&this_event);
899 }
900 }
901 }
902
MergeAssociatedEventArgs()903 void TraceAnalyzer::MergeAssociatedEventArgs() {
904 for (auto& i : raw_events_) {
905 // Merge all associated events with the first event.
906 const TraceEvent* other = i.other_event;
907 // Avoid looping by keeping set of encountered TraceEvents.
908 std::set<const TraceEvent*> encounters;
909 encounters.insert(&i);
910 while (other && encounters.find(other) == encounters.end()) {
911 encounters.insert(other);
912 i.arg_numbers.insert(other->arg_numbers.begin(),
913 other->arg_numbers.end());
914 i.arg_strings.insert(other->arg_strings.begin(),
915 other->arg_strings.end());
916 other = other->other_event;
917 }
918 }
919 }
920
FindEvents(const Query & query,TraceEventVector * output)921 size_t TraceAnalyzer::FindEvents(const Query& query, TraceEventVector* output) {
922 allow_association_changes_ = false;
923 output->clear();
924 return FindMatchingEvents(
925 raw_events_, query, output, ignore_metadata_events_);
926 }
927
FindFirstOf(const Query & query)928 const TraceEvent* TraceAnalyzer::FindFirstOf(const Query& query) {
929 TraceEventVector output;
930 if (FindEvents(query, &output) > 0)
931 return output.front();
932 return nullptr;
933 }
934
FindLastOf(const Query & query)935 const TraceEvent* TraceAnalyzer::FindLastOf(const Query& query) {
936 TraceEventVector output;
937 if (FindEvents(query, &output) > 0)
938 return output.back();
939 return nullptr;
940 }
941
GetThreadName(const TraceEvent::ProcessThreadID & thread)942 const std::string& TraceAnalyzer::GetThreadName(
943 const TraceEvent::ProcessThreadID& thread) {
944 // If thread is not found, just add and return empty string.
945 return thread_names_[thread];
946 }
947
ParseMetadata()948 void TraceAnalyzer::ParseMetadata() {
949 for (const auto& this_event : raw_events_) {
950 // Check for thread name metadata.
951 if (this_event.phase != TRACE_EVENT_PHASE_METADATA ||
952 this_event.name != "thread_name")
953 continue;
954 std::map<std::string, std::string>::const_iterator string_it =
955 this_event.arg_strings.find("name");
956 if (string_it != this_event.arg_strings.end())
957 thread_names_[this_event.thread] = string_it->second;
958 }
959 }
960
961 // Utility functions for collecting process-local traces and creating a
962 // |TraceAnalyzer| from the result.
963
Start(const std::string & category_filter_string)964 void Start(const std::string& category_filter_string) {
965 DCHECK(!base::trace_event::TraceLog::GetInstance()->IsEnabled());
966 base::trace_event::TraceLog::GetInstance()->SetEnabled(
967 base::trace_event::TraceConfig(category_filter_string, ""),
968 base::trace_event::TraceLog::RECORDING_MODE);
969 }
970
Stop()971 std::unique_ptr<TraceAnalyzer> Stop() {
972 DCHECK(base::trace_event::TraceLog::GetInstance()->IsEnabled());
973 base::trace_event::TraceLog::GetInstance()->SetDisabled();
974
975 base::trace_event::TraceResultBuffer buffer;
976 base::trace_event::TraceResultBuffer::SimpleOutput trace_output;
977 buffer.SetOutputCallback(trace_output.GetCallback());
978 base::RunLoop run_loop;
979 buffer.Start();
980 base::trace_event::TraceLog::GetInstance()->Flush(
981 base::BindRepeating(&OnTraceDataCollected, run_loop.QuitClosure(),
982 base::Unretained(&buffer)));
983 run_loop.Run();
984 buffer.Finish();
985
986 return TraceAnalyzer::Create(trace_output.json_output);
987 }
988
989 // TraceEventVector utility functions.
990
GetRateStats(const TraceEventVector & events,RateStats * stats,const RateStatsOptions * options)991 bool GetRateStats(const TraceEventVector& events,
992 RateStats* stats,
993 const RateStatsOptions* options) {
994 DCHECK(stats);
995 // Need at least 3 events to calculate rate stats.
996 const size_t kMinEvents = 3;
997 if (events.size() < kMinEvents) {
998 LOG(ERROR) << "Not enough events: " << events.size();
999 return false;
1000 }
1001
1002 std::vector<double> deltas;
1003 size_t num_deltas = events.size() - 1;
1004 for (size_t i = 0; i < num_deltas; ++i) {
1005 double delta = events.at(i + 1)->timestamp - events.at(i)->timestamp;
1006 if (delta < 0.0) {
1007 LOG(ERROR) << "Events are out of order";
1008 return false;
1009 }
1010 deltas.push_back(delta);
1011 }
1012
1013 base::ranges::sort(deltas);
1014
1015 if (options) {
1016 if (options->trim_min + options->trim_max > events.size() - kMinEvents) {
1017 LOG(ERROR) << "Attempt to trim too many events";
1018 return false;
1019 }
1020 deltas.erase(deltas.begin(), deltas.begin() + options->trim_min);
1021 deltas.erase(deltas.end() - options->trim_max, deltas.end());
1022 }
1023
1024 num_deltas = deltas.size();
1025 double delta_sum = 0.0;
1026 for (size_t i = 0; i < num_deltas; ++i)
1027 delta_sum += deltas[i];
1028
1029 stats->min_us = *base::ranges::min_element(deltas);
1030 stats->max_us = *base::ranges::max_element(deltas);
1031 stats->mean_us = delta_sum / static_cast<double>(num_deltas);
1032
1033 double sum_mean_offsets_squared = 0.0;
1034 for (size_t i = 0; i < num_deltas; ++i) {
1035 double offset = fabs(deltas[i] - stats->mean_us);
1036 sum_mean_offsets_squared += offset * offset;
1037 }
1038 stats->standard_deviation_us =
1039 sqrt(sum_mean_offsets_squared / static_cast<double>(num_deltas - 1));
1040
1041 return true;
1042 }
1043
FindFirstOf(const TraceEventVector & events,const Query & query,size_t position,size_t * return_index)1044 bool FindFirstOf(const TraceEventVector& events,
1045 const Query& query,
1046 size_t position,
1047 size_t* return_index) {
1048 DCHECK(return_index);
1049 for (size_t i = position; i < events.size(); ++i) {
1050 if (query.Evaluate(*events[i])) {
1051 *return_index = i;
1052 return true;
1053 }
1054 }
1055 return false;
1056 }
1057
FindLastOf(const TraceEventVector & events,const Query & query,size_t position,size_t * return_index)1058 bool FindLastOf(const TraceEventVector& events,
1059 const Query& query,
1060 size_t position,
1061 size_t* return_index) {
1062 DCHECK(return_index);
1063 for (size_t i = std::min(position + 1, events.size()); i != 0; --i) {
1064 if (query.Evaluate(*events[i - 1])) {
1065 *return_index = i - 1;
1066 return true;
1067 }
1068 }
1069 return false;
1070 }
1071
FindClosest(const TraceEventVector & events,const Query & query,size_t position,size_t * return_closest,size_t * return_second_closest)1072 bool FindClosest(const TraceEventVector& events,
1073 const Query& query,
1074 size_t position,
1075 size_t* return_closest,
1076 size_t* return_second_closest) {
1077 DCHECK(return_closest);
1078 if (events.empty() || position >= events.size())
1079 return false;
1080 size_t closest = events.size();
1081 size_t second_closest = events.size();
1082 for (size_t i = 0; i < events.size(); ++i) {
1083 if (!query.Evaluate(*events.at(i)))
1084 continue;
1085 if (closest == events.size()) {
1086 closest = i;
1087 continue;
1088 }
1089 if (fabs(events.at(i)->timestamp - events.at(position)->timestamp) <
1090 fabs(events.at(closest)->timestamp - events.at(position)->timestamp)) {
1091 second_closest = closest;
1092 closest = i;
1093 } else if (second_closest == events.size()) {
1094 second_closest = i;
1095 }
1096 }
1097
1098 if (closest < events.size() &&
1099 (!return_second_closest || second_closest < events.size())) {
1100 *return_closest = closest;
1101 if (return_second_closest)
1102 *return_second_closest = second_closest;
1103 return true;
1104 }
1105
1106 return false;
1107 }
1108
CountMatches(const TraceEventVector & events,const Query & query,size_t begin_position,size_t end_position)1109 size_t CountMatches(const TraceEventVector& events,
1110 const Query& query,
1111 size_t begin_position,
1112 size_t end_position) {
1113 if (begin_position >= events.size())
1114 return 0u;
1115 end_position = (end_position < events.size()) ? end_position : events.size();
1116 size_t count = 0u;
1117 for (size_t i = begin_position; i < end_position; ++i) {
1118 if (query.Evaluate(*events.at(i)))
1119 ++count;
1120 }
1121 return count;
1122 }
1123
1124 } // namespace trace_analyzer
1125