| // Copyright (c) 2012 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 "base/test/trace_event_analyzer.h" | 
 |  | 
 | #include <math.h> | 
 |  | 
 | #include <algorithm> | 
 | #include <set> | 
 |  | 
 | #include "base/json/json_reader.h" | 
 | #include "base/memory/ptr_util.h" | 
 | #include "base/memory/ref_counted_memory.h" | 
 | #include "base/run_loop.h" | 
 | #include "base/strings/pattern.h" | 
 | #include "base/trace_event/trace_buffer.h" | 
 | #include "base/trace_event/trace_config.h" | 
 | #include "base/trace_event/trace_log.h" | 
 | #include "base/values.h" | 
 |  | 
 | namespace { | 
 | void OnTraceDataCollected(base::OnceClosure quit_closure, | 
 |                           base::trace_event::TraceResultBuffer* buffer, | 
 |                           const scoped_refptr<base::RefCountedString>& json, | 
 |                           bool has_more_events) { | 
 |   buffer->AddFragment(json->data()); | 
 |   if (!has_more_events) | 
 |     std::move(quit_closure).Run(); | 
 | } | 
 | }  // namespace | 
 |  | 
 | namespace trace_analyzer { | 
 |  | 
 | // TraceEvent | 
 |  | 
 | TraceEvent::TraceEvent() | 
 |     : thread(0, 0), | 
 |       timestamp(0), | 
 |       duration(0), | 
 |       phase(TRACE_EVENT_PHASE_BEGIN), | 
 |       other_event(nullptr) {} | 
 |  | 
 | TraceEvent::TraceEvent(TraceEvent&& other) = default; | 
 |  | 
 | TraceEvent::~TraceEvent() = default; | 
 |  | 
 | TraceEvent& TraceEvent::operator=(TraceEvent&& rhs) = default; | 
 |  | 
 | bool TraceEvent::SetFromJSON(const base::Value* event_value) { | 
 |   if (event_value->type() != base::Value::Type::DICTIONARY) { | 
 |     LOG(ERROR) << "Value must be Type::DICTIONARY"; | 
 |     return false; | 
 |   } | 
 |   const base::DictionaryValue* dictionary = | 
 |       static_cast<const base::DictionaryValue*>(event_value); | 
 |  | 
 |   std::string phase_str; | 
 |   const base::DictionaryValue* args = nullptr; | 
 |  | 
 |   if (!dictionary->GetString("ph", &phase_str)) { | 
 |     LOG(ERROR) << "ph is missing from TraceEvent JSON"; | 
 |     return false; | 
 |   } | 
 |  | 
 |   phase = *phase_str.data(); | 
 |  | 
 |   bool may_have_duration = (phase == TRACE_EVENT_PHASE_COMPLETE); | 
 |   bool require_origin = (phase != TRACE_EVENT_PHASE_METADATA); | 
 |   bool require_id = (phase == TRACE_EVENT_PHASE_ASYNC_BEGIN || | 
 |                      phase == TRACE_EVENT_PHASE_ASYNC_STEP_INTO || | 
 |                      phase == TRACE_EVENT_PHASE_ASYNC_STEP_PAST || | 
 |                      phase == TRACE_EVENT_PHASE_MEMORY_DUMP || | 
 |                      phase == TRACE_EVENT_PHASE_ENTER_CONTEXT || | 
 |                      phase == TRACE_EVENT_PHASE_LEAVE_CONTEXT || | 
 |                      phase == TRACE_EVENT_PHASE_CREATE_OBJECT || | 
 |                      phase == TRACE_EVENT_PHASE_DELETE_OBJECT || | 
 |                      phase == TRACE_EVENT_PHASE_SNAPSHOT_OBJECT || | 
 |                      phase == TRACE_EVENT_PHASE_ASYNC_END); | 
 |  | 
 |   if (require_origin && !dictionary->GetInteger("pid", &thread.process_id)) { | 
 |     LOG(ERROR) << "pid is missing from TraceEvent JSON"; | 
 |     return false; | 
 |   } | 
 |   if (require_origin && !dictionary->GetInteger("tid", &thread.thread_id)) { | 
 |     LOG(ERROR) << "tid is missing from TraceEvent JSON"; | 
 |     return false; | 
 |   } | 
 |   if (require_origin && !dictionary->GetDouble("ts", ×tamp)) { | 
 |     LOG(ERROR) << "ts is missing from TraceEvent JSON"; | 
 |     return false; | 
 |   } | 
 |   if (may_have_duration) { | 
 |     dictionary->GetDouble("dur", &duration); | 
 |   } | 
 |   if (!dictionary->GetString("cat", &category)) { | 
 |     LOG(ERROR) << "cat is missing from TraceEvent JSON"; | 
 |     return false; | 
 |   } | 
 |   if (!dictionary->GetString("name", &name)) { | 
 |     LOG(ERROR) << "name is missing from TraceEvent JSON"; | 
 |     return false; | 
 |   } | 
 |   if (!dictionary->GetDictionary("args", &args)) { | 
 |     LOG(ERROR) << "args is missing from TraceEvent JSON"; | 
 |     return false; | 
 |   } | 
 |   if (require_id && !dictionary->GetString("id", &id)) { | 
 |     LOG(ERROR) << "id is missing from ASYNC_BEGIN/ASYNC_END TraceEvent JSON"; | 
 |     return false; | 
 |   } | 
 |  | 
 |   dictionary->GetDouble("tdur", &thread_duration); | 
 |   dictionary->GetDouble("tts", &thread_timestamp); | 
 |   dictionary->GetString("scope", &scope); | 
 |   dictionary->GetString("bind_id", &bind_id); | 
 |   dictionary->GetBoolean("flow_out", &flow_out); | 
 |   dictionary->GetBoolean("flow_in", &flow_in); | 
 |  | 
 |   const base::DictionaryValue* id2; | 
 |   if (dictionary->GetDictionary("id2", &id2)) { | 
 |     id2->GetString("global", &global_id2); | 
 |     id2->GetString("local", &local_id2); | 
 |   } | 
 |  | 
 |   // For each argument, copy the type and create a trace_analyzer::TraceValue. | 
 |   for (base::DictionaryValue::Iterator it(*args); !it.IsAtEnd(); | 
 |        it.Advance()) { | 
 |     std::string str; | 
 |     bool boolean = false; | 
 |     int int_num = 0; | 
 |     double double_num = 0.0; | 
 |     if (it.value().GetAsString(&str)) { | 
 |       arg_strings[it.key()] = str; | 
 |     } else if (it.value().GetAsInteger(&int_num)) { | 
 |       arg_numbers[it.key()] = static_cast<double>(int_num); | 
 |     } else if (it.value().GetAsBoolean(&boolean)) { | 
 |       arg_numbers[it.key()] = static_cast<double>(boolean ? 1 : 0); | 
 |     } else if (it.value().GetAsDouble(&double_num)) { | 
 |       arg_numbers[it.key()] = double_num; | 
 |     } | 
 |     // Record all arguments as values. | 
 |     arg_values[it.key()] = it.value().CreateDeepCopy(); | 
 |   } | 
 |  | 
 |   return true; | 
 | } | 
 |  | 
 | double TraceEvent::GetAbsTimeToOtherEvent() const { | 
 |   return fabs(other_event->timestamp - timestamp); | 
 | } | 
 |  | 
 | bool TraceEvent::GetArgAsString(const std::string& name, | 
 |                                 std::string* arg) const { | 
 |   const auto it = arg_strings.find(name); | 
 |   if (it != arg_strings.end()) { | 
 |     *arg = it->second; | 
 |     return true; | 
 |   } | 
 |   return false; | 
 | } | 
 |  | 
 | bool TraceEvent::GetArgAsNumber(const std::string& name, | 
 |                                 double* arg) const { | 
 |   const auto it = arg_numbers.find(name); | 
 |   if (it != arg_numbers.end()) { | 
 |     *arg = it->second; | 
 |     return true; | 
 |   } | 
 |   return false; | 
 | } | 
 |  | 
 | bool TraceEvent::GetArgAsValue(const std::string& name, | 
 |                                std::unique_ptr<base::Value>* arg) const { | 
 |   const auto it = arg_values.find(name); | 
 |   if (it != arg_values.end()) { | 
 |     *arg = it->second->CreateDeepCopy(); | 
 |     return true; | 
 |   } | 
 |   return false; | 
 | } | 
 |  | 
 | bool TraceEvent::HasStringArg(const std::string& name) const { | 
 |   return (arg_strings.find(name) != arg_strings.end()); | 
 | } | 
 |  | 
 | bool TraceEvent::HasNumberArg(const std::string& name) const { | 
 |   return (arg_numbers.find(name) != arg_numbers.end()); | 
 | } | 
 |  | 
 | bool TraceEvent::HasArg(const std::string& name) const { | 
 |   return (arg_values.find(name) != arg_values.end()); | 
 | } | 
 |  | 
 | std::string TraceEvent::GetKnownArgAsString(const std::string& name) const { | 
 |   std::string arg_string; | 
 |   bool result = GetArgAsString(name, &arg_string); | 
 |   DCHECK(result); | 
 |   return arg_string; | 
 | } | 
 |  | 
 | double TraceEvent::GetKnownArgAsDouble(const std::string& name) const { | 
 |   double arg_double = 0; | 
 |   bool result = GetArgAsNumber(name, &arg_double); | 
 |   DCHECK(result); | 
 |   return arg_double; | 
 | } | 
 |  | 
 | int TraceEvent::GetKnownArgAsInt(const std::string& name) const { | 
 |   double arg_double = 0; | 
 |   bool result = GetArgAsNumber(name, &arg_double); | 
 |   DCHECK(result); | 
 |   return static_cast<int>(arg_double); | 
 | } | 
 |  | 
 | bool TraceEvent::GetKnownArgAsBool(const std::string& name) const { | 
 |   double arg_double = 0; | 
 |   bool result = GetArgAsNumber(name, &arg_double); | 
 |   DCHECK(result); | 
 |   return (arg_double != 0.0); | 
 | } | 
 |  | 
 | std::unique_ptr<base::Value> TraceEvent::GetKnownArgAsValue( | 
 |     const std::string& name) const { | 
 |   std::unique_ptr<base::Value> arg_value; | 
 |   bool result = GetArgAsValue(name, &arg_value); | 
 |   DCHECK(result); | 
 |   return arg_value; | 
 | } | 
 |  | 
 | // QueryNode | 
 |  | 
 | QueryNode::QueryNode(const Query& query) : query_(query) { | 
 | } | 
 |  | 
 | QueryNode::~QueryNode() = default; | 
 |  | 
 | // Query | 
 |  | 
 | Query::Query(TraceEventMember member) | 
 |     : type_(QUERY_EVENT_MEMBER), | 
 |       operator_(OP_INVALID), | 
 |       member_(member), | 
 |       number_(0), | 
 |       is_pattern_(false) { | 
 | } | 
 |  | 
 | Query::Query(TraceEventMember member, const std::string& arg_name) | 
 |     : type_(QUERY_EVENT_MEMBER), | 
 |       operator_(OP_INVALID), | 
 |       member_(member), | 
 |       number_(0), | 
 |       string_(arg_name), | 
 |       is_pattern_(false) { | 
 | } | 
 |  | 
 | Query::Query(const Query& query) = default; | 
 |  | 
 | Query::~Query() = default; | 
 |  | 
 | Query Query::String(const std::string& str) { | 
 |   return Query(str); | 
 | } | 
 |  | 
 | Query Query::Double(double num) { | 
 |   return Query(num); | 
 | } | 
 |  | 
 | Query Query::Int(int32_t num) { | 
 |   return Query(static_cast<double>(num)); | 
 | } | 
 |  | 
 | Query Query::Uint(uint32_t num) { | 
 |   return Query(static_cast<double>(num)); | 
 | } | 
 |  | 
 | Query Query::Bool(bool boolean) { | 
 |   return Query(boolean ? 1.0 : 0.0); | 
 | } | 
 |  | 
 | Query Query::Phase(char phase) { | 
 |   return Query(static_cast<double>(phase)); | 
 | } | 
 |  | 
 | Query Query::Pattern(const std::string& pattern) { | 
 |   Query query(pattern); | 
 |   query.is_pattern_ = true; | 
 |   return query; | 
 | } | 
 |  | 
 | bool Query::Evaluate(const TraceEvent& 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 is non-empty: | 
 |   std::string str_value; | 
 |   bool is_str = GetAsString(event, &str_value); | 
 |   if (is_str) | 
 |     return !str_value.empty(); | 
 |  | 
 |   DCHECK_EQ(QUERY_BOOLEAN_OPERATOR, type_) | 
 |       << "Invalid query: missing boolean expression"; | 
 |   DCHECK(left_.get()); | 
 |   DCHECK(right_.get() || is_unary_operator()); | 
 |  | 
 |   if (is_comparison_operator()) { | 
 |     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; | 
 |     if (CompareAsString(event, &compare_result)) | 
 |       return compare_result; | 
 |     return false; | 
 |   } | 
 |   // It's a logical operator. | 
 |   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(); | 
 |       return false; | 
 |   } | 
 | } | 
 |  | 
 | bool Query::CompareAsDouble(const TraceEvent& 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; | 
 |   } | 
 | } | 
 |  | 
 | bool Query::CompareAsString(const TraceEvent& 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 = base::MatchPattern(lhs, rhs); | 
 |       else if (left().is_pattern_) | 
 |         *result = base::MatchPattern(rhs, lhs); | 
 |       else | 
 |         *result = (lhs == rhs); | 
 |       return true; | 
 |     case OP_NE: | 
 |       if (right().is_pattern_) | 
 |         *result = !base::MatchPattern(lhs, rhs); | 
 |       else if (left().is_pattern_) | 
 |         *result = !base::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; | 
 |   } | 
 | } | 
 |  | 
 | bool Query::EvaluateArithmeticOperator(const TraceEvent& event, | 
 |                                        double* num) const { | 
 |   DCHECK_EQ(QUERY_ARITHMETIC_OPERATOR, type_); | 
 |   DCHECK(left_.get()); | 
 |   DCHECK(right_.get() || is_unary_operator()); | 
 |  | 
 |   double lhs = 0, rhs = 0; | 
 |   if (!left().GetAsDouble(event, &lhs)) | 
 |     return false; | 
 |   if (!is_unary_operator() && !right().GetAsDouble(event, &rhs)) | 
 |     return false; | 
 |  | 
 |   switch (operator_) { | 
 |     case OP_ADD: | 
 |       *num = lhs + rhs; | 
 |       return true; | 
 |     case OP_SUB: | 
 |       *num = lhs - rhs; | 
 |       return true; | 
 |     case OP_MUL: | 
 |       *num = lhs * rhs; | 
 |       return true; | 
 |     case OP_DIV: | 
 |       *num = lhs / rhs; | 
 |       return true; | 
 |     case OP_MOD: | 
 |       *num = static_cast<double>(static_cast<int64_t>(lhs) % | 
 |                                  static_cast<int64_t>(rhs)); | 
 |       return true; | 
 |     case OP_NEGATE: | 
 |       *num = -lhs; | 
 |       return true; | 
 |     default: | 
 |       NOTREACHED(); | 
 |       return false; | 
 |   } | 
 | } | 
 |  | 
 | bool Query::GetAsDouble(const TraceEvent& event, double* num) const { | 
 |   switch (type_) { | 
 |     case QUERY_ARITHMETIC_OPERATOR: | 
 |       return EvaluateArithmeticOperator(event, num); | 
 |     case QUERY_EVENT_MEMBER: | 
 |       return GetMemberValueAsDouble(event, num); | 
 |     case QUERY_NUMBER: | 
 |       *num = number_; | 
 |       return true; | 
 |     default: | 
 |       return false; | 
 |   } | 
 | } | 
 |  | 
 | bool Query::GetAsString(const TraceEvent& event, std::string* str) const { | 
 |   switch (type_) { | 
 |     case QUERY_EVENT_MEMBER: | 
 |       return GetMemberValueAsString(event, str); | 
 |     case QUERY_STRING: | 
 |       *str = string_; | 
 |       return true; | 
 |     default: | 
 |       return false; | 
 |   } | 
 | } | 
 |  | 
 | const TraceEvent* Query::SelectTargetEvent(const TraceEvent* event, | 
 |                                            TraceEventMember member) { | 
 |   if (member >= OTHER_FIRST_MEMBER && member <= OTHER_LAST_MEMBER) { | 
 |     return event->other_event; | 
 |   } else if (member >= PREV_FIRST_MEMBER && member <= PREV_LAST_MEMBER) { | 
 |     return event->prev_event; | 
 |   } else { | 
 |     return event; | 
 |   } | 
 | } | 
 |  | 
 | bool Query::GetMemberValueAsDouble(const TraceEvent& event, | 
 |                                    double* num) const { | 
 |   DCHECK_EQ(QUERY_EVENT_MEMBER, type_); | 
 |  | 
 |   // This could be a request for a member of |event| or a member of |event|'s | 
 |   // associated previous or next event. Store the target event in the_event: | 
 |   const TraceEvent* the_event = SelectTargetEvent(&event, member_); | 
 |  | 
 |   // Request for member of associated event, but there is no associated event. | 
 |   if (!the_event) | 
 |     return false; | 
 |  | 
 |   switch (member_) { | 
 |     case EVENT_PID: | 
 |     case OTHER_PID: | 
 |     case PREV_PID: | 
 |       *num = static_cast<double>(the_event->thread.process_id); | 
 |       return true; | 
 |     case EVENT_TID: | 
 |     case OTHER_TID: | 
 |     case PREV_TID: | 
 |       *num = static_cast<double>(the_event->thread.thread_id); | 
 |       return true; | 
 |     case EVENT_TIME: | 
 |     case OTHER_TIME: | 
 |     case PREV_TIME: | 
 |       *num = the_event->timestamp; | 
 |       return true; | 
 |     case EVENT_DURATION: | 
 |       if (!the_event->has_other_event()) | 
 |         return false; | 
 |       *num = the_event->GetAbsTimeToOtherEvent(); | 
 |       return true; | 
 |     case EVENT_COMPLETE_DURATION: | 
 |       if (the_event->phase != TRACE_EVENT_PHASE_COMPLETE) | 
 |         return false; | 
 |       *num = the_event->duration; | 
 |       return true; | 
 |     case EVENT_PHASE: | 
 |     case OTHER_PHASE: | 
 |     case PREV_PHASE: | 
 |       *num = static_cast<double>(the_event->phase); | 
 |       return true; | 
 |     case EVENT_HAS_STRING_ARG: | 
 |     case OTHER_HAS_STRING_ARG: | 
 |     case PREV_HAS_STRING_ARG: | 
 |       *num = (the_event->HasStringArg(string_) ? 1.0 : 0.0); | 
 |       return true; | 
 |     case EVENT_HAS_NUMBER_ARG: | 
 |     case OTHER_HAS_NUMBER_ARG: | 
 |     case PREV_HAS_NUMBER_ARG: | 
 |       *num = (the_event->HasNumberArg(string_) ? 1.0 : 0.0); | 
 |       return true; | 
 |     case EVENT_ARG: | 
 |     case OTHER_ARG: | 
 |     case PREV_ARG: { | 
 |       // Search for the argument name and return its value if found. | 
 |       std::map<std::string, double>::const_iterator num_i = | 
 |           the_event->arg_numbers.find(string_); | 
 |       if (num_i == the_event->arg_numbers.end()) | 
 |         return false; | 
 |       *num = num_i->second; | 
 |       return true; | 
 |     } | 
 |     case EVENT_HAS_OTHER: | 
 |       // return 1.0 (true) if the other event exists | 
 |       *num = event.other_event ? 1.0 : 0.0; | 
 |       return true; | 
 |     case EVENT_HAS_PREV: | 
 |       *num = event.prev_event ? 1.0 : 0.0; | 
 |       return true; | 
 |     default: | 
 |       return false; | 
 |   } | 
 | } | 
 |  | 
 | bool Query::GetMemberValueAsString(const TraceEvent& event, | 
 |                                    std::string* str) const { | 
 |   DCHECK_EQ(QUERY_EVENT_MEMBER, type_); | 
 |  | 
 |   // This could be a request for a member of |event| or a member of |event|'s | 
 |   // associated previous or next event. Store the target event in the_event: | 
 |   const TraceEvent* the_event = SelectTargetEvent(&event, member_); | 
 |  | 
 |   // Request for member of associated event, but there is no associated event. | 
 |   if (!the_event) | 
 |     return false; | 
 |  | 
 |   switch (member_) { | 
 |     case EVENT_CATEGORY: | 
 |     case OTHER_CATEGORY: | 
 |     case PREV_CATEGORY: | 
 |       *str = the_event->category; | 
 |       return true; | 
 |     case EVENT_NAME: | 
 |     case OTHER_NAME: | 
 |     case PREV_NAME: | 
 |       *str = the_event->name; | 
 |       return true; | 
 |     case EVENT_ID: | 
 |     case OTHER_ID: | 
 |     case PREV_ID: | 
 |       *str = the_event->id; | 
 |       return true; | 
 |     case EVENT_ARG: | 
 |     case OTHER_ARG: | 
 |     case PREV_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 false; | 
 |       *str = str_i->second; | 
 |       return true; | 
 |     } | 
 |     default: | 
 |       return false; | 
 |   } | 
 | } | 
 |  | 
 | Query::Query(const std::string& str) | 
 |     : type_(QUERY_STRING), | 
 |       operator_(OP_INVALID), | 
 |       member_(EVENT_INVALID), | 
 |       number_(0), | 
 |       string_(str), | 
 |       is_pattern_(false) { | 
 | } | 
 |  | 
 | Query::Query(double num) | 
 |     : type_(QUERY_NUMBER), | 
 |       operator_(OP_INVALID), | 
 |       member_(EVENT_INVALID), | 
 |       number_(num), | 
 |       is_pattern_(false) { | 
 | } | 
 | 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::operator+(const Query& rhs) const { | 
 |   return Query(*this, rhs, OP_ADD); | 
 | } | 
 |  | 
 | Query Query::operator-(const Query& rhs) const { | 
 |   return Query(*this, rhs, OP_SUB); | 
 | } | 
 |  | 
 | Query Query::operator*(const Query& rhs) const { | 
 |   return Query(*this, rhs, OP_MUL); | 
 | } | 
 |  | 
 | Query Query::operator/(const Query& rhs) const { | 
 |   return Query(*this, rhs, OP_DIV); | 
 | } | 
 |  | 
 | Query Query::operator%(const Query& rhs) const { | 
 |   return Query(*this, rhs, OP_MOD); | 
 | } | 
 |  | 
 | Query Query::operator-() const { | 
 |   return Query(*this, OP_NEGATE); | 
 | } | 
 |  | 
 |  | 
 | Query::Query(const Query& left, const Query& right, Operator binary_op) | 
 |     : operator_(binary_op), | 
 |       left_(new QueryNode(left)), | 
 |       right_(new QueryNode(right)), | 
 |       member_(EVENT_INVALID), | 
 |       number_(0) { | 
 |   type_ = (binary_op < OP_ADD ? | 
 |            QUERY_BOOLEAN_OPERATOR : QUERY_ARITHMETIC_OPERATOR); | 
 | } | 
 |  | 
 | Query::Query(const Query& left, Operator unary_op) | 
 |     : operator_(unary_op), | 
 |       left_(new QueryNode(left)), | 
 |       member_(EVENT_INVALID), | 
 |       number_(0) { | 
 |   type_ = (unary_op < OP_ADD ? | 
 |            QUERY_BOOLEAN_OPERATOR : QUERY_ARITHMETIC_OPERATOR); | 
 | } | 
 |  | 
 | namespace { | 
 |  | 
 | // Search |events| for |query| and add matches to |output|. | 
 | size_t FindMatchingEvents(const std::vector<TraceEvent>& events, | 
 |                           const Query& query, | 
 |                           TraceEventVector* output, | 
 |                           bool ignore_metadata_events) { | 
 |   for (size_t i = 0; i < events.size(); ++i) { | 
 |     if (ignore_metadata_events && events[i].phase == TRACE_EVENT_PHASE_METADATA) | 
 |       continue; | 
 |     if (query.Evaluate(events[i])) | 
 |       output->push_back(&events[i]); | 
 |   } | 
 |   return output->size(); | 
 | } | 
 |  | 
 | bool ParseEventsFromJson(const std::string& json, | 
 |                          std::vector<TraceEvent>* output) { | 
 |   std::unique_ptr<base::Value> root = base::JSONReader::Read(json); | 
 |  | 
 |   base::ListValue* root_list = nullptr; | 
 |   if (!root.get() || !root->GetAsList(&root_list)) | 
 |     return false; | 
 |  | 
 |   for (size_t i = 0; i < root_list->GetSize(); ++i) { | 
 |     base::Value* item = nullptr; | 
 |     if (root_list->Get(i, &item)) { | 
 |       TraceEvent event; | 
 |       if (event.SetFromJSON(item)) | 
 |         output->push_back(std::move(event)); | 
 |       else | 
 |         return false; | 
 |     } | 
 |   } | 
 |  | 
 |   return true; | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | // TraceAnalyzer | 
 |  | 
 | TraceAnalyzer::TraceAnalyzer() | 
 |     : ignore_metadata_events_(false), allow_association_changes_(true) {} | 
 |  | 
 | TraceAnalyzer::~TraceAnalyzer() = default; | 
 |  | 
 | // static | 
 | TraceAnalyzer* TraceAnalyzer::Create(const std::string& json_events) { | 
 |   std::unique_ptr<TraceAnalyzer> analyzer(new TraceAnalyzer()); | 
 |   if (analyzer->SetEvents(json_events)) | 
 |     return analyzer.release(); | 
 |   return nullptr; | 
 | } | 
 |  | 
 | bool TraceAnalyzer::SetEvents(const std::string& json_events) { | 
 |   raw_events_.clear(); | 
 |   if (!ParseEventsFromJson(json_events, &raw_events_)) | 
 |     return false; | 
 |   std::stable_sort(raw_events_.begin(), raw_events_.end()); | 
 |   ParseMetadata(); | 
 |   return true; | 
 | } | 
 |  | 
 | void TraceAnalyzer::AssociateBeginEndEvents() { | 
 |   using trace_analyzer::Query; | 
 |  | 
 |   Query begin(Query::EventPhaseIs(TRACE_EVENT_PHASE_BEGIN)); | 
 |   Query end(Query::EventPhaseIs(TRACE_EVENT_PHASE_END)); | 
 |   Query match(Query::EventName() == Query::OtherName() && | 
 |               Query::EventCategory() == Query::OtherCategory() && | 
 |               Query::EventTid() == Query::OtherTid() && | 
 |               Query::EventPid() == Query::OtherPid()); | 
 |  | 
 |   AssociateEvents(begin, end, match); | 
 | } | 
 |  | 
 | void TraceAnalyzer::AssociateAsyncBeginEndEvents(bool match_pid) { | 
 |   using trace_analyzer::Query; | 
 |  | 
 |   Query begin( | 
 |       Query::EventPhaseIs(TRACE_EVENT_PHASE_ASYNC_BEGIN) || | 
 |       Query::EventPhaseIs(TRACE_EVENT_PHASE_ASYNC_STEP_INTO) || | 
 |       Query::EventPhaseIs(TRACE_EVENT_PHASE_ASYNC_STEP_PAST)); | 
 |   Query end(Query::EventPhaseIs(TRACE_EVENT_PHASE_ASYNC_END) || | 
 |             Query::EventPhaseIs(TRACE_EVENT_PHASE_ASYNC_STEP_INTO) || | 
 |             Query::EventPhaseIs(TRACE_EVENT_PHASE_ASYNC_STEP_PAST)); | 
 |   Query match(Query::EventCategory() == Query::OtherCategory() && | 
 |               Query::EventId() == Query::OtherId()); | 
 |  | 
 |   if (match_pid) { | 
 |     match = match && Query::EventPid() == Query::OtherPid(); | 
 |   } | 
 |  | 
 |   AssociateEvents(begin, end, match); | 
 | } | 
 |  | 
 | void TraceAnalyzer::AssociateEvents(const Query& first, | 
 |                                     const Query& second, | 
 |                                     const Query& match) { | 
 |   DCHECK(allow_association_changes_) | 
 |       << "AssociateEvents not allowed after FindEvents"; | 
 |  | 
 |   // Search for matching begin/end event pairs. When a matching end is found, | 
 |   // it is associated with the begin event. | 
 |   std::vector<TraceEvent*> begin_stack; | 
 |   for (size_t event_index = 0; event_index < raw_events_.size(); | 
 |        ++event_index) { | 
 |  | 
 |     TraceEvent& this_event = raw_events_[event_index]; | 
 |  | 
 |     if (second.Evaluate(this_event)) { | 
 |       // Search stack for matching begin, starting from end. | 
 |       for (int stack_index = static_cast<int>(begin_stack.size()) - 1; | 
 |            stack_index >= 0; --stack_index) { | 
 |         TraceEvent& begin_event = *begin_stack[stack_index]; | 
 |  | 
 |         // Temporarily set other to test against the match query. | 
 |         const TraceEvent* other_backup = begin_event.other_event; | 
 |         begin_event.other_event = &this_event; | 
 |         if (match.Evaluate(begin_event)) { | 
 |           // Found a matching begin/end pair. | 
 |           // Set the associated previous event | 
 |           this_event.prev_event = &begin_event; | 
 |           // Erase the matching begin event index from the stack. | 
 |           begin_stack.erase(begin_stack.begin() + stack_index); | 
 |           break; | 
 |         } | 
 |  | 
 |         // Not a match, restore original other and continue. | 
 |         begin_event.other_event = other_backup; | 
 |       } | 
 |     } | 
 |     // Even if this_event is a |second| event that has matched an earlier | 
 |     // |first| event, it can still also be a |first| event and be associated | 
 |     // with a later |second| event. | 
 |     if (first.Evaluate(this_event)) { | 
 |       begin_stack.push_back(&this_event); | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | void TraceAnalyzer::MergeAssociatedEventArgs() { | 
 |   for (size_t i = 0; i < raw_events_.size(); ++i) { | 
 |     // Merge all associated events with the first event. | 
 |     const TraceEvent* other = raw_events_[i].other_event; | 
 |     // Avoid looping by keeping set of encountered TraceEvents. | 
 |     std::set<const TraceEvent*> encounters; | 
 |     encounters.insert(&raw_events_[i]); | 
 |     while (other && encounters.find(other) == encounters.end()) { | 
 |       encounters.insert(other); | 
 |       raw_events_[i].arg_numbers.insert( | 
 |           other->arg_numbers.begin(), | 
 |           other->arg_numbers.end()); | 
 |       raw_events_[i].arg_strings.insert( | 
 |           other->arg_strings.begin(), | 
 |           other->arg_strings.end()); | 
 |       other = other->other_event; | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | size_t TraceAnalyzer::FindEvents(const Query& query, TraceEventVector* output) { | 
 |   allow_association_changes_ = false; | 
 |   output->clear(); | 
 |   return FindMatchingEvents( | 
 |       raw_events_, query, output, ignore_metadata_events_); | 
 | } | 
 |  | 
 | const TraceEvent* TraceAnalyzer::FindFirstOf(const Query& query) { | 
 |   TraceEventVector output; | 
 |   if (FindEvents(query, &output) > 0) | 
 |     return output.front(); | 
 |   return nullptr; | 
 | } | 
 |  | 
 | const TraceEvent* TraceAnalyzer::FindLastOf(const Query& query) { | 
 |   TraceEventVector output; | 
 |   if (FindEvents(query, &output) > 0) | 
 |     return output.back(); | 
 |   return nullptr; | 
 | } | 
 |  | 
 | const std::string& TraceAnalyzer::GetThreadName( | 
 |     const TraceEvent::ProcessThreadID& thread) { | 
 |   // If thread is not found, just add and return empty string. | 
 |   return thread_names_[thread]; | 
 | } | 
 |  | 
 | void TraceAnalyzer::ParseMetadata() { | 
 |   for (size_t i = 0; i < raw_events_.size(); ++i) { | 
 |     TraceEvent& this_event = raw_events_[i]; | 
 |     // Check for thread name metadata. | 
 |     if (this_event.phase != TRACE_EVENT_PHASE_METADATA || | 
 |         this_event.name != "thread_name") | 
 |       continue; | 
 |     std::map<std::string, std::string>::const_iterator string_it = | 
 |         this_event.arg_strings.find("name"); | 
 |     if (string_it != this_event.arg_strings.end()) | 
 |       thread_names_[this_event.thread] = string_it->second; | 
 |   } | 
 | } | 
 |  | 
 | // Utility functions for collecting process-local traces and creating a | 
 | // |TraceAnalyzer| from the result. | 
 |  | 
 | void Start(const std::string& category_filter_string) { | 
 |   DCHECK(!base::trace_event::TraceLog::GetInstance()->IsEnabled()); | 
 |   base::trace_event::TraceLog::GetInstance()->SetEnabled( | 
 |       base::trace_event::TraceConfig(category_filter_string, ""), | 
 |       base::trace_event::TraceLog::RECORDING_MODE); | 
 | } | 
 |  | 
 | std::unique_ptr<TraceAnalyzer> Stop() { | 
 |   DCHECK(base::trace_event::TraceLog::GetInstance()->IsEnabled()); | 
 |   base::trace_event::TraceLog::GetInstance()->SetDisabled(); | 
 |  | 
 |   base::trace_event::TraceResultBuffer buffer; | 
 |   base::trace_event::TraceResultBuffer::SimpleOutput trace_output; | 
 |   buffer.SetOutputCallback(trace_output.GetCallback()); | 
 |   base::RunLoop run_loop; | 
 |   buffer.Start(); | 
 |   base::trace_event::TraceLog::GetInstance()->Flush( | 
 |       base::BindRepeating(&OnTraceDataCollected, run_loop.QuitClosure(), | 
 |                           base::Unretained(&buffer))); | 
 |   run_loop.Run(); | 
 |   buffer.Finish(); | 
 |  | 
 |   return base::WrapUnique(TraceAnalyzer::Create(trace_output.json_output)); | 
 | } | 
 |  | 
 | // TraceEventVector utility functions. | 
 |  | 
 | bool GetRateStats(const TraceEventVector& events, | 
 |                   RateStats* stats, | 
 |                   const RateStatsOptions* options) { | 
 |   DCHECK(stats); | 
 |   // Need at least 3 events to calculate rate stats. | 
 |   const size_t kMinEvents = 3; | 
 |   if (events.size() < kMinEvents) { | 
 |     LOG(ERROR) << "Not enough events: " << events.size(); | 
 |     return false; | 
 |   } | 
 |  | 
 |   std::vector<double> deltas; | 
 |   size_t num_deltas = events.size() - 1; | 
 |   for (size_t i = 0; i < num_deltas; ++i) { | 
 |     double delta = events.at(i + 1)->timestamp - events.at(i)->timestamp; | 
 |     if (delta < 0.0) { | 
 |       LOG(ERROR) << "Events are out of order"; | 
 |       return false; | 
 |     } | 
 |     deltas.push_back(delta); | 
 |   } | 
 |  | 
 |   std::sort(deltas.begin(), deltas.end()); | 
 |  | 
 |   if (options) { | 
 |     if (options->trim_min + options->trim_max > events.size() - kMinEvents) { | 
 |       LOG(ERROR) << "Attempt to trim too many events"; | 
 |       return false; | 
 |     } | 
 |     deltas.erase(deltas.begin(), deltas.begin() + options->trim_min); | 
 |     deltas.erase(deltas.end() - options->trim_max, deltas.end()); | 
 |   } | 
 |  | 
 |   num_deltas = deltas.size(); | 
 |   double delta_sum = 0.0; | 
 |   for (size_t i = 0; i < num_deltas; ++i) | 
 |     delta_sum += deltas[i]; | 
 |  | 
 |   stats->min_us = *std::min_element(deltas.begin(), deltas.end()); | 
 |   stats->max_us = *std::max_element(deltas.begin(), deltas.end()); | 
 |   stats->mean_us = delta_sum / static_cast<double>(num_deltas); | 
 |  | 
 |   double sum_mean_offsets_squared = 0.0; | 
 |   for (size_t i = 0; i < num_deltas; ++i) { | 
 |     double offset = fabs(deltas[i] - stats->mean_us); | 
 |     sum_mean_offsets_squared += offset * offset; | 
 |   } | 
 |   stats->standard_deviation_us = | 
 |       sqrt(sum_mean_offsets_squared / static_cast<double>(num_deltas - 1)); | 
 |  | 
 |   return true; | 
 | } | 
 |  | 
 | bool FindFirstOf(const TraceEventVector& events, | 
 |                  const Query& query, | 
 |                  size_t position, | 
 |                  size_t* return_index) { | 
 |   DCHECK(return_index); | 
 |   for (size_t i = position; i < events.size(); ++i) { | 
 |     if (query.Evaluate(*events[i])) { | 
 |       *return_index = i; | 
 |       return true; | 
 |     } | 
 |   } | 
 |   return false; | 
 | } | 
 |  | 
 | bool FindLastOf(const TraceEventVector& events, | 
 |                 const Query& query, | 
 |                 size_t position, | 
 |                 size_t* return_index) { | 
 |   DCHECK(return_index); | 
 |   for (size_t i = std::min(position + 1, events.size()); i != 0; --i) { | 
 |     if (query.Evaluate(*events[i - 1])) { | 
 |       *return_index = i - 1; | 
 |       return true; | 
 |     } | 
 |   } | 
 |   return false; | 
 | } | 
 |  | 
 | bool FindClosest(const TraceEventVector& events, | 
 |                  const Query& query, | 
 |                  size_t position, | 
 |                  size_t* return_closest, | 
 |                  size_t* return_second_closest) { | 
 |   DCHECK(return_closest); | 
 |   if (events.empty() || position >= events.size()) | 
 |     return false; | 
 |   size_t closest = events.size(); | 
 |   size_t second_closest = events.size(); | 
 |   for (size_t i = 0; i < events.size(); ++i) { | 
 |     if (!query.Evaluate(*events.at(i))) | 
 |       continue; | 
 |     if (closest == events.size()) { | 
 |       closest = i; | 
 |       continue; | 
 |     } | 
 |     if (fabs(events.at(i)->timestamp - events.at(position)->timestamp) < | 
 |         fabs(events.at(closest)->timestamp - events.at(position)->timestamp)) { | 
 |       second_closest = closest; | 
 |       closest = i; | 
 |     } else if (second_closest == events.size()) { | 
 |       second_closest = i; | 
 |     } | 
 |   } | 
 |  | 
 |   if (closest < events.size() && | 
 |       (!return_second_closest || second_closest < events.size())) { | 
 |     *return_closest = closest; | 
 |     if (return_second_closest) | 
 |       *return_second_closest = second_closest; | 
 |     return true; | 
 |   } | 
 |  | 
 |   return false; | 
 | } | 
 |  | 
 | size_t CountMatches(const TraceEventVector& events, | 
 |                     const Query& query, | 
 |                     size_t begin_position, | 
 |                     size_t end_position) { | 
 |   if (begin_position >= events.size()) | 
 |     return 0u; | 
 |   end_position = (end_position < events.size()) ? end_position : events.size(); | 
 |   size_t count = 0u; | 
 |   for (size_t i = begin_position; i < end_position; ++i) { | 
 |     if (query.Evaluate(*events.at(i))) | 
 |       ++count; | 
 |   } | 
 |   return count; | 
 | } | 
 |  | 
 | }  // namespace trace_analyzer |