Index: tools/deep_memory_profiler/dmprof |
diff --git a/tools/deep_memory_profiler/dmprof.py b/tools/deep_memory_profiler/dmprof |
similarity index 80% |
rename from tools/deep_memory_profiler/dmprof.py |
rename to tools/deep_memory_profiler/dmprof |
index e9c642c80d168cb82a2b455cabe0aa0efb16ccf0..fdf5a7c414ead3853c2f1ba29f649819603bcb06 100755 |
--- a/tools/deep_memory_profiler/dmprof.py |
+++ b/tools/deep_memory_profiler/dmprof |
@@ -45,6 +45,11 @@ DUMP_DEEP_2 = 'DUMP_DEEP_2' |
# They should be processed by POLICY_DEEP_2. |
DUMP_DEEP_3 = 'DUMP_DEEP_3' |
+# DUMP_DEEP_4 adds some features to DUMP_DEEP_3: |
+# 1. Support comments starting with '#' |
+# 2. Support additional global stats: e.g. nonprofiled-*. |
+DUMP_DEEP_4 = 'DUMP_DEEP_4' |
+ |
# Heap Profile Policy versions |
# POLICY_DEEP_1 DOES NOT include allocation_type columns. |
@@ -109,8 +114,8 @@ class Log(object): |
"""A class representing one dumped log data.""" |
def __init__(self, log_path, buckets): |
self.log_path = log_path |
- with open(self.log_path, mode='r') as log_f: |
- self.log_lines = log_f.readlines() |
+ self.log_lines = [ |
+ l for l in open(self.log_path, 'r') if l and not l.startswith('#')] |
self.log_version = '' |
sys.stderr.write('parsing a log file:%s\n' % log_path) |
self.mmap_stacktrace_lines = [] |
@@ -282,28 +287,30 @@ class Log(object): |
return line_number |
return line_number |
- def parse_stacktraces_while_valid(self, buckets, log_lines, ln): |
+ def parse_stacktraces_while_valid(self, buckets, log_lines, line_number): |
"""Parses stacktrace lines while the lines are valid. |
Args: |
buckets: A dict mapping bucket ids and their corresponding Bucket |
objects. |
log_lines: A list of lines to be parsed. |
- ln: An integer representing the starting line number in log_lines. |
+ line_number: An integer representing the starting line number in |
+ log_lines. |
Returns: |
A pair of a list of valid lines and an integer representing the last |
line number in log_lines. |
""" |
- ln = self.skip_lines_while( |
- ln, len(log_lines), lambda n: not log_lines[n].split()[0].isdigit()) |
- stacktrace_lines_start = ln |
- ln = self.skip_lines_while( |
- ln, len(log_lines), |
+ line_number = self.skip_lines_while( |
+ line_number, len(log_lines), |
+ lambda n: not log_lines[n].split()[0].isdigit()) |
+ stacktrace_lines_start = line_number |
+ line_number = self.skip_lines_while( |
+ line_number, len(log_lines), |
lambda n: self.check_stacktrace_line(log_lines[n], buckets)) |
- return (log_lines[stacktrace_lines_start:ln], ln) |
+ return (log_lines[stacktrace_lines_start:line_number], line_number) |
- def parse_stacktraces(self, buckets): |
+ def parse_stacktraces(self, buckets, line_number): |
"""Parses lines in self.log_lines as stacktrace. |
Valid stacktrace lines are stored into self.mmap_stacktrace_lines and |
@@ -312,10 +319,81 @@ class Log(object): |
Args: |
buckets: A dict mapping bucket ids and their corresponding Bucket |
objects. |
+ line_number: An integer representing the starting line number in |
+ log_lines. |
+ |
+ Raises: |
+ RuntimeException for invalid dump versions. |
+ """ |
+ sys.stderr.write(' heap profile dump version: %s\n' % self.log_version) |
+ |
+ if self.log_version in (DUMP_DEEP_3, DUMP_DEEP_4): |
+ (self.mmap_stacktrace_lines, line_number) = ( |
+ self.parse_stacktraces_while_valid( |
+ buckets, self.log_lines, line_number)) |
+ line_number = self.skip_lines_while( |
+ line_number, len(self.log_lines), |
+ lambda n: self.log_lines[n] != 'MALLOC_STACKTRACES:\n') |
+ (self.malloc_stacktrace_lines, line_number) = ( |
+ self.parse_stacktraces_while_valid( |
+ buckets, self.log_lines, line_number)) |
+ |
+ elif self.log_version == DUMP_DEEP_2: |
+ (self.mmap_stacktrace_lines, line_number) = ( |
+ self.parse_stacktraces_while_valid( |
+ buckets, self.log_lines, line_number)) |
+ line_number = self.skip_lines_while( |
+ line_number, len(self.log_lines), |
+ lambda n: self.log_lines[n] != 'MALLOC_STACKTRACES:\n') |
+ (self.malloc_stacktrace_lines, line_number) = ( |
+ self.parse_stacktraces_while_valid( |
+ buckets, self.log_lines, line_number)) |
+ self.malloc_stacktrace_lines.extend(self.mmap_stacktrace_lines) |
+ self.mmap_stacktrace_lines = [] |
+ |
+ elif self.log_version == DUMP_DEEP_1: |
+ (self.malloc_stacktrace_lines, line_number) = ( |
+ self.parse_stacktraces_while_valid( |
+ buckets, self.log_lines, line_number)) |
+ |
+ else: |
+ raise RuntimeError('invalid heap profile dump version: %s' % ( |
+ self.log_version)) |
+ |
+ def parse_global_stats(self): |
+ """Parses lines in self.log_lines as global stats.""" |
+ ln = self.skip_lines_while( |
+ 0, len(self.log_lines), |
+ lambda n: self.log_lines[n] != 'GLOBAL_STATS:\n') |
+ |
+ if self.log_version == DUMP_DEEP_4: |
+ global_stat_names = [ |
+ 'total', 'file-exec', 'file-nonexec', 'anonymous', 'stack', 'other', |
+ 'nonprofiled-absent', 'nonprofiled-anonymous', |
+ 'nonprofiled-file-exec', 'nonprofiled-file-nonexec', |
+ 'nonprofiled-stack', 'nonprofiled-other', |
+ 'profiled-mmap', 'profiled-malloc'] |
+ else: |
+ global_stat_names = [ |
+ 'total', 'file', 'anonymous', 'other', 'mmap', 'tcmalloc'] |
+ |
+ for prefix in global_stat_names: |
+ ln = self.skip_lines_while( |
+ ln, len(self.log_lines), |
+ lambda n: self.log_lines[n].split()[0] != prefix) |
+ words = self.log_lines[ln].split() |
+ self.counters[prefix + '_virtual'] = int(words[-2]) |
+ self.counters[prefix + '_committed'] = int(words[-1]) |
+ |
+ def parse_version(self): |
+ """Parses a version string in self.log_lines. |
Returns: |
- A string representing a version of the stacktrace dump. '' for invalid |
- dump. |
+ A pair of (a string representing a version of the stacktrace dump, |
+ and an integer indicating a line number next to the version string). |
+ |
+ Raises: |
+ RuntimeException for invalid dump versions. |
""" |
version = '' |
@@ -328,69 +406,24 @@ class Log(object): |
# Identify a version. |
if self.log_lines[ln].startswith('heap profile: '): |
version = self.log_lines[ln][13:].strip() |
- if version == DUMP_DEEP_2 or version == DUMP_DEEP_3: |
+ if (version == DUMP_DEEP_2 or version == DUMP_DEEP_3 or |
+ version == DUMP_DEEP_4): |
ln = self.skip_lines_while( |
ln, len(self.log_lines), |
lambda n: self.log_lines[n] != 'MMAP_STACKTRACES:\n') |
else: |
- sys.stderr.write(' invalid heap profile dump version:%s\n' % version) |
- return '' |
+ raise RuntimeError('invalid heap profile dump version: %s' % version) |
elif self.log_lines[ln] == 'STACKTRACES:\n': |
version = DUMP_DEEP_1 |
elif self.log_lines[ln] == 'MMAP_STACKTRACES:\n': |
version = DUMP_DEEP_2 |
- if version == DUMP_DEEP_3: |
- sys.stderr.write(' heap profile dump version: %s\n' % version) |
- (self.mmap_stacktrace_lines, ln) = self.parse_stacktraces_while_valid( |
- buckets, self.log_lines, ln) |
- ln = self.skip_lines_while( |
- ln, len(self.log_lines), |
- lambda n: self.log_lines[n] != 'MALLOC_STACKTRACES:\n') |
- (self.malloc_stacktrace_lines, ln) = self.parse_stacktraces_while_valid( |
- buckets, self.log_lines, ln) |
- return version |
- |
- elif version == DUMP_DEEP_2: |
- sys.stderr.write(' heap profile dump version: %s\n' % version) |
- (self.mmap_stacktrace_lines, ln) = self.parse_stacktraces_while_valid( |
- buckets, self.log_lines, ln) |
- ln = self.skip_lines_while( |
- ln, len(self.log_lines), |
- lambda n: self.log_lines[n] != 'MALLOC_STACKTRACES:\n') |
- (self.malloc_stacktrace_lines, ln) = self.parse_stacktraces_while_valid( |
- buckets, self.log_lines, ln) |
- self.malloc_stacktrace_lines.extend(self.mmap_stacktrace_lines) |
- self.mmap_stacktrace_lines = [] |
- return version |
- |
- elif version == DUMP_DEEP_1: |
- sys.stderr.write(' heap profile dump version: %s\n' % version) |
- (self.malloc_stacktrace_lines, ln) = self.parse_stacktraces_while_valid( |
- buckets, self.log_lines, ln) |
- return version |
- |
- else: |
- sys.stderr.write(' invalid heap profile dump version:%s\n' % version) |
- return '' |
- |
- def parse_global_stats(self): |
- """Parses lines in self.log_lines as global stats.""" |
- ln = self.skip_lines_while( |
- 0, len(self.log_lines), |
- lambda n: self.log_lines[n] != 'GLOBAL_STATS:\n') |
- |
- for prefix in ['total', 'file', 'anonymous', 'other', 'mmap', 'tcmalloc']: |
- ln = self.skip_lines_while( |
- ln, len(self.log_lines), |
- lambda n: self.log_lines[n].split()[0] != prefix) |
- words = self.log_lines[ln].split() |
- self.counters[prefix + '_virtual'] = int(words[-2]) |
- self.counters[prefix + '_committed'] = int(words[-1]) |
+ return (version, ln) |
def parse_log(self, buckets): |
+ self.log_version, ln = self.parse_version() |
self.parse_global_stats() |
- self.log_version = self.parse_stacktraces(buckets) |
+ self.parse_stacktraces(buckets, ln) |
@staticmethod |
def accumulate_size_for_policy(stacktrace_lines, |
@@ -434,35 +467,61 @@ class Log(object): |
self.accumulate_size_for_policy(self.malloc_stacktrace_lines, |
policy_list, buckets, sizes, False) |
- sizes['mmap-no-log'] = self.counters['mmap_committed'] - sizes[ |
- 'mmap-total-log'] |
- sizes['mmap-total-record'] = self.counters['mmap_committed'] |
- sizes['mmap-total-record-vm'] = self.counters['mmap_virtual'] |
- |
- sizes['tc-no-log'] = self.counters['tcmalloc_committed'] - sizes[ |
- 'tc-total-log'] |
- sizes['tc-total-record'] = self.counters['tcmalloc_committed'] |
- sizes['tc-unused'] = sizes['mmap-tcmalloc'] - self.counters[ |
- 'tcmalloc_committed'] |
+ if self.log_version == DUMP_DEEP_4: |
+ mmap_prefix = 'profiled-mmap' |
+ malloc_prefix = 'profiled-malloc' |
+ else: |
+ mmap_prefix = 'mmap' |
+ malloc_prefix = 'tcmalloc' |
+ |
+ sizes['mmap-no-log'] = ( |
+ self.counters['%s_committed' % mmap_prefix] - sizes['mmap-total-log']) |
+ sizes['mmap-total-record'] = self.counters['%s_committed' % mmap_prefix] |
+ sizes['mmap-total-record-vm'] = self.counters['%s_virtual' % mmap_prefix] |
+ |
+ sizes['tc-no-log'] = ( |
+ self.counters['%s_committed' % malloc_prefix] - sizes['tc-total-log']) |
+ sizes['tc-total-record'] = self.counters['%s_committed' % malloc_prefix] |
+ sizes['tc-unused'] = ( |
+ sizes['mmap-tcmalloc'] - self.counters['%s_committed' % malloc_prefix]) |
sizes['tc-total'] = sizes['mmap-tcmalloc'] |
- for key, value in { 'total': 'total_committed', |
- 'filemapped': 'file_committed', |
- 'anonymous': 'anonymous_committed', |
- 'other': 'other_committed', |
- 'total-vm': 'total_virtual', |
- 'filemapped-vm': 'file_virtual', |
- 'anonymous-vm': 'anonymous_virtual', |
- 'other-vm': 'other_virtual' }.items(): |
+ for key, value in { |
+ 'total': 'total_committed', |
+ 'filemapped': 'file_committed', |
+ 'file-exec': 'file-exec_committed', |
+ 'file-nonexec': 'file-nonexec_committed', |
+ 'anonymous': 'anonymous_committed', |
+ 'stack': 'stack_committed', |
+ 'other': 'other_committed', |
+ 'nonprofiled-absent': 'nonprofiled-absent_committed', |
+ 'nonprofiled-anonymous': 'nonprofiled-anonymous_committed', |
+ 'nonprofiled-file-exec': 'nonprofiled-file-exec_committed', |
+ 'nonprofiled-file-nonexec': 'nonprofiled-file-nonexec_committed', |
+ 'nonprofiled-stack': 'nonprofiled-stack_committed', |
+ 'nonprofiled-other': 'nonprofiled-other_committed', |
+ 'total-vm': 'total_virtual', |
+ 'filemapped-vm': 'file_virtual', |
+ 'anonymous-vm': 'anonymous_virtual', |
+ 'other-vm': 'other_virtual' }.iteritems(): |
if key in sizes: |
sizes[key] = self.counters[value] |
- if 'unknown' in sizes: |
- sizes['unknown'] = self.counters['total_committed'] - self.counters[ |
- 'mmap_committed'] |
+ if 'mustbezero' in sizes: |
+ removed = ( |
+ '%s_committed' % mmap_prefix, |
+ 'nonprofiled-absent_committed', |
+ 'nonprofiled-anonymous_committed', |
+ 'nonprofiled-file-exec_committed', |
+ 'nonprofiled-file-nonexec_committed', |
+ 'nonprofiled-stack_committed', |
+ 'nonprofiled-other_committed') |
+ sizes['mustbezero'] = ( |
+ self.counters['total_committed'] - |
+ sum(self.counters[i] for i in removed)) |
if 'total-exclude-profiler' in sizes: |
- sizes['total-exclude-profiler'] = self.counters[ |
- 'total_committed'] - sizes['mmap-profiler'] |
+ sizes['total-exclude-profiler'] = ( |
+ self.counters['total_committed'] - sizes['mmap-profiler']) |
if 'hour' in sizes: |
sizes['hour'] = (self.log_time - first_log_time) / 60.0 / 60.0 |
if 'minute' in sizes: |
@@ -481,7 +540,7 @@ class Log(object): |
component_match = get_component(policy_list, bucket, mmap) |
if component_match == component_name: |
stacktrace_sequence = '' |
- for address in bucket.stacktrace[1 : min(len(bucket.stacktrace), |
+ for address in bucket.stacktrace[0 : min(len(bucket.stacktrace), |
1 + depth)]: |
stacktrace_sequence += address_symbol_dict[address] + ' ' |
if not stacktrace_sequence in sizes: |