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

Unified Diff: tracing/tracing/extras/importer/streaming_event_expander.html

Issue 2635023002: [tracing] Support new heap dump format (Closed)
Patch Set: remove it again Created 3 years, 9 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 side-by-side diff with in-line comments
Download patch
Index: tracing/tracing/extras/importer/streaming_event_expander.html
diff --git a/tracing/tracing/extras/importer/streaming_event_expander.html b/tracing/tracing/extras/importer/streaming_event_expander.html
new file mode 100644
index 0000000000000000000000000000000000000000..d8149e3d1bad1ec7a03527ea9d3ebe55d09c778d
--- /dev/null
+++ b/tracing/tracing/extras/importer/streaming_event_expander.html
@@ -0,0 +1,267 @@
+<!DOCTYPE html>
+<!--
+Copyright 2017 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.
+-->
+
+<link rel="import" href="/tracing/base/base.html">
+
+<script>
+'use strict';
+
+tr.exportTo('tr.e.importer', function() {
+ function singularize(word) {
+ if (word.endsWith('s')) {
+ return word.slice(0, -1);
+ }
+ return word;
+ }
+
+ function getMetadataPairs(dataJson) {
+ const isMetadata = v => typeof v !== 'object' || Array.isArray(v);
+ const pairs = Object.entries(dataJson);
+ const metadataPairs = pairs.filter(([_, v]) => isMetadata(v));
+ return metadataPairs;
+ }
+
+ function getGroupPairs(dataJson) {
+ const pairs = Object.entries(dataJson);
+ const nonMapPairs = pairs.filter(([k, _]) => k !== 'maps');
+ const groupPairs = nonMapPairs.filter(([_, v]) => typeof v === 'object');
+ return groupPairs;
+ }
+
+ function createMap(mapJson) {
+ const map = new Map();
+ for (let entry of mapJson) {
+ if (entry.id === undefined) {
+ throw new Error('Missing required key "id" in streaming event.');
+ }
+ map.set(entry.id, entry);
+ }
+ return map;
+ }
+
+ function createMaps(mapsJson) {
+ const maps = new Map();
+ for (let [name, mapJson] of Object.entries(mapsJson)) {
+ maps.set(name, createMap(mapJson));
+ }
+ return maps;
+ }
+
+ function createGroup(groupJson, opt_startTime) {
+ const entries = [];
+ const n = Object.values(groupJson)[0].length;
+
+ for (let i = 0; i < n; i++) {
+ const entry = {};
+ for (let name in groupJson) {
+ entry[name] = groupJson[name][i];
+ }
+ entries.push(entry);
+ }
+
+ const timeDelta = groupJson['timeDelta'];
+ if (opt_startTime === undefined && timeDelta !== undefined) {
+ throw new Error('Missing required key "startTime" in streaming event.');
+ }
+
+ if (opt_startTime) {
+ let delta = 0;
+ for (let entry of entries) {
+ delta += entry['timeDelta'] ? entry['timeDelta'] : 0;
+ entry['time'] = opt_startTime + delta;
+ }
+ }
+
+ return entries;
+ }
+
+ function createGroups(groupsJson, opt_startTime) {
+ const groups = new Map();
+ for (let [name, groupJson] of Object.entries(groupsJson)) {
+ groups.set(name, createGroup(groupJson, opt_startTime));
+ }
+
+ return groups;
+ }
+
+ function createMetadata(metadataPairs) {
+ const metadata = new Map();
+ for (let [name, value] of metadataPairs) {
+ metadata.set(name, value);
+ }
+ if (metadata.get('version') === undefined) {
+ throw new Error('Missing required key "version" in streaming event.');
+ }
+ return metadata;
+ }
+
+ /**
+ * Extracts data from a 'streaming p event'.
Primiano Tucci (use gerrit) 2017/03/22 15:53:52 IMHO would be great if this description could be a
fmeawad 2017/03/23 20:43:57 +1 Can this class be used to import non memory rel
+ * See goo.gl/l6KIe3.
+ * The format of these events have two features:
+ * * It encodes samples as a struct of arrays.
+ * * Common values are stored in id keyed maps. These maps build up over time.
+ * Consequently this class has two matching tasks:
+ * * It transforms the input from a struct of arrays to an array of structs.
+ * * It remembers the maps of all events it has seen and can deference and id
+ *
+ * Concretely StreamingEventExpander is a immutable data structure you build
+ * up from tracing events which carry streaming p event data. It provides
+ * access to the sample data in an array of structs format (via the 'raw'
+ * property). It provides access to the "maps" of all parsed events via the
+ * 'getMapValue' method. Finally it can automatically deference keys in the
+ * structs which refer to maps. This is done via the 'inflated' property.
+ *
+ * To use first create an empty StreamingEventExpander with
+ * StreamingEventExpander.empty then append new data using
+ * #expandData and access that data using #raw, #inflated and #getMapValue.
+ */
+ class StreamingEventExpander {
+ constructor(opt_metadata, opt_maps, opt_groups, opt_parent) {
+ this.metadata = opt_metadata || new Map();
+ this.maps = opt_maps || new Map();
+ this.groups = opt_groups || new Map();
+ this.parent_ = opt_parent || undefined;
+ this.inflated_ = undefined;
+ this.raw_ = undefined;
+ }
+
+ /**
+ * Creates an empty StreamingEventExpander.
+ */
+ static empty() {
+ return new StreamingEventExpander();
+ }
+
+ /**
+ * Returns the parent or null if this is the root StreamingEventExpander.
+ */
+ get parent() {
+ return this.parent_;
+ }
+
+ get raw() {
+ if (this.raw_) return this.raw_;
+ this.raw_ = {};
+ for (let [name, group] of this.groups.entries()) {
+ this.raw_[name] = group;
+ }
+ return this.raw_;
+ }
+
+ get inflated() {
+ if (this.inflated_) return this.inflated_;
+ this.inflated_ = {};
+ for (let [name, group] of this.groups.entries()) {
+ this.inflated_[name] = this.inflateGroup(group);
+ }
+ return this.inflated_;
+ }
+
+ /**
+ * Get a map from the newest event by name.
+ * If no map with that name was present returns an empty Map.
+ */
+ getNewMap(name) {
+ return this.maps.get(name) || new Map();
+ }
+
+ /**
+ * Get a record with the id |id| from the map with name |mapName|.
+ * This method searches through the expanded events in reverse order of
+ * expansion until it finds a matching value. If no value matches returns
+ * undefined.
+ */
+ getMapValue(mapName, id) {
+ let value = this.getNewMap(mapName).get(id);
+ if (value === undefined && this.parent) {
+ value = this.parent.getMapValue(mapName, id);
+ }
+ return value;
+ }
+
+ /**
+ * Get the string with the id |id|.
+ * This method searches through the expanded events in reverse order of
+ * expansion until it finds a string with the matching id. If there is no
+ * matching string with returns undefined.
+ */
+ getString(id) {
+ const value = this.getMapValue('strings', id);
+ if (value === undefined) return undefined;
+ return value.string;
+ }
+
+ /**
+ * True iff this or any parent has a map with name |name|.
+ */
+ hasMap(name) {
+ if (this.maps.has(name)) return true;
+ if (this.parent === undefined) return false;
+ return this.parent.hasMap(name);
+ }
+
+ inflateGroup(group) {
+ return group.map(this.inflateEntry.bind(this));
+ }
+
+ deferenceStrings(o) {
+ let clone = Object.assign({}, o);
+ for (let [key, value] of Object.entries(clone)) {
+ if (key.endsWith('_sid')) {
+ clone[key.slice(0, -4)] = this.getString(value);
+ }
+ }
+ return clone;
+ }
+
+ inflateEntry(entry) {
+ let inflatedEntry = {};
+ for (let [name, value] of Object.entries(entry)) {
+ let inflatedValue;
+ if (this.hasMap(name)) {
+ const id = value;
+ inflatedValue = this.deferenceStrings(this.getMapValue(name, id));
+ } else {
+ inflatedValue = value;
+ }
+ inflatedEntry[singularize(name)] = inflatedValue;
+ }
+ return inflatedEntry;
+ }
+
+ /**
+ * Returns a new StreamingEventExpander with this StreamingEventExpander as
+ * its parent and the fields 'maps', 'groups' and 'metadata' filled in based
+ * on |data|.
+ */
+ expandData(data) {
+ const mapsJson = data.maps || {};
+ const groupsJson = data.allocators || {};
+ const metadataPairs = getMetadataPairs(data);
+ const metadata = createMetadata(metadataPairs);
+ const opt_startTime = metadata.get('startTime');
+ const maps = createMaps(mapsJson);
+ const groups = createGroups(groupsJson, opt_startTime);
+ return new StreamingEventExpander(metadata, maps, groups, this);
+ }
+
+ /**
+ * Convenience method for this.expandData(event.args.data).
+ */
+ expandEvent(event) {
+ return this.expandData(event.args.data);
+ }
+ }
+
+ return {
+ StreamingEventExpander,
+ singularize,
+ };
+});
+</script>
+

Powered by Google App Engine
This is Rietveld 408576698