OLD | NEW |
---|---|
1 #!/usr/bin/env python | |
M-A Ruel
2012/11/17 23:23:10
Why are you removing the shebang and removing the
Dai Mikurube (NOT FULLTIME)
2012/11/18 06:01:54
It's because now dmprof can be executed by the she
| |
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 # 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 | 2 # Use of this source code is governed by a BSD-style license that can be |
4 # found in the LICENSE file. | 3 # found in the LICENSE file. |
5 | 4 |
6 """The deep heap profiler script for Chrome.""" | 5 """The deep heap profiler script for Chrome.""" |
7 | 6 |
8 from datetime import datetime | 7 from datetime import datetime |
9 import json | 8 import json |
10 import logging | 9 import logging |
11 import optparse | 10 import optparse |
12 import os | 11 import os |
13 import re | 12 import re |
14 import shutil | |
15 import subprocess | |
16 import sys | 13 import sys |
17 import tempfile | |
18 | 14 |
19 BASE_PATH = os.path.dirname(os.path.abspath(__file__)) | 15 BASE_PATH = os.path.dirname(os.path.abspath(__file__)) |
20 FIND_RUNTIME_SYMBOLS_PATH = os.path.join( | 16 FIND_RUNTIME_SYMBOLS_PATH = os.path.join( |
21 BASE_PATH, os.pardir, 'find_runtime_symbols') | 17 BASE_PATH, os.pardir, 'find_runtime_symbols') |
22 sys.path.append(FIND_RUNTIME_SYMBOLS_PATH) | 18 sys.path.append(FIND_RUNTIME_SYMBOLS_PATH) |
23 | 19 |
24 from find_runtime_symbols import find_runtime_symbols_list | 20 from find_runtime_symbols import find_runtime_symbols_list |
25 from find_runtime_symbols import find_runtime_typeinfo_symbols_list | 21 from find_runtime_symbols import find_runtime_typeinfo_symbols_list |
26 from find_runtime_symbols import RuntimeSymbolsInProcess | 22 from find_runtime_symbols import RuntimeSymbolsInProcess |
27 from prepare_symbol_info import prepare_symbol_info | 23 from prepare_symbol_info import prepare_symbol_info |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
72 | 68 |
73 # POLICY_DEEP_3 is in JSON format. | 69 # POLICY_DEEP_3 is in JSON format. |
74 POLICY_DEEP_3 = 'POLICY_DEEP_3' | 70 POLICY_DEEP_3 = 'POLICY_DEEP_3' |
75 | 71 |
76 # POLICY_DEEP_3 contains typeinfo. | 72 # POLICY_DEEP_3 contains typeinfo. |
77 POLICY_DEEP_4 = 'POLICY_DEEP_4' | 73 POLICY_DEEP_4 = 'POLICY_DEEP_4' |
78 | 74 |
79 | 75 |
80 class EmptyDumpException(Exception): | 76 class EmptyDumpException(Exception): |
81 def __init__(self, value): | 77 def __init__(self, value): |
78 super(EmptyDumpException, self).__init__() | |
82 self.value = value | 79 self.value = value |
83 def __str__(self): | 80 def __str__(self): |
84 return repr(self.value) | 81 return repr(self.value) |
85 | 82 |
86 | 83 |
87 class ParsingException(Exception): | 84 class ParsingException(Exception): |
88 def __init__(self, value): | 85 def __init__(self, value): |
86 super(ParsingException, self).__init__() | |
89 self.value = value | 87 self.value = value |
90 def __str__(self): | 88 def __str__(self): |
91 return repr(self.value) | 89 return repr(self.value) |
92 | 90 |
93 | 91 |
94 class InvalidDumpException(ParsingException): | 92 class InvalidDumpException(ParsingException): |
95 def __init__(self, value): | 93 def __init__(self, value): |
94 super(InvalidDumpException, self).__init__() | |
96 self.value = value | 95 self.value = value |
97 def __str__(self): | 96 def __str__(self): |
98 return "invalid heap profile dump: %s" % repr(self.value) | 97 return "invalid heap profile dump: %s" % repr(self.value) |
99 | 98 |
100 | 99 |
101 class ObsoleteDumpVersionException(ParsingException): | 100 class ObsoleteDumpVersionException(ParsingException): |
102 def __init__(self, value): | 101 def __init__(self, value): |
102 super(ObsoleteDumpVersionException, self).__init__() | |
103 self.value = value | 103 self.value = value |
104 def __str__(self): | 104 def __str__(self): |
105 return "obsolete heap profile dump version: %s" % repr(self.value) | 105 return "obsolete heap profile dump version: %s" % repr(self.value) |
106 | 106 |
107 | 107 |
108 def skip_while(index, max_index, skipping_condition): | 108 def skip_while(index, max_index, skipping_condition): |
109 """Increments |index| until |skipping_condition|(|index|) is False. | 109 """Increments |index| until |skipping_condition|(|index|) is False. |
110 | 110 |
111 Returns: | 111 Returns: |
112 A pair of an integer indicating a line number after skipped, and a | 112 A pair of an integer indicating a line number after skipped, and a |
(...skipping 148 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
261 try: | 261 try: |
262 with open(symbol_cache_path, mode='r') as symbol_f: | 262 with open(symbol_cache_path, mode='r') as symbol_f: |
263 for line in symbol_f: | 263 for line in symbol_f: |
264 items = line.rstrip().split(None, 1) | 264 items = line.rstrip().split(None, 1) |
265 if len(items) == 1: | 265 if len(items) == 1: |
266 items.append('??') | 266 items.append('??') |
267 self._symbol_caches[address_type][int(items[0], 16)] = items[1] | 267 self._symbol_caches[address_type][int(items[0], 16)] = items[1] |
268 LOGGER.info('Loaded %d entries from symbol cache.' % | 268 LOGGER.info('Loaded %d entries from symbol cache.' % |
269 len(self._symbol_caches[address_type])) | 269 len(self._symbol_caches[address_type])) |
270 except IOError as e: | 270 except IOError as e: |
271 LOGGER.info('No valid symbol cache file is found.') | 271 LOGGER.info('No valid symbol cache file is found: %s' % e) |
272 | 272 |
273 | 273 |
274 class Rule(object): | 274 class Rule(object): |
275 """Represents one matching rule in a policy file.""" | 275 """Represents one matching rule in a policy file.""" |
276 | 276 |
277 def __init__(self, name, mmap, stacktrace_pattern, typeinfo_pattern=None): | 277 def __init__(self, name, mmap, stacktrace_pattern, typeinfo_pattern=None): |
278 self._name = name | 278 self._name = name |
279 self._mmap = mmap | 279 self._mmap = mmap |
280 self._stacktrace_pattern = re.compile(stacktrace_pattern + r'\Z') | 280 self._stacktrace_pattern = re.compile(stacktrace_pattern + r'\Z') |
281 if typeinfo_pattern: | 281 if typeinfo_pattern: |
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
342 for rule in self._rules: | 342 for rule in self._rules: |
343 if (bucket.mmap == rule.mmap and | 343 if (bucket.mmap == rule.mmap and |
344 rule.stacktrace_pattern.match(stacktrace) and | 344 rule.stacktrace_pattern.match(stacktrace) and |
345 (not rule.typeinfo_pattern or rule.typeinfo_pattern.match(typeinfo))): | 345 (not rule.typeinfo_pattern or rule.typeinfo_pattern.match(typeinfo))): |
346 bucket.component_cache = rule.name | 346 bucket.component_cache = rule.name |
347 return rule.name | 347 return rule.name |
348 | 348 |
349 assert False | 349 assert False |
350 | 350 |
351 @staticmethod | 351 @staticmethod |
352 def load(filename, format): | 352 def load(filename, filetype): |
353 """Loads a policy file of |filename| in a |format|. | 353 """Loads a policy file of |filename| in a |format|. |
354 | 354 |
355 Args: | 355 Args: |
356 filename: A filename to be loaded. | 356 filename: A filename to be loaded. |
357 format: A string to specify a format of the file. Only 'json' is | 357 filetype: A string to specify a type of the file. Only 'json' is |
358 supported for now. | 358 supported for now. |
359 | 359 |
360 Returns: | 360 Returns: |
361 A loaded Policy object. | 361 A loaded Policy object. |
362 """ | 362 """ |
363 with open(os.path.join(BASE_PATH, filename)) as policy_f: | 363 with open(os.path.join(BASE_PATH, filename)) as policy_f: |
364 return Policy.parse(policy_f, format) | 364 return Policy.parse(policy_f, filetype) |
365 | 365 |
366 @staticmethod | 366 @staticmethod |
367 def parse(policy_f, format): | 367 def parse(policy_f, filetype): |
368 """Parses a policy file content in a |format|. | 368 """Parses a policy file content in a |format|. |
369 | 369 |
370 Args: | 370 Args: |
371 policy_f: An IO object to be loaded. | 371 policy_f: An IO object to be loaded. |
372 format: A string to specify a format of the file. Only 'json' is | 372 filetype: A string to specify a type of the file. Only 'json' is |
373 supported for now. | 373 supported for now. |
374 | 374 |
375 Returns: | 375 Returns: |
376 A loaded Policy object. | 376 A loaded Policy object. |
377 """ | 377 """ |
378 if format == 'json': | 378 if filetype == 'json': |
379 return Policy._parse_json(policy_f) | 379 return Policy._parse_json(policy_f) |
380 else: | 380 else: |
381 return None | 381 return None |
382 | 382 |
383 @staticmethod | 383 @staticmethod |
384 def _parse_json(policy_f): | 384 def _parse_json(policy_f): |
385 """Parses policy file in json format. | 385 """Parses policy file in json format. |
386 | 386 |
387 A policy file contains component's names and their stacktrace pattern | 387 A policy file contains component's names and their stacktrace pattern |
388 written in regular expression. Those patterns are matched against each | 388 written in regular expression. Those patterns are matched against each |
(...skipping 147 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
536 | 536 |
537 class BucketSet(object): | 537 class BucketSet(object): |
538 """Represents a set of bucket.""" | 538 """Represents a set of bucket.""" |
539 def __init__(self): | 539 def __init__(self): |
540 self._buckets = {} | 540 self._buckets = {} |
541 self._addresses = { | 541 self._addresses = { |
542 FUNCTION_ADDRESS: set(), | 542 FUNCTION_ADDRESS: set(), |
543 TYPEINFO_ADDRESS: set(), | 543 TYPEINFO_ADDRESS: set(), |
544 } | 544 } |
545 | 545 |
546 @staticmethod | 546 def load(self, prefix): |
547 def load(prefix): | |
548 """Loads all related bucket files. | 547 """Loads all related bucket files. |
549 | 548 |
550 Args: | 549 Args: |
551 prefix: A prefix string for bucket file names. | 550 prefix: A prefix string for bucket file names. |
552 | |
553 Returns: | |
554 A loaded BucketSet object. | |
555 """ | 551 """ |
556 LOGGER.info('Loading bucket files.') | 552 LOGGER.info('Loading bucket files.') |
557 bucket_set = BucketSet() | |
558 | 553 |
559 n = 0 | 554 n = 0 |
560 while True: | 555 while True: |
561 path = '%s.%04d.buckets' % (prefix, n) | 556 path = '%s.%04d.buckets' % (prefix, n) |
562 if not os.path.exists(path): | 557 if not os.path.exists(path): |
563 if n > 10: | 558 if n > 10: |
564 break | 559 break |
565 n += 1 | 560 n += 1 |
566 continue | 561 continue |
567 LOGGER.info(' %s' % path) | 562 LOGGER.info(' %s' % path) |
568 with open(path, 'r') as f: | 563 with open(path, 'r') as f: |
569 bucket_set._load_file(f) | 564 self._load_file(f) |
570 n += 1 | 565 n += 1 |
571 | 566 |
572 return bucket_set | |
573 | |
574 def _load_file(self, bucket_f): | 567 def _load_file(self, bucket_f): |
575 for line in bucket_f: | 568 for line in bucket_f: |
576 words = line.split() | 569 words = line.split() |
577 typeinfo = None | 570 typeinfo = None |
578 typeinfo_name = '' | 571 typeinfo_name = '' |
579 stacktrace_begin = 2 | 572 stacktrace_begin = 2 |
580 for index, word in enumerate(words): | 573 for index, word in enumerate(words): |
581 if index < 2: | 574 if index < 2: |
582 continue | 575 continue |
583 if word[0] == 't': | 576 if word[0] == 't': |
(...skipping 29 matching lines...) Expand all Loading... | |
613 bucket_content.clear_component_cache() | 606 bucket_content.clear_component_cache() |
614 | 607 |
615 def iter_addresses(self, address_type): | 608 def iter_addresses(self, address_type): |
616 for function in self._addresses[address_type]: | 609 for function in self._addresses[address_type]: |
617 yield function | 610 yield function |
618 | 611 |
619 | 612 |
620 class Dump(object): | 613 class Dump(object): |
621 """Represents a heap profile dump.""" | 614 """Represents a heap profile dump.""" |
622 | 615 |
623 def __init__(self): | 616 def __init__(self, path, time): |
624 self._path = '' | 617 self._path = path |
625 self._time = None | 618 self._time = time |
626 self._stacktrace_lines = [] | 619 self._stacktrace_lines = [] |
627 self._global_stats = {} # used only in apply_policy | 620 self._global_stats = {} # used only in apply_policy |
628 | 621 |
629 self._version = '' | 622 self._version = '' |
630 self._lines = [] | 623 self._lines = [] |
631 | 624 |
632 @property | 625 @property |
633 def path(self): | 626 def path(self): |
634 return self._path | 627 return self._path |
635 | 628 |
(...skipping 16 matching lines...) Expand all Loading... | |
652 Args: | 645 Args: |
653 path: A file path string to load. | 646 path: A file path string to load. |
654 log_header: A preceding string for log messages. | 647 log_header: A preceding string for log messages. |
655 | 648 |
656 Returns: | 649 Returns: |
657 A loaded Dump object. | 650 A loaded Dump object. |
658 | 651 |
659 Raises: | 652 Raises: |
660 ParsingException for invalid heap profile dumps. | 653 ParsingException for invalid heap profile dumps. |
661 """ | 654 """ |
662 dump = Dump() | 655 dump = Dump(path, os.stat(path).st_mtime) |
663 dump._path = path | 656 with open(path, 'r') as f: |
664 dump._time = os.stat(dump._path).st_mtime | 657 dump.load_file(f, log_header) |
665 dump._version = '' | 658 return dump |
666 | 659 |
667 dump._lines = [line for line in open(dump._path, 'r') | 660 def load_file(self, f, log_header): |
661 self._lines = [line for line in f | |
668 if line and not line.startswith('#')] | 662 if line and not line.startswith('#')] |
669 | 663 |
670 try: | 664 try: |
671 dump._version, ln = dump._parse_version() | 665 self._version, ln = self._parse_version() |
672 dump._parse_global_stats() | 666 self._parse_global_stats() |
673 dump._extract_stacktrace_lines(ln) | 667 self._extract_stacktrace_lines(ln) |
674 except EmptyDumpException: | 668 except EmptyDumpException: |
675 LOGGER.info('%s%s ...ignored an empty dump.' % (log_header, path)) | 669 LOGGER.info('%s%s ...ignored an empty dump.' % (log_header, self._path)) |
676 except ParsingException, e: | 670 except ParsingException, e: |
677 LOGGER.error('%s%s ...error %s' % (log_header, path, e)) | 671 LOGGER.error('%s%s ...error %s' % (log_header, self._path, e)) |
678 raise | 672 raise |
679 else: | 673 else: |
680 LOGGER.info('%s%s (version: %s)' % (log_header, path, dump._version)) | 674 LOGGER.info('%s%s (version:%s)' % (log_header, self._path, self._version)) |
681 | |
682 return dump | |
683 | 675 |
684 def _parse_version(self): | 676 def _parse_version(self): |
685 """Parses a version string in self._lines. | 677 """Parses a version string in self._lines. |
686 | 678 |
687 Returns: | 679 Returns: |
688 A pair of (a string representing a version of the stacktrace dump, | 680 A pair of (a string representing a version of the stacktrace dump, |
689 and an integer indicating a line number next to the version string). | 681 and an integer indicating a line number next to the version string). |
690 | 682 |
691 Raises: | 683 Raises: |
692 ParsingException for invalid dump versions. | 684 ParsingException for invalid dump versions. |
(...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
818 See COMMANDS in main(). | 810 See COMMANDS in main(). |
819 """ | 811 """ |
820 def __init__(self, usage): | 812 def __init__(self, usage): |
821 self._parser = optparse.OptionParser(usage) | 813 self._parser = optparse.OptionParser(usage) |
822 | 814 |
823 @staticmethod | 815 @staticmethod |
824 def load_basic_files(dump_path, multiple): | 816 def load_basic_files(dump_path, multiple): |
825 prefix = Command._find_prefix(dump_path) | 817 prefix = Command._find_prefix(dump_path) |
826 symbol_mapping = SymbolMapping(prefix) | 818 symbol_mapping = SymbolMapping(prefix) |
827 symbol_mapping.prepare() | 819 symbol_mapping.prepare() |
828 bucket_set = BucketSet.load(prefix) | 820 bucket_set = BucketSet() |
821 bucket_set.load(prefix) | |
829 if multiple: | 822 if multiple: |
830 dump_list = DumpList.load(Command._find_all_dumps(dump_path)) | 823 dump_list = DumpList.load(Command._find_all_dumps(dump_path)) |
831 else: | 824 else: |
832 dump = Dump.load(dump_path) | 825 dump = Dump.load(dump_path) |
833 symbol_cache = SymbolCache(prefix) | 826 symbol_cache = SymbolCache(prefix) |
834 symbol_cache.update(FUNCTION_ADDRESS, bucket_set, symbol_mapping) | 827 symbol_cache.update(FUNCTION_ADDRESS, bucket_set, symbol_mapping) |
835 symbol_cache.update(TYPEINFO_ADDRESS, bucket_set, symbol_mapping) | 828 symbol_cache.update(TYPEINFO_ADDRESS, bucket_set, symbol_mapping) |
836 bucket_set.symbolize(symbol_cache) | 829 bucket_set.symbolize(symbol_cache) |
837 if multiple: | 830 if multiple: |
838 return (bucket_set, dump_list) | 831 return (bucket_set, dump_list) |
(...skipping 21 matching lines...) Expand all Loading... | |
860 | 853 |
861 return dump_path_list | 854 return dump_path_list |
862 | 855 |
863 def _parse_args(self, sys_argv, required): | 856 def _parse_args(self, sys_argv, required): |
864 options, args = self._parser.parse_args(sys_argv) | 857 options, args = self._parser.parse_args(sys_argv) |
865 if len(args) != required + 1: | 858 if len(args) != required + 1: |
866 self._parser.error('needs %d argument(s).\n' % required) | 859 self._parser.error('needs %d argument(s).\n' % required) |
867 return None | 860 return None |
868 return (options, args) | 861 return (options, args) |
869 | 862 |
870 def _parse_policy_list(self, options_policy): | 863 @staticmethod |
864 def _parse_policy_list(options_policy): | |
871 if options_policy: | 865 if options_policy: |
872 return options_policy.split(',') | 866 return options_policy.split(',') |
873 else: | 867 else: |
874 return None | 868 return None |
875 | 869 |
876 | 870 |
877 class StacktraceCommand(Command): | 871 class StacktraceCommand(Command): |
878 def __init__(self): | 872 def __init__(self): |
879 super(StacktraceCommand, self).__init__( | 873 super(StacktraceCommand, self).__init__( |
880 'Usage: %prog stacktrace <dump>') | 874 'Usage: %prog stacktrace <dump>') |
881 | 875 |
882 def do(self, sys_argv): | 876 def do(self, sys_argv): |
883 options, args = self._parse_args(sys_argv, 1) | 877 _, args = self._parse_args(sys_argv, 1) |
884 dump_path = args[1] | 878 dump_path = args[1] |
885 (bucket_set, dump) = Command.load_basic_files(dump_path, False) | 879 (bucket_set, dump) = Command.load_basic_files(dump_path, False) |
886 | 880 |
887 StacktraceCommand._output(dump, bucket_set, sys.stdout) | 881 StacktraceCommand._output(dump, bucket_set, sys.stdout) |
888 return 0 | 882 return 0 |
889 | 883 |
890 @staticmethod | 884 @staticmethod |
891 def _output(dump, bucket_set, out): | 885 def _output(dump, bucket_set, out): |
892 """Outputs a given stacktrace. | 886 """Outputs a given stacktrace. |
893 | 887 |
(...skipping 18 matching lines...) Expand all Loading... | |
912 super(PolicyCommands, self).__init__( | 906 super(PolicyCommands, self).__init__( |
913 'Usage: %%prog %s [-p POLICY] <first-dump>' % command) | 907 'Usage: %%prog %s [-p POLICY] <first-dump>' % command) |
914 self._parser.add_option('-p', '--policy', type='string', dest='policy', | 908 self._parser.add_option('-p', '--policy', type='string', dest='policy', |
915 help='profile with POLICY', metavar='POLICY') | 909 help='profile with POLICY', metavar='POLICY') |
916 | 910 |
917 def _set_up(self, sys_argv): | 911 def _set_up(self, sys_argv): |
918 options, args = self._parse_args(sys_argv, 1) | 912 options, args = self._parse_args(sys_argv, 1) |
919 dump_path = args[1] | 913 dump_path = args[1] |
920 (bucket_set, dumps) = Command.load_basic_files(dump_path, True) | 914 (bucket_set, dumps) = Command.load_basic_files(dump_path, True) |
921 | 915 |
922 policy_set = PolicySet.load(self._parse_policy_list(options.policy)) | 916 policy_set = PolicySet.load(Command._parse_policy_list(options.policy)) |
923 return policy_set, dumps, bucket_set | 917 return policy_set, dumps, bucket_set |
924 | 918 |
925 def _apply_policy(self, dump, policy, bucket_set, first_dump_time): | 919 @staticmethod |
920 def _apply_policy(dump, policy, bucket_set, first_dump_time): | |
926 """Aggregates the total memory size of each component. | 921 """Aggregates the total memory size of each component. |
927 | 922 |
928 Iterate through all stacktraces and attribute them to one of the components | 923 Iterate through all stacktraces and attribute them to one of the components |
929 based on the policy. It is important to apply policy in right order. | 924 based on the policy. It is important to apply policy in right order. |
930 | 925 |
931 Args: | 926 Args: |
932 dump: A Dump object. | 927 dump: A Dump object. |
933 policy: A Policy object. | 928 policy: A Policy object. |
934 bucket_set: A BucketSet object. | 929 bucket_set: A BucketSet object. |
935 first_dump_time: An integer representing time when the first dump is | 930 first_dump_time: An integer representing time when the first dump is |
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1019 else: | 1014 else: |
1020 sizes['other-total-log'] += int(words[COMMITTED]) | 1015 sizes['other-total-log'] += int(words[COMMITTED]) |
1021 | 1016 |
1022 | 1017 |
1023 class CSVCommand(PolicyCommands): | 1018 class CSVCommand(PolicyCommands): |
1024 def __init__(self): | 1019 def __init__(self): |
1025 super(CSVCommand, self).__init__('csv') | 1020 super(CSVCommand, self).__init__('csv') |
1026 | 1021 |
1027 def do(self, sys_argv): | 1022 def do(self, sys_argv): |
1028 policy_set, dumps, bucket_set = self._set_up(sys_argv) | 1023 policy_set, dumps, bucket_set = self._set_up(sys_argv) |
1029 return self._output(policy_set, dumps, bucket_set, sys.stdout) | 1024 return CSVCommand._output(policy_set, dumps, bucket_set, sys.stdout) |
1030 | 1025 |
1031 def _output(self, policy_set, dumps, bucket_set, out): | 1026 @staticmethod |
1027 def _output(policy_set, dumps, bucket_set, out): | |
1032 max_components = 0 | 1028 max_components = 0 |
1033 for label in policy_set: | 1029 for label in policy_set: |
1034 max_components = max(max_components, len(policy_set[label].components)) | 1030 max_components = max(max_components, len(policy_set[label].components)) |
1035 | 1031 |
1036 for label in sorted(policy_set): | 1032 for label in sorted(policy_set): |
1037 components = policy_set[label].components | 1033 components = policy_set[label].components |
1038 if len(policy_set) > 1: | 1034 if len(policy_set) > 1: |
1039 out.write('%s%s\n' % (label, ',' * (max_components - 1))) | 1035 out.write('%s%s\n' % (label, ',' * (max_components - 1))) |
1040 out.write('%s%s\n' % ( | 1036 out.write('%s%s\n' % ( |
1041 ','.join(components), ',' * (max_components - len(components)))) | 1037 ','.join(components), ',' * (max_components - len(components)))) |
1042 | 1038 |
1043 LOGGER.info('Applying a policy %s to...' % label) | 1039 LOGGER.info('Applying a policy %s to...' % label) |
1044 for dump in dumps: | 1040 for dump in dumps: |
1045 component_sizes = self._apply_policy( | 1041 component_sizes = PolicyCommands._apply_policy( |
1046 dump, policy_set[label], bucket_set, dumps[0].time) | 1042 dump, policy_set[label], bucket_set, dumps[0].time) |
1047 s = [] | 1043 s = [] |
1048 for c in components: | 1044 for c in components: |
1049 if c in ('hour', 'minute', 'second'): | 1045 if c in ('hour', 'minute', 'second'): |
1050 s.append('%05.5f' % (component_sizes[c])) | 1046 s.append('%05.5f' % (component_sizes[c])) |
1051 else: | 1047 else: |
1052 s.append('%05.5f' % (component_sizes[c] / 1024.0 / 1024.0)) | 1048 s.append('%05.5f' % (component_sizes[c] / 1024.0 / 1024.0)) |
1053 out.write('%s%s\n' % ( | 1049 out.write('%s%s\n' % ( |
1054 ','.join(s), ',' * (max_components - len(components)))) | 1050 ','.join(s), ',' * (max_components - len(components)))) |
1055 | 1051 |
1056 bucket_set.clear_component_cache() | 1052 bucket_set.clear_component_cache() |
1057 | 1053 |
1058 return 0 | 1054 return 0 |
1059 | 1055 |
1060 | 1056 |
1061 class JSONCommand(PolicyCommands): | 1057 class JSONCommand(PolicyCommands): |
1062 def __init__(self): | 1058 def __init__(self): |
1063 super(JSONCommand, self).__init__('json') | 1059 super(JSONCommand, self).__init__('json') |
1064 | 1060 |
1065 def do(self, sys_argv): | 1061 def do(self, sys_argv): |
1066 policy_set, dumps, bucket_set = self._set_up(sys_argv) | 1062 policy_set, dumps, bucket_set = self._set_up(sys_argv) |
1067 return self._output(policy_set, dumps, bucket_set, sys.stdout) | 1063 return JSONCommand._output(policy_set, dumps, bucket_set, sys.stdout) |
1068 | 1064 |
1069 def _output(self, policy_set, dumps, bucket_set, out): | 1065 @staticmethod |
1066 def _output(policy_set, dumps, bucket_set, out): | |
1070 json_base = { | 1067 json_base = { |
1071 'version': 'JSON_DEEP_2', | 1068 'version': 'JSON_DEEP_2', |
1072 'policies': {}, | 1069 'policies': {}, |
1073 } | 1070 } |
1074 | 1071 |
1075 for label in sorted(policy_set): | 1072 for label in sorted(policy_set): |
1076 json_base['policies'][label] = { | 1073 json_base['policies'][label] = { |
1077 'legends': policy_set[label].components, | 1074 'legends': policy_set[label].components, |
1078 'snapshots': [], | 1075 'snapshots': [], |
1079 } | 1076 } |
1080 | 1077 |
1081 LOGGER.info('Applying a policy %s to...' % label) | 1078 LOGGER.info('Applying a policy %s to...' % label) |
1082 for dump in dumps: | 1079 for dump in dumps: |
1083 component_sizes = self._apply_policy( | 1080 component_sizes = PolicyCommands._apply_policy( |
1084 dump, policy_set[label], bucket_set, dumps[0].time) | 1081 dump, policy_set[label], bucket_set, dumps[0].time) |
1085 component_sizes['dump_path'] = dump.path | 1082 component_sizes['dump_path'] = dump.path |
1086 component_sizes['dump_time'] = datetime.fromtimestamp( | 1083 component_sizes['dump_time'] = datetime.fromtimestamp( |
1087 dump.time).strftime('%Y-%m-%d %H:%M:%S') | 1084 dump.time).strftime('%Y-%m-%d %H:%M:%S') |
1088 json_base['policies'][label]['snapshots'].append(component_sizes) | 1085 json_base['policies'][label]['snapshots'].append(component_sizes) |
1089 | 1086 |
1090 bucket_set.clear_component_cache() | 1087 bucket_set.clear_component_cache() |
1091 | 1088 |
1092 json.dump(json_base, out, indent=2, sort_keys=True) | 1089 json.dump(json_base, out, indent=2, sort_keys=True) |
1093 | 1090 |
1094 return 0 | 1091 return 0 |
1095 | 1092 |
1096 | 1093 |
1097 class ListCommand(PolicyCommands): | 1094 class ListCommand(PolicyCommands): |
1098 def __init__(self): | 1095 def __init__(self): |
1099 super(ListCommand, self).__init__('list') | 1096 super(ListCommand, self).__init__('list') |
1100 | 1097 |
1101 def do(self, sys_argv): | 1098 def do(self, sys_argv): |
1102 policy_set, dumps, bucket_set = self._set_up(sys_argv) | 1099 policy_set, dumps, bucket_set = self._set_up(sys_argv) |
1103 return self._output(policy_set, dumps, bucket_set, sys.stdout) | 1100 return ListCommand._output(policy_set, dumps, bucket_set, sys.stdout) |
1104 | 1101 |
1105 def _output(self, policy_set, dumps, bucket_set, out): | 1102 @staticmethod |
1103 def _output(policy_set, dumps, bucket_set, out): | |
1106 for label in sorted(policy_set): | 1104 for label in sorted(policy_set): |
1107 LOGGER.info('Applying a policy %s to...' % label) | 1105 LOGGER.info('Applying a policy %s to...' % label) |
1108 for dump in dumps: | 1106 for dump in dumps: |
1109 component_sizes = self._apply_policy( | 1107 component_sizes = PolicyCommands._apply_policy( |
1110 dump, policy_set[label], bucket_set, dump.time) | 1108 dump, policy_set[label], bucket_set, dump.time) |
1111 out.write('%s for %s:\n' % (label, dump.path)) | 1109 out.write('%s for %s:\n' % (label, dump.path)) |
1112 for c in policy_set[label].components: | 1110 for c in policy_set[label].components: |
1113 if c in ['hour', 'minute', 'second']: | 1111 if c in ['hour', 'minute', 'second']: |
1114 out.write('%40s %12.3f\n' % (c, component_sizes[c])) | 1112 out.write('%40s %12.3f\n' % (c, component_sizes[c])) |
1115 else: | 1113 else: |
1116 out.write('%40s %12d\n' % (c, component_sizes[c])) | 1114 out.write('%40s %12d\n' % (c, component_sizes[c])) |
1117 | 1115 |
1118 bucket_set.clear_component_cache() | 1116 bucket_set.clear_component_cache() |
1119 | 1117 |
1120 return 0 | 1118 return 0 |
1121 | 1119 |
1122 | 1120 |
1123 class ExpandCommand(Command): | 1121 class ExpandCommand(Command): |
1124 def __init__(self): | 1122 def __init__(self): |
1125 super(ExpandCommand, self).__init__( | 1123 super(ExpandCommand, self).__init__( |
1126 'Usage: %prog expand <dump> <policy> <component> <depth>') | 1124 'Usage: %prog expand <dump> <policy> <component> <depth>') |
1127 | 1125 |
1128 def do(self, sys_argv): | 1126 def do(self, sys_argv): |
1129 options, args = self._parse_args(sys_argv, 4) | 1127 _, args = self._parse_args(sys_argv, 4) |
1130 dump_path = args[1] | 1128 dump_path = args[1] |
1131 target_policy = args[2] | 1129 target_policy = args[2] |
1132 component_name = args[3] | 1130 component_name = args[3] |
1133 depth = args[4] | 1131 depth = args[4] |
1134 (bucket_set, dump) = Command.load_basic_files(dump_path, False) | 1132 (bucket_set, dump) = Command.load_basic_files(dump_path, False) |
1135 policy_set = PolicySet.load(self._parse_policy_list(target_policy)) | 1133 policy_set = PolicySet.load(Command._parse_policy_list(target_policy)) |
1136 | 1134 |
1137 self._output(dump, policy_set[target_policy], bucket_set, | 1135 ExpandCommand._output(dump, policy_set[target_policy], bucket_set, |
1138 component_name, int(depth), sys.stdout) | 1136 component_name, int(depth), sys.stdout) |
1139 return 0 | 1137 return 0 |
1140 | 1138 |
1141 def _output(self, dump, policy, bucket_set, component_name, depth, out): | 1139 @staticmethod |
1140 def _output(dump, policy, bucket_set, component_name, depth, out): | |
1142 """Prints all stacktraces in a given component of given depth. | 1141 """Prints all stacktraces in a given component of given depth. |
1143 | 1142 |
1144 Args: | 1143 Args: |
1145 dump: A Dump object. | 1144 dump: A Dump object. |
1146 policy: A Policy object. | 1145 policy: A Policy object. |
1147 bucket_set: A BucketSet object. | 1146 bucket_set: A BucketSet object. |
1148 component_name: A name of component for filtering. | 1147 component_name: A name of component for filtering. |
1149 depth: An integer representing depth to be printed. | 1148 depth: An integer representing depth to be printed. |
1150 out: An IO object to output. | 1149 out: An IO object to output. |
1151 """ | 1150 """ |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1190 help='restrict to COMPONENT', metavar='COMPONENT') | 1189 help='restrict to COMPONENT', metavar='COMPONENT') |
1191 | 1190 |
1192 def do(self, sys_argv): | 1191 def do(self, sys_argv): |
1193 options, args = self._parse_args(sys_argv, 2) | 1192 options, args = self._parse_args(sys_argv, 2) |
1194 | 1193 |
1195 dump_path = args[1] | 1194 dump_path = args[1] |
1196 target_policy = args[2] | 1195 target_policy = args[2] |
1197 component = options.component | 1196 component = options.component |
1198 | 1197 |
1199 (bucket_set, dump) = Command.load_basic_files(dump_path, False) | 1198 (bucket_set, dump) = Command.load_basic_files(dump_path, False) |
1200 policy_set = PolicySet.load(self._parse_policy_list(target_policy)) | 1199 policy_set = PolicySet.load(Command._parse_policy_list(target_policy)) |
1201 | 1200 |
1202 with open(Command._find_prefix(dump_path) + '.maps', 'r') as maps_f: | 1201 with open(Command._find_prefix(dump_path) + '.maps', 'r') as maps_f: |
1203 maps_lines = maps_f.readlines() | 1202 maps_lines = maps_f.readlines() |
1204 PProfCommand._output( | 1203 PProfCommand._output( |
1205 dump, policy_set[target_policy], bucket_set, maps_lines, component, | 1204 dump, policy_set[target_policy], bucket_set, maps_lines, component, |
1206 sys.stdout) | 1205 sys.stdout) |
1207 | 1206 |
1208 return 0 | 1207 return 0 |
1209 | 1208 |
1210 @staticmethod | 1209 @staticmethod |
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1293 COMMANDS = { | 1292 COMMANDS = { |
1294 'csv': CSVCommand, | 1293 'csv': CSVCommand, |
1295 'expand': ExpandCommand, | 1294 'expand': ExpandCommand, |
1296 'json': JSONCommand, | 1295 'json': JSONCommand, |
1297 'list': ListCommand, | 1296 'list': ListCommand, |
1298 'pprof': PProfCommand, | 1297 'pprof': PProfCommand, |
1299 'stacktrace': StacktraceCommand, | 1298 'stacktrace': StacktraceCommand, |
1300 } | 1299 } |
1301 | 1300 |
1302 if len(sys.argv) < 2 or (not sys.argv[1] in COMMANDS): | 1301 if len(sys.argv) < 2 or (not sys.argv[1] in COMMANDS): |
1303 sys.stderr.write("""Usage: %s <command> [options] [<args>] | 1302 sys.stderr.write("""Usage: dmprof <command> [options] [<args>] |
1304 | 1303 |
1305 Commands: | 1304 Commands: |
1306 csv Classify memory usage in CSV | 1305 csv Classify memory usage in CSV |
1307 expand Show all stacktraces contained in the specified component | 1306 expand Show all stacktraces contained in the specified component |
1308 json Classify memory usage in JSON | 1307 json Classify memory usage in JSON |
1309 list Classify memory usage in simple listing format | 1308 list Classify memory usage in simple listing format |
1310 pprof Format the profile dump so that it can be processed by pprof | 1309 pprof Format the profile dump so that it can be processed by pprof |
1311 stacktrace Convert runtime addresses to symbol names | 1310 stacktrace Convert runtime addresses to symbol names |
1312 | 1311 |
1313 Quick Reference: | 1312 Quick Reference: |
1314 dmprof csv [-p POLICY] <first-dump> | 1313 dmprof csv [-p POLICY] <first-dump> |
1315 dmprof expand <dump> <policy> <component> <depth> | 1314 dmprof expand <dump> <policy> <component> <depth> |
1316 dmprof json [-p POLICY] <first-dump> | 1315 dmprof json [-p POLICY] <first-dump> |
1317 dmprof list [-p POLICY] <first-dump> | 1316 dmprof list [-p POLICY] <first-dump> |
1318 dmprof pprof [-c COMPONENT] <dump> <policy> | 1317 dmprof pprof [-c COMPONENT] <dump> <policy> |
1319 dmprof stacktrace <dump> | 1318 dmprof stacktrace <dump> |
1320 """ % (sys.argv[0])) | 1319 """) |
1321 sys.exit(1) | 1320 sys.exit(1) |
1322 action = sys.argv.pop(1) | 1321 action = sys.argv.pop(1) |
1323 | 1322 |
1324 LOGGER.setLevel(logging.DEBUG) | 1323 LOGGER.setLevel(logging.DEBUG) |
1325 handler = logging.StreamHandler() | 1324 handler = logging.StreamHandler() |
1326 handler.setLevel(logging.INFO) | 1325 handler.setLevel(logging.INFO) |
1327 formatter = logging.Formatter('%(message)s') | 1326 formatter = logging.Formatter('%(message)s') |
1328 handler.setFormatter(formatter) | 1327 handler.setFormatter(formatter) |
1329 LOGGER.addHandler(handler) | 1328 LOGGER.addHandler(handler) |
1330 | 1329 |
1331 try: | 1330 try: |
1332 errorcode = COMMANDS[action]().do(sys.argv) | 1331 errorcode = COMMANDS[action]().do(sys.argv) |
1333 except ParsingException, e: | 1332 except ParsingException, e: |
1334 errorcode = 1 | 1333 errorcode = 1 |
1335 sys.stderr.write('Exit by parsing error: %s\n' % e) | 1334 sys.stderr.write('Exit by parsing error: %s\n' % e) |
1336 | 1335 |
1337 return errorcode | 1336 return errorcode |
1338 | 1337 |
1339 | 1338 |
1340 if __name__ == '__main__': | 1339 if __name__ == '__main__': |
1341 sys.exit(main()) | 1340 sys.exit(main()) |
OLD | NEW |