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

Unified Diff: tools/memory_inspector/memory_inspector/core/native_heap.py

Issue 563183003: [Android] memory_inspector: add resident memory accounting. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@mi4
Patch Set: Created 6 years, 3 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: tools/memory_inspector/memory_inspector/core/native_heap.py
diff --git a/tools/memory_inspector/memory_inspector/core/native_heap.py b/tools/memory_inspector/memory_inspector/core/native_heap.py
index ab52a3927a0e86d721f919c9a76d3b898c799498..4a5537b7a629a425fecd8ea7ae0d9d8c7bbac9f9 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):

Powered by Google App Engine
This is Rietveld 408576698