Przemyslaw Pietrzkiewicz | 1d90994 | 2015-08-28 16:27:56 +0200 | [diff] [blame] | 1 | // 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 Pietrzkiewicz | 421c1f7 | 2015-09-08 14:52:36 +0200 | [diff] [blame] | 7 | #include <map> |
| 8 | #include <stack> |
| 9 | |
Przemyslaw Pietrzkiewicz | 1d90994 | 2015-08-28 16:27:56 +0200 | [diff] [blame] | 10 | #include "base/json/json_reader.h" |
Przemyslaw Pietrzkiewicz | 421c1f7 | 2015-09-08 14:52:36 +0200 | [diff] [blame] | 11 | #include "base/macros.h" |
Przemyslaw Pietrzkiewicz | 1d90994 | 2015-08-28 16:27:56 +0200 | [diff] [blame] | 12 | #include "base/memory/scoped_ptr.h" |
Etienne Membrives | 014f4f9 | 2015-10-01 16:02:11 +0200 | [diff] [blame] | 13 | #include "base/strings/string_number_conversions.h" |
Przemyslaw Pietrzkiewicz | 1d90994 | 2015-08-28 16:27:56 +0200 | [diff] [blame] | 14 | #include "base/values.h" |
| 15 | |
| 16 | namespace benchmark { |
| 17 | |
| 18 | Event::Event() {} |
| 19 | |
Przemyslaw Pietrzkiewicz | 421c1f7 | 2015-09-08 14:52:36 +0200 | [diff] [blame] | 20 | Event::Event(EventType type, |
| 21 | std::string name, |
| 22 | std::string categories, |
Przemyslaw Pietrzkiewicz | 1d90994 | 2015-08-28 16:27:56 +0200 | [diff] [blame] | 23 | base::TimeTicks timestamp, |
| 24 | base::TimeDelta duration) |
Przemyslaw Pietrzkiewicz | 421c1f7 | 2015-09-08 14:52:36 +0200 | [diff] [blame] | 25 | : type(type), |
| 26 | name(name), |
| 27 | categories(categories), |
Przemyslaw Pietrzkiewicz | 1d90994 | 2015-08-28 16:27:56 +0200 | [diff] [blame] | 28 | timestamp(timestamp), |
| 29 | duration(duration) {} |
| 30 | |
| 31 | Event::~Event() {} |
| 32 | |
Przemyslaw Pietrzkiewicz | 421c1f7 | 2015-09-08 14:52:36 +0200 | [diff] [blame] | 33 | namespace { |
Etienne Membrives | b47d7cf | 2015-09-29 16:25:42 +0200 | [diff] [blame] | 34 | // ID uniquely identifying an asynchronous event stack. |
| 35 | struct AsyncEventStackId { |
Etienne Membrives | 014f4f9 | 2015-10-01 16:02:11 +0200 | [diff] [blame] | 36 | std::string id; |
Etienne Membrives | b47d7cf | 2015-09-29 16:25:42 +0200 | [diff] [blame] | 37 | std::string cat; |
| 38 | }; |
Przemyslaw Pietrzkiewicz | 1d90994 | 2015-08-28 16:27:56 +0200 | [diff] [blame] | 39 | |
Etienne Membrives | b47d7cf | 2015-09-29 16:25:42 +0200 | [diff] [blame] | 40 | bool 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 Membrives | 014f4f9 | 2015-10-01 16:02:11 +0200 | [diff] [blame] | 45 | typedef std::string DurationEventStackId; |
Etienne Membrives | b47d7cf | 2015-09-29 16:25:42 +0200 | [diff] [blame] | 46 | |
Etienne Membrives | 014f4f9 | 2015-10-01 16:02:11 +0200 | [diff] [blame] | 47 | bool 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 Membrives | 007c1b9 | 2015-10-01 17:09:31 +0200 | [diff] [blame] | 58 | int id_int = 0; |
Etienne Membrives | 014f4f9 | 2015-10-01 16:02:11 +0200 | [diff] [blame] | 59 | // We already verified the type, so it should be an integer. |
Etienne Membrives | 32402f4 | 2015-10-01 17:59:23 +0200 | [diff] [blame] | 60 | CHECK(value->GetAsInteger(&id_int)); |
Etienne Membrives | 014f4f9 | 2015-10-01 16:02:11 +0200 | [diff] [blame] | 61 | *output = base::IntToString(id_int); |
| 62 | } else if (value->IsType(base::Value::TYPE_STRING)) { |
Etienne Membrives | 32402f4 | 2015-10-01 17:59:23 +0200 | [diff] [blame] | 63 | CHECK(value->GetAsString(output)); |
Etienne Membrives | 014f4f9 | 2015-10-01 16:02:11 +0200 | [diff] [blame] | 64 | } else { |
| 65 | LOG(ERROR) << "Incorrect trace event (" + key + |
| 66 | " not a string or integer): " |
| 67 | << *event_dict; |
Etienne Membrives | b47d7cf | 2015-09-29 16:25:42 +0200 | [diff] [blame] | 68 | return false; |
| 69 | } |
| 70 | return true; |
| 71 | } |
| 72 | |
Etienne Membrives | 014f4f9 | 2015-10-01 16:02:11 +0200 | [diff] [blame] | 73 | // Makes a unique id for a duration event stack. |
| 74 | bool GetDurationEventStackId(base::DictionaryValue* event_dict, |
| 75 | DurationEventStackId* durationId) { |
| 76 | return ExtractKeyAsString(event_dict, "tid", durationId); |
| 77 | } |
| 78 | |
Etienne Membrives | b47d7cf | 2015-09-29 16:25:42 +0200 | [diff] [blame] | 79 | // Makes a unique key for an async event stack. |
| 80 | bool GetAsyncEventStackId(base::DictionaryValue* event_dict, |
| 81 | AsyncEventStackId* asyncId) { |
Etienne Membrives | 014f4f9 | 2015-10-01 16:02:11 +0200 | [diff] [blame] | 82 | if (!ExtractKeyAsString(event_dict, "id", &asyncId->id)) { |
Etienne Membrives | b47d7cf | 2015-09-29 16:25:42 +0200 | [diff] [blame] | 83 | return false; |
| 84 | } |
| 85 | |
Etienne Membrives | 014f4f9 | 2015-10-01 16:02:11 +0200 | [diff] [blame] | 86 | // We can have an empty category, but it is still relevant for event |
| 87 | // merging per the documentation. |
Etienne Membrives | b47d7cf | 2015-09-29 16:25:42 +0200 | [diff] [blame] | 88 | 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. |
| 95 | template <typename T> |
| 96 | void 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. |
| 104 | template <typename T> |
| 105 | bool 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. |
| 141 | bool JoinEvents(base::ListValue* event_list) { |
Przemyslaw Pietrzkiewicz | 421c1f7 | 2015-09-08 14:52:36 +0200 | [diff] [blame] | 142 | // Maps thread ids to stacks of unmatched duration begin events on the given |
| 143 | // thread. |
Etienne Membrives | b47d7cf | 2015-09-29 16:25:42 +0200 | [diff] [blame] | 144 | 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 Pietrzkiewicz | 1d90994 | 2015-08-28 16:27:56 +0200 | [diff] [blame] | 148 | |
| 149 | for (base::Value* val : *event_list) { |
Przemyslaw Pietrzkiewicz | 421c1f7 | 2015-09-08 14:52:36 +0200 | [diff] [blame] | 150 | 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 Membrives | b47d7cf | 2015-09-29 16:25:42 +0200 | [diff] [blame] | 160 | if (phase == "B" || phase == "E") { |
| 161 | DurationEventStackId durationId; |
| 162 | if (!GetDurationEventStackId(event_dict, &durationId)) { |
Przemyslaw Pietrzkiewicz | 421c1f7 | 2015-09-08 14:52:36 +0200 | [diff] [blame] | 163 | return false; |
| 164 | } |
Etienne Membrives | b47d7cf | 2015-09-29 16:25:42 +0200 | [diff] [blame] | 165 | 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 Pietrzkiewicz | 421c1f7 | 2015-09-08 14:52:36 +0200 | [diff] [blame] | 170 | return false; |
| 171 | } |
Etienne Membrives | b47d7cf | 2015-09-29 16:25:42 +0200 | [diff] [blame] | 172 | } else if (phase == "b" || phase == "S" || phase == "e" || phase == "F") { |
| 173 | AsyncEventStackId asyncId; |
| 174 | if (!GetAsyncEventStackId(event_dict, &asyncId)) { |
Przemyslaw Pietrzkiewicz | 421c1f7 | 2015-09-08 14:52:36 +0200 | [diff] [blame] | 175 | return false; |
| 176 | } |
Etienne Membrives | b47d7cf | 2015-09-29 16:25:42 +0200 | [diff] [blame] | 177 | 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 Pietrzkiewicz | 421c1f7 | 2015-09-08 14:52:36 +0200 | [diff] [blame] | 182 | return false; |
| 183 | } |
Przemyslaw Pietrzkiewicz | 421c1f7 | 2015-09-08 14:52:36 +0200 | [diff] [blame] | 184 | } |
| 185 | } |
| 186 | return true; |
| 187 | } |
| 188 | |
| 189 | bool 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 Pietrzkiewicz | 447d2f9 | 2015-09-16 15:57:36 +0200 | [diff] [blame] | 194 | if (!val->GetAsDictionary(&event_dict)) { |
| 195 | LOG(WARNING) << "Ignoring incorrect trace event (not a dictionary)"; |
John McCutchan | d8a0f59 | 2016-02-04 14:13:27 -0800 | [diff] [blame] | 196 | LOG(WARNING) << *event_dict; |
Przemyslaw Pietrzkiewicz | 447d2f9 | 2015-09-16 15:57:36 +0200 | [diff] [blame] | 197 | continue; |
| 198 | } |
Przemyslaw Pietrzkiewicz | 421c1f7 | 2015-09-08 14:52:36 +0200 | [diff] [blame] | 199 | |
Przemyslaw Pietrzkiewicz | 1d90994 | 2015-08-28 16:27:56 +0200 | [diff] [blame] | 200 | Event event; |
Przemyslaw Pietrzkiewicz | 1d90994 | 2015-08-28 16:27:56 +0200 | [diff] [blame] | 201 | |
Przemyslaw Pietrzkiewicz | 421c1f7 | 2015-09-08 14:52:36 +0200 | [diff] [blame] | 202 | std::string phase; |
| 203 | if (!event_dict->GetString("ph", &phase)) { |
Przemyslaw Pietrzkiewicz | 447d2f9 | 2015-09-16 15:57:36 +0200 | [diff] [blame] | 204 | LOG(WARNING) << "Ignoring incorrect trace event (missing phase)"; |
John McCutchan | d8a0f59 | 2016-02-04 14:13:27 -0800 | [diff] [blame] | 205 | LOG(WARNING) << *event_dict; |
Przemyslaw Pietrzkiewicz | 447d2f9 | 2015-09-16 15:57:36 +0200 | [diff] [blame] | 206 | continue; |
Przemyslaw Pietrzkiewicz | 421c1f7 | 2015-09-08 14:52:36 +0200 | [diff] [blame] | 207 | } |
| 208 | if (phase == "X") { |
| 209 | event.type = EventType::COMPLETE; |
Etienne Membrives | b47d7cf | 2015-09-29 16:25:42 +0200 | [diff] [blame] | 210 | } else if (phase == "I" || phase == "n") { |
Przemyslaw Pietrzkiewicz | 421c1f7 | 2015-09-08 14:52:36 +0200 | [diff] [blame] | 211 | event.type = EventType::INSTANT; |
| 212 | } else { |
| 213 | // Skip all event types we do not handle. |
| 214 | continue; |
| 215 | } |
Przemyslaw Pietrzkiewicz | 1d90994 | 2015-08-28 16:27:56 +0200 | [diff] [blame] | 216 | |
Przemyslaw Pietrzkiewicz | 421c1f7 | 2015-09-08 14:52:36 +0200 | [diff] [blame] | 217 | if (!event_dict->GetString("name", &event.name)) { |
| 218 | LOG(ERROR) << "Incorrect trace event (no name)"; |
John McCutchan | d8a0f59 | 2016-02-04 14:13:27 -0800 | [diff] [blame] | 219 | LOG(ERROR) << *event_dict; |
Przemyslaw Pietrzkiewicz | 1d90994 | 2015-08-28 16:27:56 +0200 | [diff] [blame] | 220 | return false; |
Przemyslaw Pietrzkiewicz | 421c1f7 | 2015-09-08 14:52:36 +0200 | [diff] [blame] | 221 | } |
| 222 | |
Etienne Membrives | 014f4f9 | 2015-10-01 16:02:11 +0200 | [diff] [blame] | 223 | // 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 Pietrzkiewicz | 1d90994 | 2015-08-28 16:27:56 +0200 | [diff] [blame] | 226 | |
| 227 | double timestamp; |
Przemyslaw Pietrzkiewicz | 421c1f7 | 2015-09-08 14:52:36 +0200 | [diff] [blame] | 228 | if (!event_dict->GetDouble("ts", ×tamp)) { |
Przemyslaw Pietrzkiewicz | 447d2f9 | 2015-09-16 15:57:36 +0200 | [diff] [blame] | 229 | LOG(WARNING) << "Ingoring incorrect trace event (no timestamp)"; |
John McCutchan | d8a0f59 | 2016-02-04 14:13:27 -0800 | [diff] [blame] | 230 | LOG(WARNING) << *event_dict; |
Przemyslaw Pietrzkiewicz | 447d2f9 | 2015-09-16 15:57:36 +0200 | [diff] [blame] | 231 | continue; |
Przemyslaw Pietrzkiewicz | 421c1f7 | 2015-09-08 14:52:36 +0200 | [diff] [blame] | 232 | } |
Przemyslaw Pietrzkiewicz | 1d90994 | 2015-08-28 16:27:56 +0200 | [diff] [blame] | 233 | event.timestamp = base::TimeTicks::FromInternalValue(timestamp); |
| 234 | |
Przemyslaw Pietrzkiewicz | 421c1f7 | 2015-09-08 14:52:36 +0200 | [diff] [blame] | 235 | if (event.type == EventType::COMPLETE) { |
| 236 | double duration; |
| 237 | if (!event_dict->GetDouble("dur", &duration)) { |
Przemyslaw Pietrzkiewicz | 447d2f9 | 2015-09-16 15:57:36 +0200 | [diff] [blame] | 238 | LOG(WARNING) << "Ignoring incorrect complete event (no duration)"; |
John McCutchan | d8a0f59 | 2016-02-04 14:13:27 -0800 | [diff] [blame] | 239 | LOG(WARNING) << *event_dict; |
Przemyslaw Pietrzkiewicz | 447d2f9 | 2015-09-16 15:57:36 +0200 | [diff] [blame] | 240 | continue; |
Przemyslaw Pietrzkiewicz | 421c1f7 | 2015-09-08 14:52:36 +0200 | [diff] [blame] | 241 | } |
| 242 | |
Przemyslaw Pietrzkiewicz | 1d90994 | 2015-08-28 16:27:56 +0200 | [diff] [blame] | 243 | event.duration = base::TimeDelta::FromInternalValue(duration); |
Przemyslaw Pietrzkiewicz | 421c1f7 | 2015-09-08 14:52:36 +0200 | [diff] [blame] | 244 | } else { |
| 245 | event.duration = base::TimeDelta(); |
Przemyslaw Pietrzkiewicz | 1d90994 | 2015-08-28 16:27:56 +0200 | [diff] [blame] | 246 | } |
| 247 | |
| 248 | result->push_back(event); |
| 249 | } |
| 250 | return true; |
| 251 | } |
Przemyslaw Pietrzkiewicz | 421c1f7 | 2015-09-08 14:52:36 +0200 | [diff] [blame] | 252 | |
| 253 | } // namespace |
| 254 | |
| 255 | bool 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 Membrives | b47d7cf | 2015-09-29 16:25:42 +0200 | [diff] [blame] | 271 | if (!JoinEvents(event_list)) |
Przemyslaw Pietrzkiewicz | 421c1f7 | 2015-09-08 14:52:36 +0200 | [diff] [blame] | 272 | return false; |
| 273 | |
| 274 | if (!ParseEvents(event_list, result)) |
| 275 | return false; |
| 276 | return true; |
| 277 | } |
Przemyslaw Pietrzkiewicz | 1d90994 | 2015-08-28 16:27:56 +0200 | [diff] [blame] | 278 | } // namespace benchmark |