1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832
|
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Use trace_analyzer::Query and trace_analyzer::TraceAnalyzer to search for
// specific trace events that were generated by the trace_event.h API.
//
// Basic procedure:
// - Get trace events JSON string from base::trace_event::TraceLog.
// - Create TraceAnalyzer with JSON string.
// - Call TraceAnalyzer::AssociateBeginEndEvents (optional).
// - Call TraceAnalyzer::AssociateEvents (zero or more times).
// - Call TraceAnalyzer::FindEvents with queries to find specific events.
//
// A Query is a boolean expression tree that evaluates to true or false for a
// given trace event. Queries can be combined into a tree using boolean,
// arithmetic and comparison operators that refer to data of an individual trace
// event.
//
// The events are returned as trace_analyzer::TraceEvent objects.
// TraceEvent contains a single trace event's data, as well as a pointer to
// a related trace event. The related trace event is typically the matching end
// of a begin event or the matching begin of an end event.
//
// The following examples use this basic setup code to construct TraceAnalyzer
// with the json trace string retrieved from TraceLog and construct an event
// vector for retrieving events:
//
// TraceAnalyzer analyzer(json_events);
// TraceEventVector events;
//
// EXAMPLE 1: Find events named "my_event".
//
// analyzer.FindEvents(Query(EVENT_NAME) == "my_event", &events);
//
// EXAMPLE 2: Find begin events named "my_event" with duration > 1 second.
//
// Query q = (Query(EVENT_NAME) == Query::String("my_event") &&
// Query(EVENT_PHASE) == Query::Phase(TRACE_EVENT_PHASE_BEGIN) &&
// Query(EVENT_DURATION) > Query::Double(1000000.0));
// analyzer.FindEvents(q, &events);
//
// EXAMPLE 3: Associating event pairs across threads.
//
// If the test needs to analyze something that starts and ends on different
// threads, the test needs to use INSTANT events. The typical procedure is to
// specify the same unique ID as a TRACE_EVENT argument on both the start and
// finish INSTANT events. Then use the following procedure to associate those
// events.
//
// Step 1: instrument code with custom begin/end trace events.
// [Thread 1 tracing code]
// TRACE_EVENT_INSTANT1("test_latency", "timing1_begin", "id", 3);
// [Thread 2 tracing code]
// TRACE_EVENT_INSTANT1("test_latency", "timing1_end", "id", 3);
//
// Step 2: associate these custom begin/end pairs.
// Query begin(Query(EVENT_NAME) == Query::String("timing1_begin"));
// Query end(Query(EVENT_NAME) == Query::String("timing1_end"));
// Query match(Query(EVENT_ARG, "id") == Query(OTHER_ARG, "id"));
// analyzer.AssociateEvents(begin, end, match);
//
// Step 3: search for "timing1_begin" events with existing other event.
// Query q = (Query(EVENT_NAME) == Query::String("timing1_begin") &&
// Query(EVENT_HAS_OTHER));
// analyzer.FindEvents(q, &events);
//
// Step 4: analyze events, such as checking durations.
// for (size_t i = 0; i < events.size(); ++i) {
// double duration;
// EXPECT_TRUE(events[i].GetAbsTimeToOtherEvent(&duration));
// EXPECT_LT(duration, 1000000.0/60.0); // expect less than 1/60 second.
// }
//
// There are two helper functions, Start(category_filter_string) and Stop(), for
// facilitating the collection of process-local traces and building a
// TraceAnalyzer from them. A typical test, that uses the helper functions,
// looks like the following:
//
// TEST_F(...) {
// Start("*");
// [Invoke the functions you want to test their traces]
// auto analyzer = Stop();
//
// [Use the analyzer to verify produced traces, as explained above]
// }
//
// Note: The Stop() function needs a SingleThreadTaskRunner.
#ifndef BASE_TEST_TRACE_EVENT_ANALYZER_H_
#define BASE_TEST_TRACE_EVENT_ANALYZER_H_
#include <stddef.h>
#include <stdint.h>
#include <map>
#include <memory>
#include <string>
#include <vector>
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h"
#include "base/trace_event/trace_event.h"
namespace base {
class Value;
}
namespace trace_analyzer {
class QueryNode;
// trace_analyzer::TraceEvent is a more convenient form of the
// base::trace_event::TraceEvent class to make tracing-based tests easier to
// write.
struct TraceEvent {
// ProcessThreadID contains a Process ID and Thread ID.
struct ProcessThreadID {
ProcessThreadID() = default;
ProcessThreadID(int process_id, int thread_id)
: process_id(process_id), thread_id(thread_id) {}
auto operator<=>(const ProcessThreadID&) const = default;
bool operator==(const ProcessThreadID&) const = default;
int process_id = 0;
int thread_id = 0;
};
TraceEvent();
TraceEvent(TraceEvent&& other);
~TraceEvent();
[[nodiscard]] bool SetFromJSON(const base::Value* event_value);
TraceEvent& operator=(TraceEvent&& rhs);
bool has_other_event() const { return other_event; }
// Returns absolute duration in microseconds between this event and other
// event. Must have already verified that other_event exists by
// Query(EVENT_HAS_OTHER) or by calling has_other_event().
double GetAbsTimeToOtherEvent() const;
// Return the argument value if it exists and it is a string.
bool GetArgAsString(const std::string& arg_name, std::string* arg) const;
// Return the argument value if it exists and it is a number.
bool GetArgAsNumber(const std::string& arg_name, double* arg) const;
// Return the argument value if it exists and is a dictionary.
bool GetArgAsDict(const std::string& arg_name, base::Value::Dict* arg) const;
// Check if argument exists and is string.
bool HasStringArg(const std::string& arg_name) const;
// Check if argument exists and is number (double, int or bool).
bool HasNumberArg(const std::string& arg_name) const;
// Check if argument exists and is a dictionary.
bool HasDictArg(const std::string& arg_name) const;
// Get known existing arguments as specific types.
// Useful when you have already queried the argument with
// Query(HAS_NUMBER_ARG) or Query(HAS_STRING_ARG).
std::string GetKnownArgAsString(const std::string& arg_name) const;
double GetKnownArgAsDouble(const std::string& arg_name) const;
int GetKnownArgAsInt(const std::string& arg_name) const;
bool GetKnownArgAsBool(const std::string& arg_name) const;
base::Value::Dict GetKnownArgAsDict(const std::string& arg_name) const;
// Process ID and Thread ID.
ProcessThreadID thread;
// Time since epoch in microseconds.
// Stored as double to match its JSON representation.
double timestamp = 0.0;
double duration = 0.0;
char phase = TRACE_EVENT_PHASE_BEGIN;
std::string category;
std::string name;
std::string id;
double thread_duration = 0.0;
double thread_timestamp = 0.0;
std::string scope;
std::string bind_id;
bool flow_out = false;
bool flow_in = false;
std::string global_id2;
std::string local_id2;
// All numbers and bool values from TraceEvent args are cast to double.
// bool becomes 1.0 (true) or 0.0 (false).
std::map<std::string, double> arg_numbers;
std::map<std::string, std::string> arg_strings;
std::map<std::string, base::Value::Dict> arg_dicts;
// The other event associated with this event (or NULL).
raw_ptr<const TraceEvent> other_event = nullptr;
// A back-link for |other_event|. That is, if other_event is not null, then
// |event->other_event->prev_event == event| is always true.
raw_ptr<const TraceEvent> prev_event;
};
typedef std::vector<const TraceEvent*> TraceEventVector;
class Query {
public:
Query(const Query& query);
~Query();
////////////////////////////////////////////////////////////////
// Query literal values
// Compare with the given string.
static Query String(const std::string& str);
// Compare with the given number.
static Query Double(double num);
static Query Int(int32_t num);
static Query Uint(uint32_t num);
// Compare with the given bool.
static Query Bool(bool boolean);
// Compare with the given phase.
static Query Phase(char phase);
// Compare with the given string pattern. Only works with == and != operators.
// Example: Query(EVENT_NAME) == Query::Pattern("MyEvent*")
static Query Pattern(const std::string& pattern);
////////////////////////////////////////////////////////////////
// Query event members
static Query EventPid() { return Query(EVENT_PID); }
static Query EventTid() { return Query(EVENT_TID); }
// Return the timestamp of the event in microseconds since epoch.
static Query EventTime() { return Query(EVENT_TIME); }
// Return the absolute time between event and other event in microseconds.
// Only works if Query::EventHasOther() == true.
static Query EventDuration() { return Query(EVENT_DURATION); }
// Return the duration of a COMPLETE event.
static Query EventCompleteDuration() {
return Query(EVENT_COMPLETE_DURATION);
}
static Query EventPhase() { return Query(EVENT_PHASE); }
static Query EventCategory() { return Query(EVENT_CATEGORY); }
static Query EventName() { return Query(EVENT_NAME); }
static Query EventId() { return Query(EVENT_ID); }
static Query EventPidIs(int process_id) {
return Query(EVENT_PID) == Query::Int(process_id);
}
static Query EventTidIs(int thread_id) {
return Query(EVENT_TID) == Query::Int(thread_id);
}
static Query EventThreadIs(const TraceEvent::ProcessThreadID& thread) {
return EventPidIs(thread.process_id) && EventTidIs(thread.thread_id);
}
static Query EventTimeIs(double timestamp) {
return Query(EVENT_TIME) == Query::Double(timestamp);
}
static Query EventDurationIs(double duration) {
return Query(EVENT_DURATION) == Query::Double(duration);
}
static Query EventPhaseIs(char phase) {
return Query(EVENT_PHASE) == Query::Phase(phase);
}
static Query EventCategoryIs(const std::string& category) {
return Query(EVENT_CATEGORY) == Query::String(category);
}
static Query EventNameIs(const std::string& name) {
return Query(EVENT_NAME) == Query::String(name);
}
static Query EventIdIs(const std::string& id) {
return Query(EVENT_ID) == Query::String(id);
}
// Evaluates to true if arg exists and is a string.
static Query EventHasStringArg(const std::string& arg_name) {
return Query(EVENT_HAS_STRING_ARG, arg_name);
}
// Evaluates to true if arg exists and is a number.
// Number arguments include types double, int and bool.
static Query EventHasNumberArg(const std::string& arg_name) {
return Query(EVENT_HAS_NUMBER_ARG, arg_name);
}
// Evaluates to arg value (string or number).
static Query EventArg(const std::string& arg_name) {
return Query(EVENT_ARG, arg_name);
}
// Return true if associated event exists.
static Query EventHasOther() { return Query(EVENT_HAS_OTHER); }
// Access the associated other_event's members:
static Query OtherPid() { return Query(OTHER_PID); }
static Query OtherTid() { return Query(OTHER_TID); }
static Query OtherTime() { return Query(OTHER_TIME); }
static Query OtherPhase() { return Query(OTHER_PHASE); }
static Query OtherCategory() { return Query(OTHER_CATEGORY); }
static Query OtherName() { return Query(OTHER_NAME); }
static Query OtherId() { return Query(OTHER_ID); }
static Query OtherPidIs(int process_id) {
return Query(OTHER_PID) == Query::Int(process_id);
}
static Query OtherTidIs(int thread_id) {
return Query(OTHER_TID) == Query::Int(thread_id);
}
static Query OtherThreadIs(const TraceEvent::ProcessThreadID& thread) {
return OtherPidIs(thread.process_id) && OtherTidIs(thread.thread_id);
}
static Query OtherTimeIs(double timestamp) {
return Query(OTHER_TIME) == Query::Double(timestamp);
}
static Query OtherPhaseIs(char phase) {
return Query(OTHER_PHASE) == Query::Phase(phase);
}
static Query OtherCategoryIs(const std::string& category) {
return Query(OTHER_CATEGORY) == Query::String(category);
}
static Query OtherNameIs(const std::string& name) {
return Query(OTHER_NAME) == Query::String(name);
}
static Query OtherIdIs(const std::string& id) {
return Query(OTHER_ID) == Query::String(id);
}
// Evaluates to true if arg exists and is a string.
static Query OtherHasStringArg(const std::string& arg_name) {
return Query(OTHER_HAS_STRING_ARG, arg_name);
}
// Evaluates to true if arg exists and is a number.
// Number arguments include types double, int and bool.
static Query OtherHasNumberArg(const std::string& arg_name) {
return Query(OTHER_HAS_NUMBER_ARG, arg_name);
}
// Evaluates to arg value (string or number).
static Query OtherArg(const std::string& arg_name) {
return Query(OTHER_ARG, arg_name);
}
// Access the associated prev_event's members:
static Query PrevPid() { return Query(PREV_PID); }
static Query PrevTid() { return Query(PREV_TID); }
static Query PrevTime() { return Query(PREV_TIME); }
static Query PrevPhase() { return Query(PREV_PHASE); }
static Query PrevCategory() { return Query(PREV_CATEGORY); }
static Query PrevName() { return Query(PREV_NAME); }
static Query PrevId() { return Query(PREV_ID); }
static Query PrevPidIs(int process_id) {
return Query(PREV_PID) == Query::Int(process_id);
}
static Query PrevTidIs(int thread_id) {
return Query(PREV_TID) == Query::Int(thread_id);
}
static Query PrevThreadIs(const TraceEvent::ProcessThreadID& thread) {
return PrevPidIs(thread.process_id) && PrevTidIs(thread.thread_id);
}
static Query PrevTimeIs(double timestamp) {
return Query(PREV_TIME) == Query::Double(timestamp);
}
static Query PrevPhaseIs(char phase) {
return Query(PREV_PHASE) == Query::Phase(phase);
}
static Query PrevCategoryIs(const std::string& category) {
return Query(PREV_CATEGORY) == Query::String(category);
}
static Query PrevNameIs(const std::string& name) {
return Query(PREV_NAME) == Query::String(name);
}
static Query PrevIdIs(const std::string& id) {
return Query(PREV_ID) == Query::String(id);
}
// Evaluates to true if arg exists and is a string.
static Query PrevHasStringArg(const std::string& arg_name) {
return Query(PREV_HAS_STRING_ARG, arg_name);
}
// Evaluates to true if arg exists and is a number.
// Number arguments include types double, int and bool.
static Query PrevHasNumberArg(const std::string& arg_name) {
return Query(PREV_HAS_NUMBER_ARG, arg_name);
}
// Evaluates to arg value (string or number).
static Query PrevArg(const std::string& arg_name) {
return Query(PREV_ARG, arg_name);
}
////////////////////////////////////////////////////////////////
// Common queries:
// Find BEGIN events that have a corresponding END event.
static Query MatchBeginWithEnd() {
return (Query(EVENT_PHASE) == Query::Phase(TRACE_EVENT_PHASE_BEGIN)) &&
Query(EVENT_HAS_OTHER);
}
// Find COMPLETE events.
static Query MatchComplete() {
return (Query(EVENT_PHASE) == Query::Phase(TRACE_EVENT_PHASE_COMPLETE));
}
// Find ASYNC_BEGIN events that have a corresponding ASYNC_END event.
static Query MatchAsyncBeginWithNext() {
return (Query(EVENT_PHASE) ==
Query::Phase(TRACE_EVENT_PHASE_ASYNC_BEGIN)) &&
Query(EVENT_HAS_OTHER);
}
// Find BEGIN events of given |name| which also have associated END events.
static Query MatchBeginName(const std::string& name) {
return (Query(EVENT_NAME) == Query(name)) && MatchBeginWithEnd();
}
// Find COMPLETE events of given |name|.
static Query MatchCompleteName(const std::string& name) {
return (Query(EVENT_NAME) == Query(name)) && MatchComplete();
}
// Match given Process ID and Thread ID.
static Query MatchThread(const TraceEvent::ProcessThreadID& thread) {
return (Query(EVENT_PID) == Query::Int(thread.process_id)) &&
(Query(EVENT_TID) == Query::Int(thread.thread_id));
}
// Match event pair that spans multiple threads.
static Query MatchCrossThread() {
return (Query(EVENT_PID) != Query(OTHER_PID)) ||
(Query(EVENT_TID) != Query(OTHER_TID));
}
////////////////////////////////////////////////////////////////
// Operators:
// Boolean operators:
Query operator==(const Query& rhs) const;
Query operator!=(const Query& rhs) const;
Query operator<(const Query& rhs) const;
Query operator<=(const Query& rhs) const;
Query operator>(const Query& rhs) const;
Query operator>=(const Query& rhs) const;
Query operator&&(const Query& rhs) const;
Query operator||(const Query& rhs) const;
Query operator!() const;
// Arithmetic operators:
// Following operators are applied to double arguments:
Query operator+(const Query& rhs) const;
Query operator-(const Query& rhs) const;
Query operator*(const Query& rhs) const;
Query operator/(const Query& rhs) const;
Query operator-() const;
// Mod operates on int64_t args (doubles are casted to int64_t beforehand):
Query operator%(const Query& rhs) const;
// Return true if the given event matches this query tree.
// This is a recursive method that walks the query tree.
bool Evaluate(const TraceEvent& event) const;
enum TraceEventMember {
EVENT_INVALID,
EVENT_PID,
EVENT_TID,
EVENT_TIME,
EVENT_DURATION,
EVENT_COMPLETE_DURATION,
EVENT_PHASE,
EVENT_CATEGORY,
EVENT_NAME,
EVENT_ID,
EVENT_HAS_STRING_ARG,
EVENT_HAS_NUMBER_ARG,
EVENT_ARG,
EVENT_HAS_OTHER,
EVENT_HAS_PREV,
OTHER_PID,
OTHER_TID,
OTHER_TIME,
OTHER_PHASE,
OTHER_CATEGORY,
OTHER_NAME,
OTHER_ID,
OTHER_HAS_STRING_ARG,
OTHER_HAS_NUMBER_ARG,
OTHER_ARG,
PREV_PID,
PREV_TID,
PREV_TIME,
PREV_PHASE,
PREV_CATEGORY,
PREV_NAME,
PREV_ID,
PREV_HAS_STRING_ARG,
PREV_HAS_NUMBER_ARG,
PREV_ARG,
OTHER_FIRST_MEMBER = OTHER_PID,
OTHER_LAST_MEMBER = OTHER_ARG,
PREV_FIRST_MEMBER = PREV_PID,
PREV_LAST_MEMBER = PREV_ARG,
};
enum Operator {
OP_INVALID,
// Boolean operators:
OP_EQ,
OP_NE,
OP_LT,
OP_LE,
OP_GT,
OP_GE,
OP_AND,
OP_OR,
OP_NOT,
// Arithmetic operators:
OP_ADD,
OP_SUB,
OP_MUL,
OP_DIV,
OP_MOD,
OP_NEGATE
};
enum QueryType {
QUERY_BOOLEAN_OPERATOR,
QUERY_ARITHMETIC_OPERATOR,
QUERY_EVENT_MEMBER,
QUERY_NUMBER,
QUERY_STRING
};
// Compare with the given member.
explicit Query(TraceEventMember member);
// Compare with the given member argument value.
Query(TraceEventMember member, const std::string& arg_name);
// Compare with the given string.
explicit Query(const std::string& str);
// Compare with the given number.
explicit Query(double num);
// Construct a boolean Query that returns (left <binary_op> right).
Query(const Query& left, const Query& right, Operator binary_op);
// Construct a boolean Query that returns (<binary_op> left).
Query(const Query& left, Operator unary_op);
// Try to compare left_ against right_ based on operator_.
// If either left or right does not convert to double, false is returned.
// Otherwise, true is returned and |result| is set to the comparison result.
bool CompareAsDouble(const TraceEvent& event, bool* result) const;
// Try to compare left_ against right_ based on operator_.
// If either left or right does not convert to string, false is returned.
// Otherwise, true is returned and |result| is set to the comparison result.
bool CompareAsString(const TraceEvent& event, bool* result) const;
// Attempt to convert this Query to a double. On success, true is returned
// and the double value is stored in |num|.
bool GetAsDouble(const TraceEvent& event, double* num) const;
// Attempt to convert this Query to a string. On success, true is returned
// and the string value is stored in |str|.
bool GetAsString(const TraceEvent& event, std::string* str) const;
// Evaluate this Query as an arithmetic operator on left_ and right_.
bool EvaluateArithmeticOperator(const TraceEvent& event, double* num) const;
// For QUERY_EVENT_MEMBER Query: attempt to get the double value of the Query.
bool GetMemberValueAsDouble(const TraceEvent& event, double* num) const;
// For QUERY_EVENT_MEMBER Query: attempt to get the string value of the Query.
bool GetMemberValueAsString(const TraceEvent& event, std::string* num) const;
// Does this Query represent a value?
bool is_value() const { return type_ != QUERY_BOOLEAN_OPERATOR; }
bool is_unary_operator() const {
return operator_ == OP_NOT || operator_ == OP_NEGATE;
}
bool is_comparison_operator() const {
return operator_ != OP_INVALID && operator_ < OP_AND;
}
static const TraceEvent* SelectTargetEvent(const TraceEvent* ev,
TraceEventMember member);
const Query& left() const;
const Query& right() const;
private:
QueryType type_;
Operator operator_;
scoped_refptr<QueryNode> left_;
scoped_refptr<QueryNode> right_;
TraceEventMember member_;
double number_;
std::string string_;
bool is_pattern_;
};
// Implementation detail:
// QueryNode allows Query to store a ref-counted query tree.
class QueryNode : public base::RefCounted<QueryNode> {
public:
explicit QueryNode(const Query& query);
const Query& query() const { return query_; }
private:
friend class base::RefCounted<QueryNode>;
~QueryNode();
Query query_;
};
// TraceAnalyzer helps tests search for trace events.
class TraceAnalyzer {
public:
TraceAnalyzer(const TraceAnalyzer&) = delete;
TraceAnalyzer& operator=(const TraceAnalyzer&) = delete;
~TraceAnalyzer();
// Use trace events from JSON string generated by tracing API.
// Returns non-NULL if the JSON is successfully parsed.
[[nodiscard]] static std::unique_ptr<TraceAnalyzer> Create(
const std::string& json_events);
void SetIgnoreMetadataEvents(bool ignore) {
ignore_metadata_events_ = ignore;
}
// Associate BEGIN and END events with each other. This allows Query(OTHER_*)
// to access the associated event and enables Query(EVENT_DURATION).
// An end event will match the most recent begin event with the same name,
// category, process ID and thread ID. This matches what is shown in
// about:tracing. After association, the BEGIN event will point to the
// matching END event, but the END event will not point to the BEGIN event.
void AssociateBeginEndEvents();
// Associate ASYNC_BEGIN, ASYNC_STEP and ASYNC_END events with each other.
// An ASYNC_END event will match the most recent ASYNC_BEGIN or ASYNC_STEP
// event with the same name, category, and ID. This creates a singly linked
// list of ASYNC_BEGIN->ASYNC_STEP...->ASYNC_END.
// |match_pid| - If true, will only match async events which are running
// under the same process ID, otherwise will allow linking
// async events from different processes.
void AssociateAsyncBeginEndEvents(bool match_pid = true);
// AssociateEvents can be used to customize event associations by setting the
// other_event member of TraceEvent. This should be used to associate two
// INSTANT events.
//
// The assumptions are:
// - |first| events occur before |second| events.
// - the closest matching |second| event is the correct match.
//
// |first| - Eligible |first| events match this query.
// |second| - Eligible |second| events match this query.
// |match| - This query is run on the |first| event. The OTHER_* EventMember
// queries will point to an eligible |second| event. The query
// should evaluate to true if the |first|/|second| pair is a match.
//
// When a match is found, the pair will be associated by having the first
// event's other_event member point to the other. AssociateEvents does not
// clear previous associations, so it is possible to associate multiple pairs
// of events by calling AssociateEvents more than once with different queries.
//
// NOTE: AssociateEvents will overwrite existing other_event associations if
// the queries pass for events that already had a previous association.
//
// After calling any Find* method, it is not allowed to call AssociateEvents
// again.
void AssociateEvents(const Query& first,
const Query& second,
const Query& match);
// For each event, copy its arguments to the other_event argument map. If
// argument name already exists, it will not be overwritten.
void MergeAssociatedEventArgs();
// Find all events that match query and replace output vector.
size_t FindEvents(const Query& query, TraceEventVector* output);
// Find first event that matches query or NULL if not found.
const TraceEvent* FindFirstOf(const Query& query);
// Find last event that matches query or NULL if not found.
const TraceEvent* FindLastOf(const Query& query);
const std::string& GetThreadName(const TraceEvent::ProcessThreadID& thread);
private:
TraceAnalyzer();
[[nodiscard]] bool SetEvents(const std::string& json_events);
// Read metadata (thread names, etc) from events.
void ParseMetadata();
std::map<TraceEvent::ProcessThreadID, std::string> thread_names_;
std::vector<TraceEvent> raw_events_;
bool ignore_metadata_events_;
bool allow_association_changes_;
};
// Utility functions for collecting process-local traces and creating a
// |TraceAnalyzer| from the result. Please see comments in trace_config.h to
// understand how the |category_filter_string| works. Use "*" to enable all
// default categories.
void Start(const std::string& category_filter_string);
std::unique_ptr<TraceAnalyzer> Stop();
// Utility functions for TraceEventVector.
struct RateStats {
double min_us;
double max_us;
double mean_us;
double standard_deviation_us;
};
struct RateStatsOptions {
RateStatsOptions() : trim_min(0u), trim_max(0u) {}
// After the times between events are sorted, the number of specified elements
// will be trimmed before calculating the RateStats. This is useful in cases
// where extreme outliers are tolerable and should not skew the overall
// average.
size_t trim_min; // Trim this many minimum times.
size_t trim_max; // Trim this many maximum times.
};
// Calculate min/max/mean and standard deviation from the times between
// adjacent events.
bool GetRateStats(const TraceEventVector& events,
RateStats* stats,
const RateStatsOptions* options);
// Starting from |position|, find the first event that matches |query|.
// Returns true if found, false otherwise.
bool FindFirstOf(const TraceEventVector& events,
const Query& query,
size_t position,
size_t* return_index);
// Starting from |position|, find the last event that matches |query|.
// Returns true if found, false otherwise.
bool FindLastOf(const TraceEventVector& events,
const Query& query,
size_t position,
size_t* return_index);
// Find the closest events to |position| in time that match |query|.
// return_second_closest may be NULL. Closeness is determined by comparing
// with the event timestamp.
// Returns true if found, false otherwise. If both return parameters are
// requested, both must be found for a successful result.
bool FindClosest(const TraceEventVector& events,
const Query& query,
size_t position,
size_t* return_closest,
size_t* return_second_closest);
// Count matches, inclusive of |begin_position|, exclusive of |end_position|.
size_t CountMatches(const TraceEventVector& events,
const Query& query,
size_t begin_position,
size_t end_position);
// Count all matches.
inline size_t CountMatches(const TraceEventVector& events, const Query& query) {
return CountMatches(events, query, 0u, events.size());
}
} // namespace trace_analyzer
#endif // BASE_TEST_TRACE_EVENT_ANALYZER_H_
|