blob: 69f56d27f518394269a6330c2ec346828d0b021f [file] [log] [blame]
Przemyslaw Pietrzkiewicz1d909942015-08-28 16:27:56 +02001// Copyright 2015 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "apps/benchmark/event.h"
6
Przemyslaw Pietrzkiewicz421c1f72015-09-08 14:52:36 +02007#include <map>
8#include <stack>
9
Przemyslaw Pietrzkiewicz1d909942015-08-28 16:27:56 +020010#include "base/json/json_reader.h"
Przemyslaw Pietrzkiewicz421c1f72015-09-08 14:52:36 +020011#include "base/macros.h"
Przemyslaw Pietrzkiewicz1d909942015-08-28 16:27:56 +020012#include "base/memory/scoped_ptr.h"
Etienne Membrives014f4f92015-10-01 16:02:11 +020013#include "base/strings/string_number_conversions.h"
Przemyslaw Pietrzkiewicz1d909942015-08-28 16:27:56 +020014#include "base/values.h"
15
16namespace benchmark {
17
18Event::Event() {}
19
Przemyslaw Pietrzkiewicz421c1f72015-09-08 14:52:36 +020020Event::Event(EventType type,
21 std::string name,
22 std::string categories,
Przemyslaw Pietrzkiewicz1d909942015-08-28 16:27:56 +020023 base::TimeTicks timestamp,
24 base::TimeDelta duration)
Przemyslaw Pietrzkiewicz421c1f72015-09-08 14:52:36 +020025 : type(type),
26 name(name),
27 categories(categories),
Przemyslaw Pietrzkiewicz1d909942015-08-28 16:27:56 +020028 timestamp(timestamp),
29 duration(duration) {}
30
31Event::~Event() {}
32
Przemyslaw Pietrzkiewicz421c1f72015-09-08 14:52:36 +020033namespace {
Etienne Membrivesb47d7cf2015-09-29 16:25:42 +020034// ID uniquely identifying an asynchronous event stack.
35struct AsyncEventStackId {
Etienne Membrives014f4f92015-10-01 16:02:11 +020036 std::string id;
Etienne Membrivesb47d7cf2015-09-29 16:25:42 +020037 std::string cat;
38};
Przemyslaw Pietrzkiewicz1d909942015-08-28 16:27:56 +020039
Etienne Membrivesb47d7cf2015-09-29 16:25:42 +020040bool operator<(const AsyncEventStackId& t, const AsyncEventStackId& o) {
41 return t.id < o.id || (t.id == o.id && t.cat < o.cat);
42}
43
44// ID uniquely identifying a duration event stack.
Etienne Membrives014f4f92015-10-01 16:02:11 +020045typedef std::string DurationEventStackId;
Etienne Membrivesb47d7cf2015-09-29 16:25:42 +020046
Etienne Membrives014f4f92015-10-01 16:02:11 +020047bool ExtractKeyAsString(base::DictionaryValue* event_dict,
48 const std::string& key,
49 std::string* output) {
50 const base::Value* value;
51 if (!event_dict->Get(key, &value)) {
52 LOG(ERROR) << "Incorrect trace event (missing " + key + "): "
53 << *event_dict;
54 return false;
55 }
56
57 if (value->IsType(base::Value::TYPE_INTEGER)) {
Etienne Membrives007c1b92015-10-01 17:09:31 +020058 int id_int = 0;
Etienne Membrives014f4f92015-10-01 16:02:11 +020059 // We already verified the type, so it should be an integer.
Etienne Membrives32402f42015-10-01 17:59:23 +020060 CHECK(value->GetAsInteger(&id_int));
Etienne Membrives014f4f92015-10-01 16:02:11 +020061 *output = base::IntToString(id_int);
62 } else if (value->IsType(base::Value::TYPE_STRING)) {
Etienne Membrives32402f42015-10-01 17:59:23 +020063 CHECK(value->GetAsString(output));
Etienne Membrives014f4f92015-10-01 16:02:11 +020064 } else {
65 LOG(ERROR) << "Incorrect trace event (" + key +
66 " not a string or integer): "
67 << *event_dict;
Etienne Membrivesb47d7cf2015-09-29 16:25:42 +020068 return false;
69 }
70 return true;
71}
72
Etienne Membrives014f4f92015-10-01 16:02:11 +020073// Makes a unique id for a duration event stack.
74bool GetDurationEventStackId(base::DictionaryValue* event_dict,
75 DurationEventStackId* durationId) {
76 return ExtractKeyAsString(event_dict, "tid", durationId);
77}
78
Etienne Membrivesb47d7cf2015-09-29 16:25:42 +020079// Makes a unique key for an async event stack.
80bool GetAsyncEventStackId(base::DictionaryValue* event_dict,
81 AsyncEventStackId* asyncId) {
Etienne Membrives014f4f92015-10-01 16:02:11 +020082 if (!ExtractKeyAsString(event_dict, "id", &asyncId->id)) {
Etienne Membrivesb47d7cf2015-09-29 16:25:42 +020083 return false;
84 }
85
Etienne Membrives014f4f92015-10-01 16:02:11 +020086 // We can have an empty category, but it is still relevant for event
87 // merging per the documentation.
Etienne Membrivesb47d7cf2015-09-29 16:25:42 +020088 std::string cat;
89 event_dict->GetString("cat", &asyncId->cat);
90
91 return true;
92}
93
94// Given a beginning event, registers its beginning for future merging.
95template <typename T>
96void RegisterEventBegin(
97 T key,
98 base::DictionaryValue* event_dict,
99 std::map<T, std::stack<base::DictionaryValue*>>* open_events) {
100 (*open_events)[key].push(event_dict);
101}
102
103// Given an end event, merges it with a previously-seen beginning event.
104template <typename T>
105bool MergeEventEnd(
106 T key,
107 base::DictionaryValue* event_dict,
108 std::map<T, std::stack<base::DictionaryValue*>>* open_events) {
109 if (!open_events->count(key) || (*open_events)[key].empty()) {
110 LOG(ERROR) << "Incorrect trace event (event end without begin).";
111 return false;
112 }
113
114 base::DictionaryValue* begin_event_dict = (*open_events)[key].top();
115 (*open_events)[key].pop();
116 double begin_ts;
117 if (!begin_event_dict->GetDouble("ts", &begin_ts)) {
118 LOG(ERROR) << "Incorrect trace event (no timestamp)";
119 return false;
120 }
121
122 double end_ts;
123 if (!event_dict->GetDouble("ts", &end_ts)) {
124 LOG(ERROR) << "Incorrect trace event (no timestamp)";
125 return false;
126 }
127
128 if (end_ts < begin_ts) {
129 LOG(ERROR) << "Incorrect trace event (event ends before it begins)";
130 return false;
131 }
132
133 begin_event_dict->SetDouble("dur", end_ts - begin_ts);
134 begin_event_dict->SetString("ph", "X");
135 return true;
136}
137
138// Finds the matching "end" event for each "begin" event of type duration or
139// async, and rewrites the "begin event" as a "complete" event with a duration.
140// Leaves all events that are not "begin" unchanged.
141bool JoinEvents(base::ListValue* event_list) {
Przemyslaw Pietrzkiewicz421c1f72015-09-08 14:52:36 +0200142 // Maps thread ids to stacks of unmatched duration begin events on the given
143 // thread.
Etienne Membrivesb47d7cf2015-09-29 16:25:42 +0200144 std::map<DurationEventStackId, std::stack<base::DictionaryValue*>>
145 open_begin_duration_events;
146 std::map<AsyncEventStackId, std::stack<base::DictionaryValue*>>
147 open_begin_async_events;
Przemyslaw Pietrzkiewicz1d909942015-08-28 16:27:56 +0200148
149 for (base::Value* val : *event_list) {
Przemyslaw Pietrzkiewicz421c1f72015-09-08 14:52:36 +0200150 base::DictionaryValue* event_dict;
151 if (!val->GetAsDictionary(&event_dict))
152 return false;
153
154 std::string phase;
155 if (!event_dict->GetString("ph", &phase)) {
156 LOG(ERROR) << "Incorrect trace event (missing phase)";
157 return false;
158 }
159
Etienne Membrivesb47d7cf2015-09-29 16:25:42 +0200160 if (phase == "B" || phase == "E") {
161 DurationEventStackId durationId;
162 if (!GetDurationEventStackId(event_dict, &durationId)) {
Przemyslaw Pietrzkiewicz421c1f72015-09-08 14:52:36 +0200163 return false;
164 }
Etienne Membrivesb47d7cf2015-09-29 16:25:42 +0200165 if (phase == "B") {
166 RegisterEventBegin(durationId, event_dict, &open_begin_duration_events);
167 } else if (phase == "E" &&
168 !MergeEventEnd(durationId, event_dict,
169 &open_begin_duration_events)) {
Przemyslaw Pietrzkiewicz421c1f72015-09-08 14:52:36 +0200170 return false;
171 }
Etienne Membrivesb47d7cf2015-09-29 16:25:42 +0200172 } else if (phase == "b" || phase == "S" || phase == "e" || phase == "F") {
173 AsyncEventStackId asyncId;
174 if (!GetAsyncEventStackId(event_dict, &asyncId)) {
Przemyslaw Pietrzkiewicz421c1f72015-09-08 14:52:36 +0200175 return false;
176 }
Etienne Membrivesb47d7cf2015-09-29 16:25:42 +0200177 if (phase == "b" || phase == "S") {
178 RegisterEventBegin(asyncId, event_dict, &open_begin_async_events);
179 } else if ((phase == "e" || phase == "F") &&
180 !MergeEventEnd(asyncId, event_dict,
181 &open_begin_async_events)) {
Przemyslaw Pietrzkiewicz421c1f72015-09-08 14:52:36 +0200182 return false;
183 }
Przemyslaw Pietrzkiewicz421c1f72015-09-08 14:52:36 +0200184 }
185 }
186 return true;
187}
188
189bool ParseEvents(base::ListValue* event_list, std::vector<Event>* result) {
190 result->clear();
191
192 for (base::Value* val : *event_list) {
193 base::DictionaryValue* event_dict;
Przemyslaw Pietrzkiewicz447d2f92015-09-16 15:57:36 +0200194 if (!val->GetAsDictionary(&event_dict)) {
195 LOG(WARNING) << "Ignoring incorrect trace event (not a dictionary)";
John McCutchand8a0f592016-02-04 14:13:27 -0800196 LOG(WARNING) << *event_dict;
Przemyslaw Pietrzkiewicz447d2f92015-09-16 15:57:36 +0200197 continue;
198 }
Przemyslaw Pietrzkiewicz421c1f72015-09-08 14:52:36 +0200199
Przemyslaw Pietrzkiewicz1d909942015-08-28 16:27:56 +0200200 Event event;
Przemyslaw Pietrzkiewicz1d909942015-08-28 16:27:56 +0200201
Przemyslaw Pietrzkiewicz421c1f72015-09-08 14:52:36 +0200202 std::string phase;
203 if (!event_dict->GetString("ph", &phase)) {
Przemyslaw Pietrzkiewicz447d2f92015-09-16 15:57:36 +0200204 LOG(WARNING) << "Ignoring incorrect trace event (missing phase)";
John McCutchand8a0f592016-02-04 14:13:27 -0800205 LOG(WARNING) << *event_dict;
Przemyslaw Pietrzkiewicz447d2f92015-09-16 15:57:36 +0200206 continue;
Przemyslaw Pietrzkiewicz421c1f72015-09-08 14:52:36 +0200207 }
208 if (phase == "X") {
209 event.type = EventType::COMPLETE;
Etienne Membrivesb47d7cf2015-09-29 16:25:42 +0200210 } else if (phase == "I" || phase == "n") {
Przemyslaw Pietrzkiewicz421c1f72015-09-08 14:52:36 +0200211 event.type = EventType::INSTANT;
212 } else {
213 // Skip all event types we do not handle.
214 continue;
215 }
Przemyslaw Pietrzkiewicz1d909942015-08-28 16:27:56 +0200216
Przemyslaw Pietrzkiewicz421c1f72015-09-08 14:52:36 +0200217 if (!event_dict->GetString("name", &event.name)) {
218 LOG(ERROR) << "Incorrect trace event (no name)";
John McCutchand8a0f592016-02-04 14:13:27 -0800219 LOG(ERROR) << *event_dict;
Przemyslaw Pietrzkiewicz1d909942015-08-28 16:27:56 +0200220 return false;
Przemyslaw Pietrzkiewicz421c1f72015-09-08 14:52:36 +0200221 }
222
Etienne Membrives014f4f92015-10-01 16:02:11 +0200223 // Some clients do not add categories to events, but we don't want to fail
224 // nor skip the event.
225 event_dict->GetString("cat", &event.categories);
Przemyslaw Pietrzkiewicz1d909942015-08-28 16:27:56 +0200226
227 double timestamp;
Przemyslaw Pietrzkiewicz421c1f72015-09-08 14:52:36 +0200228 if (!event_dict->GetDouble("ts", &timestamp)) {
Przemyslaw Pietrzkiewicz447d2f92015-09-16 15:57:36 +0200229 LOG(WARNING) << "Ingoring incorrect trace event (no timestamp)";
John McCutchand8a0f592016-02-04 14:13:27 -0800230 LOG(WARNING) << *event_dict;
Przemyslaw Pietrzkiewicz447d2f92015-09-16 15:57:36 +0200231 continue;
Przemyslaw Pietrzkiewicz421c1f72015-09-08 14:52:36 +0200232 }
Przemyslaw Pietrzkiewicz1d909942015-08-28 16:27:56 +0200233 event.timestamp = base::TimeTicks::FromInternalValue(timestamp);
234
Przemyslaw Pietrzkiewicz421c1f72015-09-08 14:52:36 +0200235 if (event.type == EventType::COMPLETE) {
236 double duration;
237 if (!event_dict->GetDouble("dur", &duration)) {
Przemyslaw Pietrzkiewicz447d2f92015-09-16 15:57:36 +0200238 LOG(WARNING) << "Ignoring incorrect complete event (no duration)";
John McCutchand8a0f592016-02-04 14:13:27 -0800239 LOG(WARNING) << *event_dict;
Przemyslaw Pietrzkiewicz447d2f92015-09-16 15:57:36 +0200240 continue;
Przemyslaw Pietrzkiewicz421c1f72015-09-08 14:52:36 +0200241 }
242
Przemyslaw Pietrzkiewicz1d909942015-08-28 16:27:56 +0200243 event.duration = base::TimeDelta::FromInternalValue(duration);
Przemyslaw Pietrzkiewicz421c1f72015-09-08 14:52:36 +0200244 } else {
245 event.duration = base::TimeDelta();
Przemyslaw Pietrzkiewicz1d909942015-08-28 16:27:56 +0200246 }
247
248 result->push_back(event);
249 }
250 return true;
251}
Przemyslaw Pietrzkiewicz421c1f72015-09-08 14:52:36 +0200252
253} // namespace
254
255bool GetEvents(const std::string& trace_json, std::vector<Event>* result) {
256 // Parse the JSON string describing the events.
257 base::JSONReader reader;
258 scoped_ptr<base::Value> trace_data = reader.ReadToValue(trace_json);
259 if (!trace_data) {
260 LOG(ERROR) << "Failed to parse the JSON string: "
261 << reader.GetErrorMessage();
262 return false;
263 }
264
265 base::ListValue* event_list;
266 if (!trace_data->GetAsList(&event_list)) {
267 LOG(ERROR) << "Incorrect format of the trace data.";
268 return false;
269 }
270
Etienne Membrivesb47d7cf2015-09-29 16:25:42 +0200271 if (!JoinEvents(event_list))
Przemyslaw Pietrzkiewicz421c1f72015-09-08 14:52:36 +0200272 return false;
273
274 if (!ParseEvents(event_list, result))
275 return false;
276 return true;
277}
Przemyslaw Pietrzkiewicz1d909942015-08-28 16:27:56 +0200278} // namespace benchmark