blob: 74dc83b699e2149484113e1911c18ed6034cdb87 [file] [log] [blame]
// Copyright 2015 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 "apps/benchmark/event.h"
#include <map>
#include <stack>
#include "base/json/json_reader.h"
#include "base/macros.h"
#include "base/memory/scoped_ptr.h"
#include "base/values.h"
namespace benchmark {
Event::Event() {}
Event::Event(EventType type,
std::string name,
std::string categories,
base::TimeTicks timestamp,
base::TimeDelta duration)
: type(type),
name(name),
categories(categories),
timestamp(timestamp),
duration(duration) {}
Event::~Event() {}
namespace {
// Finds the matching "duration end" event for each "duration begin" event and
// rewrites the "begin event" as a "complete" event with a duration. Leaves all
// events that are not "duration begin" unchanged.
bool JoinDurationEvents(base::ListValue* event_list) {
// Maps thread ids to stacks of unmatched duration begin events on the given
// thread.
std::map<int, std::stack<base::DictionaryValue*>> open_begin_events;
for (base::Value* val : *event_list) {
base::DictionaryValue* event_dict;
if (!val->GetAsDictionary(&event_dict))
return false;
std::string phase;
if (!event_dict->GetString("ph", &phase)) {
LOG(ERROR) << "Incorrect trace event (missing phase)";
return false;
}
if (phase != "B" && phase != "E")
continue;
int tid;
if (!event_dict->GetInteger("tid", &tid)) {
LOG(ERROR) << "Incorrect trace event (missing tid).";
return false;
}
if (phase == "B") {
open_begin_events[tid].push(event_dict);
} else if (phase == "E") {
if (!open_begin_events.count(tid) || open_begin_events[tid].empty()) {
LOG(ERROR) << "Incorrect trace event (duration end without begin).";
return false;
}
base::DictionaryValue* begin_event_dict = open_begin_events[tid].top();
open_begin_events[tid].pop();
double begin_ts;
if (!begin_event_dict->GetDouble("ts", &begin_ts)) {
LOG(ERROR) << "Incorrect trace event (no timestamp)";
return false;
}
double end_ts;
if (!event_dict->GetDouble("ts", &end_ts)) {
LOG(ERROR) << "Incorrect trace event (no timestamp)";
return false;
}
if (end_ts < begin_ts) {
LOG(ERROR) << "Incorrect trace event (duration timestamps decreasing)";
return false;
}
begin_event_dict->SetDouble("dur", end_ts - begin_ts);
begin_event_dict->SetString("ph", "X");
} else {
NOTREACHED();
}
}
return true;
}
bool ParseEvents(base::ListValue* event_list, std::vector<Event>* result) {
result->clear();
for (base::Value* val : *event_list) {
base::DictionaryValue* event_dict;
if (!val->GetAsDictionary(&event_dict)) {
LOG(WARNING) << "Ignoring incorrect trace event (not a dictionary)";
continue;
}
Event event;
std::string phase;
if (!event_dict->GetString("ph", &phase)) {
LOG(WARNING) << "Ignoring incorrect trace event (missing phase)";
continue;
}
if (phase == "X") {
event.type = EventType::COMPLETE;
} else if (phase == "I") {
event.type = EventType::INSTANT;
} else {
// Skip all event types we do not handle.
continue;
}
if (!event_dict->GetString("name", &event.name)) {
LOG(ERROR) << "Incorrect trace event (no name)";
return false;
}
if (!event_dict->GetString("cat", &event.categories)) {
LOG(WARNING) << "Ignoring incorrect trace event (no categories)";
continue;
}
double timestamp;
if (!event_dict->GetDouble("ts", &timestamp)) {
LOG(WARNING) << "Ingoring incorrect trace event (no timestamp)";
continue;
}
event.timestamp = base::TimeTicks::FromInternalValue(timestamp);
if (event.type == EventType::COMPLETE) {
double duration;
if (!event_dict->GetDouble("dur", &duration)) {
LOG(WARNING) << "Ignoring incorrect complete event (no duration)";
continue;
}
event.duration = base::TimeDelta::FromInternalValue(duration);
} else {
event.duration = base::TimeDelta();
}
result->push_back(event);
}
return true;
}
} // namespace
bool GetEvents(const std::string& trace_json, std::vector<Event>* result) {
// Parse the JSON string describing the events.
base::JSONReader reader;
scoped_ptr<base::Value> trace_data = reader.ReadToValue(trace_json);
if (!trace_data) {
LOG(ERROR) << "Failed to parse the JSON string: "
<< reader.GetErrorMessage();
return false;
}
base::ListValue* event_list;
if (!trace_data->GetAsList(&event_list)) {
LOG(ERROR) << "Incorrect format of the trace data.";
return false;
}
if (!JoinDurationEvents(event_list))
return false;
if (!ParseEvents(event_list, result))
return false;
return true;
}
} // namespace benchmark