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

Unified Diff: base/debug/trace_event_test_utils.cc

Issue 7866026: Added trace query code and wired tracing through BrowserProxy so tests can run traces. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: comments Created 9 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « base/debug/trace_event_test_utils.h ('k') | base/debug/trace_event_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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
+
« no previous file with comments | « base/debug/trace_event_test_utils.h ('k') | base/debug/trace_event_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698