forked from chromium/chromium
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbitmap_to_reached.py
executable file
·136 lines (117 loc) · 4.41 KB
/
bitmap_to_reached.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
#!/usr/bin/env python
#
# Copyright 2018 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.
"""From several dumps of reached offsets, create the list of reached symbols."""
import argparse
import logging
import os
import struct
import sys
_SRC_PATH = os.path.abspath(os.path.join(
os.path.dirname(__file__), os.pardir, os.pardir))
path = os.path.join(_SRC_PATH, 'tools', 'cygprofile')
sys.path.append(path)
import process_profiles
SIZEOF_INT = 4
BITS_IN_INT = SIZEOF_INT * 8
# This is the number of bytes represented in a dump by one bit.
BYTES_GRANULARITY = 4
def _DumpToOffsets(filename):
"""From a dump, returns a list of offsets in it.
Args:
filename: (str) Dump filename.
Returns:
([int]) offsets in the dump, that is relative to .text start.
"""
bitfield = None
offsets = []
with open(filename, 'r') as f:
bitfield = f.read()
assert len(bitfield) % SIZEOF_INT == 0
count = len(bitfield) / SIZEOF_INT
for i in xrange(count):
entry = struct.unpack_from('<I', bitfield, offset=i * SIZEOF_INT)[0]
for bit in range(BITS_IN_INT):
if entry & (1 << bit):
offsets.append((i * BITS_IN_INT + bit) * BYTES_GRANULARITY)
return offsets
def _ReachedSymbols(offsets, offset_to_symbol):
"""Returns a list of reached symbols from offsets in .text.
Args:
offsets: ([int]) List of reached offsets.
offset_to_symbol: [symbol_extractor.SymbolInfo or None] as returned by
|SymbolOffsetProcessor.GetDumpOffsetToSymbolInfo()|
Returns:
([symbol_extractor.SymbolInfo])
"""
symbol_infos = set()
missing = 0
for offset in offsets:
# |offset_to_symbol| has one entry per BYTES_GRANULARITY bytes.
index = offset / BYTES_GRANULARITY
if index > len(offset_to_symbol):
missing += 1
continue
symbol_infos.add(offset_to_symbol[index])
if missing:
logging.warning('Couldn\'t match %d symbols', missing)
return symbol_infos
def _CreateArgumentParser():
"""Returns an ArgumentParser."""
parser = argparse.ArgumentParser(description='Outputs reached symbols')
parser.add_argument('--build-dir', type=str,
help='Path to the build directory', required=True)
parser.add_argument('--dumps', type=str, help='A comma-separated list of '
'files with instrumentation dumps', required=False)
parser.add_argument('--dumps-dir', type=str, help='Directory name with'
'reached code dumps', required=False)
parser.add_argument('--library-name', default='libchrome.so',
help=('Chrome shared library name (usually libchrome.so '
'or libmonochrome.so'))
parser.add_argument('--output', required=True, help='Output filename')
return parser
def main():
logging.basicConfig(level=logging.INFO)
parser = _CreateArgumentParser()
args = parser.parse_args()
dumps = []
if args.dumps:
dumps.extend(args.dumps.split(','))
elif args.dumps_dir:
for file_name in os.listdir(args.dumps_dir):
dumps.append(os.path.join(args.dumps_dir, file_name))
else:
logging.error('Either --dumps or --dumps-dir must be provided')
parser.print_help()
return 1
logging.info('Parsing dumps')
offsets = set()
for dump_filename in dumps:
offsets |= set(_DumpToOffsets(dump_filename))
logging.info('Found %d reached locations', len(offsets))
library_path = os.path.join(args.build_dir, 'lib.unstripped',
args.library_name)
processor = process_profiles.SymbolOffsetProcessor(library_path)
logging.info('Finding Symbols')
offset_to_symbol = processor.GetDumpOffsetToSymbolInfo()
reached_symbol_infos = _ReachedSymbols(offsets, offset_to_symbol)
reached_symbol_infos.remove(None)
with open(args.output, 'w') as f:
for s in reached_symbol_infos:
f.write('%s\n' % s.name)
# Print some stats.
reached_size = sum(s.size for s in reached_symbol_infos)
logging.info('Total reached size = {}'.format(reached_size))
all_symbol_infos = set()
for i in offset_to_symbol:
if i is not None:
all_symbol_infos.add(i)
total_size = sum(s.size for s in all_symbol_infos)
logging.info('Total size of known symbols = {}'.format(total_size))
coverage_percent = float(reached_size) / total_size * 100;
logging.info('Coverage: {0:.2f}%'.format(coverage_percent))
if __name__ == '__main__':
main()
sys.exit(0)