Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(330)

Side by Side Diff: tools/deep_memory_profiler/dmprof.py

Issue 11417048: Retry: Add a first test for tools/deep_memory_profiler. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « tools/deep_memory_profiler/dmprof ('k') | tools/deep_memory_profiler/tests/dmprof_test.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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
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
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
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
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
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
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
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
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())
OLDNEW
« no previous file with comments | « tools/deep_memory_profiler/dmprof ('k') | tools/deep_memory_profiler/tests/dmprof_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698