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 |
+ |