普通文本  |  1028行  |  28.65 KB

// 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 <memory>
#include <set>

#include "base/json/json_reader.h"
#include "base/strings/pattern.h"
#include "base/values.h"

namespace trace_analyzer {

// TraceEvent

TraceEvent::TraceEvent()
    : thread(0, 0),
      timestamp(0),
      duration(0),
      phase(TRACE_EVENT_PHASE_BEGIN),
      other_event(NULL) {
}

TraceEvent::TraceEvent(TraceEvent&& other) = default;

TraceEvent::~TraceEvent() {
}

TraceEvent& TraceEvent::operator=(TraceEvent&& rhs) = default;

bool TraceEvent::SetFromJSON(const base::Value* event_value) {
  if (event_value->GetType() != 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 = NULL;

  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", &timestamp)) {
    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;
  }

  // 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() {
}

// 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)
    : type_(query.type_),
      operator_(query.operator_),
      left_(query.left_),
      right_(query.right_),
      member_(query.member_),
      number_(query.number_),
      string_(query.string_),
      is_pattern_(query.is_pattern_) {
}

Query::~Query() {
}

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 = NULL;
  if (!root.get() || !root->GetAsList(&root_list))
    return false;

  for (size_t i = 0; i < root_list->GetSize(); ++i) {
    base::Value* item = NULL;
    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_assocation_changes_(true) {}

TraceAnalyzer::~TraceAnalyzer() {
}

// 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 NULL;
}

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_assocation_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_assocation_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 NULL;
}

const TraceEvent* TraceAnalyzer::FindLastOf(const Query& query) {
  TraceEventVector output;
  if (FindEvents(query, &output) > 0)
    return output.back();
  return NULL;
}

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;
  }
}

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