| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 | 5 |
| 6 """The deep heap profiler script for Chrome.""" | 6 """The deep heap profiler script for Chrome.""" |
| 7 | 7 |
| 8 from datetime import datetime | 8 from datetime import datetime |
| 9 import json | 9 import json |
| 10 import optparse | 10 import optparse |
| 11 import os | 11 import os |
| 12 import re | 12 import re |
| 13 import shutil | 13 import shutil |
| 14 import subprocess | 14 import subprocess |
| 15 import sys | 15 import sys |
| 16 import tempfile | 16 import tempfile |
| 17 | 17 |
| 18 FIND_RUNTIME_SYMBOLS_PATH = os.path.join( | 18 FIND_RUNTIME_SYMBOLS_PATH = os.path.join( |
| 19 os.path.dirname(os.path.abspath(__file__)), | 19 os.path.dirname(os.path.abspath(__file__)), |
| 20 os.pardir, | 20 os.pardir, |
| 21 'find_runtime_symbols') | 21 'find_runtime_symbols') |
| 22 sys.path.append(FIND_RUNTIME_SYMBOLS_PATH) | 22 sys.path.append(FIND_RUNTIME_SYMBOLS_PATH) |
| 23 | 23 |
| 24 from find_runtime_symbols import find_runtime_symbols_list | 24 from find_runtime_symbols import find_runtime_symbols_list |
| 25 from find_runtime_symbols import find_runtime_typeinfo_symbols_list |
| 26 from find_runtime_symbols import RuntimeSymbolsInProcess |
| 25 from prepare_symbol_info import prepare_symbol_info | 27 from prepare_symbol_info import prepare_symbol_info |
| 26 from static_symbols import StaticSymbols | |
| 27 | 28 |
| 28 BUCKET_ID = 5 | 29 BUCKET_ID = 5 |
| 29 VIRTUAL = 0 | 30 VIRTUAL = 0 |
| 30 COMMITTED = 1 | 31 COMMITTED = 1 |
| 31 ALLOC_COUNT = 2 | 32 ALLOC_COUNT = 2 |
| 32 FREE_COUNT = 3 | 33 FREE_COUNT = 3 |
| 33 NULL_REGEX = re.compile('') | 34 NULL_REGEX = re.compile('') |
| 34 | 35 |
| 35 POLICIES_JSON_PATH = os.path.join( | 36 POLICIES_JSON_PATH = os.path.join( |
| 36 os.path.dirname(os.path.abspath(__file__)), | 37 os.path.dirname(os.path.abspath(__file__)), |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 74 # mmap regions are distincted w/ mmap frames in the pattern column. | 75 # mmap regions are distincted w/ mmap frames in the pattern column. |
| 75 POLICY_DEEP_1 = 'POLICY_DEEP_1' | 76 POLICY_DEEP_1 = 'POLICY_DEEP_1' |
| 76 | 77 |
| 77 # POLICY_DEEP_2 DOES include allocation_type columns. | 78 # POLICY_DEEP_2 DOES include allocation_type columns. |
| 78 # mmap regions are distincted w/ the allocation_type column. | 79 # mmap regions are distincted w/ the allocation_type column. |
| 79 POLICY_DEEP_2 = 'POLICY_DEEP_2' | 80 POLICY_DEEP_2 = 'POLICY_DEEP_2' |
| 80 | 81 |
| 81 # POLICY_DEEP_3 is in JSON format. | 82 # POLICY_DEEP_3 is in JSON format. |
| 82 POLICY_DEEP_3 = 'POLICY_DEEP_3' | 83 POLICY_DEEP_3 = 'POLICY_DEEP_3' |
| 83 | 84 |
| 85 # POLICY_DEEP_3 contains typeinfo. |
| 86 POLICY_DEEP_4 = 'POLICY_DEEP_4' |
| 87 |
| 84 | 88 |
| 85 class EmptyDumpException(Exception): | 89 class EmptyDumpException(Exception): |
| 86 def __init__(self, value): | 90 def __init__(self, value): |
| 87 self.value = value | 91 self.value = value |
| 88 def __str__(self): | 92 def __str__(self): |
| 89 return repr(self.value) | 93 return repr(self.value) |
| 90 | 94 |
| 91 | 95 |
| 92 class ParsingException(Exception): | 96 class ParsingException(Exception): |
| 93 def __init__(self, value): | 97 def __init__(self, value): |
| (...skipping 18 matching lines...) Expand all Loading... |
| 112 | 116 |
| 113 class DelayedStaticSymbols(object): | 117 class DelayedStaticSymbols(object): |
| 114 """Represents static symbol information loaded lazily.""" | 118 """Represents static symbol information loaded lazily.""" |
| 115 | 119 |
| 116 def __init__(self, prefix, keep=False): | 120 def __init__(self, prefix, keep=False): |
| 117 self.maps_path = prefix + '.maps' | 121 self.maps_path = prefix + '.maps' |
| 118 self.keep = keep | 122 self.keep = keep |
| 119 if keep: | 123 if keep: |
| 120 self.prepared_data_dir = prefix + '.pre' | 124 self.prepared_data_dir = prefix + '.pre' |
| 121 self.loaded_static_symbols = None | 125 self.loaded_static_symbols = None |
| 126 self.loaded_symbols_in_process = None |
| 122 | 127 |
| 123 def get(self): | 128 def get(self): |
| 124 if not self.loaded_static_symbols: | 129 if not self.loaded_symbols_in_process: |
| 125 if not self.keep: | 130 if not self.keep: |
| 126 self.prepared_data_dir = tempfile.mkdtemp() | 131 self.prepared_data_dir = tempfile.mkdtemp() |
| 127 try: | 132 try: |
| 128 prepare_symbol_info(self.maps_path, self.prepared_data_dir) | 133 prepare_symbol_info(self.maps_path, self.prepared_data_dir) |
| 129 self.loaded_static_symbols = StaticSymbols.load(self.prepared_data_dir) | 134 self.loaded_symbols_in_process = RuntimeSymbolsInProcess.load( |
| 135 self.prepared_data_dir) |
| 130 finally: | 136 finally: |
| 131 if not self.keep: | 137 if not self.keep: |
| 132 shutil.rmtree(self.prepared_data_dir) | 138 shutil.rmtree(self.prepared_data_dir) |
| 133 return self.loaded_static_symbols | 139 return self.loaded_symbols_in_process |
| 134 | 140 |
| 135 | 141 |
| 136 class Rule(object): | 142 class Rule(object): |
| 137 """Represents one matching rule in a policy file.""" | 143 """Represents one matching rule in a policy file.""" |
| 138 | 144 |
| 139 def __init__(self, name, mmap, stacktrace_pattern): | 145 def __init__(self, name, mmap, stacktrace_pattern, typeinfo_pattern=None): |
| 140 self.name = name | 146 self.name = name |
| 141 self.mmap = mmap | 147 self.mmap = mmap |
| 142 self.stacktrace_pattern = re.compile(stacktrace_pattern + r'\Z') | 148 self.stacktrace_pattern = re.compile(stacktrace_pattern + r'\Z') |
| 149 if typeinfo_pattern: |
| 150 self.typeinfo_pattern = re.compile(typeinfo_pattern + r'\Z') |
| 151 else: |
| 152 self.typeinfo_pattern = None |
| 143 | 153 |
| 144 | 154 |
| 145 class Policy(object): | 155 class Policy(object): |
| 146 """Represents a policy, a content of a policy file.""" | 156 """Represents a policy, a content of a policy file.""" |
| 147 | 157 |
| 148 def __init__(self, rules, version, components): | 158 def __init__(self, rules, version, components): |
| 149 self.rules = rules | 159 self.rules = rules |
| 150 self.version = version | 160 self.version = version |
| 151 self.components = components | 161 self.components = components |
| 152 | 162 |
| (...skipping 11 matching lines...) Expand all Loading... |
| 164 | 174 |
| 165 Returns: | 175 Returns: |
| 166 A string representing a component name. | 176 A string representing a component name. |
| 167 """ | 177 """ |
| 168 if not bucket: | 178 if not bucket: |
| 169 return 'no-bucket' | 179 return 'no-bucket' |
| 170 if bucket.component_cache: | 180 if bucket.component_cache: |
| 171 return bucket.component_cache | 181 return bucket.component_cache |
| 172 | 182 |
| 173 stacktrace = ''.join(symbols[a] + ' ' for a in bucket.stacktrace).strip() | 183 stacktrace = ''.join(symbols[a] + ' ' for a in bucket.stacktrace).strip() |
| 184 typeinfo = bucket.typeinfo_symbol |
| 185 if typeinfo.startswith('0x'): |
| 186 typeinfo = bucket.typename |
| 174 | 187 |
| 175 for rule in rule_list: | 188 for rule in rule_list: |
| 176 if bucket.mmap == rule.mmap and rule.stacktrace_pattern.match(stacktrace): | 189 if (bucket.mmap == rule.mmap and |
| 190 rule.stacktrace_pattern.match(stacktrace) and |
| 191 (not rule.typeinfo_pattern or rule.typeinfo_pattern.match(typeinfo))): |
| 177 bucket.component_cache = rule.name | 192 bucket.component_cache = rule.name |
| 178 return rule.name | 193 return rule.name |
| 179 | 194 |
| 180 assert False | 195 assert False |
| 181 | 196 |
| 182 | 197 |
| 183 class Bucket(object): | 198 class Bucket(object): |
| 184 """Represents a bucket, which is a unit of memory classification.""" | 199 """Represents a bucket, which is a unit of memory classification.""" |
| 185 | 200 |
| 186 def __init__(self, stacktrace, mmap): | 201 def __init__(self, stacktrace, mmap, typeinfo, typename): |
| 187 self.stacktrace = stacktrace | 202 self.stacktrace = stacktrace |
| 188 self.mmap = mmap | 203 self.mmap = mmap |
| 204 self.typeinfo = typeinfo |
| 205 self.typeinfo_symbol = typename |
| 206 self.typename = typename |
| 189 self.component_cache = '' | 207 self.component_cache = '' |
| 190 | 208 |
| 191 def clear_component_cache(self): | 209 def clear_component_cache(self): |
| 192 self.component_cache = '' | 210 self.component_cache = '' |
| 193 | 211 |
| 194 | 212 |
| 195 class Dump(object): | 213 class Dump(object): |
| 196 """Represents one heap profile dump.""" | 214 """Represents one heap profile dump.""" |
| 197 | 215 |
| 198 def __init__(self, dump_path): | 216 def __init__(self, dump_path): |
| (...skipping 357 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 556 sizes['hour'] = (self.dump_time - first_dump_time) / 60.0 / 60.0 | 574 sizes['hour'] = (self.dump_time - first_dump_time) / 60.0 / 60.0 |
| 557 if 'minute' in sizes: | 575 if 'minute' in sizes: |
| 558 sizes['minute'] = (self.dump_time - first_dump_time) / 60.0 | 576 sizes['minute'] = (self.dump_time - first_dump_time) / 60.0 |
| 559 if 'second' in sizes: | 577 if 'second' in sizes: |
| 560 sizes['second'] = self.dump_time - first_dump_time | 578 sizes['second'] = self.dump_time - first_dump_time |
| 561 | 579 |
| 562 return sizes | 580 return sizes |
| 563 | 581 |
| 564 @staticmethod | 582 @staticmethod |
| 565 def accumulate_size_for_expand(stacktrace_lines, rule_list, buckets, | 583 def accumulate_size_for_expand(stacktrace_lines, rule_list, buckets, |
| 566 component_name, depth, sizes, symbols): | 584 component_name, depth, sizes, symbols, |
| 585 typeinfo_symbols): |
| 567 for line in stacktrace_lines: | 586 for line in stacktrace_lines: |
| 568 words = line.split() | 587 words = line.split() |
| 569 bucket = buckets.get(int(words[BUCKET_ID])) | 588 bucket = buckets.get(int(words[BUCKET_ID])) |
| 570 component_match = get_component(rule_list, bucket, symbols) | 589 component_match = get_component(rule_list, bucket, symbols) |
| 571 if component_match == component_name: | 590 if component_match == component_name: |
| 572 stacktrace_sequence = '' | 591 stacktrace_sequence = '' |
| 592 if bucket.typeinfo: |
| 593 stacktrace_sequence += '(type=%s)' % typeinfo_symbols[bucket.typeinfo] |
| 594 stacktrace_sequence += ' (type.name=%s) ' % bucket.typename |
| 573 for address in bucket.stacktrace[0 : min(len(bucket.stacktrace), | 595 for address in bucket.stacktrace[0 : min(len(bucket.stacktrace), |
| 574 1 + depth)]: | 596 1 + depth)]: |
| 575 stacktrace_sequence += symbols[address] + ' ' | 597 stacktrace_sequence += symbols[address] + ' ' |
| 576 if not stacktrace_sequence in sizes: | 598 if not stacktrace_sequence in sizes: |
| 577 sizes[stacktrace_sequence] = 0 | 599 sizes[stacktrace_sequence] = 0 |
| 578 sizes[stacktrace_sequence] += int(words[COMMITTED]) | 600 sizes[stacktrace_sequence] += int(words[COMMITTED]) |
| 579 | 601 |
| 580 def expand(self, rule_list, buckets, component_name, depth, symbols): | 602 def expand( |
| 603 self, rule_list, buckets, component_name, depth, symbols, |
| 604 typeinfo_symbols): |
| 581 """Prints all stacktraces in a given component of given depth. | 605 """Prints all stacktraces in a given component of given depth. |
| 582 | 606 |
| 583 Args: | 607 Args: |
| 584 rule_list: A list of Rule objects. | 608 rule_list: A list of Rule objects. |
| 585 buckets: A dict mapping bucket ids to Bucket objects. | 609 buckets: A dict mapping bucket ids to Bucket objects. |
| 586 component_name: A name of component for filtering. | 610 component_name: A name of component for filtering. |
| 587 depth: An integer representing depth to be printed. | 611 depth: An integer representing depth to be printed. |
| 588 symbols: A dict mapping runtime addresses to symbol names. | 612 symbols: A dict mapping runtime addresses to symbol names. |
| 589 """ | 613 """ |
| 590 sizes = {} | 614 sizes = {} |
| 591 | 615 |
| 592 self.accumulate_size_for_expand( | 616 self.accumulate_size_for_expand( |
| 593 self.stacktrace_lines, rule_list, buckets, component_name, | 617 self.stacktrace_lines, rule_list, buckets, component_name, |
| 594 depth, sizes, symbols) | 618 depth, sizes, symbols, typeinfo_symbols) |
| 595 | 619 |
| 596 sorted_sizes_list = sorted( | 620 sorted_sizes_list = sorted( |
| 597 sizes.iteritems(), key=(lambda x: x[1]), reverse=True) | 621 sizes.iteritems(), key=(lambda x: x[1]), reverse=True) |
| 598 total = 0 | 622 total = 0 |
| 599 for size_pair in sorted_sizes_list: | 623 for size_pair in sorted_sizes_list: |
| 600 sys.stdout.write('%10d %s\n' % (size_pair[1], size_pair[0])) | 624 sys.stdout.write('%10d %s\n' % (size_pair[1], size_pair[0])) |
| 601 total += size_pair[1] | 625 total += size_pair[1] |
| 602 sys.stderr.write('total: %d\n' % (total)) | 626 sys.stderr.write('total: %d\n' % (total)) |
| 603 | 627 |
| 604 | 628 |
| 605 def update_symbols( | 629 def update_symbols( |
| 606 symbol_path, delayed_static_symbols, appeared_addresses, symbols): | 630 symbol_path, delayed_static_symbols, appeared_addresses, |
| 631 parameter_find_runtime_symbols_list, symbols): |
| 607 """Updates address/symbol mapping on memory and in a .symbol cache file. | 632 """Updates address/symbol mapping on memory and in a .symbol cache file. |
| 608 | 633 |
| 609 It reads cached address/symbol mapping from a .symbol file if it exists. | 634 It reads cached address/symbol mapping from a .symbol file if it exists. |
| 610 Then, it resolves unresolved addresses from a Chrome binary with pprof. | 635 Then, it resolves unresolved addresses from a Chrome binary with pprof. |
| 611 Both mappings on memory and in a .symbol cache file are updated. | 636 Both mappings on memory and in a .symbol cache file are updated. |
| 612 | 637 |
| 613 Symbol files are formatted as follows: | 638 Symbol files are formatted as follows: |
| 614 <Address> <Symbol> | 639 <Address> <Symbol> |
| 615 <Address> <Symbol> | 640 <Address> <Symbol> |
| 616 <Address> <Symbol> | 641 <Address> <Symbol> |
| 617 ... | 642 ... |
| 618 | 643 |
| 619 Args: | 644 Args: |
| 620 symbol_path: A string representing a path for a .symbol file. | 645 symbol_path: A string representing a path for a .symbol file. |
| 621 delayed_static_symbols: A DelayedStaticSymbols object. | 646 delayed_static_symbols: A DelayedStaticSymbols object. |
| 622 appeared_addresses: A list of known addresses. | 647 appeared_addresses: A list of known addresses. |
| 648 parameter_find_runtime_symbols_list: A function to find symbols. |
| 623 symbols: A dict mapping runtime addresses to symbol names. | 649 symbols: A dict mapping runtime addresses to symbol names. |
| 624 """ | 650 """ |
| 625 with open(symbol_path, mode='a+') as symbol_f: | 651 with open(symbol_path, mode='a+') as symbol_f: |
| 626 symbol_lines = symbol_f.readlines() | 652 symbol_lines = symbol_f.readlines() |
| 627 if symbol_lines: | 653 if symbol_lines: |
| 628 for line in symbol_lines: | 654 for line in symbol_lines: |
| 629 items = line.split(None, 1) | 655 items = line.split(None, 1) |
| 630 if len(items) == 1: | 656 if len(items) == 1: |
| 631 items.append('??') | 657 items.append('??') |
| 632 symbols[int(items[0], 16)] = items[1].rstrip() | 658 symbols[int(items[0], 16)] = items[1].rstrip() |
| 633 if symbols: | 659 if symbols: |
| 634 sys.stderr.write(' Found %d symbols in cache.\n' % len(symbols)) | 660 sys.stderr.write(' Found %d symbols in cache.\n' % len(symbols)) |
| 635 else: | 661 else: |
| 636 sys.stderr.write(' No symbols found in cache.\n') | 662 sys.stderr.write(' No symbols found in cache.\n') |
| 637 | 663 |
| 638 unresolved_addresses = sorted( | 664 unresolved_addresses = sorted( |
| 639 a for a in appeared_addresses if a not in symbols) | 665 a for a in appeared_addresses if a not in symbols) |
| 640 | 666 |
| 641 if not unresolved_addresses: | 667 if not unresolved_addresses: |
| 642 sys.stderr.write(' No need to resolve any more addresses.\n') | 668 sys.stderr.write(' No need to resolve any more addresses.\n') |
| 643 else: | 669 else: |
| 644 sys.stderr.write(' %d addresses unresolved.\n' % | 670 sys.stderr.write(' %d addresses unresolved.\n' % |
| 645 len(unresolved_addresses)) | 671 len(unresolved_addresses)) |
| 646 static_symbols = delayed_static_symbols.get() | 672 |
| 647 symbol_list = find_runtime_symbols_list( | 673 sys.stderr.write(' Loading symbols\n') |
| 648 static_symbols, unresolved_addresses) | 674 symbols_in_process = delayed_static_symbols.get() |
| 675 symbol_list = parameter_find_runtime_symbols_list( |
| 676 symbols_in_process, unresolved_addresses) |
| 677 sys.stderr.write(' Loaded\n') |
| 649 | 678 |
| 650 for address, symbol in zip(unresolved_addresses, symbol_list): | 679 for address, symbol in zip(unresolved_addresses, symbol_list): |
| 651 if not symbol: | 680 if not symbol: |
| 652 symbol = '??' | 681 symbol = '??' |
| 653 stripped_symbol = symbol.strip() | 682 stripped_symbol = symbol.strip() |
| 654 symbols[address] = stripped_symbol | 683 symbols[address] = stripped_symbol |
| 655 symbol_f.write('%x %s\n' % (address, stripped_symbol)) | 684 symbol_f.write('%x %s\n' % (address, stripped_symbol)) |
| 656 | 685 |
| 657 sys.stderr.write(' All symbols resolved.\n') | 686 sys.stderr.write(' All symbols resolved.\n') |
| 658 | 687 |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 720 Args: | 749 Args: |
| 721 policy_path: A path for a policy file. | 750 policy_path: A path for a policy file. |
| 722 Returns: | 751 Returns: |
| 723 A loaded policy object. | 752 A loaded policy object. |
| 724 """ | 753 """ |
| 725 with open(policy_path, mode='r') as f: | 754 with open(policy_path, mode='r') as f: |
| 726 policy = json.load(f) | 755 policy = json.load(f) |
| 727 | 756 |
| 728 rules = [] | 757 rules = [] |
| 729 for rule in policy['rules']: | 758 for rule in policy['rules']: |
| 730 rules.append(Rule( | 759 if 'typeinfo' in rule: |
| 731 rule['name'], rule['allocator'] == 'mmap', rule['stacktrace'])) | 760 rules.append(Rule( |
| 761 rule['name'], rule['allocator'] == 'mmap', rule['stacktrace'], |
| 762 rule['typeinfo'])) |
| 763 else: |
| 764 rules.append(Rule( |
| 765 rule['name'], rule['allocator'] == 'mmap', rule['stacktrace'])) |
| 732 return Policy(rules, policy['version'], policy['components']) | 766 return Policy(rules, policy['version'], policy['components']) |
| 733 | 767 |
| 734 | 768 |
| 735 def find_prefix(path): | 769 def find_prefix(path): |
| 736 return re.sub('\.[0-9][0-9][0-9][0-9]\.heap', '', path) | 770 return re.sub('\.[0-9][0-9][0-9][0-9]\.heap', '', path) |
| 737 | 771 |
| 738 | 772 |
| 739 def load_buckets(prefix): | 773 def load_buckets(prefix): |
| 740 # Reading buckets | 774 # Reading buckets |
| 741 sys.stderr.write('Loading bucket files.\n') | 775 sys.stderr.write('Loading bucket files.\n') |
| 776 appeared_typeinfo_addresses = set() |
| 742 buckets = {} | 777 buckets = {} |
| 743 bucket_count = 0 | 778 bucket_count = 0 |
| 744 n = 0 | 779 n = 0 |
| 745 while True: | 780 while True: |
| 746 buckets_path = '%s.%04d.buckets' % (prefix, n) | 781 buckets_path = '%s.%04d.buckets' % (prefix, n) |
| 747 if not os.path.exists(buckets_path): | 782 if not os.path.exists(buckets_path): |
| 748 if n > 10: | 783 if n > 10: |
| 749 break | 784 break |
| 750 n += 1 | 785 n += 1 |
| 751 continue | 786 continue |
| 752 sys.stderr.write(' %s\n' % buckets_path) | 787 sys.stderr.write(' %s\n' % buckets_path) |
| 753 with open(buckets_path, 'r') as buckets_f: | 788 with open(buckets_path, 'r') as buckets_f: |
| 754 for line in buckets_f: | 789 for line in buckets_f: |
| 755 words = line.split() | 790 words = line.split() |
| 756 stacktrace = [int(address, 16) for address in words[2:]] | 791 typeinfo = None |
| 757 buckets[int(words[0])] = Bucket(stacktrace, words[1] == 'mmap') | 792 typename = '' |
| 793 stacktrace_begin = 2 |
| 794 for index, word in enumerate(words): |
| 795 if index < 2: |
| 796 continue |
| 797 if word[0] == 't': |
| 798 typeinfo = int(word[1:], 16) |
| 799 appeared_typeinfo_addresses.add(typeinfo) |
| 800 elif word[0] == 'n': |
| 801 typename = word[1:] |
| 802 else: |
| 803 stacktrace_begin = index |
| 804 break |
| 805 stacktrace = [int(address, 16) for address in words[stacktrace_begin:]] |
| 806 buckets[int(words[0])] = Bucket( |
| 807 stacktrace, words[1] == 'mmap', typeinfo, typename) |
| 758 n += 1 | 808 n += 1 |
| 759 | 809 |
| 760 return buckets | 810 return buckets, appeared_typeinfo_addresses |
| 761 | 811 |
| 762 | 812 |
| 763 def determine_dump_path_list(dump_path, prefix): | 813 def determine_dump_path_list(dump_path, prefix): |
| 764 dump_path_list = [dump_path] | 814 dump_path_list = [dump_path] |
| 765 | 815 |
| 766 # search for the sequence of files | 816 # search for the sequence of files |
| 767 n = int(dump_path[len(dump_path) - 9 : len(dump_path) - 5]) | 817 n = int(dump_path[len(dump_path) - 9 : len(dump_path) - 5]) |
| 768 n += 1 # skip current file | 818 n += 1 # skip current file |
| 769 while True: | 819 while True: |
| 770 p = '%s.%04d.heap' % (prefix, n) | 820 p = '%s.%04d.heap' % (prefix, n) |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 805 appeared_addresses = set() | 855 appeared_addresses = set() |
| 806 dumps = [] | 856 dumps = [] |
| 807 for path in dump_path_list: | 857 for path in dump_path_list: |
| 808 sys.stderr.write(' %s' % path) | 858 sys.stderr.write(' %s' % path) |
| 809 dumps.append(load_single_dump(path, buckets, appeared_addresses)) | 859 dumps.append(load_single_dump(path, buckets, appeared_addresses)) |
| 810 sys.stderr.write('\n') | 860 sys.stderr.write('\n') |
| 811 return dumps, appeared_addresses | 861 return dumps, appeared_addresses |
| 812 | 862 |
| 813 | 863 |
| 814 def load_and_update_symbol_cache( | 864 def load_and_update_symbol_cache( |
| 815 prefix, appeared_addresses, delayed_static_symbols): | 865 prefix, appeared_addresses, appeared_typeinfo_addresses, |
| 866 delayed_static_symbols): |
| 816 symbol_path = prefix + '.symbols' | 867 symbol_path = prefix + '.symbols' |
| 817 sys.stderr.write('Loading and updating symbol cache: "%s".\n' % symbol_path) | 868 sys.stderr.write('Loading and updating symbol cache: "%s".\n' % symbol_path) |
| 818 symbols = {} | 869 symbols = {} |
| 819 update_symbols( | 870 update_symbols( |
| 820 symbol_path, delayed_static_symbols, appeared_addresses, symbols) | 871 symbol_path, delayed_static_symbols, appeared_addresses, |
| 821 return symbols | 872 find_runtime_symbols_list, symbols) |
| 873 |
| 874 typeinfo_symbol_path = prefix + '.tsymbols' |
| 875 sys.stderr.write('Loading and updating typeinfo symbol cache: "%s".\n' % |
| 876 typeinfo_symbol_path) |
| 877 typeinfo_symbols = {} |
| 878 update_symbols( |
| 879 typeinfo_symbol_path, delayed_static_symbols, appeared_typeinfo_addresses, |
| 880 find_runtime_typeinfo_symbols_list, typeinfo_symbols) |
| 881 |
| 882 return symbols, typeinfo_symbols |
| 822 | 883 |
| 823 | 884 |
| 824 def load_default_policies(): | 885 def load_default_policies(): |
| 825 with open(POLICIES_JSON_PATH, mode='r') as policies_f: | 886 with open(POLICIES_JSON_PATH, mode='r') as policies_f: |
| 826 default_policies = json.load(policies_f) | 887 default_policies = json.load(policies_f) |
| 827 return default_policies | 888 return default_policies |
| 828 | 889 |
| 829 | 890 |
| 830 def load_policy(policies_dict, policy_label): | 891 def load_policy(policies_dict, policy_label): |
| 831 policy_file = policies_dict[policy_label]['file'] | 892 policy_file = policies_dict[policy_label]['file'] |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 863 specified_policies[specified_policy] = ( | 924 specified_policies[specified_policy] = ( |
| 864 default_policies[specified_policy]) | 925 default_policies[specified_policy]) |
| 865 policies = load_policies_dict(specified_policies) | 926 policies = load_policies_dict(specified_policies) |
| 866 else: | 927 else: |
| 867 policies = load_policies_dict(default_policies) | 928 policies = load_policies_dict(default_policies) |
| 868 return policies | 929 return policies |
| 869 | 930 |
| 870 | 931 |
| 871 def load_basic_files_with_multiple_dumps(dump_path, keep): | 932 def load_basic_files_with_multiple_dumps(dump_path, keep): |
| 872 prefix = find_prefix(dump_path) | 933 prefix = find_prefix(dump_path) |
| 873 buckets = load_buckets(prefix) | 934 buckets, appeared_typeinfo_addresses = load_buckets(prefix) |
| 874 dumps, appeared_addresses = load_dumps( | 935 dumps, appeared_addresses = load_dumps( |
| 875 determine_dump_path_list(dump_path, prefix), buckets) | 936 determine_dump_path_list(dump_path, prefix), buckets) |
| 876 delayed_static_symbols = DelayedStaticSymbols(prefix, keep) | 937 delayed_static_symbols = DelayedStaticSymbols(prefix, keep) |
| 877 symbols = load_and_update_symbol_cache( | 938 symbols, typeinfo_symbols = load_and_update_symbol_cache( |
| 878 prefix, appeared_addresses, delayed_static_symbols) | 939 prefix, appeared_addresses, appeared_typeinfo_addresses, |
| 879 return buckets, dumps, appeared_addresses, delayed_static_symbols, symbols | 940 delayed_static_symbols) |
| 941 for bucket in buckets: |
| 942 if buckets[bucket].typeinfo != None: |
| 943 buckets[bucket].typeinfo_symbol = typeinfo_symbols[ |
| 944 buckets[bucket].typeinfo] |
| 945 |
| 946 return (buckets, dumps, appeared_addresses, appeared_typeinfo_addresses, |
| 947 delayed_static_symbols, symbols, typeinfo_symbols) |
| 880 | 948 |
| 881 | 949 |
| 882 def load_basic_files_with_single_dump(dump_path, keep): | 950 def load_basic_files_with_single_dump(dump_path, keep): |
| 883 prefix = find_prefix(dump_path) | 951 prefix = find_prefix(dump_path) |
| 884 buckets = load_buckets(prefix) | 952 buckets, appeared_typeinfo_addresses = load_buckets(prefix) |
| 885 dump, appeared_addresses = load_dump(dump_path, buckets) | 953 dump, appeared_addresses = load_dump(dump_path, buckets) |
| 886 delayed_static_symbols = DelayedStaticSymbols(prefix, keep) | 954 delayed_static_symbols = DelayedStaticSymbols(prefix, keep) |
| 887 symbols = load_and_update_symbol_cache( | 955 symbols, typeinfo_symbols = load_and_update_symbol_cache( |
| 888 prefix, appeared_addresses, delayed_static_symbols) | 956 prefix, appeared_addresses, appeared_typeinfo_addresses, |
| 889 return buckets, dump, appeared_addresses, delayed_static_symbols, symbols | 957 delayed_static_symbols) |
| 958 for bucket in buckets: |
| 959 if buckets[bucket].typeinfo != None: |
| 960 buckets[bucket].typeinfo_symbol = typeinfo_symbols[ |
| 961 buckets[bucket].typeinfo] |
| 962 |
| 963 return (buckets, dump, appeared_addresses, appeared_typeinfo_addresses, |
| 964 delayed_static_symbols, symbols, typeinfo_symbols) |
| 890 | 965 |
| 891 | 966 |
| 892 def do_stacktrace(sys_argv): | 967 def do_stacktrace(sys_argv): |
| 893 parser = optparse.OptionParser( | 968 parser = optparse.OptionParser( |
| 894 'Usage: %prog stacktrace [--keep] <dump>') | 969 'Usage: %prog stacktrace [--keep] <dump>') |
| 895 parser.add_option('--keep', dest='keep', action='store_true') | 970 parser.add_option('--keep', dest='keep', action='store_true') |
| 896 options, args = parser.parse_args(sys_argv) | 971 options, args = parser.parse_args(sys_argv) |
| 897 | 972 |
| 898 if len(args) != 2: | 973 if len(args) != 2: |
| 899 parser.error('needs 1 argument.') | 974 parser.error('needs 1 argument.') |
| 900 return 1 | 975 return 1 |
| 901 | 976 |
| 902 dump_path = args[1] | 977 dump_path = args[1] |
| 903 | 978 |
| 904 buckets, dump, appeared_addresses, delayed_static_symbols, symbols = ( | 979 (buckets, dump, appeared_addresses, appeared_typeinfo_addresses, |
| 905 load_basic_files_with_single_dump(dump_path, options.keep)) | 980 delayed_static_symbols, symbols, typeinfo_symbols) = ( |
| 981 load_basic_files_with_single_dump(dump_path, options.keep)) |
| 906 | 982 |
| 907 dump.print_stacktrace(buckets, symbols) | 983 dump.print_stacktrace(buckets, symbols) |
| 908 | 984 |
| 909 return 0 | 985 return 0 |
| 910 | 986 |
| 911 | 987 |
| 912 def do_csv(sys_argv): | 988 def do_csv(sys_argv): |
| 913 parser = optparse.OptionParser( | 989 parser = optparse.OptionParser( |
| 914 'Usage: %prog csv [-p POLICY] [--keep] <first-dump>') | 990 'Usage: %prog csv [-p POLICY] [--keep] <first-dump>') |
| 915 parser.add_option('-p', '--policy', type='string', dest='policy', | 991 parser.add_option('-p', '--policy', type='string', dest='policy', |
| 916 help='profile with POLICY', metavar='POLICY') | 992 help='profile with POLICY', metavar='POLICY') |
| 917 parser.add_option('--keep', dest='keep', action='store_true') | 993 parser.add_option('--keep', dest='keep', action='store_true') |
| 918 options, args = parser.parse_args(sys_argv) | 994 options, args = parser.parse_args(sys_argv) |
| 919 | 995 |
| 920 if len(args) != 2: | 996 if len(args) != 2: |
| 921 parser.error('needs 1 argument.') | 997 parser.error('needs 1 argument.') |
| 922 return 1 | 998 return 1 |
| 923 | 999 |
| 924 dump_path = args[1] | 1000 dump_path = args[1] |
| 925 | 1001 |
| 926 buckets, dumps, appeared_addresses, delayed_static_symbols, symbols = ( | 1002 (buckets, dumps, appeared_addresses, appeared_typeinfo_addresses, |
| 927 load_basic_files_with_multiple_dumps(dump_path, options.keep)) | 1003 delayed_static_symbols, symbols, typeinfo_symbols) = ( |
| 1004 load_basic_files_with_multiple_dumps(dump_path, options.keep)) |
| 1005 |
| 928 policies = load_policies(options.policy) | 1006 policies = load_policies(options.policy) |
| 929 | 1007 |
| 930 max_components = 0 | 1008 max_components = 0 |
| 931 for policy in policies: | 1009 for policy in policies: |
| 932 max_components = max(max_components, len(policies[policy].components)) | 1010 max_components = max(max_components, len(policies[policy].components)) |
| 933 | 1011 |
| 934 for policy in sorted(policies): | 1012 for policy in sorted(policies): |
| 935 rule_list = policies[policy].rules | 1013 rule_list = policies[policy].rules |
| 936 components = policies[policy].components | 1014 components = policies[policy].components |
| 937 | 1015 |
| (...skipping 27 matching lines...) Expand all Loading... |
| 965 help='profile with POLICY', metavar='POLICY') | 1043 help='profile with POLICY', metavar='POLICY') |
| 966 parser.add_option('--keep', dest='keep', action='store_true') | 1044 parser.add_option('--keep', dest='keep', action='store_true') |
| 967 options, args = parser.parse_args(sys_argv) | 1045 options, args = parser.parse_args(sys_argv) |
| 968 | 1046 |
| 969 if len(args) != 2: | 1047 if len(args) != 2: |
| 970 parser.error('needs 1 argument.') | 1048 parser.error('needs 1 argument.') |
| 971 return 1 | 1049 return 1 |
| 972 | 1050 |
| 973 dump_path = args[1] | 1051 dump_path = args[1] |
| 974 | 1052 |
| 975 buckets, dumps, appeared_addresses, delayed_static_symbols, symbols = ( | 1053 (buckets, dumps, appeared_addresses, appeared_typeinfo_addresses, |
| 976 load_basic_files_with_multiple_dumps(dump_path, options.keep)) | 1054 delayed_static_symbols, symbols, typeinfo_symbols) = ( |
| 1055 load_basic_files_with_multiple_dumps(dump_path, options.keep)) |
| 977 policies = load_policies(options.policy) | 1056 policies = load_policies(options.policy) |
| 978 | 1057 |
| 979 json_base = { | 1058 json_base = { |
| 980 'version': 'JSON_DEEP_2', | 1059 'version': 'JSON_DEEP_2', |
| 981 'policies': {}, | 1060 'policies': {}, |
| 982 } | 1061 } |
| 983 | 1062 |
| 984 for policy in sorted(policies): | 1063 for policy in sorted(policies): |
| 985 rule_list = policies[policy].rules | 1064 rule_list = policies[policy].rules |
| 986 components = policies[policy].components | 1065 components = policies[policy].components |
| (...skipping 26 matching lines...) Expand all Loading... |
| 1013 help='profile with POLICY', metavar='POLICY') | 1092 help='profile with POLICY', metavar='POLICY') |
| 1014 parser.add_option('--keep', dest='keep', action='store_true') | 1093 parser.add_option('--keep', dest='keep', action='store_true') |
| 1015 options, args = parser.parse_args(sys_argv) | 1094 options, args = parser.parse_args(sys_argv) |
| 1016 | 1095 |
| 1017 if len(args) != 2: | 1096 if len(args) != 2: |
| 1018 parser.error('needs 1 argument.') | 1097 parser.error('needs 1 argument.') |
| 1019 return 1 | 1098 return 1 |
| 1020 | 1099 |
| 1021 dump_path = args[1] | 1100 dump_path = args[1] |
| 1022 | 1101 |
| 1023 buckets, dumps, appeared_addresses, delayed_static_symbols, symbols = ( | 1102 (buckets, dumps, appeared_addresses, appeared_typeinfo_addresses, |
| 1024 load_basic_files_with_multiple_dumps(dump_path, options.keep)) | 1103 delayed_static_symbols, symbols, typeinfo_symbols) = ( |
| 1104 load_basic_files_with_multiple_dumps(dump_path, options.keep)) |
| 1025 policies = load_policies(options.policy) | 1105 policies = load_policies(options.policy) |
| 1026 | 1106 |
| 1027 for policy in sorted(policies): | 1107 for policy in sorted(policies): |
| 1028 rule_list = policies[policy].rules | 1108 rule_list = policies[policy].rules |
| 1029 components = policies[policy].components | 1109 components = policies[policy].components |
| 1030 | 1110 |
| 1031 component_sizes = dumps[0].apply_policy( | 1111 component_sizes = dumps[0].apply_policy( |
| 1032 rule_list, buckets, dumps[0].dump_time, components, symbols) | 1112 rule_list, buckets, dumps[0].dump_time, components, symbols) |
| 1033 sys.stdout.write('%s:\n' % policy) | 1113 sys.stdout.write('%s:\n' % policy) |
| 1034 for c in components: | 1114 for c in components: |
| (...skipping 17 matching lines...) Expand all Loading... |
| 1052 | 1132 |
| 1053 if len(args) != 5: | 1133 if len(args) != 5: |
| 1054 parser.error('needs 4 arguments.') | 1134 parser.error('needs 4 arguments.') |
| 1055 return 1 | 1135 return 1 |
| 1056 | 1136 |
| 1057 dump_path = args[1] | 1137 dump_path = args[1] |
| 1058 target_policy = args[2] | 1138 target_policy = args[2] |
| 1059 component_name = args[3] | 1139 component_name = args[3] |
| 1060 depth = args[4] | 1140 depth = args[4] |
| 1061 | 1141 |
| 1062 buckets, dump, appeared_addresses, delayed_static_symbols, symbols = ( | 1142 (buckets, dump, appeared_addresses, appeared_typeinfo_addresses, |
| 1063 load_basic_files_with_single_dump(dump_path, options.keep)) | 1143 delayed_static_symbols, symbols, typeinfo_symbols) = ( |
| 1144 load_basic_files_with_single_dump(dump_path, options.keep)) |
| 1064 policies = load_policies(target_policy) | 1145 policies = load_policies(target_policy) |
| 1065 | 1146 |
| 1066 rule_list = policies[target_policy].rules | 1147 rule_list = policies[target_policy].rules |
| 1067 | 1148 |
| 1068 dump.expand(rule_list, buckets, component_name, int(depth), symbols) | 1149 dump.expand(rule_list, buckets, component_name, int(depth), symbols, |
| 1150 typeinfo_symbols) |
| 1069 | 1151 |
| 1070 return 0 | 1152 return 0 |
| 1071 | 1153 |
| 1072 | 1154 |
| 1073 def do_pprof(sys_argv): | 1155 def do_pprof(sys_argv): |
| 1074 parser = optparse.OptionParser( | 1156 parser = optparse.OptionParser( |
| 1075 'Usage: %prog pprof [-c COMPONENT] [--keep] <dump> <policy>') | 1157 'Usage: %prog pprof [-c COMPONENT] [--keep] <dump> <policy>') |
| 1076 parser.add_option('-c', '--component', type='string', dest='component', | 1158 parser.add_option('-c', '--component', type='string', dest='component', |
| 1077 help='restrict to COMPONENT', metavar='COMPONENT') | 1159 help='restrict to COMPONENT', metavar='COMPONENT') |
| 1078 parser.add_option('--keep', dest='keep', action='store_true') | 1160 parser.add_option('--keep', dest='keep', action='store_true') |
| 1079 options, args = parser.parse_args(sys_argv) | 1161 options, args = parser.parse_args(sys_argv) |
| 1080 | 1162 |
| 1081 if len(args) != 3: | 1163 if len(args) != 3: |
| 1082 parser.error('needs 2 arguments.') | 1164 parser.error('needs 2 arguments.') |
| 1083 return 1 | 1165 return 1 |
| 1084 | 1166 |
| 1085 dump_path = args[1] | 1167 dump_path = args[1] |
| 1086 target_policy = args[2] | 1168 target_policy = args[2] |
| 1087 component = options.component | 1169 component = options.component |
| 1088 | 1170 |
| 1089 buckets, dump, appeared_addresses, delayed_static_symbols, symbols = ( | 1171 (buckets, dump, appeared_addresses, appeared_typeinfo_addresses, |
| 1090 load_basic_files_with_single_dump(dump_path, options.keep)) | 1172 delayed_static_symbols, symbols, typeinfo_symbols) = ( |
| 1173 load_basic_files_with_single_dump(dump_path, options.keep)) |
| 1091 policies = load_policies(target_policy) | 1174 policies = load_policies(target_policy) |
| 1092 | 1175 |
| 1093 rule_list = policies[target_policy].rules | 1176 rule_list = policies[target_policy].rules |
| 1094 | 1177 |
| 1095 with open(find_prefix(dump_path) + '.maps', 'r') as maps_f: | 1178 with open(find_prefix(dump_path) + '.maps', 'r') as maps_f: |
| 1096 maps_lines = maps_f.readlines() | 1179 maps_lines = maps_f.readlines() |
| 1097 dump.print_for_pprof(rule_list, buckets, maps_lines, component, symbols) | 1180 dump.print_for_pprof(rule_list, buckets, maps_lines, component, symbols) |
| 1098 | 1181 |
| 1099 return 0 | 1182 return 0 |
| 1100 | 1183 |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1139 dmprof stacktrace [--keep] <dump> | 1222 dmprof stacktrace [--keep] <dump> |
| 1140 """ % (sys.argv[0])) | 1223 """ % (sys.argv[0])) |
| 1141 sys.exit(1) | 1224 sys.exit(1) |
| 1142 action = sys.argv.pop(1) | 1225 action = sys.argv.pop(1) |
| 1143 | 1226 |
| 1144 return COMMANDS[action](sys.argv) | 1227 return COMMANDS[action](sys.argv) |
| 1145 | 1228 |
| 1146 | 1229 |
| 1147 if __name__ == '__main__': | 1230 if __name__ == '__main__': |
| 1148 sys.exit(main()) | 1231 sys.exit(main()) |
| OLD | NEW |