Commit d7339a4b authored by primiano's avatar primiano Committed by Commit bot

[Android] Add 64-bit support to memdump (and memory_inspector parser)

This change makes memdump able to parse and print out 64 bit addresses.
No change is intended for the 32-bit output.
This also updates the memdump parser in memory_inspector to match the
memdump change and updates its prebuilts.

BUG=340294

Review URL: https://codereview.chromium.org/550093003

Cr-Commit-Position: refs/heads/master@{#293927}
parent ad25c603
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include "base/containers/hash_tables.h" #include "base/containers/hash_tables.h"
#include "base/file_util.h" #include "base/file_util.h"
#include "base/files/scoped_file.h" #include "base/files/scoped_file.h"
#include "base/format_macros.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/strings/string_number_conversions.h" #include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h" #include "base/strings/string_split.h"
...@@ -45,7 +46,16 @@ class BitSet { ...@@ -45,7 +46,16 @@ class BitSet {
} }
std::string AsB64String() const { std::string AsB64String() const {
std::string bits(&data_[0], data_.size()); // Simple optimization: strip trailing zero bytes from the bitmap.
// For instance, if a region has 32 pages but only the first 9 are resident,
// The full bitmap would be 0xff 0x01 0x00 0x00, the stripped one 0xff 0x01.
// It can save up to some seconds when printing large mmaps, in particular
// in presence of large virtual address space reservations (where none of
// the pages are resident).
size_t end = data_.size();
while (end > 0 && data_[end - 1] == '\0')
--end;
std::string bits(&data_[0], end);
std::string b64_string; std::string b64_string;
base::Base64Encode(bits, &b64_string); base::Base64Encode(bits, &b64_string);
return b64_string; return b64_string;
...@@ -79,9 +89,9 @@ struct PageCount { ...@@ -79,9 +89,9 @@ struct PageCount {
struct MemoryMap { struct MemoryMap {
std::string name; std::string name;
std::string flags; std::string flags;
uint start_address; uint64 start_address;
uint end_address; uint64 end_address;
uint offset; uint64 offset;
PageCount private_pages; PageCount private_pages;
// app_shared_pages[i] contains the number of pages mapped in i+2 processes // app_shared_pages[i] contains the number of pages mapped in i+2 processes
// (only among the processes that are being analyzed). // (only among the processes that are being analyzed).
...@@ -127,23 +137,20 @@ bool ParseMemoryMapLine(const std::string& line, ...@@ -127,23 +137,20 @@ bool ParseMemoryMapLine(const std::string& line,
const std::string& addr_range = tokens->at(0); const std::string& addr_range = tokens->at(0);
std::vector<std::string> range_tokens; std::vector<std::string> range_tokens;
base::SplitString(addr_range, '-', &range_tokens); base::SplitString(addr_range, '-', &range_tokens);
uint64 tmp = 0;
const std::string& start_address_token = range_tokens.at(0); const std::string& start_address_token = range_tokens.at(0);
if (!base::HexStringToUInt64(start_address_token, &tmp)) { if (!base::HexStringToUInt64(start_address_token,
&memory_map->start_address)) {
return false; return false;
} }
memory_map->start_address = static_cast<uint>(tmp);
const std::string& end_address_token = range_tokens.at(1); const std::string& end_address_token = range_tokens.at(1);
if (!base::HexStringToUInt64(end_address_token, &tmp)) { if (!base::HexStringToUInt64(end_address_token, &memory_map->end_address)) {
return false; return false;
} }
memory_map->end_address = static_cast<uint>(tmp);
if (tokens->at(1).size() != strlen("rwxp")) if (tokens->at(1).size() != strlen("rwxp"))
return false; return false;
memory_map->flags.swap(tokens->at(1)); memory_map->flags.swap(tokens->at(1));
if (!base::HexStringToUInt64(tokens->at(2), &tmp)) if (!base::HexStringToUInt64(tokens->at(2), &memory_map->offset))
return false; return false;
memory_map->offset = static_cast<uint>(tmp);
memory_map->committed_pages_bits.resize( memory_map->committed_pages_bits.resize(
(memory_map->end_address - memory_map->start_address) / kPageSize); (memory_map->end_address - memory_map->start_address) / kPageSize);
const int map_name_index = 5; const int map_name_index = 5;
...@@ -206,7 +213,7 @@ bool GetPagesForMemoryMap(int pagemap_fd, ...@@ -206,7 +213,7 @@ bool GetPagesForMemoryMap(int pagemap_fd,
PLOG(ERROR) << "lseek"; PLOG(ERROR) << "lseek";
return false; return false;
} }
for (uint addr = memory_map.start_address, page_index = 0; for (uint64 addr = memory_map.start_address, page_index = 0;
addr < memory_map.end_address; addr < memory_map.end_address;
addr += kPageSize, ++page_index) { addr += kPageSize, ++page_index) {
DCHECK_EQ(0, addr % kPageSize); DCHECK_EQ(0, addr % kPageSize);
...@@ -415,8 +422,9 @@ void DumpProcessesMemoryMapsInExtendedFormat( ...@@ -415,8 +422,9 @@ void DumpProcessesMemoryMapsInExtendedFormat(
AppendAppSharedField(memory_map.app_shared_pages, &app_shared_buf); AppendAppSharedField(memory_map.app_shared_pages, &app_shared_buf);
base::SStringPrintf( base::SStringPrintf(
&buf, &buf,
"%x-%x %s %x private_unevictable=%d private=%d shared_app=%s " "%"PRIx64"-%"PRIx64" %s %"PRIx64" private_unevictable=%d private=%d "
"shared_other_unevictable=%d shared_other=%d \"%s\" [%s]\n", "shared_app=%s shared_other_unevictable=%d shared_other=%d "
"\"%s\" [%s]\n",
memory_map.start_address, memory_map.start_address,
memory_map.end_address, memory_map.end_address,
memory_map.flags.c_str(), memory_map.flags.c_str(),
......
...@@ -34,10 +34,11 @@ _MOCK_MEMDUMP_OUT = ("""[ PID=1] ...@@ -34,10 +34,11 @@ _MOCK_MEMDUMP_OUT = ("""[ PID=1]
"""shared_other_unevictable=0 shared_other=0 "" [AQ==] """shared_other_unevictable=0 shared_other=0 "" [AQ==]
40127000-40147000 rw-s 0 private_unevictable=4096 private=4096 shared_app=[] """ 40127000-40147000 rw-s 0 private_unevictable=4096 private=4096 shared_app=[] """
"""shared_other_unevictable=32768 shared_other=32768 "/X" [/////w==] """shared_other_unevictable=32768 shared_other=32768 "/X" [/////w==]
be8b7000-be8d8000 rw-p 0 private_unevictable=8192 private=8192 shared_app=[] """ be8b7000-be8d8000 rw-p 8 private_unevictable=8192 private=8192 shared_app=[] """
"""shared_other_unevictable=4096 shared_other=12288 "[stack]" [AAAA4AE=] """shared_other_unevictable=4096 shared_other=12288 "[stack]" [//8=]
ffff0000-ffff1000 r-xp 0 private_unevictable=0 private=0 shared_app=[] """ ffff00ffff0000-ffff00ffff1000 r-xp 0 private_unevictable=0 private=0 """
"""shared_other_unevictable=0 shared_other=0 "[vectors]" [AA==]""") """shared_app=[] shared_other_unevictable=0 shared_other=0 "[vectors]" []"""
)
_MOCK_DUMPHEAP_OUT = """Android Native Heap Dump v1.0 _MOCK_DUMPHEAP_OUT = """Android Native Heap Dump v1.0
...@@ -137,21 +138,23 @@ class AndroidBackendTest(unittest.TestCase): ...@@ -137,21 +138,23 @@ class AndroidBackendTest(unittest.TestCase):
self.assertIsNotNone(mmaps.Lookup(0x32FFF)) self.assertIsNotNone(mmaps.Lookup(0x32FFF))
self.assertIsNotNone(mmaps.Lookup(0x33000)) self.assertIsNotNone(mmaps.Lookup(0x33000))
self.assertIsNone(mmaps.Lookup(0x6d000)) self.assertIsNone(mmaps.Lookup(0x6d000))
self.assertIsNotNone(mmaps.Lookup(0xffff0000)) self.assertIsNotNone(mmaps.Lookup(0xffff00ffff0000))
self.assertIsNone(mmaps.Lookup(0xffff0000))
entry = mmaps.Lookup(0xbe8b7000) entry = mmaps.Lookup(0xbe8b7000)
self.assertIsNotNone(entry) self.assertIsNotNone(entry)
self.assertEqual(entry.start, 0xbe8b7000) self.assertEqual(entry.start, 0xbe8b7000)
self.assertEqual(entry.end, 0xbe8d7fff) self.assertEqual(entry.end, 0xbe8d7fff)
self.assertEqual(entry.mapped_offset, 0x8)
self.assertEqual(entry.prot_flags, 'rw-p') self.assertEqual(entry.prot_flags, 'rw-p')
self.assertEqual(entry.priv_dirty_bytes, 8192) self.assertEqual(entry.priv_dirty_bytes, 8192)
self.assertEqual(entry.priv_clean_bytes, 0) self.assertEqual(entry.priv_clean_bytes, 0)
self.assertEqual(entry.shared_dirty_bytes, 4096) self.assertEqual(entry.shared_dirty_bytes, 4096)
self.assertEqual(entry.shared_clean_bytes, 8192) self.assertEqual(entry.shared_clean_bytes, 8192)
for i in xrange(29): for i in xrange(16):
self.assertFalse(entry.IsPageResident(i))
for i in xrange(29, 33):
self.assertTrue(entry.IsPageResident(i)) self.assertTrue(entry.IsPageResident(i))
for i in xrange(16, 33):
self.assertFalse(entry.IsPageResident(i))
# Test dumpheap parsing. # Test dumpheap parsing.
heap = processes[0].DumpNativeHeap() heap = processes[0].DumpNativeHeap()
......
...@@ -42,7 +42,8 @@ def Parse(lines): ...@@ -42,7 +42,8 @@ def Parse(lines):
An instance of |memory_map.Map|. An instance of |memory_map.Map|.
""" """
RE = (r'^([0-9a-f]+)-([0-9a-f]+)\s+' RE = (r'^([0-9a-f]+)-([0-9a-f]+)\s+'
r'([rwxps-]{4})\s*.*?' r'([rwxps-]{4})\s+'
r'([0-9a-f]+)\s+'
r'private_unevictable=(\d+) private=(\d+) ' r'private_unevictable=(\d+) private=(\d+) '
r'shared_app=(.*?) ' r'shared_app=(.*?) '
r'shared_other_unevictable=(\d+) shared_other=(\d+) ' r'shared_other_unevictable=(\d+) shared_other=(\d+) '
...@@ -64,20 +65,23 @@ def Parse(lines): ...@@ -64,20 +65,23 @@ def Parse(lines):
logging.warning('Skipping unrecognized memdump line "%s"' % line) logging.warning('Skipping unrecognized memdump line "%s"' % line)
continue continue
# TODO(primiano): proper offset handling requires a change in memdump. In start = int(m.group(1), 16)
# the meanwhile, it should pretty safe assuming zero-offset for libs (for end = int(m.group(2), 16) - 1 # end addr is inclusive in memdump output.
# symbolization). Also, offsets for other mappings don't really matter. if (start > end):
# Sadly, this actually happened. Probably a kernel bug, see b/17402069.
logging.warning('Skipping unfeasible mmap "%s"' % line)
continue
entry = memory_map.MapEntry( entry = memory_map.MapEntry(
start=int(m.group(1), 16), start=start,
end=int(m.group(2), 16) - 1, # end addr is inclusive in memdump output. end=end,
prot_flags=m.group(3), prot_flags=m.group(3),
mapped_file=m.group(9), mapped_file=m.group(10),
mapped_offset=0) mapped_offset=int(m.group(4), 16))
entry.priv_dirty_bytes = int(m.group(4)) entry.priv_dirty_bytes = int(m.group(5))
entry.priv_clean_bytes = int(m.group(5)) - entry.priv_dirty_bytes entry.priv_clean_bytes = int(m.group(6)) - entry.priv_dirty_bytes
entry.shared_dirty_bytes = int(m.group(7)) entry.shared_dirty_bytes = int(m.group(8))
entry.shared_clean_bytes = int(m.group(8)) - entry.shared_dirty_bytes entry.shared_clean_bytes = int(m.group(9)) - entry.shared_dirty_bytes
entry.resident_pages = [ord(c) for c in base64.b64decode(m.group(10))] entry.resident_pages = [ord(c) for c in base64.b64decode(m.group(11))]
maps.Add(entry) maps.Add(entry)
return maps return maps
...@@ -5,13 +5,16 @@ ...@@ -5,13 +5,16 @@
import bisect import bisect
PAGE_SIZE = 4096
class Map(object): class Map(object):
"""Models the memory map of a given |backends.Process|. """Models the memory map of a given |backends.Process|.
This is typically obtained by calling backends.Process.DumpMemoryMaps().""" This is typically obtained by calling backends.Process.DumpMemoryMaps()."""
def __init__(self): def __init__(self):
self.entries = [] self.entries = [] # List of |MapEntry|, sorted by start address.
def Add(self, entry): def Add(self, entry):
assert(isinstance(entry, MapEntry)) assert(isinstance(entry, MapEntry))
...@@ -39,7 +42,6 @@ class Map(object): ...@@ -39,7 +42,6 @@ class Map(object):
class MapEntry(object): class MapEntry(object):
"""An entry (address range + stats) in a memory |Map|.""" """An entry (address range + stats) in a memory |Map|."""
PAGE_SIZE = 4096
def __init__(self, start, end, prot_flags, mapped_file, mapped_offset, def __init__(self, start, end, prot_flags, mapped_file, mapped_offset,
priv_dirty_bytes=0, priv_clean_bytes=0, shared_dirty_bytes=0, priv_dirty_bytes=0, priv_clean_bytes=0, shared_dirty_bytes=0,
...@@ -67,17 +69,19 @@ class MapEntry(object): ...@@ -67,17 +69,19 @@ class MapEntry(object):
def IsPageResident(self, relative_page_index): def IsPageResident(self, relative_page_index):
"""Checks whether a given memory page is resident in memory.""" """Checks whether a given memory page is resident in memory."""
assert(relative_page_index >= 0 and assert(relative_page_index >= 0 and
relative_page_index < self.len / MapEntry.PAGE_SIZE) relative_page_index < self.len / PAGE_SIZE)
assert(len(self.resident_pages) * MapEntry.PAGE_SIZE * 8 >= self.len)
arr_idx = relative_page_index / 8 arr_idx = relative_page_index / 8
arr_bit = relative_page_index % 8 arr_bit = relative_page_index % 8
# Trailing zeros are trimmed by memdump (to optimize dump time).
if arr_idx >= len(self.resident_pages):
return False
return (self.resident_pages[arr_idx] & (1 << arr_bit)) != 0 return (self.resident_pages[arr_idx] & (1 << arr_bit)) != 0
def __cmp__(self, other): def __cmp__(self, other):
"""Comparison operator required for bisect.""" """Comparison operator required for bisect."""
if isinstance(other, MapEntry): if isinstance(other, MapEntry):
return self.start - other.start return self.start - other.start
elif isinstance(other, int): elif isinstance(other, (long, int)):
return self.start - other return self.start - other
else: else:
raise Exception('Cannot compare with %s' % other.__class__) raise Exception('Cannot compare with %s' % other.__class__)
...@@ -85,3 +89,8 @@ class MapEntry(object): ...@@ -85,3 +89,8 @@ class MapEntry(object):
@property @property
def len(self): def len(self):
return self.end - self.start + 1 return self.end - self.start + 1
@property
def rss_bytes(self):
return (self.priv_dirty_bytes + self.priv_clean_bytes +
self.shared_dirty_bytes + self.shared_clean_bytes)
...@@ -37,7 +37,7 @@ class Frame(object): ...@@ -37,7 +37,7 @@ class Frame(object):
address: the absolute (virtual) address of the stack frame in the address: the absolute (virtual) address of the stack frame in the
original process virtual address space. original process virtual address space.
""" """
assert(isinstance(address, int)) assert(isinstance(address, (long, int)))
self.address = address self.address = address
self.symbol = None self.symbol = None
self.exec_file_rel_path = None self.exec_file_rel_path = None
...@@ -54,7 +54,7 @@ class Frame(object): ...@@ -54,7 +54,7 @@ class Frame(object):
relative to the target device (e.g., /system/lib/libc.so). relative to the target device (e.g., /system/lib/libc.so).
offset: the offset in the executable. offset: the offset in the executable.
""" """
assert(isinstance(offset, int)) assert(isinstance(offset, (long, int)))
self.exec_file_rel_path = exec_file_rel_path self.exec_file_rel_path = exec_file_rel_path
self.offset = offset self.offset = offset
......
...@@ -114,7 +114,7 @@ class FileStorageTest(unittest.TestCase): ...@@ -114,7 +114,7 @@ class FileStorageTest(unittest.TestCase):
if a is None: if a is None:
return return
_BASICTYPES = (int, basestring, float) _BASICTYPES = (long, int, basestring, float)
if isinstance(a, _BASICTYPES) and isinstance(b, _BASICTYPES): if isinstance(a, _BASICTYPES) and isinstance(b, _BASICTYPES):
return self.assertEqual(a, b, prefix) return self.assertEqual(a, b, prefix)
......
43e7d83da69a418d6fe6a72dbed168ddd9748aef e9576bce3acbb3475a99ffa4624bc7eecbf2988d
\ No newline at end of file \ No newline at end of file
a91c8177cef1ff9c9064dbe4f9cf2eb8fb40e174
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment