1a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch#!/usr/bin/env python 2a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch# 3a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch# Copyright 2013 The Chromium Authors. All rights reserved. 4a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch# Use of this source code is governed by a BSD-style license that can be 5a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch# found in the LICENSE file. 6a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch 7a3f7b4e666c476898878fa745f637129375cd889Ben Murdochimport base64 8a3f7b4e666c476898878fa745f637129375cd889Ben Murdochimport os 9a3f7b4e666c476898878fa745f637129375cd889Ben Murdochimport sys 10a3f7b4e666c476898878fa745f637129375cd889Ben Murdochimport re 11a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch 12a3f7b4e666c476898878fa745f637129375cd889Ben Murdochfrom optparse import OptionParser 13a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch 14a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch"""Extracts the list of resident symbols of a library loaded in a process. 15a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch 16a3f7b4e666c476898878fa745f637129375cd889Ben MurdochThis scripts combines the extended output of memdump for a given process 17a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch(obtained through memdump -x PID) and the symbol table of a .so loaded in that 18a3f7b4e666c476898878fa745f637129375cd889Ben Murdochprocess (obtained through nm -C lib-with-symbols.so), filtering out only those 19a3f7b4e666c476898878fa745f637129375cd889Ben Murdochsymbols that, at the time of the snapshot, were resident in memory (that are, 20a3f7b4e666c476898878fa745f637129375cd889Ben Murdochthe symbols which start address belongs to a mapped page of the .so which was 21a3f7b4e666c476898878fa745f637129375cd889Ben Murdochresident at the time of the snapshot). 22a3f7b4e666c476898878fa745f637129375cd889Ben MurdochThe aim is to perform a "code coverage"-like profiling of a binary, intersecting 23a3f7b4e666c476898878fa745f637129375cd889Ben Murdochrun-time information (list of resident pages) and debug symbols. 24a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch""" 25a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch 26a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch_PAGE_SIZE = 4096 27a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch 28a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch 29a3f7b4e666c476898878fa745f637129375cd889Ben Murdochdef _TestBit(word, bit): 30a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch assert(bit >= 0 and bit < 8) 31a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch return not not ((word >> bit) & 1) 32a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch 33a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch 34a3f7b4e666c476898878fa745f637129375cd889Ben Murdochdef _HexAddr(addr): 35a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch return hex(addr)[2:].zfill(8) 36a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch 37a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch 38a3f7b4e666c476898878fa745f637129375cd889Ben Murdochdef _GetResidentPagesSet(memdump_contents, lib_name, verbose): 39a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch """Parses the memdump output and extracts the resident page set for lib_name. 40a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch Args: 41a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch memdump_contents: Array of strings (lines) of a memdump output. 42a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch lib_name: A string containing the name of the library.so to be matched. 43a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch verbose: Print a verbose header for each mapping matched. 44a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch 45a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch Returns: 46a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch A set of resident pages (the key is the page index) for all the 47a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch mappings matching .*lib_name. 48a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch """ 49a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch resident_pages = set() 50a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch MAP_RX = re.compile( 51a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch r'^([0-9a-f]+)-([0-9a-f]+) ([\w-]+) ([0-9a-f]+) .* "(.*)" \[(.*)\]$') 52a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch for line in memdump_contents: 53a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch line = line.rstrip('\r\n') 54a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch if line.startswith('[ PID'): 55a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch continue 56a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch 57a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch r = MAP_RX.match(line) 58a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch if not r: 59a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch sys.stderr.write('Skipping %s from %s\n' % (line, memdump_file)) 60a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch continue 61a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch 62a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch map_start = int(r.group(1), 16) 63a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch map_end = int(r.group(2), 16) 64a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch prot = r.group(3) 65a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch offset = int(r.group(4), 16) 66a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch assert(offset % _PAGE_SIZE == 0) 67a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch lib = r.group(5) 68a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch enc_bitmap = r.group(6) 69a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch 70a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch if not lib.endswith(lib_name): 71a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch continue 72a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch 73a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch bitmap = base64.b64decode(enc_bitmap) 74a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch map_pages_count = (map_end - map_start + 1) / _PAGE_SIZE 75a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch bitmap_pages_count = len(bitmap) * 8 76a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch 77a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch if verbose: 78a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch print 'Found %s: mapped %d pages in mode %s @ offset %s.' % ( 79a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch lib, map_pages_count, prot, _HexAddr(offset)) 80a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch print ' Map range in the process VA: [%s - %s]. Len: %s' % ( 81a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch _HexAddr(map_start), 82a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch _HexAddr(map_end), 83a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch _HexAddr(map_pages_count * _PAGE_SIZE)) 84a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch print ' Corresponding addresses in the binary: [%s - %s]. Len: %s' % ( 85a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch _HexAddr(offset), 86a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch _HexAddr(offset + map_end - map_start), 87a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch _HexAddr(map_pages_count * _PAGE_SIZE)) 88a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch print ' Bitmap: %d pages' % bitmap_pages_count 89a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch print '' 90a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch 91a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch assert(bitmap_pages_count >= map_pages_count) 92a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch for i in xrange(map_pages_count): 93a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch bitmap_idx = i / 8 94a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch bitmap_off = i % 8 95a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch if (bitmap_idx < len(bitmap) and 96a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch _TestBit(ord(bitmap[bitmap_idx]), bitmap_off)): 97a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch resident_pages.add(offset / _PAGE_SIZE + i) 98a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch return resident_pages 99a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch 100a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch 101a3f7b4e666c476898878fa745f637129375cd889Ben Murdochdef main(argv): 102a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch NM_RX = re.compile(r'^([0-9a-f]+)\s+.*$') 103a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch 104a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch parser = OptionParser() 105a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch parser.add_option("-r", "--reverse", 106a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch action="store_true", dest="reverse", default=False, 107a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch help="Print out non present symbols") 108a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch parser.add_option("-v", "--verbose", 109a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch action="store_true", dest="verbose", default=False, 110a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch help="Print out verbose debug information.") 111a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch 112a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch (options, args) = parser.parse_args() 113a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch 114a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch if len(args) != 3: 115a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch print 'Usage: %s [-v] memdump.file nm.file library.so' % ( 116a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch os.path.basename(argv[0])) 117a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch return 1 118a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch 119a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch memdump_file = args[0] 120a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch nm_file = args[1] 121a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch lib_name = args[2] 122a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch 123a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch if memdump_file == '-': 124a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch memdump_contents = sys.stdin.readlines() 125a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch else: 126a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch memdump_contents = open(memdump_file, 'r').readlines() 127a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch resident_pages = _GetResidentPagesSet(memdump_contents, 128a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch lib_name, 129a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch options.verbose) 130a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch 131a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch # Process the nm symbol table, filtering out the resident symbols. 132a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch nm_fh = open(nm_file, 'r') 133a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch for line in nm_fh: 134a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch line = line.rstrip('\r\n') 135a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch # Skip undefined symbols (lines with no address). 136a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch if line.startswith(' '): 137a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch continue 138a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch 139a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch r = NM_RX.match(line) 140a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch if not r: 141a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch sys.stderr.write('Skipping %s from %s\n' % (line, nm_file)) 142a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch continue 143a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch 144a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch sym_addr = int(r.group(1), 16) 145a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch sym_page = sym_addr / _PAGE_SIZE 146a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch last_sym_matched = (sym_page in resident_pages) 147a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch if (sym_page in resident_pages) != options.reverse: 148a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch print line 149a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch return 0 150a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch 151a3f7b4e666c476898878fa745f637129375cd889Ben Murdochif __name__ == '__main__': 152a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch sys.exit(main(sys.argv)) 153