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 |