| 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 prepare_symbol_info import prepare_symbol_info | 25 from prepare_symbol_info import prepare_symbol_info |
| 25 from find_runtime_symbols import find_runtime_symbols_list | 26 from static_symbols import StaticSymbols |
| 26 | 27 |
| 27 BUCKET_ID = 5 | 28 BUCKET_ID = 5 |
| 28 VIRTUAL = 0 | 29 VIRTUAL = 0 |
| 29 COMMITTED = 1 | 30 COMMITTED = 1 |
| 30 ALLOC_COUNT = 2 | 31 ALLOC_COUNT = 2 |
| 31 FREE_COUNT = 3 | 32 FREE_COUNT = 3 |
| 32 NULL_REGEX = re.compile('') | 33 NULL_REGEX = re.compile('') |
| 33 | 34 |
| 34 POLICIES_JSON_PATH = os.path.join( | 35 POLICIES_JSON_PATH = os.path.join( |
| 35 os.path.dirname(os.path.abspath(__file__)), | 36 os.path.dirname(os.path.abspath(__file__)), |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 99 return "invalid heap profile dump: %s" % repr(self.value) | 100 return "invalid heap profile dump: %s" % repr(self.value) |
| 100 | 101 |
| 101 | 102 |
| 102 class ObsoleteDumpVersionException(ParsingException): | 103 class ObsoleteDumpVersionException(ParsingException): |
| 103 def __init__(self, value): | 104 def __init__(self, value): |
| 104 self.value = value | 105 self.value = value |
| 105 def __str__(self): | 106 def __str__(self): |
| 106 return "obsolete heap profile dump version: %s" % repr(self.value) | 107 return "obsolete heap profile dump version: %s" % repr(self.value) |
| 107 | 108 |
| 108 | 109 |
| 110 class DelayedStaticSymbols(object): |
| 111 """Represents static symbol information loaded lazily.""" |
| 112 |
| 113 def __init__(self, prefix, keep=False): |
| 114 self.maps_path = prefix + '.maps' |
| 115 self.keep = keep |
| 116 if keep: |
| 117 self.prepared_data_dir = prefix + '.pre' |
| 118 self.loaded_static_symbols = None |
| 119 |
| 120 def get(self): |
| 121 if not self.loaded_static_symbols: |
| 122 if not self.keep: |
| 123 self.prepared_data_dir = tempfile.mkdtemp() |
| 124 try: |
| 125 prepare_symbol_info(self.maps_path, self.prepared_data_dir) |
| 126 self.loaded_static_symbols = StaticSymbols.load(self.prepared_data_dir) |
| 127 finally: |
| 128 if not self.keep: |
| 129 shutil.rmtree(self.prepared_data_dir) |
| 130 return self.loaded_static_symbols |
| 131 |
| 132 |
| 109 class Rule(object): | 133 class Rule(object): |
| 110 """Represents one matching rule in a policy file.""" | 134 """Represents one matching rule in a policy file.""" |
| 111 | 135 |
| 112 def __init__(self, name, mmap, pattern): | 136 def __init__(self, name, mmap, pattern): |
| 113 self.name = name | 137 self.name = name |
| 114 self.mmap = mmap | 138 self.mmap = mmap |
| 115 self.condition = re.compile(pattern + r'\Z') | 139 self.condition = re.compile(pattern + r'\Z') |
| 116 | 140 |
| 117 | 141 |
| 118 class Policy(object): | 142 class Policy(object): |
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 185 symbols: A dict mapping runtime addresses to symbol names. | 209 symbols: A dict mapping runtime addresses to symbol names. |
| 186 """ | 210 """ |
| 187 for line in self.stacktrace_lines: | 211 for line in self.stacktrace_lines: |
| 188 words = line.split() | 212 words = line.split() |
| 189 bucket = buckets.get(int(words[BUCKET_ID])) | 213 bucket = buckets.get(int(words[BUCKET_ID])) |
| 190 if not bucket: | 214 if not bucket: |
| 191 continue | 215 continue |
| 192 for i in range(0, BUCKET_ID - 1): | 216 for i in range(0, BUCKET_ID - 1): |
| 193 sys.stdout.write(words[i] + ' ') | 217 sys.stdout.write(words[i] + ' ') |
| 194 for address in bucket.stacktrace: | 218 for address in bucket.stacktrace: |
| 195 sys.stdout.write((symbols.get(address) or address) + ' ') | 219 sys.stdout.write((symbols.get(address) or ('0x%016x' % address)) + ' ') |
| 196 sys.stdout.write('\n') | 220 sys.stdout.write('\n') |
| 197 | 221 |
| 198 @staticmethod | 222 @staticmethod |
| 199 def accumulate_size_for_pprof(stacktrace_lines, rule_list, buckets, | 223 def accumulate_size_for_pprof(stacktrace_lines, rule_list, buckets, |
| 200 component_name, symbols): | 224 component_name, symbols): |
| 201 """Accumulates size of committed chunks and the number of allocated chunks. | 225 """Accumulates size of committed chunks and the number of allocated chunks. |
| 202 | 226 |
| 203 Args: | 227 Args: |
| 204 stacktrace_lines: A list of strings which are valid as stacktraces. | 228 stacktrace_lines: A list of strings which are valid as stacktraces. |
| 205 rule_list: A list of Rule objects. | 229 rule_list: A list of Rule objects. |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 245 (component_name and | 269 (component_name and |
| 246 component_name != get_component(rule_list, bucket, symbols))): | 270 component_name != get_component(rule_list, bucket, symbols))): |
| 247 continue | 271 continue |
| 248 | 272 |
| 249 sys.stdout.write('%6d: %8s [%6d: %8s] @' % ( | 273 sys.stdout.write('%6d: %8s [%6d: %8s] @' % ( |
| 250 int(words[ALLOC_COUNT]) - int(words[FREE_COUNT]), | 274 int(words[ALLOC_COUNT]) - int(words[FREE_COUNT]), |
| 251 words[COMMITTED], | 275 words[COMMITTED], |
| 252 int(words[ALLOC_COUNT]) - int(words[FREE_COUNT]), | 276 int(words[ALLOC_COUNT]) - int(words[FREE_COUNT]), |
| 253 words[COMMITTED])) | 277 words[COMMITTED])) |
| 254 for address in bucket.stacktrace: | 278 for address in bucket.stacktrace: |
| 255 sys.stdout.write(' ' + address) | 279 sys.stdout.write(' 0x%016x' % address) |
| 256 sys.stdout.write('\n') | 280 sys.stdout.write('\n') |
| 257 | 281 |
| 258 def print_for_pprof( | 282 def print_for_pprof( |
| 259 self, rule_list, buckets, maps_lines, component_name, symbols): | 283 self, rule_list, buckets, maps_lines, component_name, symbols): |
| 260 """Converts the heap profile dump so it can be processed by pprof. | 284 """Converts the heap profile dump so it can be processed by pprof. |
| 261 | 285 |
| 262 Args: | 286 Args: |
| 263 rule_list: A list of Rule objects. | 287 rule_list: A list of Rule objects. |
| 264 buckets: A dict mapping bucket ids to Bucket objects. | 288 buckets: A dict mapping bucket ids to Bucket objects. |
| 265 maps_lines: A list of strings containing /proc/.../maps. | 289 maps_lines: A list of strings containing /proc/.../maps. |
| (...skipping 303 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 569 sorted_sizes_list = sorted( | 593 sorted_sizes_list = sorted( |
| 570 sizes.iteritems(), key=(lambda x: x[1]), reverse=True) | 594 sizes.iteritems(), key=(lambda x: x[1]), reverse=True) |
| 571 total = 0 | 595 total = 0 |
| 572 for size_pair in sorted_sizes_list: | 596 for size_pair in sorted_sizes_list: |
| 573 sys.stdout.write('%10d %s\n' % (size_pair[1], size_pair[0])) | 597 sys.stdout.write('%10d %s\n' % (size_pair[1], size_pair[0])) |
| 574 total += size_pair[1] | 598 total += size_pair[1] |
| 575 sys.stderr.write('total: %d\n' % (total)) | 599 sys.stderr.write('total: %d\n' % (total)) |
| 576 | 600 |
| 577 | 601 |
| 578 def update_symbols( | 602 def update_symbols( |
| 579 symbol_path, maps_path, appeared_addresses, symbols): | 603 symbol_path, delayed_static_symbols, appeared_addresses, symbols): |
| 580 """Updates address/symbol mapping on memory and in a .symbol cache file. | 604 """Updates address/symbol mapping on memory and in a .symbol cache file. |
| 581 | 605 |
| 582 It reads cached address/symbol mapping from a .symbol file if it exists. | 606 It reads cached address/symbol mapping from a .symbol file if it exists. |
| 583 Then, it resolves unresolved addresses from a Chrome binary with pprof. | 607 Then, it resolves unresolved addresses from a Chrome binary with pprof. |
| 584 Both mappings on memory and in a .symbol cache file are updated. | 608 Both mappings on memory and in a .symbol cache file are updated. |
| 585 | 609 |
| 586 Symbol files are formatted as follows: | 610 Symbol files are formatted as follows: |
| 587 <Address> <Symbol> | 611 <Address> <Symbol> |
| 588 <Address> <Symbol> | 612 <Address> <Symbol> |
| 589 <Address> <Symbol> | 613 <Address> <Symbol> |
| 590 ... | 614 ... |
| 591 | 615 |
| 592 Args: | 616 Args: |
| 593 symbol_path: A string representing a path for a .symbol file. | 617 symbol_path: A string representing a path for a .symbol file. |
| 594 maps_path: A string of the path of /proc/.../maps. | 618 delayed_static_symbols: A DelayedStaticSymbols object. |
| 595 appeared_addresses: A list of known addresses. | 619 appeared_addresses: A list of known addresses. |
| 596 symbols: A dict mapping runtime addresses to symbol names. | 620 symbols: A dict mapping runtime addresses to symbol names. |
| 597 """ | 621 """ |
| 598 with open(symbol_path, mode='a+') as symbol_f: | 622 with open(symbol_path, mode='a+') as symbol_f: |
| 599 symbol_lines = symbol_f.readlines() | 623 symbol_lines = symbol_f.readlines() |
| 600 if symbol_lines: | 624 if symbol_lines: |
| 601 for line in symbol_lines: | 625 for line in symbol_lines: |
| 602 items = line.split(None, 1) | 626 items = line.split(None, 1) |
| 603 if len(items) == 1: | 627 if len(items) == 1: |
| 604 items.append('??') | 628 items.append('??') |
| 605 symbols[items[0]] = items[1].rstrip() | 629 symbols[int(items[0], 16)] = items[1].rstrip() |
| 606 if symbols: | 630 if symbols: |
| 607 sys.stderr.write(' Found %d symbols in cache.\n' % len(symbols)) | 631 sys.stderr.write(' Found %d symbols in cache.\n' % len(symbols)) |
| 608 else: | 632 else: |
| 609 sys.stderr.write(' No symbols found in cache.\n') | 633 sys.stderr.write(' No symbols found in cache.\n') |
| 610 | 634 |
| 611 unresolved_addresses = sorted( | 635 unresolved_addresses = sorted( |
| 612 a for a in appeared_addresses if a not in symbols) | 636 a for a in appeared_addresses if a not in symbols) |
| 613 | 637 |
| 614 if not unresolved_addresses: | 638 if not unresolved_addresses: |
| 615 sys.stderr.write(' No need to resolve any more addresses.\n') | 639 sys.stderr.write(' No need to resolve any more addresses.\n') |
| 616 else: | 640 else: |
| 617 sys.stderr.write(' %d addresses are unresolved.\n' % | 641 sys.stderr.write(' %d addresses unresolved.\n' % |
| 618 len(unresolved_addresses)) | 642 len(unresolved_addresses)) |
| 619 prepared_data_dir = tempfile.mkdtemp() | 643 static_symbols = delayed_static_symbols.get() |
| 620 try: | 644 symbol_list = find_runtime_symbols_list( |
| 621 prepare_symbol_info(maps_path, prepared_data_dir) | 645 static_symbols, unresolved_addresses) |
| 622 | 646 |
| 623 symbol_list = find_runtime_symbols_list( | 647 for address, symbol in zip(unresolved_addresses, symbol_list): |
| 624 prepared_data_dir, unresolved_addresses) | 648 if not symbol: |
| 649 symbol = '??' |
| 650 stripped_symbol = symbol.strip() |
| 651 symbols[address] = stripped_symbol |
| 652 symbol_f.write('%x %s\n' % (address, stripped_symbol)) |
| 625 | 653 |
| 626 for address, symbol in zip(unresolved_addresses, symbol_list): | 654 sys.stderr.write(' All symbols resolved.\n') |
| 627 if not symbol: | |
| 628 symbol = '??' | |
| 629 stripped_symbol = symbol.strip() | |
| 630 symbols[address] = stripped_symbol | |
| 631 symbol_f.write('%s %s\n' % (address, stripped_symbol)) | |
| 632 finally: | |
| 633 shutil.rmtree(prepared_data_dir) | |
| 634 | 655 |
| 635 | 656 |
| 636 def parse_policy(policy_path): | 657 def parse_policy(policy_path): |
| 637 """Parses policy file. | 658 """Parses policy file. |
| 638 | 659 |
| 639 A policy file contains component's names and their | 660 A policy file contains component's names and their |
| 640 stacktrace pattern written in regular expression. | 661 stacktrace pattern written in regular expression. |
| 641 Those patterns are matched against each symbols of | 662 Those patterns are matched against each symbols of |
| 642 each stacktraces in the order written in the policy file | 663 each stacktraces in the order written in the policy file |
| 643 | 664 |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 697 buckets_path = '%s.%04d.buckets' % (prefix, n) | 718 buckets_path = '%s.%04d.buckets' % (prefix, n) |
| 698 if not os.path.exists(buckets_path): | 719 if not os.path.exists(buckets_path): |
| 699 if n > 10: | 720 if n > 10: |
| 700 break | 721 break |
| 701 n += 1 | 722 n += 1 |
| 702 continue | 723 continue |
| 703 sys.stderr.write(' %s\n' % buckets_path) | 724 sys.stderr.write(' %s\n' % buckets_path) |
| 704 with open(buckets_path, 'r') as buckets_f: | 725 with open(buckets_path, 'r') as buckets_f: |
| 705 for line in buckets_f: | 726 for line in buckets_f: |
| 706 words = line.split() | 727 words = line.split() |
| 707 buckets[int(words[0])] = Bucket(words[2:], words[1] == 'mmap') | 728 stacktrace = [int(address, 16) for address in words[2:]] |
| 729 buckets[int(words[0])] = Bucket(stacktrace, words[1] == 'mmap') |
| 708 n += 1 | 730 n += 1 |
| 709 | 731 |
| 710 return buckets | 732 return buckets |
| 711 | 733 |
| 712 | 734 |
| 713 def determine_dump_path_list(dump_path, prefix): | 735 def determine_dump_path_list(dump_path, prefix): |
| 714 dump_path_list = [dump_path] | 736 dump_path_list = [dump_path] |
| 715 | 737 |
| 716 # search for the sequence of files | 738 # search for the sequence of files |
| 717 n = int(dump_path[len(dump_path) - 9 : len(dump_path) - 5]) | 739 n = int(dump_path[len(dump_path) - 9 : len(dump_path) - 5]) |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 754 sys.stderr.write('Loading heap dump files.\n') | 776 sys.stderr.write('Loading heap dump files.\n') |
| 755 appeared_addresses = set() | 777 appeared_addresses = set() |
| 756 dumps = [] | 778 dumps = [] |
| 757 for path in dump_path_list: | 779 for path in dump_path_list: |
| 758 sys.stderr.write(' %s' % path) | 780 sys.stderr.write(' %s' % path) |
| 759 dumps.append(load_single_dump(path, buckets, appeared_addresses)) | 781 dumps.append(load_single_dump(path, buckets, appeared_addresses)) |
| 760 sys.stderr.write('\n') | 782 sys.stderr.write('\n') |
| 761 return dumps, appeared_addresses | 783 return dumps, appeared_addresses |
| 762 | 784 |
| 763 | 785 |
| 764 def load_and_update_symbol_cache(prefix, appeared_addresses): | 786 def load_and_update_symbol_cache( |
| 765 maps_path = prefix + '.maps' | 787 prefix, appeared_addresses, delayed_static_symbols): |
| 766 symbol_path = prefix + '.symbols' | 788 symbol_path = prefix + '.symbols' |
| 767 sys.stderr.write('Loading and updating symbol cache: "%s".\n' % symbol_path) | 789 sys.stderr.write('Loading and updating symbol cache: "%s".\n' % symbol_path) |
| 768 symbols = {} | 790 symbols = {} |
| 769 update_symbols(symbol_path, maps_path, appeared_addresses, symbols) | 791 update_symbols( |
| 792 symbol_path, delayed_static_symbols, appeared_addresses, symbols) |
| 770 return symbols | 793 return symbols |
| 771 | 794 |
| 772 | 795 |
| 773 def load_default_policies(): | 796 def load_default_policies(): |
| 774 with open(POLICIES_JSON_PATH, mode='r') as policies_f: | 797 with open(POLICIES_JSON_PATH, mode='r') as policies_f: |
| 775 default_policies = json.load(policies_f) | 798 default_policies = json.load(policies_f) |
| 776 return default_policies | 799 return default_policies |
| 777 | 800 |
| 778 | 801 |
| 779 def load_policy(policies_dict, policy_label): | 802 def load_policy(policies_dict, policy_label): |
| (...skipping 21 matching lines...) Expand all Loading... |
| 801 for specified_policy in policy_labels: | 824 for specified_policy in policy_labels: |
| 802 if specified_policy in default_policies: | 825 if specified_policy in default_policies: |
| 803 specified_policies[specified_policy] = ( | 826 specified_policies[specified_policy] = ( |
| 804 default_policies[specified_policy]) | 827 default_policies[specified_policy]) |
| 805 policies = load_policies_dict(specified_policies) | 828 policies = load_policies_dict(specified_policies) |
| 806 else: | 829 else: |
| 807 policies = load_policies_dict(default_policies) | 830 policies = load_policies_dict(default_policies) |
| 808 return policies | 831 return policies |
| 809 | 832 |
| 810 | 833 |
| 834 def load_basic_files_with_multiple_dumps(dump_path, keep): |
| 835 prefix = find_prefix(dump_path) |
| 836 buckets = load_buckets(prefix) |
| 837 dumps, appeared_addresses = load_dumps( |
| 838 determine_dump_path_list(dump_path, prefix), buckets) |
| 839 delayed_static_symbols = DelayedStaticSymbols(prefix, keep) |
| 840 symbols = load_and_update_symbol_cache( |
| 841 prefix, appeared_addresses, delayed_static_symbols) |
| 842 return buckets, dumps, appeared_addresses, delayed_static_symbols, symbols |
| 843 |
| 844 |
| 845 def load_basic_files_with_single_dump(dump_path, keep): |
| 846 prefix = find_prefix(dump_path) |
| 847 buckets = load_buckets(prefix) |
| 848 dump, appeared_addresses = load_dump(dump_path, buckets) |
| 849 delayed_static_symbols = DelayedStaticSymbols(prefix, keep) |
| 850 symbols = load_and_update_symbol_cache( |
| 851 prefix, appeared_addresses, delayed_static_symbols) |
| 852 return buckets, dump, appeared_addresses, delayed_static_symbols, symbols |
| 853 |
| 854 |
| 811 def do_stacktrace(sys_argv): | 855 def do_stacktrace(sys_argv): |
| 812 parser = optparse.OptionParser(usage='Usage: %prog stacktrace <dump>') | 856 parser = optparse.OptionParser( |
| 857 'Usage: %prog stacktrace [--keep] <dump>') |
| 858 parser.add_option('--keep', dest='keep', action='store_true') |
| 813 options, args = parser.parse_args(sys_argv) | 859 options, args = parser.parse_args(sys_argv) |
| 814 | 860 |
| 815 if len(args) != 2: | 861 if len(args) != 2: |
| 816 parser.error('needs 1 argument.') | 862 parser.error('needs 1 argument.') |
| 817 return 1 | 863 return 1 |
| 818 | 864 |
| 819 dump_path = args[1] | 865 dump_path = args[1] |
| 820 | 866 |
| 821 prefix = find_prefix(dump_path) | 867 buckets, dump, appeared_addresses, delayed_static_symbols, symbols = ( |
| 822 buckets = load_buckets(prefix) | 868 load_basic_files_with_single_dump(dump_path, options.keep)) |
| 823 dump, appeared_addresses = load_dump(dump_path, buckets) | |
| 824 symbols = load_and_update_symbol_cache(prefix, appeared_addresses) | |
| 825 | 869 |
| 826 dump.print_stacktrace(buckets, symbols) | 870 dump.print_stacktrace(buckets, symbols) |
| 827 | 871 |
| 828 return 0 | 872 return 0 |
| 829 | 873 |
| 830 | 874 |
| 831 def do_csv(sys_argv): | 875 def do_csv(sys_argv): |
| 832 parser = optparse.OptionParser('Usage: %prog csv [-p POLICY] <first-dump>') | 876 parser = optparse.OptionParser( |
| 877 'Usage: %prog csv [-p POLICY] [--keep] <first-dump>') |
| 833 parser.add_option('-p', '--policy', type='string', dest='policy', | 878 parser.add_option('-p', '--policy', type='string', dest='policy', |
| 834 help='profile with POLICY', metavar='POLICY') | 879 help='profile with POLICY', metavar='POLICY') |
| 880 parser.add_option('--keep', dest='keep', action='store_true') |
| 835 options, args = parser.parse_args(sys_argv) | 881 options, args = parser.parse_args(sys_argv) |
| 836 | 882 |
| 837 if len(args) != 2: | 883 if len(args) != 2: |
| 838 parser.error('needs 1 argument.') | 884 parser.error('needs 1 argument.') |
| 839 return 1 | 885 return 1 |
| 840 | 886 |
| 841 dump_path = args[1] | 887 dump_path = args[1] |
| 842 | 888 |
| 843 prefix = find_prefix(dump_path) | 889 buckets, dumps, appeared_addresses, delayed_static_symbols, symbols = ( |
| 844 buckets = load_buckets(prefix) | 890 load_basic_files_with_multiple_dumps(dump_path, options.keep)) |
| 845 dumps, appeared_addresses = load_dumps( | |
| 846 determine_dump_path_list(dump_path, prefix), buckets) | |
| 847 symbols = load_and_update_symbol_cache(prefix, appeared_addresses) | |
| 848 policies = load_policies(options.policy) | 891 policies = load_policies(options.policy) |
| 849 | 892 |
| 850 max_components = 0 | 893 max_components = 0 |
| 851 for policy in policies: | 894 for policy in policies: |
| 852 max_components = max(max_components, len(policies[policy].components)) | 895 max_components = max(max_components, len(policies[policy].components)) |
| 853 | 896 |
| 854 for policy in sorted(policies): | 897 for policy in sorted(policies): |
| 855 rule_list = policies[policy].rules | 898 rule_list = policies[policy].rules |
| 856 components = policies[policy].components | 899 components = policies[policy].components |
| 857 | 900 |
| (...skipping 14 matching lines...) Expand all Loading... |
| 872 sys.stdout.write('%s%s\n' % ( | 915 sys.stdout.write('%s%s\n' % ( |
| 873 ','.join(s), ',' * (max_components - len(components)))) | 916 ','.join(s), ',' * (max_components - len(components)))) |
| 874 | 917 |
| 875 for bucket in buckets.itervalues(): | 918 for bucket in buckets.itervalues(): |
| 876 bucket.clear_component_cache() | 919 bucket.clear_component_cache() |
| 877 | 920 |
| 878 return 0 | 921 return 0 |
| 879 | 922 |
| 880 | 923 |
| 881 def do_json(sys_argv): | 924 def do_json(sys_argv): |
| 882 parser = optparse.OptionParser('Usage: %prog json [-p POLICY] <first-dump>') | 925 parser = optparse.OptionParser( |
| 926 'Usage: %prog json [-p POLICY] [--keep] <first-dump>') |
| 883 parser.add_option('-p', '--policy', type='string', dest='policy', | 927 parser.add_option('-p', '--policy', type='string', dest='policy', |
| 884 help='profile with POLICY', metavar='POLICY') | 928 help='profile with POLICY', metavar='POLICY') |
| 929 parser.add_option('--keep', dest='keep', action='store_true') |
| 885 options, args = parser.parse_args(sys_argv) | 930 options, args = parser.parse_args(sys_argv) |
| 886 | 931 |
| 887 if len(args) != 2: | 932 if len(args) != 2: |
| 888 parser.error('needs 1 argument.') | 933 parser.error('needs 1 argument.') |
| 889 return 1 | 934 return 1 |
| 890 | 935 |
| 891 dump_path = args[1] | 936 dump_path = args[1] |
| 892 | 937 |
| 893 prefix = find_prefix(dump_path) | 938 buckets, dumps, appeared_addresses, delayed_static_symbols, symbols = ( |
| 894 buckets = load_buckets(prefix) | 939 load_basic_files_with_multiple_dumps(dump_path, options.keep)) |
| 895 dumps, appeared_addresses = load_dumps( | |
| 896 determine_dump_path_list(dump_path, prefix), buckets) | |
| 897 symbols = load_and_update_symbol_cache(prefix, appeared_addresses) | |
| 898 policies = load_policies(options.policy) | 940 policies = load_policies(options.policy) |
| 899 | 941 |
| 900 json_base = { | 942 json_base = { |
| 901 'version': 'JSON_DEEP_2', | 943 'version': 'JSON_DEEP_2', |
| 902 'policies': {}, | 944 'policies': {}, |
| 903 } | 945 } |
| 904 | 946 |
| 905 for policy in sorted(policies): | 947 for policy in sorted(policies): |
| 906 rule_list = policies[policy].rules | 948 rule_list = policies[policy].rules |
| 907 components = policies[policy].components | 949 components = policies[policy].components |
| (...skipping 13 matching lines...) Expand all Loading... |
| 921 | 963 |
| 922 for bucket in buckets.itervalues(): | 964 for bucket in buckets.itervalues(): |
| 923 bucket.clear_component_cache() | 965 bucket.clear_component_cache() |
| 924 | 966 |
| 925 json.dump(json_base, sys.stdout, indent=2, sort_keys=True) | 967 json.dump(json_base, sys.stdout, indent=2, sort_keys=True) |
| 926 | 968 |
| 927 return 0 | 969 return 0 |
| 928 | 970 |
| 929 | 971 |
| 930 def do_list(sys_argv): | 972 def do_list(sys_argv): |
| 931 parser = optparse.OptionParser('Usage: %prog [-p POLICY] list <first-dump>') | 973 parser = optparse.OptionParser( |
| 974 'Usage: %prog [-p POLICY] [--keep] list <first-dump>') |
| 932 parser.add_option('-p', '--policy', type='string', dest='policy', | 975 parser.add_option('-p', '--policy', type='string', dest='policy', |
| 933 help='profile with POLICY', metavar='POLICY') | 976 help='profile with POLICY', metavar='POLICY') |
| 977 parser.add_option('--keep', dest='keep', action='store_true') |
| 934 options, args = parser.parse_args(sys_argv) | 978 options, args = parser.parse_args(sys_argv) |
| 935 | 979 |
| 936 if len(args) != 2: | 980 if len(args) != 2: |
| 937 parser.error('needs 1 argument.') | 981 parser.error('needs 1 argument.') |
| 938 return 1 | 982 return 1 |
| 939 | 983 |
| 940 dump_path = args[1] | 984 dump_path = args[1] |
| 941 | 985 |
| 942 prefix = find_prefix(dump_path) | 986 buckets, dumps, appeared_addresses, delayed_static_symbols, symbols = ( |
| 943 buckets = load_buckets(prefix) | 987 load_basic_files_with_multiple_dumps(dump_path, options.keep)) |
| 944 dumps, appeared_addresses = load_dumps( | |
| 945 determine_dump_path_list(dump_path, prefix), buckets) | |
| 946 symbols = load_and_update_symbol_cache(prefix, appeared_addresses) | |
| 947 policies = load_policies(options.policy) | 988 policies = load_policies(options.policy) |
| 948 | 989 |
| 949 for policy in sorted(policies): | 990 for policy in sorted(policies): |
| 950 rule_list = policies[policy].rules | 991 rule_list = policies[policy].rules |
| 951 components = policies[policy].components | 992 components = policies[policy].components |
| 952 | 993 |
| 953 component_sizes = dumps[0].apply_policy( | 994 component_sizes = dumps[0].apply_policy( |
| 954 rule_list, buckets, dumps[0].dump_time, components, symbols) | 995 rule_list, buckets, dumps[0].dump_time, components, symbols) |
| 955 sys.stdout.write('%s:\n' % policy) | 996 sys.stdout.write('%s:\n' % policy) |
| 956 for c in components: | 997 for c in components: |
| 957 if c in ['hour', 'minute', 'second']: | 998 if c in ['hour', 'minute', 'second']: |
| 958 sys.stdout.write('%30s %10.3f\n' % (c, component_sizes[c])) | 999 sys.stdout.write('%30s %10.3f\n' % (c, component_sizes[c])) |
| 959 else: | 1000 else: |
| 960 sys.stdout.write('%30s %10.3f\n' % ( | 1001 sys.stdout.write('%30s %10.3f\n' % ( |
| 961 c, component_sizes[c] / 1024.0 / 1024.0)) | 1002 c, component_sizes[c] / 1024.0 / 1024.0)) |
| 962 | 1003 |
| 963 for bucket in buckets.itervalues(): | 1004 for bucket in buckets.itervalues(): |
| 964 bucket.clear_component_cache() | 1005 bucket.clear_component_cache() |
| 965 | 1006 |
| 966 return 0 | 1007 return 0 |
| 967 | 1008 |
| 968 | 1009 |
| 969 def do_expand(sys_argv): | 1010 def do_expand(sys_argv): |
| 970 parser = optparse.OptionParser( | 1011 parser = optparse.OptionParser( |
| 971 'Usage: %prog expand <dump> <policy> <component> <depth>') | 1012 'Usage: %prog expand [--keep] <dump> <policy> <component> <depth>') |
| 1013 parser.add_option('--keep', dest='keep', action='store_true') |
| 972 options, args = parser.parse_args(sys_argv) | 1014 options, args = parser.parse_args(sys_argv) |
| 973 | 1015 |
| 974 if len(args) != 5: | 1016 if len(args) != 5: |
| 975 parser.error('needs 4 arguments.') | 1017 parser.error('needs 4 arguments.') |
| 976 return 1 | 1018 return 1 |
| 977 | 1019 |
| 978 dump_path = args[1] | 1020 dump_path = args[1] |
| 979 target_policy = args[2] | 1021 target_policy = args[2] |
| 980 component_name = args[3] | 1022 component_name = args[3] |
| 981 depth = args[4] | 1023 depth = args[4] |
| 982 | 1024 |
| 983 prefix = find_prefix(dump_path) | 1025 buckets, dump, appeared_addresses, delayed_static_symbols, symbols = ( |
| 984 buckets = load_buckets(prefix) | 1026 load_basic_files_with_single_dump(dump_path, options.keep)) |
| 985 dump, appeared_addresses = load_dump(dump_path, buckets) | |
| 986 symbols = load_and_update_symbol_cache(prefix, appeared_addresses) | |
| 987 policies = load_policies(target_policy) | 1027 policies = load_policies(target_policy) |
| 988 | 1028 |
| 989 rule_list = policies[target_policy].rules | 1029 rule_list = policies[target_policy].rules |
| 990 | 1030 |
| 991 dump.expand(rule_list, buckets, component_name, int(depth), symbols) | 1031 dump.expand(rule_list, buckets, component_name, int(depth), symbols) |
| 992 | 1032 |
| 993 return 0 | 1033 return 0 |
| 994 | 1034 |
| 995 | 1035 |
| 996 def do_pprof(sys_argv): | 1036 def do_pprof(sys_argv): |
| 997 parser = optparse.OptionParser( | 1037 parser = optparse.OptionParser( |
| 998 'Usage: %prog pprof [-c COMPONENT] <dump> <policy>') | 1038 'Usage: %prog pprof [-c COMPONENT] [--keep] <dump> <policy>') |
| 999 parser.add_option('-c', '--component', type='string', dest='component', | 1039 parser.add_option('-c', '--component', type='string', dest='component', |
| 1000 help='restrict to COMPONENT', metavar='COMPONENT') | 1040 help='restrict to COMPONENT', metavar='COMPONENT') |
| 1041 parser.add_option('--keep', dest='keep', action='store_true') |
| 1001 options, args = parser.parse_args(sys_argv) | 1042 options, args = parser.parse_args(sys_argv) |
| 1002 | 1043 |
| 1003 if len(args) != 3: | 1044 if len(args) != 3: |
| 1004 parser.error('needs 2 arguments.') | 1045 parser.error('needs 2 arguments.') |
| 1005 return 1 | 1046 return 1 |
| 1006 | 1047 |
| 1007 dump_path = args[1] | 1048 dump_path = args[1] |
| 1008 target_policy = args[2] | 1049 target_policy = args[2] |
| 1009 component = options.component | 1050 component = options.component |
| 1010 | 1051 |
| 1011 prefix = find_prefix(dump_path) | 1052 buckets, dump, appeared_addresses, delayed_static_symbols, symbols = ( |
| 1012 buckets = load_buckets(prefix) | 1053 load_basic_files_with_single_dump(dump_path, options.keep)) |
| 1013 dump, appeared_addresses = load_dump(dump_path, buckets) | |
| 1014 symbols = load_and_update_symbol_cache(prefix, appeared_addresses) | |
| 1015 policies = load_policies(target_policy) | 1054 policies = load_policies(target_policy) |
| 1016 | 1055 |
| 1017 rule_list = policies[target_policy].rules | 1056 rule_list = policies[target_policy].rules |
| 1018 | 1057 |
| 1019 with open(prefix + '.maps', 'r') as maps_f: | 1058 with open(find_prefix(dump_path) + '.maps', 'r') as maps_f: |
| 1020 maps_lines = maps_f.readlines() | 1059 maps_lines = maps_f.readlines() |
| 1021 dump.print_for_pprof(rule_list, buckets, maps_lines, component, symbols) | 1060 dump.print_for_pprof(rule_list, buckets, maps_lines, component, symbols) |
| 1022 | 1061 |
| 1023 return 0 | 1062 return 0 |
| 1024 | 1063 |
| 1025 | 1064 |
| 1026 def main(): | 1065 def main(): |
| 1027 COMMANDS = { | 1066 COMMANDS = { |
| 1028 'csv': do_csv, | 1067 'csv': do_csv, |
| 1029 'expand': do_expand, | 1068 'expand': do_expand, |
| (...skipping 18 matching lines...) Expand all Loading... |
| 1048 | 1087 |
| 1049 Commands: | 1088 Commands: |
| 1050 csv Classify memory usage in CSV | 1089 csv Classify memory usage in CSV |
| 1051 expand Show all stacktraces contained in the specified component | 1090 expand Show all stacktraces contained in the specified component |
| 1052 json Classify memory usage in JSON | 1091 json Classify memory usage in JSON |
| 1053 list Classify memory usage in simple listing format | 1092 list Classify memory usage in simple listing format |
| 1054 pprof Format the profile dump so that it can be processed by pprof | 1093 pprof Format the profile dump so that it can be processed by pprof |
| 1055 stacktrace Convert runtime addresses to symbol names | 1094 stacktrace Convert runtime addresses to symbol names |
| 1056 | 1095 |
| 1057 Quick Reference: | 1096 Quick Reference: |
| 1058 dmprof csv [-p POLICY] <first-dump> | 1097 dmprof csv [-p POLICY] [--keep] <first-dump> |
| 1059 dmprof expand <dump> <policy> <component> <depth> | 1098 dmprof expand [--keep] <dump> <policy> <component> <depth> |
| 1060 dmprof json [-p POLICY] <first-dump> | 1099 dmprof json [-p POLICY] [--keep] <first-dump> |
| 1061 dmprof list [-p POLICY] <first-dump> | 1100 dmprof list [-p POLICY] [--keep] <first-dump> |
| 1062 dmprof pprof [-c COMPONENT] <dump> <policy> | 1101 dmprof pprof [-c COMPONENT] [--keep] <dump> <policy> |
| 1063 dmprof stacktrace <dump> | 1102 dmprof stacktrace [--keep] <dump> |
| 1064 """ % (sys.argv[0])) | 1103 """ % (sys.argv[0])) |
| 1065 sys.exit(1) | 1104 sys.exit(1) |
| 1066 action = sys.argv.pop(1) | 1105 action = sys.argv.pop(1) |
| 1067 | 1106 |
| 1068 return COMMANDS[action](sys.argv) | 1107 return COMMANDS[action](sys.argv) |
| 1069 | 1108 |
| 1070 | 1109 |
| 1071 if __name__ == '__main__': | 1110 if __name__ == '__main__': |
| 1072 sys.exit(main()) | 1111 sys.exit(main()) |
| OLD | NEW |