summaryrefslogtreecommitdiffstats
path: root/tools/memory_inspector
diff options
context:
space:
mode:
authorprimiano <primiano@chromium.org>2014-09-16 01:49:30 -0700
committerCommit bot <commit-bot@chromium.org>2014-09-16 08:50:33 +0000
commit786950036c44447667426bb07a51b911531ccc79 (patch)
treee73fa8f2a082c8fe74437aa0d1392fc2324b8574 /tools/memory_inspector
parentc8cd4b801e8c511456ad813b80930fc46279d034 (diff)
downloadchromium_src-786950036c44447667426bb07a51b911531ccc79.zip
chromium_src-786950036c44447667426bb07a51b911531ccc79.tar.gz
chromium_src-786950036c44447667426bb07a51b911531ccc79.tar.bz2
[Android] memory_inspector: add resident memory accounting.
Introduce the logic which is able to intersect mmap stats (dirty/clean priv/shared) with allocations. Essentially it contains the math to calculate the overlap (even partial) between allocations and mmaps and attribute stats counters to allocations. For the moment only resident memory is accounted. Finer grained accounting (dirty/clean) requires some changes to memdump (to have more than one bit per page in its output bitmap) and will come soon. BUG=340294 NOTRY=true Review URL: https://codereview.chromium.org/563183003 Cr-Commit-Position: refs/heads/master@{#295032}
Diffstat (limited to 'tools/memory_inspector')
-rw-r--r--tools/memory_inspector/memory_inspector/backends/android/android_backend.py7
-rw-r--r--tools/memory_inspector/memory_inspector/backends/android/android_backend_unittest.py5
-rw-r--r--tools/memory_inspector/memory_inspector/classification/native_heap_classifier.py5
-rw-r--r--tools/memory_inspector/memory_inspector/classification/native_heap_classifier_unittest.py46
-rw-r--r--tools/memory_inspector/memory_inspector/core/native_heap.py70
-rw-r--r--tools/memory_inspector/memory_inspector/core/native_heap_unittest.py147
-rw-r--r--tools/memory_inspector/memory_inspector/core/stacktrace.py2
-rw-r--r--tools/memory_inspector/memory_inspector/data/serialization.py3
-rw-r--r--tools/memory_inspector/memory_inspector/frontends/background_tasks.py2
-rw-r--r--tools/memory_inspector/memory_inspector/frontends/www_content/index.html12
-rw-r--r--tools/memory_inspector/memory_inspector/frontends/www_content/js/nheap.js23
-rw-r--r--tools/memory_inspector/memory_inspector/frontends/www_server.py2
12 files changed, 276 insertions, 48 deletions
diff --git a/tools/memory_inspector/memory_inspector/backends/android/android_backend.py b/tools/memory_inspector/memory_inspector/backends/android/android_backend.py
index e35e12d..c7cfe81 100644
--- a/tools/memory_inspector/memory_inspector/backends/android/android_backend.py
+++ b/tools/memory_inspector/memory_inspector/backends/android/android_backend.py
@@ -124,6 +124,8 @@ class AndroidBackend(backends.Backend):
exec_file_name = posixpath.basename(exec_file_rel_path)
if exec_file_rel_path.startswith('/'):
exec_file_rel_path = exec_file_rel_path[1:]
+ if not exec_file_rel_path:
+ continue
exec_file_abs_path = ''
for sym_path in sym_paths:
# First try to locate the symbol file following the full relative path
@@ -145,7 +147,7 @@ class AndroidBackend(backends.Backend):
if os.path.exists(exec_file_abs_path):
break
- if not os.path.exists(exec_file_abs_path):
+ if not os.path.isfile(exec_file_abs_path):
continue
symbolizer = elf_symbolizer.ELFSymbolizer(
@@ -177,8 +179,9 @@ class AndroidDevice(backends.Device):
backend=backend,
settings=backends.Settings(AndroidDevice._SETTINGS_KEYS))
self.adb = adb
+ self._name = '%s %s' % (adb.GetProp('ro.product.model'),
+ adb.GetProp('ro.build.id'))
self._id = str(adb)
- self._name = adb.GetProp('ro.product.model')
self._sys_stats = None
self._last_device_stats = None
self._sys_stats_last_update = None
diff --git a/tools/memory_inspector/memory_inspector/backends/android/android_backend_unittest.py b/tools/memory_inspector/memory_inspector/backends/android/android_backend_unittest.py
index 9a843c4..29cfae3 100644
--- a/tools/memory_inspector/memory_inspector/backends/android/android_backend_unittest.py
+++ b/tools/memory_inspector/memory_inspector/backends/android/android_backend_unittest.py
@@ -81,6 +81,7 @@ class AndroidBackendTest(unittest.TestCase):
'devices': _MOCK_DEVICES_OUT,
'shell getprop ro.product.model': 'Mock device',
'shell getprop ro.build.type': 'userdebug',
+ 'shell getprop ro.build.id': 'ZZ007',
'shell getprop ro.product.cpu.abi': 'armeabi',
'root': 'adbd is already running as root',
'shell /data/local/tmp/ps_ext': _MOCK_PS_EXT_OUT,
@@ -103,9 +104,9 @@ class AndroidBackendTest(unittest.TestCase):
# Test device enumeration.
devices = list(ab.EnumerateDevices())
self.assertEqual(len(devices), 2)
- self.assertEqual(devices[0].name, 'Mock device')
+ self.assertEqual(devices[0].name, 'Mock device ZZ007')
self.assertEqual(devices[0].id, '0000000000000001')
- self.assertEqual(devices[1].name, 'Mock device')
+ self.assertEqual(devices[1].name, 'Mock device ZZ007')
self.assertEqual(devices[1].id, '0000000000000002')
# Initialize device (checks that sha1 are checked in).
diff --git a/tools/memory_inspector/memory_inspector/classification/native_heap_classifier.py b/tools/memory_inspector/memory_inspector/classification/native_heap_classifier.py
index d51117c..e424afc 100644
--- a/tools/memory_inspector/memory_inspector/classification/native_heap_classifier.py
+++ b/tools/memory_inspector/memory_inspector/classification/native_heap_classifier.py
@@ -34,7 +34,7 @@ from memory_inspector.core import exceptions
from memory_inspector.core import native_heap
-_RESULT_KEYS = ['bytes_allocated']
+_RESULT_KEYS = ['bytes_allocated', 'bytes_resident']
def LoadRules(content):
@@ -61,7 +61,8 @@ def Classify(nativeheap, rule_tree):
res = results.AggreatedResults(rule_tree, _RESULT_KEYS)
for allocation in nativeheap.allocations:
- res.AddToMatchingNodes(allocation, [allocation.size])
+ res.AddToMatchingNodes(allocation,
+ [allocation.size, allocation.resident_size])
return res
diff --git a/tools/memory_inspector/memory_inspector/classification/native_heap_classifier_unittest.py b/tools/memory_inspector/memory_inspector/classification/native_heap_classifier_unittest.py
index 2160961..63f58c4 100644
--- a/tools/memory_inspector/memory_inspector/classification/native_heap_classifier_unittest.py
+++ b/tools/memory_inspector/memory_inspector/classification/native_heap_classifier_unittest.py
@@ -66,13 +66,13 @@ _TEST_STACK_TRACES = [
]
_EXPECTED_RESULTS = {
- 'Total': [238],
- 'Total::content': [95],
- 'Total::content::browser': [12], # 5 + 7.
- 'Total::content::renderer': [49], # 13 + 17 + 19.
- 'Total::content::content-other': [34],
- 'Total::ashmem_in_skia': [68], # 31 + 37.
- 'Total::Total-other': [75], # 3 + 29 + 43.
+ 'Total': [238, 0],
+ 'Total::content': [95, 0],
+ 'Total::content::browser': [12, 0], # 5 + 7.
+ 'Total::content::renderer': [49, 0], # 13 + 17 + 19.
+ 'Total::content::content-other': [34, 0],
+ 'Total::ashmem_in_skia': [68, 0], # 31 + 37.
+ 'Total::Total-other': [75, 0], # 3 + 29 + 43.
}
_HEURISTIC_TEST_STACK_TRACES = [
@@ -86,18 +86,18 @@ _HEURISTIC_TEST_STACK_TRACES = [
]
_HEURISTIC_EXPECTED_RESULTS = {
- 'Total': [76],
- 'Total::/root/': [76],
- 'Total::/root/::base1/foo/': [31], # 10 + 20 +1
- 'Total::/root/::base1/foo/::bar/': [10],
- 'Total::/root/::base1/foo/::baz/': [20],
- 'Total::/root/::base1/foo/::base1/foo/-other': [1],
- 'Total::/root/::base2/': [43], # 3 + 22 + 18
- 'Total::/root/::base2/::subpath/': [22],
- 'Total::/root/::base2/::subpath2/': [18],
- 'Total::/root/::base2/::base2/-other': [3],
- 'Total::/root/::/root/-other': [2],
- 'Total::Total-other': [0],
+ 'Total': [76, 0],
+ 'Total::/root/': [76, 0],
+ 'Total::/root/::base1/foo/': [31, 0], # 10 + 20 +1
+ 'Total::/root/::base1/foo/::bar/': [10, 0],
+ 'Total::/root/::base1/foo/::baz/': [20, 0],
+ 'Total::/root/::base1/foo/::base1/foo/-other': [1, 0],
+ 'Total::/root/::base2/': [43, 0], # 3 + 22 + 18
+ 'Total::/root/::base2/::subpath/': [22, 0],
+ 'Total::/root/::base2/::subpath2/': [18, 0],
+ 'Total::/root/::base2/::base2/-other': [3, 0],
+ 'Total::/root/::/root/-other': [2, 0],
+ 'Total::Total-other': [0, 0],
}
@@ -113,8 +113,8 @@ class NativeHeapClassifierTest(unittest.TestCase):
mock_frame = stacktrace.Frame(mock_addr)
mock_frame.SetSymbolInfo(symbol.Symbol(mock_btstr, mock_source_path))
mock_strace.Add(mock_frame)
- nheap.Add(native_heap.Allocation(size=test_entry[0],
- stack_trace=mock_strace))
+ nheap.Add(native_heap.Allocation(
+ size=test_entry[0], stack_trace=mock_strace))
res = native_heap_classifier.Classify(nheap, rule_tree)
self._CheckResult(res.total, '', _EXPECTED_RESULTS)
@@ -129,8 +129,8 @@ class NativeHeapClassifierTest(unittest.TestCase):
mock_frame.SetSymbolInfo(symbol.Symbol(str(mock_addr), mock_source_path))
for _ in xrange(10): # Just repeat the same stack frame 10 times
mock_strace.Add(mock_frame)
- nheap.Add(native_heap.Allocation(size=mock_alloc_size,
- stack_trace=mock_strace))
+ nheap.Add(native_heap.Allocation(
+ size=mock_alloc_size, stack_trace=mock_strace))
rule_tree = native_heap_classifier.InferHeuristicRulesFromHeap(
nheap, threshold=0.05)
diff --git a/tools/memory_inspector/memory_inspector/core/native_heap.py b/tools/memory_inspector/memory_inspector/core/native_heap.py
index ab52a39..4a5537b 100644
--- a/tools/memory_inspector/memory_inspector/core/native_heap.py
+++ b/tools/memory_inspector/memory_inspector/core/native_heap.py
@@ -2,9 +2,12 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+from memory_inspector.core import memory_map
from memory_inspector.core import stacktrace
from memory_inspector.core import symbol
+from memory_inspector.core.memory_map import PAGE_SIZE
+
class NativeHeap(object):
"""A snapshot of outstanding (i.e. not freed) native allocations.
@@ -32,31 +35,88 @@ class NativeHeap(object):
def SymbolizeUsingSymbolDB(self, symbols):
assert(isinstance(symbols, symbol.Symbols))
for stack_frame in self.stack_frames.itervalues():
- if stack_frame.exec_file_rel_path is None:
+ if not stack_frame.exec_file_rel_path:
continue
sym = symbols.Lookup(stack_frame.exec_file_rel_path, stack_frame.offset)
if sym:
stack_frame.SetSymbolInfo(sym)
+ def RelativizeStackFrames(self, mmap):
+ """Turns stack frames' absolute addresses into mmap relative addresses.
+
+ For each absolute address, the containing mmap is looked up and the frame
+ is decorated with the mapped file + relative address in the file."""
+ assert(isinstance(mmap, memory_map.Map))
+ for abs_addr, stack_frame in self.stack_frames.iteritems():
+ assert(abs_addr == stack_frame.address)
+ map_entry = mmap.Lookup(abs_addr)
+ if not map_entry:
+ continue
+ stack_frame.SetExecFileInfo(map_entry.mapped_file,
+ map_entry.GetRelativeFileOffset(abs_addr))
+
+ def CalculateResidentSize(self, mmap):
+ """Updates the |Allocation|.|resident_size|s by looking at mmap stats.
+
+ Not all the allocated memory is always used (read: resident). This function
+ estimates the resident size of an allocation intersecting the mmaps dump.
+ """
+ assert(isinstance(mmap, memory_map.Map))
+ for alloc in self.allocations:
+ # This function loops over all the memory pages that intersect, partially
+ # or fully, with each allocation. For each of them, the allocation is
+ # attributed a resident size equal to the size of intersecting range iff
+ # the page is resident.
+ # The tricky part is that, in the general case, an allocation can span
+ # over multiple (contiguous) mmaps. See the chart below for a reference:
+ #
+ # VA space: |0 |4k |8k |12k |16k |20k |24k |28k |32k |
+ # Mmaps: [ mm 1 ][ mm2 ] [ map 3 ]
+ # Allocs: <a1> < a2 > < a3 >
+ #
+ # Note: this accounting technique is not fully correct but is generally a
+ # good tradeoff between accuracy and speed of profiling. The OS provides
+ # resident information with the page granularity (typ. 4k). Finer values
+ # would require more fancy techniques based, for instance, on run-time
+ # instrumentation tools like Valgrind or *sanitizer.
+ cur_start = alloc.start
+ mm = None
+ while cur_start < alloc.end:
+ if not mm or not mm.Contains(cur_start):
+ mm = mmap.Lookup(cur_start)
+ if mm:
+ page, page_off = mm.GetRelativeMMOffset(cur_start)
+ if mm.IsPageResident(page):
+ page_end = mm.start + page * PAGE_SIZE + PAGE_SIZE - 1
+ alloc_memory_in_current_page = PAGE_SIZE - page_off
+ if alloc.end < page_end:
+ alloc_memory_in_current_page -= page_end - alloc.end
+ alloc.resident_size += alloc_memory_in_current_page
+ # Move to the next page boundary.
+ cur_start = (cur_start + PAGE_SIZE) & ~(PAGE_SIZE - 1)
+
class Allocation(object):
- """Records profiling information aobut a native heap allocation.
+ """Records profiling information about a native heap allocation.
Args:
size: size of the allocation, in bytes.
stack_trace: the allocation call-site. See |stacktrace.Stacktrace|.
- start: (Optional) Absolute start address in the process VMA. Optional.
- It is required only for |CalculateResidentSize|.
+ start: (Optional) Absolute start address in the process VMA. It is
+ required only for |CalculateResidentSize|.
flags: (Optional) More details about the call site (e.g., mmap vs malloc).
+ resident_size: this is normally obtained through |CalculateResidentSize|
+ and is part of the initializer just for deserialization purposes.
"""
- def __init__(self, size, stack_trace, start=0, flags=0):
+ def __init__(self, size, stack_trace, start=0, flags=0, resident_size=0):
assert(size > 0)
assert(isinstance(stack_trace, stacktrace.Stacktrace))
self.size = size # in bytes.
self.stack_trace = stack_trace
self.start = start # Optional, for using the resident size logic.
self.flags = flags
+ self.resident_size = resident_size # see |CalculateResidentSize|.
@property
def end(self):
diff --git a/tools/memory_inspector/memory_inspector/core/native_heap_unittest.py b/tools/memory_inspector/memory_inspector/core/native_heap_unittest.py
new file mode 100644
index 0000000..2201d0e
--- /dev/null
+++ b/tools/memory_inspector/memory_inspector/core/native_heap_unittest.py
@@ -0,0 +1,147 @@
+# 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.
+
+"""
+The test scenario is as follows:
+VA space: |0 |4k |8k |12k |16k |20k ... |64k |65k |66k
+Mmaps: [ anon 1 ][anon 2] [anon 3] ... [ exe1 ][ exe2 ]
+Resident: *******-------*******-------******* (*:resident, -:not resident)
+Allocs: <1> <2> < 3 >
+ | | |
+S.Traces: | | +-----------> st1[exe1 + 0, exe1 + 4]
+ | +--------------------------> st1[exe1 + 0, exe1 + 4]
+ +------------------------------> st2[exe1 + 0, exe2 + 4, post-exe2]
+
+Furthermore, the exe2 is a file mapping with non-zero (8k) offset.
+"""
+
+import unittest
+
+from memory_inspector.core import memory_map
+from memory_inspector.core import native_heap
+from memory_inspector.core import stacktrace
+from memory_inspector.core import symbol
+
+from memory_inspector.core.memory_map import PAGE_SIZE
+
+
+class NativeHeapTest(unittest.TestCase):
+ def runTest(self):
+ nheap = native_heap.NativeHeap()
+
+ EXE_1_MM_BASE = 64 * PAGE_SIZE
+ EXE_2_MM_BASE = 65 * PAGE_SIZE
+ EXE_2_FILE_OFF = 8192
+ st1 = stacktrace.Stacktrace()
+ st1.Add(nheap.GetStackFrame(EXE_1_MM_BASE))
+ st1.Add(nheap.GetStackFrame(EXE_1_MM_BASE + 4))
+
+ st2 = stacktrace.Stacktrace()
+ st2.Add(nheap.GetStackFrame(EXE_1_MM_BASE))
+ st2.Add(nheap.GetStackFrame(EXE_2_MM_BASE + 4))
+ st2.Add(nheap.GetStackFrame(EXE_2_MM_BASE + PAGE_SIZE + 4))
+
+ # Check that GetStackFrames keeps one unique object instance per address.
+ # This is to guarantee that the symbolization logic (SymbolizeUsingSymbolDB)
+ # can cheaply iterate on distinct stack frames rather than re-processing
+ # every stack frame for each allocation (and save memory as well).
+ self.assertIs(st1[0], st2[0])
+ self.assertIsNot(st1[0], st1[1])
+ self.assertIsNot(st2[0], st2[1])
+
+ alloc1 = native_heap.Allocation(start=4, size=4, stack_trace=st1)
+ alloc2 = native_heap.Allocation(start=4090, size=8, stack_trace=st1)
+ alloc3 = native_heap.Allocation(start=8190, size=10000, stack_trace=st2)
+ nheap.Add(alloc1)
+ nheap.Add(alloc2)
+ nheap.Add(alloc3)
+
+ self.assertEqual(len(nheap.allocations), 3)
+ self.assertIn(alloc1, nheap.allocations)
+ self.assertIn(alloc2, nheap.allocations)
+ self.assertIn(alloc3, nheap.allocations)
+
+ ############################################################################
+ # Test the relativization (absolute address -> mmap + offset) logic.
+ ############################################################################
+ mmap = memory_map
+ mmap = memory_map.Map()
+ mmap.Add(memory_map.MapEntry(EXE_1_MM_BASE, EXE_1_MM_BASE + PAGE_SIZE - 1,
+ 'rw--', '/d/exe1', 0))
+ mmap.Add(memory_map.MapEntry(EXE_2_MM_BASE, EXE_2_MM_BASE + PAGE_SIZE - 1,
+ 'rw--', 'exe2',EXE_2_FILE_OFF))
+ # Entry for EXE_3 is deliberately missing to check the fallback behavior.
+
+ nheap.RelativizeStackFrames(mmap)
+
+ self.assertEqual(st1[0].exec_file_rel_path, '/d/exe1')
+ self.assertEqual(st1[0].exec_file_name, 'exe1')
+ self.assertEqual(st1[0].offset, 0)
+
+ self.assertEqual(st1[1].exec_file_rel_path, '/d/exe1')
+ self.assertEqual(st1[1].exec_file_name, 'exe1')
+ self.assertEqual(st1[1].offset, 4)
+
+ self.assertEqual(st2[0].exec_file_rel_path, '/d/exe1')
+ self.assertEqual(st2[0].exec_file_name, 'exe1')
+ self.assertEqual(st2[0].offset, 0)
+
+ self.assertEqual(st2[1].exec_file_rel_path, 'exe2')
+ self.assertEqual(st2[1].exec_file_name, 'exe2')
+ self.assertEqual(st2[1].offset, 4 + EXE_2_FILE_OFF)
+
+ self.assertIsNone(st2[2].exec_file_rel_path)
+ self.assertIsNone(st2[2].exec_file_name)
+ self.assertIsNone(st2[2].offset)
+
+ ############################################################################
+ # Test the symbolization logic.
+ ############################################################################
+ syms = symbol.Symbols()
+ syms.Add('/d/exe1', 0, symbol.Symbol('sym1', 'src1.c', 1)) # st1[0]
+ syms.Add('exe2', 4 + EXE_2_FILE_OFF, symbol.Symbol('sym3')) # st2[1]
+
+ nheap.SymbolizeUsingSymbolDB(syms)
+ self.assertEqual(st1[0].symbol.name, 'sym1')
+ self.assertEqual(st1[0].symbol.source_info[0].source_file_path, 'src1.c')
+ self.assertEqual(st1[0].symbol.source_info[0].line_number, 1)
+
+ # st1[1] should have no symbol info, because we didn't provide any above.
+ self.assertIsNone(st1[1].symbol)
+
+ # st2[0] and st1[0] were the same Frame. Expect identical symbols instances.
+ self.assertIs(st2[0].symbol, st1[0].symbol)
+
+ # st2[1] should have a symbols name, but no source line info.
+ self.assertEqual(st2[1].symbol.name, 'sym3')
+ self.assertEqual(len(st2[1].symbol.source_info), 0)
+
+ # st2[2] should have no sym because we didn't even provide a mmap for exe3.
+ self.assertIsNone(st2[2].symbol)
+
+ ############################################################################
+ # Test the resident size calculation logic (intersects mmaps and allocs).
+ ############################################################################
+ mmap.Add(
+ memory_map.MapEntry(0, 8191, 'rw--', '', 0, resident_pages=[1]))
+ mmap.Add(
+ memory_map.MapEntry(8192, 12287, 'rw--', '', 0, resident_pages=[1]))
+ # [12k, 16k] is deliberately missing to check the fallback behavior.
+ mmap.Add(
+ memory_map.MapEntry(16384, 20479, 'rw--', '', 0, resident_pages=[1]))
+ nheap.CalculateResidentSize(mmap)
+
+ # alloc1 [4, 8] is fully resident because it lays in the first resident 4k.
+ self.assertEqual(alloc1.resident_size, 4)
+
+ # alloc2 [4090, 4098] should have only 6 resident bytes ([4090,4096]), but
+ # not the last two, which lay on the second page which is noijt resident.
+ self.assertEqual(alloc2.resident_size, 6)
+
+ # alloc3 [8190, 18190] is split as follows (* = resident):
+ # [8190, 8192]: these 2 bytes are NOT resident, they lay in the 2nd page.
+ # *[8192, 12288]: the 3rd page is resident and is fully covered by alloc3.
+ # [12288, 16384]: the 4th page is fully covered as well, but not resident.
+ # *[16384, 18190]: the 5th page is partially covered and resident.
+ self.assertEqual(alloc3.resident_size, (12288 - 8192) + (18190 - 16384))
diff --git a/tools/memory_inspector/memory_inspector/core/stacktrace.py b/tools/memory_inspector/memory_inspector/core/stacktrace.py
index 050c976..3f64e82 100644
--- a/tools/memory_inspector/memory_inspector/core/stacktrace.py
+++ b/tools/memory_inspector/memory_inspector/core/stacktrace.py
@@ -67,7 +67,7 @@ class Frame(object):
@property
def exec_file_name(self):
"""Returns the file name (stripped of the path) of the executable."""
- if self.exec_file_rel_path is None:
+ if not self.exec_file_rel_path:
return None
return posixpath.basename(self.exec_file_rel_path.replace('\\', '/'))
diff --git a/tools/memory_inspector/memory_inspector/data/serialization.py b/tools/memory_inspector/memory_inspector/data/serialization.py
index 25e6068..b829267 100644
--- a/tools/memory_inspector/memory_inspector/data/serialization.py
+++ b/tools/memory_inspector/memory_inspector/data/serialization.py
@@ -108,5 +108,6 @@ class NativeHeapDecoder(json.JSONDecoder):
nh.Add(native_heap.Allocation(start=alloc_dict['start'],
size=alloc_dict['size'],
stack_trace=stack_trace,
- flags=alloc_dict['flags']))
+ flags=alloc_dict['flags'],
+ resident_size=alloc_dict['resident_size']))
return nh \ No newline at end of file
diff --git a/tools/memory_inspector/memory_inspector/frontends/background_tasks.py b/tools/memory_inspector/memory_inspector/frontends/background_tasks.py
index 96f03aa..61f3b40e 100644
--- a/tools/memory_inspector/memory_inspector/frontends/background_tasks.py
+++ b/tools/memory_inspector/memory_inspector/frontends/background_tasks.py
@@ -105,6 +105,8 @@ def TracerMain_(log, storage_path, backend_name, device_id, pid, interval,
archive.StoreMemMaps(mmaps)
if trace_native_heap:
+ nheap.RelativizeStackFrames(mmaps)
+ nheap.CalculateResidentSize(mmaps)
archive.StoreNativeHeap(nheap)
heaps_to_symbolize += [nheap]
finally:
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 a78820b..a3db078 100644
--- a/tools/memory_inspector/memory_inspector/frontends/www_content/index.html
+++ b/tools/memory_inspector/memory_inspector/frontends/www_content/index.html
@@ -169,10 +169,14 @@
<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>Total (allocated): </label>
+ <input type="text" id="nheap-total-allocated" values="0 KB" readonly>
+ <label>Total (resident): </label>
+ <input type="text" id="nheap-total-resident" values="0 KB" readonly>
+ <label>Selected (allocated): </label>
+ <input type="text" id="nheap-selected-allocated" values="0 KB" readonly>
+ <label>Selected (resident): </label>
+ <input type="text" id="nheap-selected-resident" values="0 KB" readonly>
<label>Filter: </label>
<input type="text" id="nheap-filter">
</div>
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
index 005a42a..91190c2 100644
--- a/tools/memory_inspector/memory_inspector/frontends/www_content/js/nheap.js
+++ b/tools/memory_inspector/memory_inspector/frontends/www_content/js/nheap.js
@@ -4,8 +4,10 @@
nheap = new (function() {
-this.COL_STACKTRACE = 3;
this.COL_TOTAL = 0;
+this.COL_RESIDENT = 1;
+this.COL_STACKTRACE = 3;
+
this.nheapData_ = null;
this.nheapTable_ = null;
@@ -44,17 +46,20 @@ this.applyTableFilters_ = function() {
var rx = $('#nheap-filter').val();
var rows = [];
- var total = 0;
+ var total_allocated = 0;
+ var total_resident = 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);
+ total_allocated += this.nheapData_.getValue(row, this.COL_TOTAL);
+ total_resident += this.nheapData_.getValue(row, this.COL_RESIDENT);
}
- $('#nheap-totals').val(Math.floor(total / 1024) + ' KB');
+ $('#nheap-total-allocated').val(Math.floor(total_allocated / 1024) + ' KB');
+ $('#nheap-total-resident').val(Math.floor(total_resident / 1024) + ' KB');
this.nheapFilter_.setRows(rows);
this.redraw();
};
@@ -63,14 +68,18 @@ this.onNheapTableRowSelect_ = function() {
if (!this.nheapFilter_)
return;
- var total = 0;
+ var total_allocated = 0;
+ var total_resident = 0;
this.nheapTable_.getSelection().forEach(function(sel) {
var row = sel.row;
- total += this.nheapFilter_.getValue(row, this.COL_TOTAL);
+ total_allocated += this.nheapFilter_.getValue(row, this.COL_TOTAL);
+ total_resident += this.nheapFilter_.getValue(row, this.COL_RESIDENT);
}, this);
- $('#nheap-selected').val(Math.floor(total / 1024) + ' KB');
+ $('#nheap-selected-allocated').val(Math.floor(total_allocated / 1024) +
+ ' KB');
+ $('#nheap-selected-resident').val(Math.floor(total_resident / 1024) + ' KB');
};
this.redraw = function() {
diff --git a/tools/memory_inspector/memory_inspector/frontends/www_server.py b/tools/memory_inspector/memory_inspector/frontends/www_server.py
index d8054f2..b5f8efc 100644
--- a/tools/memory_inspector/memory_inspector/frontends/www_server.py
+++ b/tools/memory_inspector/memory_inspector/frontends/www_server.py
@@ -603,7 +603,7 @@ def _LoadNheapFromStorage(args, req_vars):
resp['rows'] += [{'c': [
{'v': alloc.size, 'f': _StrMem(alloc.size)},
- {'v': 0, 'f': 0}, # TODO(primiano): support resident_size (next CLs).
+ {'v': alloc.resident_size, 'f': _StrMem(alloc.resident_size)},
{'v': alloc.flags, 'f': None},
{'v': strace, 'f': None},
]}]