summaryrefslogtreecommitdiffstats
path: root/tools/memory_inspector
diff options
context:
space:
mode:
authorprimiano@chromium.org <primiano@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-04-23 13:03:44 +0000
committerprimiano@chromium.org <primiano@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-04-23 13:03:44 +0000
commit408f1e11721894d1375ba041f5405fcacfddb204 (patch)
treea8120aba0f0dc846718f735f83bbbbda9e3b9b46 /tools/memory_inspector
parent32011ffedcc1559c923ea88aba75b8a8c9da8359 (diff)
downloadchromium_src-408f1e11721894d1375ba041f5405fcacfddb204.zip
chromium_src-408f1e11721894d1375ba041f5405fcacfddb204.tar.gz
chromium_src-408f1e11721894d1375ba041f5405fcacfddb204.tar.bz2
Add native_heap support to the memory_inspector frontend.
This is a follow-up to crrev.com/239543009/ and adds the frontend code (both server-side and client side) to support native heaps and symbolization. BUG=340294 NOTRY=true Review URL: https://codereview.chromium.org/237743006 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@265621 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'tools/memory_inspector')
-rw-r--r--tools/memory_inspector/memory_inspector/frontends/background_tasks.py13
-rw-r--r--tools/memory_inspector/memory_inspector/frontends/www_content/css/nheap.css16
-rw-r--r--tools/memory_inspector/memory_inspector/frontends/www_content/css/processes.css5
-rw-r--r--tools/memory_inspector/memory_inspector/frontends/www_content/index.html21
-rw-r--r--tools/memory_inspector/memory_inspector/frontends/www_content/js/devices.js11
-rw-r--r--tools/memory_inspector/memory_inspector/frontends/www_content/js/nheap.js86
-rw-r--r--tools/memory_inspector/memory_inspector/frontends/www_content/js/processes.js13
-rw-r--r--tools/memory_inspector/memory_inspector/frontends/www_content/js/profiler.js15
-rw-r--r--tools/memory_inspector/memory_inspector/frontends/www_content/js/rootUi.js2
-rw-r--r--tools/memory_inspector/memory_inspector/frontends/www_content/js/storage.js45
-rw-r--r--tools/memory_inspector/memory_inspector/frontends/www_server.py128
11 files changed, 318 insertions, 37 deletions
diff --git a/tools/memory_inspector/memory_inspector/frontends/background_tasks.py b/tools/memory_inspector/memory_inspector/frontends/background_tasks.py
index 09323ba..1d6d602 100644
--- a/tools/memory_inspector/memory_inspector/frontends/background_tasks.py
+++ b/tools/memory_inspector/memory_inspector/frontends/background_tasks.py
@@ -78,6 +78,7 @@ def TracerMain_(log, storage_path, backend_name, device_id, pid, interval,
datetime_str = datetime.datetime.now().strftime('%Y-%m-%d_%H-%M')
archive_name = '%s - %s - %s' % (datetime_str, device.name, process.name)
archive = storage.OpenArchive(archive_name, create=True)
+ heaps_to_symbolize = []
for i in xrange(1, count + 1): # [1, count] range is easier to handle.
process = device.GetProcess(pid)
@@ -96,10 +97,22 @@ def TracerMain_(log, storage_path, backend_name, device_id, pid, interval,
nheap = process.DumpNativeHeap()
log.put((completion, 'Dumped %d native allocs' % len(nheap.allocations)))
archive.StoreNativeHeap(nheap)
+ heaps_to_symbolize += [nheap]
if i < count:
time.sleep(interval)
+ log.put((90, 'Symbolizing'))
+ symbols = backend.ExtractSymbols(heaps_to_symbolize,
+ device.settings['native_symbol_paths'] or '')
+
+ expected_symbols_count = len(set.union(
+ *[set(x.stack_frames.iterkeys()) for x in heaps_to_symbolize]))
+ log.put((99, 'Symbolization complete. Got %d symbols (%.1f%%).' % (
+ len(symbols), 100.0 * len(symbols) / expected_symbols_count)))
+
+ archive.StoreSymbols(symbols)
+
log.put((100, 'Trace complete.'))
return 0
diff --git a/tools/memory_inspector/memory_inspector/frontends/www_content/css/nheap.css b/tools/memory_inspector/memory_inspector/frontends/www_content/css/nheap.css
new file mode 100644
index 0000000..358237c
--- /dev/null
+++ b/tools/memory_inspector/memory_inspector/frontends/www_content/css/nheap.css
@@ -0,0 +1,16 @@
+/* Copyright 2014 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. */
+
+#nheap-table dl {
+ line-height: 1em;
+ -webkit-user-select: text;
+}
+
+#nheap-table dt {
+}
+
+#nheap-table dd {
+ display: inline;
+ float: right;
+}
diff --git a/tools/memory_inspector/memory_inspector/frontends/www_content/css/processes.css b/tools/memory_inspector/memory_inspector/frontends/www_content/css/processes.css
index f8d99b22..0aa1f9d 100644
--- a/tools/memory_inspector/memory_inspector/frontends/www_content/css/processes.css
+++ b/tools/memory_inspector/memory_inspector/frontends/www_content/css/processes.css
@@ -15,4 +15,9 @@
clear: both;
font-size: 0.9em;
margin-bottom: 1em;
+}
+
+#ps-tracer-dialog > div > :last-child {
+ float: right;
+ width: 60%;
} \ No newline at end of file
diff --git a/tools/memory_inspector/memory_inspector/frontends/www_content/index.html b/tools/memory_inspector/memory_inspector/frontends/www_content/index.html
index e1df30c..514fa18 100644
--- a/tools/memory_inspector/memory_inspector/frontends/www_content/index.html
+++ b/tools/memory_inspector/memory_inspector/frontends/www_content/index.html
@@ -11,6 +11,7 @@
<link href='//fonts.googleapis.com/css?family=Coda' rel='stylesheet' type='text/css'>
<link href="//ajax.googleapis.com/ajax/libs/jqueryui/1.10.4/themes/flick/jquery-ui.css" rel="stylesheet">
<link href="/css/mmap.css" rel="stylesheet" type="text/css">
+ <link href="/css/nheap.css" rel="stylesheet" type="text/css">
<link href="/css/processes.css" rel="stylesheet" type="text/css">
<link href="/css/profiler.css" rel="stylesheet" type="text/css">
<link href="/css/rootUi.css" rel="stylesheet" type="text/css">
@@ -24,6 +25,7 @@
</script>
<script src="/js/devices.js"></script>
<script src="/js/mmap.js"></script>
+ <script src="/js/nheap.js"></script>
<script src="/js/processes.js"></script>
<script src="/js/profiler.js"></script>
<script src="/js/rootUi.js"></script>
@@ -40,6 +42,7 @@
<li><a href="#tabs-ps">Processes</a></li>
<li><a href="#tabs-prof">Profiler</a></li>
<li><a href="#tabs-mm">Memory maps table</a></li>
+ <li><a href="#tabs-nheap">Native S.Traces</a></li>
<li><a href="#tabs-storage">Archived traces</a></li>
<li><a href="#tabs-settings">Settings</a></li>
</ul>
@@ -89,6 +92,10 @@
<label for="ps-tracer-snapshots">Num snapshots</label>
<input type="text" id="ps-tracer-snapshots" value="1">
</div>
+ <div>
+ <input type="checkbox" id="ps-tracer-bt" class="ui-widget-content">
+ <label for="ps-tracer-bt">Detailed (w/ backtraces)</label>
+ </div>
</div>
</div>
@@ -159,12 +166,26 @@
<div id="mm-table"></div>
</div>
+ <div id="tabs-nheap">
+ <div id="nheap-toolbar" class="ui-widget-header ui-corner-all">
+ <label>Totals: </label>
+ <input type="text" id="nheap-totals" values="0 KB" readonly>
+ <label>Selected: </label>
+ <input type="text" id="nheap-selected" values="0 KB" readonly>
+ <label>Filter: </label>
+ <input type="text" id="nheap-filter">
+ </div>
+ <div id="nheap-table"></div>
+ </div>
+
<div id="tabs-storage">
<div id="storage-toolbar" class="ui-widget-header ui-corner-all">
<label>Group:</label>
<button id="storage-profile-mmaps">Profile memory maps</button>
+ <button id="storage-profile-native">Profile native allocations</button>
<label>Single snapshot:</label>
<button id="storage-dump-mmaps">Show memory maps</button>
+ <button id="storage-dump-nheap">Show native heap</button>
</div>
<div id="storage-table"></div>
</div>
diff --git a/tools/memory_inspector/memory_inspector/frontends/www_content/js/devices.js b/tools/memory_inspector/memory_inspector/frontends/www_content/js/devices.js
index 9d3ac04..2686ef15 100644
--- a/tools/memory_inspector/memory_inspector/frontends/www_content/js/devices.js
+++ b/tools/memory_inspector/memory_inspector/frontends/www_content/js/devices.js
@@ -75,15 +75,20 @@ this.onDeviceSelectionChange_ = function() {
if (!this.selDeviceUri_)
return;
- // Initialize device and start processes / OS stats (it is a POST request).
+ this.initializeSelectedDevice(false);
+};
+
+this.initializeSelectedDevice = function(enableNativeTracing) {
webservice.ajaxRequest(
'/initialize/' + this.selDeviceUri_,
this.onDeviceInitializationComplete_.bind(this),
null, // default error handler.
- {});
+ {enableNativeTracing: (enableNativeTracing ? '1' : '')});
};
-this.onDeviceInitializationComplete_ = function() {
+this.onDeviceInitializationComplete_ = function(data) {
+ this.devices_[this.selDeviceUri_].isNativeTracingEnabled =
+ data.isNativeTracingEnabled;
processes.startPsTable();
processes.startDeviceStats();
};
diff --git a/tools/memory_inspector/memory_inspector/frontends/www_content/js/nheap.js b/tools/memory_inspector/memory_inspector/frontends/www_content/js/nheap.js
new file mode 100644
index 0000000..005a42a
--- /dev/null
+++ b/tools/memory_inspector/memory_inspector/frontends/www_content/js/nheap.js
@@ -0,0 +1,86 @@
+// Copyright 2014 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.
+
+nheap = new (function() {
+
+this.COL_STACKTRACE = 3;
+this.COL_TOTAL = 0;
+
+this.nheapData_ = null;
+this.nheapTable_ = null;
+this.nheapFilter_ = null;
+
+this.onDomReady_ = function() {
+ // Create the mmaps table.
+ this.nheapTable_ = new google.visualization.Table($('#nheap-table')[0]);
+ google.visualization.events.addListener(
+ this.nheapTable_, 'select', this.onNheapTableRowSelect_.bind(this));
+ $('#nheap-filter').on('change', this.applyTableFilters_.bind(this));
+};
+
+this.dumpNheapFromStorage = function(archiveName, snapshot) {
+ webservice.ajaxRequest('/storage/' + archiveName + '/' + snapshot + '/nheap',
+ this.onDumpAjaxResponse_.bind(this));
+ rootUi.showDialog('Loading native heap allocs from archive ...');
+ this.resetTableFilters_();
+};
+
+this.onDumpAjaxResponse_ = function(data) {
+ this.nheapData_ = new google.visualization.DataTable(data); // TODO remove .table form mmap
+ this.nheapFilter_ = new google.visualization.DataView(this.nheapData_);
+ this.applyTableFilters_();
+ rootUi.hideDialog();
+};
+
+this.resetTableFilters_ = function() {
+ $('#nheap-filter').val('');
+}
+
+this.applyTableFilters_ = function() {
+ // Filters the rows according to the user-provided file and prot regexps.
+ if (!this.nheapFilter_)
+ return;
+
+ var rx = $('#nheap-filter').val();
+ var rows = [];
+ var total = 0;
+
+ for (var row = 0; row < this.nheapData_.getNumberOfRows(); ++row) {
+ stackTrace = this.nheapData_.getValue(row, this.COL_STACKTRACE);
+ if (!stackTrace.match(rx))
+ continue;
+ rows.push(row);
+ total += this.nheapData_.getValue(row, this.COL_TOTAL);
+ }
+
+ $('#nheap-totals').val(Math.floor(total / 1024) + ' KB');
+ this.nheapFilter_.setRows(rows);
+ this.redraw();
+};
+
+this.onNheapTableRowSelect_ = function() {
+ if (!this.nheapFilter_)
+ return;
+
+ var total = 0;
+
+ this.nheapTable_.getSelection().forEach(function(sel) {
+ var row = sel.row;
+ total += this.nheapFilter_.getValue(row, this.COL_TOTAL);
+ }, this);
+
+ $('#nheap-selected').val(Math.floor(total / 1024) + ' KB');
+};
+
+this.redraw = function() {
+ if (!this.nheapFilter_)
+ return;
+ this.nheapTable_.draw(this.nheapFilter_, {allowHtml: true,
+ page: 'enable',
+ pageSize: 25});
+};
+
+$(document).ready(this.onDomReady_.bind(this));
+
+})(); \ No newline at end of file
diff --git a/tools/memory_inspector/memory_inspector/frontends/www_content/js/processes.js b/tools/memory_inspector/memory_inspector/frontends/www_content/js/processes.js
index 4c38c03..deac58b 100644
--- a/tools/memory_inspector/memory_inspector/frontends/www_content/js/processes.js
+++ b/tools/memory_inspector/memory_inspector/frontends/www_content/js/processes.js
@@ -87,12 +87,23 @@ this.showTracingDialog_ = function() {
this.startTracingSelectedProcess_ = function() {
if (!this.selProcUri_)
return alert('The process ' + this.selProcUri_ + ' died.');
+ var traceNativeHeap = $('#ps-tracer-bt').prop('checked');
$('#ps-tracer-dialog').dialog('close');
+ if (traceNativeHeap && !devices.getSelectedDevice().isNativeTracingEnabled) {
+ var shouldProvision = confirm('Native heap tracing is not enabled.\n' +
+ 'Do you want to enable it (will cause a reboot on Android)?');
+ if (shouldProvision) {
+ devices.initializeSelectedDevice(true);
+ alert('Wait device to complete reboot and then retry.');
+ return;
+ }
+ }
+
var postArgs = {interval: $('#ps-tracer-period').val(),
count: $('#ps-tracer-snapshots').val(),
- traceNativeHeap: false};
+ traceNativeHeap: traceNativeHeap};
webservice.ajaxRequest('/tracer/start/' + this.selProcUri_,
this.onStartTracerAjaxResponse_.bind(this),
diff --git a/tools/memory_inspector/memory_inspector/frontends/www_content/js/profiler.js b/tools/memory_inspector/memory_inspector/frontends/www_content/js/profiler.js
index ea8841d1..1920aa7 100644
--- a/tools/memory_inspector/memory_inspector/frontends/www_content/js/profiler.js
+++ b/tools/memory_inspector/memory_inspector/frontends/www_content/js/profiler.js
@@ -65,7 +65,7 @@ this.profileCachedMmapDump = function(mmapDumpId) {
};
this.profileArchivedMmaps = function(archiveName, snapshots) {
- // Creates a profile using the data from the storage.
+ // Creates a mmap profile using the data from the storage.
webservice.ajaxRequest('/profile/create', // This is a POST request.
this.onProfileAjaxResponse_.bind(this),
null, // use the default error handler.
@@ -76,6 +76,19 @@ this.profileArchivedMmaps = function(archiveName, snapshots) {
ruleset: $('#prof-ruleset').val()});
};
+this.profileArchivedNHeaps = function(archiveName, snapshots) {
+ // Creates a native-heap profile using the data from the storage.
+ webservice.ajaxRequest('/profile/create', // This is a POST request.
+ this.onProfileAjaxResponse_.bind(this),
+ null, // use the default error handler.
+ {type: 'nheap',
+ source: 'archive',
+ archive: archiveName,
+ snapshots: snapshots,
+ ruleset: 'heuristic'});
+ // TODO(primiano): Next CLs: support custom rules, not just the heuristic one.
+};
+
this.onProfileAjaxResponse_ = function(data) {
// This AJAX response contains a summary of the profile requested via the
// /profile endpoint, which consists of:
diff --git a/tools/memory_inspector/memory_inspector/frontends/www_content/js/rootUi.js b/tools/memory_inspector/memory_inspector/frontends/www_content/js/rootUi.js
index 5da2a04..d135188 100644
--- a/tools/memory_inspector/memory_inspector/frontends/www_content/js/rootUi.js
+++ b/tools/memory_inspector/memory_inspector/frontends/www_content/js/rootUi.js
@@ -34,6 +34,8 @@ this.onTabChange_ = function(_, ui) {
return profiler.redraw();
case 'mm':
return mmap.redraw();
+ case 'nheap':
+ return nheap.redraw();
case 'settings':
return settings.reload();
case 'storage':
diff --git a/tools/memory_inspector/memory_inspector/frontends/www_content/js/storage.js b/tools/memory_inspector/memory_inspector/frontends/www_content/js/storage.js
index f67af86..1c9c336 100644
--- a/tools/memory_inspector/memory_inspector/frontends/www_content/js/storage.js
+++ b/tools/memory_inspector/memory_inspector/frontends/www_content/js/storage.js
@@ -15,6 +15,8 @@ this.onDomReady_ = function() {
.click(this.dumpMmapForSelectedSnapshot_.bind(this));
$('#storage-profile-native').button({icons:{primary: 'ui-icon-image'}})
.click(this.profileNativeForSelectedSnapshots.bind(this));
+ $('#storage-dump-nheap').button({icons:{primary: 'ui-icon-calculator'}})
+ .click(this.dumpNheapForSelectedSnapshot_.bind(this));
// Create the table.
this.table_ = new google.visualization.Table($('#storage-table')[0]);
@@ -64,10 +66,53 @@ this.dumpMmapForSelectedSnapshot_ = function() {
rootUi.showTab('mm');
};
+this.dumpNheapForSelectedSnapshot_ = function() {
+ var sel = this.table_.getSelection();
+ if (sel.length != 1) {
+ alert('Please select only one snapshot.')
+ return;
+ }
+
+ var row = sel[0].row;
+ if (!this.checkHasNativeHapDump_(row))
+ return;
+ nheap.dumpNheapFromStorage(this.tableData_.getValue(row, 0),
+ this.tableData_.getValue(row, 1))
+ rootUi.showTab('nheap');
+};
+
this.profileNativeForSelectedSnapshots = function() {
+ // Generates a native heap profile for the selected snapshots.
+ var sel = this.table_.getSelection();
+ if (!sel.length || !this.tableData_)
+ return;
+ var archiveName = null;
+ var snapshots = [];
+ for (var i = 0; i < sel.length; ++i) {
+ var row = sel[i].row;
+ var curArchive = this.tableData_.getValue(row, 0);
+ if (archiveName && curArchive != archiveName) {
+ alert('All the selected snapshots must belong to the same archive!');
+ return;
+ }
+ if (!this.checkHasNativeHapDump_(row))
+ return;
+ archiveName = curArchive;
+ snapshots.push(this.tableData_.getValue(row, 1));
+ }
+ profiler.profileArchivedNHeaps(archiveName, snapshots);
+ rootUi.showTab('prof');
};
+this.checkHasNativeHapDump_ = function(row) {
+ if (!this.tableData_.getValue(row, 3)) {
+ alert('The selected snapshot doesn\'t have a heap dump!');
+ return false;
+ }
+ return true;
+}
+
this.redraw = function() {
if (!this.tableData_)
return;
diff --git a/tools/memory_inspector/memory_inspector/frontends/www_server.py b/tools/memory_inspector/memory_inspector/frontends/www_server.py
index d1a54ae..3d120d4 100644
--- a/tools/memory_inspector/memory_inspector/frontends/www_server.py
+++ b/tools/memory_inspector/memory_inspector/frontends/www_server.py
@@ -18,6 +18,7 @@ The following HTTP status code are returned by the server:
This typically happens when the target device is disconnected.
"""
+import cgi
import collections
import datetime
import dateutil.parser
@@ -33,6 +34,7 @@ import wsgiref.simple_server
from memory_inspector.core import backends
from memory_inspector.core import memory_map
from memory_inspector.classification import mmap_classifier
+from memory_inspector.classification import native_heap_classifier
from memory_inspector.data import serialization
from memory_inspector.data import file_storage
from memory_inspector.frontends import background_tasks
@@ -154,8 +156,10 @@ def _InitializeDevice(args, req_vars): # pylint: disable=W0613
if not device:
return _HTTP_GONE, [], 'Device not found'
device.Initialize()
+ if req_vars['enableNativeTracing']:
+ device.EnableNativeTracing(True)
return _HTTP_OK, [], {
- 'isNativeTracingEnabled': device.IsNativeTracingEnabled()}
+ 'isNativeTracingEnabled': device.IsNativeTracingEnabled()}
@AjaxHandler(r'/ajax/profile/create', 'POST')
@@ -173,41 +177,58 @@ def _CreateProfile(args, req_vars): # pylint: disable=W0613
# Step 1: collect the memory dumps, according to what the client specified in
# the 'type' and 'source' POST arguments.
- # Case 1: Generate a profile from a set of mmap dumps.
- if req_vars['type'] == 'mmap':
- classifier = mmap_classifier
- # Case 1a: Use a cached mmap dumps.
- if req_vars['source'] == 'cache':
- dumps[0] = _GetCacheObject(req_vars['id'])
- # Case 1b: Load mem dumps from an archive.
- elif req_vars['source'] == 'archive':
- archive = _persistent_storage.OpenArchive(req_vars['archive'])
- if not archive:
- return _HTTP_GONE, [], 'Cannot open archive %s' % req_vars['archive']
- first_timestamp = None
- for timestamp_str in req_vars['snapshots']:
- timestamp = dateutil.parser.parse(timestamp_str)
- first_timestamp = timestamp if not first_timestamp else first_timestamp
- time_delta = int((timestamp - first_timestamp).total_seconds())
+ # Case 1a: The client requests to load data from an archive.
+ if req_vars['source'] == 'archive':
+ archive = _persistent_storage.OpenArchive(req_vars['archive'])
+ if not archive:
+ return _HTTP_GONE, [], 'Cannot open archive %s' % req_vars['archive']
+ first_timestamp = None
+ for timestamp_str in req_vars['snapshots']:
+ timestamp = dateutil.parser.parse(timestamp_str)
+ first_timestamp = first_timestamp or timestamp
+ time_delta = int((timestamp - first_timestamp).total_seconds())
+ if req_vars['type'] == 'mmap':
dumps[time_delta] = archive.LoadMemMaps(timestamp)
+ elif req_vars['type'] == 'nheap':
+ dumps[time_delta] = archive.LoadNativeHeap(timestamp)
- # TODO(primiano): Add support for native_heap types.
+ # Case 1b: Use a dump recently cached (only mmap, via _DumpMmapsForProcess).
+ elif req_vars['source'] == 'cache':
+ assert(req_vars['type'] == 'mmap'), 'Only cached mmap dumps are supported.'
+ dumps[0] = _GetCacheObject(req_vars['id'])
- # Step 2: Load the rule-set specified by the client in the 'ruleset' POST arg.
- # Also, perform some basic sanity checking.
- rules_path = os.path.join(memory_inspector.ROOT_DIR, 'classification_rules',
- req_vars['ruleset'])
- if not classifier:
- return _HTTP_GONE, [], 'Classifier %s not supported.' % req_vars['type']
if not dumps:
return _HTTP_GONE, [], 'No memory dumps could be retrieved'
- if not os.path.isfile(rules_path):
- return _HTTP_GONE, [], 'Cannot find the rule-set %s' % rules_path
- with open(rules_path) as f:
- rules = mmap_classifier.LoadRules(f.read())
- # Step 3: Aggregate the data using the desired classifier and generate the
- # profile dictionary (which will be kept cached here in the server).
+ # Initialize the classifier (mmap or nheap) and prepare symbols for nheap.
+ if req_vars['type'] == 'mmap':
+ classifier = mmap_classifier
+ elif req_vars['type'] == 'nheap':
+ classifier = native_heap_classifier
+ if not archive.HasSymbols():
+ return _HTTP_GONE, [], 'No symbols in archive %s' % req_vars['archive']
+ symbols = archive.LoadSymbols()
+ for nheap in dumps.itervalues():
+ nheap.SymbolizeUsingSymbolDB(symbols)
+
+ if not classifier:
+ return _HTTP_GONE, [], 'Classifier %s not supported.' % req_vars['type']
+
+ # Step 2: Load the rule-set specified by the client in the 'ruleset' POST arg.
+ if req_vars['ruleset'] == 'heuristic':
+ assert(req_vars['type'] == 'nheap'), (
+ 'heuristic rules are supported only for nheap')
+ rules = native_heap_classifier.InferHeuristicRulesFromHeap(dumps[0])
+ else:
+ rules_path = os.path.join(
+ memory_inspector.ROOT_DIR, 'classification_rules', req_vars['ruleset'])
+ if not os.path.isfile(rules_path):
+ return _HTTP_GONE, [], 'Cannot find the rule-set %s' % rules_path
+ with open(rules_path) as f:
+ rules = classifier.LoadRules(f.read())
+
+ # Step 3: Aggregate the dump data using the classifier and generate the
+ # profile data (which will be kept cached here in the server).
# The resulting profile will consist of 1+ snapshots (depending on the number
# dumps the client has requested to process) and a number of 1+ metrics
# (depending on the buckets' keys returned by the classifier).
@@ -223,8 +244,6 @@ def _CreateProfile(args, req_vars): # pylint: disable=W0613
profile_id = _CacheObject(snapshots)
first_snapshot = next(snapshots.itervalues())
-
- # |metrics| is the key set of any of the aggregated result
return _HTTP_OK, [], {'id': profile_id,
'times': snapshots.keys(),
'metrics': first_snapshot.keys,
@@ -531,6 +550,51 @@ def _LoadMmapsFromStorage(args, req_vars): # pylint: disable=W0613
return _HTTP_OK, [], {'table': _ConvertMmapToGTable(mmap)}
+@AjaxHandler(r'/ajax/storage/(.+)/(.+)/nheap')
+def _LoadNheapFromStorage(args, req_vars):
+ """Returns a Google Charts DataTable dictionary for the nheap."""
+ archive = _persistent_storage.OpenArchive(args[0])
+ if not archive:
+ return _HTTP_GONE, [], 'Cannot open archive %s' % req_vars['archive']
+
+ timestamp = dateutil.parser.parse(args[1])
+ if not archive.HasNativeHeap(timestamp):
+ return _HTTP_GONE, [], 'No native heap dump for snapshot %s' % timestamp
+
+ nheap = archive.LoadNativeHeap(timestamp)
+ symbols = archive.LoadSymbols()
+ nheap.SymbolizeUsingSymbolDB(symbols)
+
+ resp = {
+ 'cols': [
+ {'label': 'Total size [KB]', 'type':'number'},
+ {'label': 'Alloc size [B]', 'type':'number'},
+ {'label': 'Count', 'type':'number'},
+ {'label': 'Stack Trace', 'type':'string'},
+ ],
+ 'rows': []}
+ for alloc in nheap.allocations:
+ strace = '<dl>'
+ for frame in alloc.stack_trace.frames:
+ # Use the fallback libname.so+0xaddr if symbol info is not available.
+ symbol_name = frame.symbol.name if frame.symbol else '??'
+ source_info = (str(frame.symbol.source_info[0]) if
+ frame.symbol and frame.symbol.source_info else frame.raw_address)
+ strace += '<dd title="%s">%s</dd><dt>%s</dt>' % (
+ cgi.escape(source_info),
+ cgi.escape(os.path.basename(source_info)),
+ cgi.escape(symbol_name))
+ strace += '</dl>'
+
+ resp['rows'] += [{'c': [
+ {'v': alloc.total_size, 'f': alloc.total_size / 1024},
+ {'v': alloc.size, 'f': None},
+ {'v': alloc.count, 'f': None},
+ {'v': strace, 'f': None},
+ ]}]
+ return _HTTP_OK, [], resp
+
+
# /ajax/tracer/start/Android/device-id/pid
@AjaxHandler(r'/ajax/tracer/start/(\w+)/(\w+)/(\d+)', 'POST')
def _StartTracer(args, req_vars):