Chromium Code Reviews| 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 |