Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(12)

Side by Side Diff: tracing/tracing/extras/importer/profiling_dictionary_reader.html

Issue 2635023002: [tracing] Support new heap dump format (Closed)
Patch Set: fix bug''fix bug Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 <!DOCTYPE html>
2 <!--
3 Copyright 2017 The Chromium Authors. All rights reserved.
4 Use of this source code is governed by a BSD-style license that can be
5 found in the LICENSE file.
6 -->
7
8 <link rel="import" href="/tracing/base/base.html">
9
10 <script>
11 'use strict';
12
13 tr.exportTo('tr.e.importer', function() {
14 const STRING_ID_SUFFIX = '_sid';
15 const PLURAL_STRING_ID_SUFFIX = '_sids';
16
17 function isStringReference(s) {
18 return s.endsWith(STRING_ID_SUFFIX) || s.endsWith(PLURAL_STRING_ID_SUFFIX);
19 }
20
21 function getStringReferenceName(name) {
22 if (name.endsWith(PLURAL_STRING_ID_SUFFIX)) {
23 return name.slice(0, -PLURAL_STRING_ID_SUFFIX.length);
24 }
25 return name.slice(0, -STRING_ID_SUFFIX.length);
26 }
27
28 function deferenceStrings(idToString, o) {
29 const clone = Object.assign({}, o);
30 for (const [key, value] of Object.entries(clone)) {
31 if (isStringReference(key)) {
32 const name = getStringReferenceName(key);
33 clone[name] = idToString(value);
34 }
35 }
36 return clone;
37 }
38
39 function singularize(word) {
40 if (word.endsWith('s')) {
41 return word.slice(0, -1);
42 }
43 return word;
44 }
45
46 function getMetadataPairs(dataJson) {
47 const isMetadata = v => typeof v !== 'object' || Array.isArray(v);
48 const pairs = Object.entries(dataJson);
49 const metadataPairs = pairs.filter(([_, v]) => isMetadata(v));
50 return metadataPairs;
51 }
52
53 function getGroupPairs(dataJson) {
54 const pairs = Object.entries(dataJson);
55 const nonMapPairs = pairs.filter(([k, _]) => k !== 'maps');
56 const groupPairs = nonMapPairs.filter(([_, v]) => typeof v === 'object');
57 return groupPairs;
58 }
59
60 function createMap(mapJson) {
61 const map = new Map();
62 for (const entry of mapJson) {
63 if (entry.id === undefined) {
64 throw new Error('Missing required key "id" in streaming event.');
65 }
66 map.set(entry.id, entry);
67 }
68 return map;
69 }
70
71 function createMaps(mapsJson) {
72 const maps = new Map();
73 for (const [name, mapJson] of Object.entries(mapsJson)) {
74 maps.set(name, createMap(mapJson));
75 }
76 return maps;
77 }
78
79 function createGroup(groupJson, opt_startTime) {
80 const entries = [];
81 const n = Object.values(groupJson)[0].length;
82
83 for (let i = 0; i < n; i++) {
84 const entry = {};
85 for (const name in groupJson) {
86 entry[name] = groupJson[name][i];
87 }
88 entries.push(entry);
89 }
90
91 const timeDelta = groupJson.timeDelta;
92 if (opt_startTime === undefined && timeDelta !== undefined) {
93 throw new Error('Missing required key "startTime" in streaming event.');
94 }
95
96 if (opt_startTime) {
97 let delta = 0;
98 for (const entry of entries) {
99 delta += entry.timeDelta ? entry.timeDelta : 0;
100 entry.time = opt_startTime + delta;
101 }
102 }
103
104 return entries;
105 }
106
107 function createGroups(groupsJson, opt_startTime) {
108 const groups = new Map();
109 for (const [name, groupJson] of Object.entries(groupsJson)) {
110 groups.set(name, createGroup(groupJson, opt_startTime));
111 }
112
113 return groups;
114 }
115
116 function createMetadata(metadataPairs) {
117 const metadata = new Map();
118 for (const [name, value] of metadataPairs) {
119 metadata.set(name, value);
120 }
121 if (metadata.get('version') === undefined) {
122 throw new Error('Missing required key "version" in streaming event.');
123 }
124 return metadata;
125 }
126
127 /**
128 * Extracts data from a profiling dictionary. See goo.gl/R0Ae4f.
129 *
130 * A profiling dictionary is a compressed format that is good for recording
131 * sampling data. ProfilingDictionaryReader unpacks that data. To use the
132 * ProfilingDictionaryReader first create an 'empty' reader using .empty()
133 * then call #expandData(data) on your dictionary or the helper:
134 * #expandEvent(event) on a tracing event containing the profiling dictionary.
135 * ProfilingDictionaryReader is an immutable data structure so these methods
136 * don't modify the ProfilingDictionaryReader instead they return new
137 * ProfilingDictionaryReaders which wrap the data you passed. To access the
138 * unpacked data use the #inflated property and the #getMapValue() method.
139 *
140 * Usage example, given input like:
141 * $ let input = {
142 * version: 1,
143 * allocators: {
144 * books: {
145 * authors: [1, 1, 2],
146 * title_sid: [10, 11, 12],
147 * },
148 * },
149 * maps: {
150 * authors: [
151 * { id: 1, name_sid: 1 },
152 * { id: 2, name_sid: 2 },
153 * ],
154 * strings: [
155 * { id: 1, string: 'DFW' },
156 * { id: 2, string: 'C. Stross' },
157 * { id: 10, string: 'Book A' },
158 * { id: 11, string: 'Book B' },
159 * { id: 12, string: 'Book C' },
160 * ],
161 * }
162 * };
163 * We can create an empty reader:
164 * $ let reader = ProfilingDictionaryReader.empty();
165 * Then read in the input:
166 * $ reader = reader.expandData(input);
167 * Then view the expanded data:
168 * $ console.log(reader.inflated);
169 * {
170 * books: [
171 * { author: { id: 1, name: 'DFW' }, title: "Book A", },
172 * { author: { id: 2, name: 'C. Stross' }, title: "Book B", },
173 * { author: { id: 2, name: 'C. Stross' }, title: "Book C", },
174 * ],
175 * }
176 *
177 */
178 class ProfilingDictionaryReader {
179 constructor(opt_metadata, opt_maps, opt_groups, opt_parent) {
180 this.metadata = opt_metadata || new Map();
181 this.maps = opt_maps || new Map();
182 this.groups = opt_groups || new Map();
183 this.parent_ = opt_parent || undefined;
184 this.inflated_ = undefined;
185 this.raw_ = undefined;
186 this.boundGetString_ = this.getString.bind(this);
187 this.deferenceStrings_ = o => deferenceStrings(this.boundGetString_, o);
188 }
189
190 /**
191 * Creates an empty ProfilingDictionaryReader.
192 */
193 static empty() {
194 return new ProfilingDictionaryReader();
195 }
196
197 /**
198 * Returns the parent or null if this is the root ProfilingDictionaryReader.
199 */
200 get parent() {
201 return this.parent_;
202 }
203
204 get raw() {
205 if (this.raw_) return this.raw_;
206 this.raw_ = {};
207 for (const [name, group] of this.groups.entries()) {
208 this.raw_[name] = group;
209 }
210 return this.raw_;
211 }
212
213 get inflated() {
214 if (this.inflated_) return this.inflated_;
215 this.inflated_ = {};
216 for (const [name, group] of this.groups.entries()) {
217 this.inflated_[name] = this.inflateGroup(group);
218 }
219 return this.inflated_;
220 }
221
222 /**
223 * Get a map from the newest event by name.
224 * If no map with that name was present returns an empty Map.
225 */
226 getNewMap(name) {
227 return this.maps.get(name) || new Map();
228 }
229
230 /**
231 * Get a record with the id |id| from the map with name |mapName|.
232 * This method searches through the expanded events in reverse order of
233 * expansion until it finds a matching value. If no value matches returns
234 * undefined.
235 */
236 getMapValue(mapName, id) {
237 let value = this.getNewMap(mapName).get(id);
238 if (value === undefined && this.parent) {
239 value = this.parent.getMapValue(mapName, id);
240 }
241 return value;
242 }
243
244 /**
245 * Get the string with the id |id|.
246 * This method searches through the expanded events in reverse order of
247 * expansion until it finds a string with the matching id. If there is no
248 * matching string with returns undefined.
249 */
250 getString(id) {
251 const value = this.getMapValue('strings', id);
252 if (value === undefined) return undefined;
253 return value.string;
254 }
255
256 /**
257 * True iff this or any parent has a map with name |name|.
258 */
259 hasMap(name) {
260 if (this.maps.has(name)) return true;
261 if (this.parent === undefined) return false;
262 return this.parent.hasMap(name);
263 }
264
265 inflateGroup(group) {
266 return group.map(this.inflateEntry.bind(this));
267 }
268
269 inflateEntry(entry) {
270 const inflatedEntry = {};
271 for (const [name, value] of Object.entries(entry)) {
272 let inflatedValue;
273 if (this.hasMap(name)) {
274 const id = value;
275 inflatedValue = this.deferenceStrings_(this.getMapValue(name, id));
276 } else {
277 inflatedValue = value;
278 }
279 inflatedEntry[singularize(name)] = inflatedValue;
280 }
281 return this.deferenceStrings_(inflatedEntry);
282 }
283
284 /**
285 * Returns a new ProfilingDictionaryReader with this
286 * ProfilingDictionaryReader as its parent and the fields 'maps', 'groups'
287 * and 'metadata' filled in based on |data|.
288 */
289 expandData(data) {
290 const mapsJson = data.maps || {};
291 const groupsJson = data.allocators || {};
292 const metadataPairs = getMetadataPairs(data);
293 const metadata = createMetadata(metadataPairs);
294 const opt_startTime = metadata.get('startTime');
295 const maps = createMaps(mapsJson);
296 const groups = createGroups(groupsJson, opt_startTime);
297 return new ProfilingDictionaryReader(metadata, maps, groups, this);
298 }
299
300 /**
301 * Convenience method for this.expandData(event.args.data).
302 */
303 expandEvent(event) {
304 return this.expandData(event.args.data);
305 }
306 }
307
308 return {
309 ProfilingDictionaryReader,
310 singularize,
311 deferenceStringsForTest: deferenceStrings,
312 };
313 });
314 </script>
315
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698