| Index: base/debug/trace_event_test_utils.cc
|
| diff --git a/base/debug/trace_event_test_utils.cc b/base/debug/trace_event_test_utils.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..3c60f00bbdcba3536c8f8668ff8361b5766aa211
|
| --- /dev/null
|
| +++ b/base/debug/trace_event_test_utils.cc
|
| @@ -0,0 +1,493 @@
|
| +// Copyright (c) 2011 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include <algorithm>
|
| +
|
| +#include "base/debug/trace_event_test_utils.h"
|
| +
|
| +namespace base {
|
| +namespace debug {
|
| +
|
| +namespace trace {
|
| +
|
| +QueryNode::QueryNode(const Query& query) : query_(query) {
|
| +}
|
| +
|
| +QueryNode::~QueryNode() {
|
| +}
|
| +
|
| +Query::Query(TraceEventMember member)
|
| + : type_(QUERY_EventMember),
|
| + operator_(OP_NONE),
|
| + member_(member),
|
| + number_(0),
|
| + is_pattern_(false) {
|
| +}
|
| +
|
| +Query::Query(TraceEventMember member, const std::string& arg_name)
|
| + : type_(QUERY_EventMember),
|
| + operator_(OP_NONE),
|
| + member_(member),
|
| + number_(0),
|
| + string_(arg_name),
|
| + is_pattern_(false) {
|
| +}
|
| +
|
| +Query::Query(const std::string& str)
|
| + : type_(QUERY_String),
|
| + operator_(OP_NONE),
|
| + member_(EVENT_INVALID),
|
| + number_(0),
|
| + string_(str),
|
| + is_pattern_(false) {
|
| +}
|
| +
|
| +Query::Query(double num)
|
| + : type_(QUERY_Number),
|
| + operator_(OP_NONE),
|
| + member_(EVENT_INVALID),
|
| + number_(num),
|
| + is_pattern_(false) {
|
| +}
|
| +
|
| +Query::~Query() {
|
| +}
|
| +
|
| +// static
|
| +Query Query::Pattern(const std::string& pattern) {
|
| + Query query(pattern);
|
| + query.is_pattern_ = true;
|
| + return query;
|
| +}
|
| +
|
| +bool Query::Evaluate(const TestTraceEvent& event) const {
|
| + // First check for values that can convert to bool.
|
| +
|
| + // double is true if != 0:
|
| + double bool_value = 0.0;
|
| + bool is_bool = GetAsDouble(event, &bool_value);
|
| + if (is_bool)
|
| + return (bool_value != 0.0);
|
| +
|
| + // string is true if it exists:
|
| + std::string str_value;
|
| + bool is_str = GetAsString(event, &str_value);
|
| + if (is_str)
|
| + return !str_value.empty();
|
| +
|
| + DCHECK(type_ == QUERY_Operator)
|
| + << "Invalid query: missing boolean expression";
|
| + DCHECK(left_.get() && (right_.get() || is_unary_operator()));
|
| +
|
| + if (operator_ < OP_AND) {
|
| + DCHECK(left().is_value() && right().is_value())
|
| + << "Invalid query: comparison operator used between event member and "
|
| + "value.";
|
| + bool compare_result = false;
|
| + if (CompareAsDouble(event, &compare_result))
|
| + return compare_result;
|
| + else if (CompareAsString(event, &compare_result))
|
| + return compare_result;
|
| + } else {
|
| + switch (operator_) {
|
| + case OP_AND:
|
| + return left().Evaluate(event) && right().Evaluate(event);
|
| + case OP_OR:
|
| + return left().Evaluate(event) || right().Evaluate(event);
|
| + case OP_NOT:
|
| + return !left().Evaluate(event);
|
| + default:
|
| + NOTREACHED();
|
| + }
|
| + }
|
| +
|
| + NOTREACHED();
|
| + return false;
|
| +}
|
| +
|
| +bool Query::CompareAsDouble(const TestTraceEvent& event, bool* result) const {
|
| + double lhs, rhs;
|
| + if (!left().GetAsDouble(event, &lhs) || !right().GetAsDouble(event, &rhs))
|
| + return false;
|
| + switch (operator_) {
|
| + case OP_EQ:
|
| + *result = (lhs == rhs);
|
| + return true;
|
| + case OP_NE:
|
| + *result = (lhs != rhs);
|
| + return true;
|
| + case OP_LT:
|
| + *result = (lhs < rhs);
|
| + return true;
|
| + case OP_LE:
|
| + *result = (lhs <= rhs);
|
| + return true;
|
| + case OP_GT:
|
| + *result = (lhs > rhs);
|
| + return true;
|
| + case OP_GE:
|
| + *result = (lhs >= rhs);
|
| + return true;
|
| + default:
|
| + NOTREACHED();
|
| + return false;
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +bool Query::CompareAsString(const TestTraceEvent& event, bool* result) const {
|
| + std::string lhs, rhs;
|
| + if (!left().GetAsString(event, &lhs) || !right().GetAsString(event, &rhs))
|
| + return false;
|
| + switch (operator_) {
|
| + case OP_EQ:
|
| + if (right().is_pattern_)
|
| + *result = MatchPattern(lhs, rhs);
|
| + else if (left().is_pattern_)
|
| + *result = MatchPattern(rhs, lhs);
|
| + else
|
| + *result = (lhs == rhs);
|
| + return true;
|
| + case OP_NE:
|
| + if (right().is_pattern_)
|
| + *result = !MatchPattern(lhs, rhs);
|
| + else if (left().is_pattern_)
|
| + *result = !MatchPattern(rhs, lhs);
|
| + else
|
| + *result = (lhs != rhs);
|
| + return true;
|
| + case OP_LT:
|
| + *result = (lhs < rhs);
|
| + return true;
|
| + case OP_LE:
|
| + *result = (lhs <= rhs);
|
| + return true;
|
| + case OP_GT:
|
| + *result = (lhs > rhs);
|
| + return true;
|
| + case OP_GE:
|
| + *result = (lhs >= rhs);
|
| + return true;
|
| + default:
|
| + NOTREACHED();
|
| + return false;
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +bool Query::GetAsDouble(const TestTraceEvent& event, double* num) const {
|
| + TraceValue value;
|
| + switch (type_) {
|
| + case QUERY_EventMember:
|
| + value = GetMemberValue(event);
|
| + if(value.type() == TraceValue::TRACE_TYPE_DOUBLE) {
|
| + *num = value.as_double();
|
| + return true;
|
| + }
|
| + return false;
|
| + case QUERY_Number:
|
| + *num = number_;
|
| + return true;
|
| + default:
|
| + return false;
|
| + }
|
| +}
|
| +
|
| +bool Query::GetAsString(const TestTraceEvent& event, std::string* str) const {
|
| + TraceValue value;
|
| + switch (type_) {
|
| + case QUERY_EventMember:
|
| + value = GetMemberValue(event);
|
| + if(value.is_string()) {
|
| + *str = value.as_string();
|
| + return true;
|
| + }
|
| + return false;
|
| + case QUERY_String:
|
| + *str = string_;
|
| + return true;
|
| + default:
|
| + return false;
|
| + }
|
| +}
|
| +
|
| +TraceValue Query::GetMemberValue(const TestTraceEvent& event) const {
|
| + DCHECK(type_ == QUERY_EventMember);
|
| +
|
| + // This could be a request for a member of |event| or a member of |event|'s
|
| + // associated event. Store the target event in the_event:
|
| + const TestTraceEvent* the_event = (member_ < OTHER_PID) ?
|
| + &event : event.associated_event;
|
| +
|
| + // Request for member of associated event, but there is no associated event.
|
| + if (!the_event)
|
| + return TraceValue();
|
| +
|
| + switch (member_) {
|
| + case EVENT_PID:
|
| + case OTHER_PID:
|
| + return static_cast<double>(the_event->pid_tid.pid);
|
| + case EVENT_TID:
|
| + case OTHER_TID:
|
| + return static_cast<double>(the_event->pid_tid.tid);
|
| + case EVENT_TIME:
|
| + case OTHER_TIME:
|
| + return the_event->timestamp;
|
| + case EVENT_DURATION:
|
| + {
|
| + double duration;
|
| + if (the_event->GetDuration(&duration))
|
| + return duration;
|
| + else
|
| + return TraceValue();
|
| + }
|
| + case EVENT_PHASE:
|
| + case OTHER_PHASE:
|
| + return static_cast<double>(the_event->phase);
|
| + case EVENT_CATEGORY:
|
| + return the_event->category;
|
| + case EVENT_NAME:
|
| + return the_event->name;
|
| + case EVENT_HAS_ARG:
|
| + case OTHER_HAS_ARG:
|
| + // Search for the argument name and return true if found.
|
| + return static_cast<double>((the_event->arg_strings.find(string_) !=
|
| + the_event->arg_strings.end()) ||
|
| + (the_event->arg_numbers.find(string_) !=
|
| + the_event->arg_numbers.end()) ? 1 : 0);
|
| + case EVENT_ARG:
|
| + case OTHER_ARG:
|
| + {
|
| + // Search for the argument name and return its value if found.
|
| +
|
| + std::map<std::string, std::string>::const_iterator str_i =
|
| + the_event->arg_strings.find(string_);
|
| + if (str_i != the_event->arg_strings.end())
|
| + return str_i->second;
|
| +
|
| + std::map<std::string, double>::const_iterator num_i =
|
| + the_event->arg_numbers.find(string_);
|
| + if (num_i != the_event->arg_numbers.end())
|
| + return num_i->second;
|
| +
|
| + return TraceValue();
|
| + }
|
| + case EVENT_HAS_OTHER:
|
| + {
|
| + // return 1.0 (true) if the associated event exists
|
| + double result = event.associated_event ? 1.0 : 0.0;
|
| + return result;
|
| + }
|
| + default:
|
| + NOTREACHED();
|
| + return TraceValue();
|
| + }
|
| +}
|
| +
|
| +const Query& Query::left() const {
|
| + return left_->query();
|
| +}
|
| +
|
| +const Query& Query::right() const {
|
| + return right_->query();
|
| +}
|
| +
|
| +Query Query::operator==(const Query& rhs) const {
|
| + return Query(*this, rhs, OP_EQ);
|
| +}
|
| +
|
| +Query Query::operator!=(const Query& rhs) const {
|
| + return Query(*this, rhs, OP_NE);
|
| +}
|
| +
|
| +Query Query::operator<(const Query& rhs) const {
|
| + return Query(*this, rhs, OP_LT);
|
| +}
|
| +
|
| +Query Query::operator<=(const Query& rhs) const {
|
| + return Query(*this, rhs, OP_LE);
|
| +}
|
| +
|
| +Query Query::operator>(const Query& rhs) const {
|
| + return Query(*this, rhs, OP_GT);
|
| +}
|
| +
|
| +Query Query::operator>=(const Query& rhs) const {
|
| + return Query(*this, rhs, OP_GE);
|
| +}
|
| +
|
| +Query Query::operator&&(const Query& rhs) const {
|
| + return Query(*this, rhs, OP_AND);
|
| +}
|
| +
|
| +Query Query::operator||(const Query& rhs) const {
|
| + return Query(*this, rhs, OP_OR);
|
| +}
|
| +
|
| +Query Query::operator!() const {
|
| + return Query(*this, OP_NOT);
|
| +}
|
| +
|
| +Query::Query(const Query& left, const Query& right, Operator binary_op)
|
| + : type_(QUERY_Operator),
|
| + operator_(binary_op),
|
| + left_(new QueryNode(left)),
|
| + right_(new QueryNode(right)),
|
| + member_(EVENT_INVALID),
|
| + number_(0) {
|
| +}
|
| +
|
| +Query::Query(const Query& left, Operator unary_op)
|
| + : type_(QUERY_Operator),
|
| + operator_(unary_op),
|
| + left_(new QueryNode(left)),
|
| + member_(EVENT_INVALID),
|
| + number_(0) {
|
| +}
|
| +
|
| +} // namespace trace
|
| +
|
| +namespace {
|
| +
|
| +// Search |events| for |query| and add matches to |output|.
|
| +size_t FindMatchingEvents(const TraceAnalyzer::EventVector& events,
|
| + const trace::Query& query,
|
| + TraceAnalyzer::ConstEventPtrVector* output) {
|
| + for (size_t i = 0; i < events.size(); ++i) {
|
| + if (query.Evaluate(events[i]))
|
| + output->push_back(&events[i]);
|
| + }
|
| + return output->size();
|
| +}
|
| +
|
| +// < operator for TestTraceEvent pointers.
|
| +bool CompareTestTraceEventPtr(const TestTraceEvent* lhs,
|
| + const TestTraceEvent* rhs) {
|
| + return lhs->timestamp < rhs->timestamp;
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +TraceAnalyzer::TraceAnalyzer() {
|
| +}
|
| +
|
| +TraceAnalyzer::TraceAnalyzer(const std::string& json_events) {
|
| + SetEvents(json_events);
|
| +}
|
| +
|
| +TraceAnalyzer::TraceAnalyzer(const EventVector& events) {
|
| + SetEvents(events);
|
| +}
|
| +
|
| +TraceAnalyzer::~TraceAnalyzer() {
|
| +}
|
| +
|
| +void TraceAnalyzer::SetEvents(const std::string& json_events) {
|
| + TestTraceEvent::ParseEventsFromJson(json_events, &raw_events_);
|
| + AssociateAllEvents();
|
| +}
|
| +
|
| +void TraceAnalyzer::SetEvents(const EventVector& events) {
|
| + raw_events_ = events;
|
| + AssociateAllEvents();
|
| +}
|
| +
|
| +const std::string& TraceAnalyzer::GetThreadName(
|
| + const base::debug::TestTraceEvent::PidTid& pid_tid) {
|
| + // If pid_tid is not found, just add and return empty string.
|
| + return thread_names_[pid_tid];
|
| +}
|
| +
|
| +size_t TraceAnalyzer::FindEvents(const trace::Query& query,
|
| + ConstEventPtrVector* output) const {
|
| + return FindMatchingEvents(raw_events_, query, output);
|
| +}
|
| +
|
| +const base::debug::TestTraceEvent* TraceAnalyzer::FindEvent(
|
| + const trace::Query& query) const {
|
| + ConstEventPtrVector output;
|
| + if (FindEvents(query, &output) > 0)
|
| + return output.front();
|
| + return NULL;
|
| +}
|
| +
|
| +void TraceAnalyzer::AssociateAllEvents() {
|
| + EventPtrTree threaded_events;
|
| +
|
| + // Divide events into their respective threads.
|
| + for (size_t i = 0; i < raw_events_.size(); ++i)
|
| + threaded_events[raw_events_[i].pid_tid].push_back(&raw_events_[i]);
|
| +
|
| + // For each thread, find associated events.
|
| + EventPtrVector orphans;
|
| + EventPtrTree::iterator thread_i = threaded_events.begin();
|
| + for (; thread_i != threaded_events.end(); ++thread_i)
|
| + AssociateEvents(thread_i->second, orphans);
|
| +
|
| + // The BEGIN/END events in orphans had no associated events. They could be
|
| + // cross thread BEGIN/END pairs, or they could be fragments from the start or
|
| + // end of the trace.
|
| + // Determine if any orphans are cross-thread begin/end pairs.
|
| +
|
| + // Sort, because raw events are not in order across threads.
|
| + std::sort(orphans.begin(), orphans.end(), CompareTestTraceEventPtr);
|
| +
|
| + // Find associations between BEGIN/END events that span multiple threads:
|
| + EventPtrVector final_orphans;
|
| + AssociateEvents(orphans, final_orphans);
|
| +
|
| + // final_orphans contains likely fragments from the start/end of the trace.
|
| +}
|
| +
|
| +void TraceAnalyzer::AssociateEvents(EventPtrVector& events_input,
|
| + EventPtrVector& orphans_output) {
|
| + // Search for matching BEGIN/END pairs. When a matching END is found, it is
|
| + // merged with the BEGIN event to create a single DURATION event.
|
| + std::vector<TestTraceEvent*> event_stack;
|
| + for (size_t input_i = 0; input_i < events_input.size(); ++input_i) {
|
| +
|
| + TestTraceEvent& this_event = *events_input[input_i];
|
| +
|
| + if (this_event.phase == TRACE_EVENT_PHASE_BEGIN) {
|
| + event_stack.push_back(&this_event);
|
| + } else if (this_event.phase == TRACE_EVENT_PHASE_END) {
|
| + // Search stack for matching begin, starting from top (end).
|
| + bool found = false;
|
| + for (int si = static_cast<int>(event_stack.size()) - 1; si >= 0; --si) {
|
| + TestTraceEvent& begin_event = *event_stack[si];
|
| + if (begin_event.name == this_event.name &&
|
| + begin_event.category == this_event.category) {
|
| + // Found a matching begin/end pair.
|
| + // Set event association:
|
| + begin_event.SetAssociatedEvent(&this_event);
|
| + // Erase the matching begin event index from the stack.
|
| + event_stack.erase(event_stack.begin() + si);
|
| + found = true;
|
| + break;
|
| + }
|
| + }
|
| + if (!found) {
|
| + // END without matching BEGIN, add it to orphans.
|
| + orphans_output.push_back(&this_event);
|
| + }
|
| + } else if (this_event.phase == TRACE_EVENT_PHASE_METADATA) {
|
| + // Check for thread name metadata.
|
| + if (this_event.name == "thread_name") {
|
| + std::map<std::string, std::string>::const_iterator string_i =
|
| + this_event.arg_strings.find("name");
|
| + if (string_i != this_event.arg_strings.end())
|
| + thread_names_[this_event.pid_tid] = string_i->second;
|
| + }
|
| + }
|
| + }
|
| +
|
| + for (size_t si = 0; si < event_stack.size(); ++si) {
|
| + // BEGIN without matching END, add it to orphans.
|
| + orphans_output.push_back(event_stack[si]);
|
| + }
|
| +}
|
| +
|
| +} // namespace debug
|
| +} // namespace base
|
| +
|
|
|