| Index: third_party/tcmalloc/chromium/src/dmprof
|
| diff --git a/third_party/tcmalloc/chromium/src/dmprof b/third_party/tcmalloc/chromium/src/dmprof
|
| new file mode 100755
|
| index 0000000000000000000000000000000000000000..cfe7426ee4685ddbdca0a2914b3b2c5140cfcbab
|
| --- /dev/null
|
| +++ b/third_party/tcmalloc/chromium/src/dmprof
|
| @@ -0,0 +1,492 @@
|
| +#!/usr/bin/python
|
| +# Copyright (c) 2011 The Chromium Authors. All rights reserved.
|
| +
|
| +import sys
|
| +import subprocess
|
| +import re
|
| +import os
|
| +
|
| +BUCKET_ID = 5
|
| +VIRTUAL = 0
|
| +COMMITTED = 1
|
| +ALLOC_COUNT = 2
|
| +FREE_COUNT = 3
|
| +addr_symbol_dict = dict()
|
| +components = list()
|
| +action = ''
|
| +
|
| +def get_val(l):
|
| + return l[1]
|
| +
|
| +
|
| +def get_component(policy, bucket):
|
| + if bucket == None:
|
| + return 'no-bucket'
|
| + if bucket.component != '':
|
| + return bucket.component
|
| + for name, condition in policy:
|
| + st = ''
|
| + for addr in bucket.stacktrace:
|
| + st += addr_symbol_dict[addr] + ' '
|
| + st = st.strip()
|
| + if condition.match(st):
|
| + bucket.component = name
|
| + return name
|
| +
|
| + assert False
|
| +
|
| +class Bucket(object):
|
| + stacktrace = list()
|
| + component = ''
|
| +
|
| + def __init__(self, st):
|
| + self.stacktrace = st
|
| + component = ''
|
| +
|
| +class Log(object):
|
| + log_path = ''
|
| + log_lines = list()
|
| + stacktrace_lines = list()
|
| + total_committed = 0
|
| + total_virtual = 0
|
| + filemapped_committed = 0
|
| + filemapped_virtual = 0
|
| + anonymous_committed = 0
|
| + anonymous_virtual = 0
|
| + other_committed = 0
|
| + other_virtual = 0
|
| + mmap_committed = 0
|
| + mmap_virtual = 0
|
| + tcmalloc_committed = 0
|
| + tcmalloc_virtual = 0
|
| + log_time = 0
|
| +
|
| + def __init__(self, log_path, buckets):
|
| + self.log_path = log_path
|
| + log = open(self.log_path, mode='r')
|
| + self.log_lines = log.readlines();
|
| + log.close()
|
| + sys.stderr.write('parsing a log file:%s\n' % (log_path))
|
| + self.parse_log(buckets)
|
| + self.log_time = os.stat(self.log_path).st_mtime
|
| +
|
| +
|
| + def dump_stacktrace(self, buckets):
|
| + for l in self.stacktrace_lines:
|
| + words = l.split()
|
| + bucket = buckets[int(words[BUCKET_ID])]
|
| + if bucket == None:
|
| + continue
|
| + for i in range(0, BUCKET_ID - 1):
|
| + sys.stdout.write(words[i] + ' ')
|
| + for addr in bucket.stacktrace:
|
| + if addr_symbol_dict.has_key(addr):
|
| + if addr_symbol_dict[addr] != '':
|
| + sys.stdout.write(addr_symbol_dict[addr] + ' ')
|
| + else:
|
| + sys.stdout.write(addr + ' ')
|
| + else:
|
| + sys.stdout.write(addr + ' ')
|
| + sys.stdout.write('\n')
|
| +
|
| +
|
| + def dump_for_pprof(self, policy, buckets, mapping_lines, com):
|
| + """ Convert the log file so it can be processed by pprof
|
| + Args:
|
| + com: component name for filtering
|
| + """
|
| + sys.stdout.write('heap profile: ')
|
| + com_committed = 0
|
| + com_allocs = 0
|
| + for l in self.stacktrace_lines:
|
| + words = l.split()
|
| + bucket = buckets[int(words[BUCKET_ID])]
|
| + if bucket == None:
|
| + continue
|
| + if com == None or com == '':
|
| + pass
|
| + elif com != get_component(policy, bucket):
|
| + continue
|
| +
|
| + com_committed += int(words[COMMITTED])
|
| + com_allocs += int(words[ALLOC_COUNT])-int(words[FREE_COUNT])
|
| +
|
| + sys.stdout.write('%6d: %8s [%6d: %8s] @ heapprofile\n' % (com_allocs,
|
| + com_committed,
|
| + com_allocs,
|
| + com_committed))
|
| +
|
| + for l in self.stacktrace_lines:
|
| + words = l.split()
|
| + bucket = buckets[int(words[BUCKET_ID])]
|
| + if bucket == None:
|
| + continue
|
| + if com == None or com == '':
|
| + pass
|
| + elif com != get_component(policy, bucket):
|
| + continue
|
| +
|
| + com_committed += int(words[COMMITTED])
|
| + com_allocs += int(words[ALLOC_COUNT])
|
| + sys.stdout.write('%6d: %8s [%6d: %8s] @' % (int(words[ALLOC_COUNT])-int(words[FREE_COUNT]),
|
| + words[COMMITTED],
|
| + int(words[ALLOC_COUNT])-int(words[FREE_COUNT]),
|
| + words[COMMITTED]))
|
| + for addr in bucket.stacktrace:
|
| + sys.stdout.write(' ' + addr)
|
| + sys.stdout.write('\n')
|
| +
|
| + sys.stdout.write('MAPPED_LIBRARIES:\n')
|
| + for l in mapping_lines:
|
| + sys.stdout.write(l)
|
| +
|
| + def parse_stacktraces(self, buckets):
|
| + ln = 0
|
| + while self.log_lines[ln] != "STACKTRACES:\n":
|
| + ln += 1
|
| + while self.log_lines[ln].split()[0].isdigit() == False:
|
| + ln += 1
|
| + lines_start = ln
|
| + while ln < len(self.log_lines):
|
| + words = self.log_lines[ln].split()
|
| + if len(words) < BUCKET_ID + 1:
|
| + break
|
| + if words[BUCKET_ID - 1] != '@':
|
| + break
|
| + bucket = buckets[int(words[BUCKET_ID])]
|
| + if bucket != None:
|
| + for addr in bucket.stacktrace:
|
| + addr_symbol_dict[addr] = ""
|
| + ln += 1
|
| + lines_end = ln
|
| + self.stacktrace_lines = self.log_lines[lines_start:lines_end]
|
| +
|
| + def parse_global_stats(self):
|
| + ln = 0
|
| + while self.log_lines[ln] != "GLOBAL_STATS:\n":
|
| + ln += 1
|
| +
|
| + while self.log_lines[ln].split()[0] != "total":
|
| + ln += 1
|
| + words = self.log_lines[ln].split()
|
| + self.total_virtual = int(words[1])
|
| + self.total_committed = int(words[2])
|
| +
|
| + while self.log_lines[ln].split()[0] != "file":
|
| + ln += 1
|
| + words = self.log_lines[ln].split()
|
| + self.filemapped_virtual = int(words[2])
|
| + self.filemapped_committed = int(words[3])
|
| +
|
| + while self.log_lines[ln].split()[0] != "anonymous":
|
| + ln += 1
|
| + words = self.log_lines[ln].split()
|
| + self.anonymous_virtual = int(words[1])
|
| + self.anonymous_committed = int(words[2])
|
| +
|
| + while self.log_lines[ln].split()[0] != "other":
|
| + ln += 1
|
| + words = self.log_lines[ln].split()
|
| + self.other_virtual = int(words[1])
|
| + self.other_committed = int(words[2])
|
| +
|
| + while self.log_lines[ln].split()[0] != "mmap":
|
| + ln += 1
|
| + words = self.log_lines[ln].split()
|
| + self.mmap_virtual = int(words[1])
|
| + self.mmap_committed = int(words[2])
|
| +
|
| + while self.log_lines[ln].split()[0] != "tcmalloc":
|
| + ln += 1
|
| + words = self.log_lines[ln].split()
|
| + self.tcmalloc_virtual = int(words[1])
|
| + self.tcmalloc_committed = int(words[2])
|
| +
|
| + def parse_log(self, buckets):
|
| + self.parse_global_stats()
|
| + self.parse_stacktraces(buckets)
|
| +
|
| + def apply_policy(self, policy, buckets):
|
| + """ Aggregate the total memory size of each component
|
| +
|
| + Iterate through all stacktraces and attribute them
|
| + to one of the components based on the policy.
|
| + It is important to apply policy in right order.
|
| + """
|
| +
|
| + sys.stderr.write('apply policy:%s\n' % (self.log_path))
|
| + sizes = dict()
|
| + for c in components:
|
| + sizes[c] = 0
|
| +
|
| + for l in self.stacktrace_lines:
|
| + words = l.split()
|
| + bucket = buckets[int(words[BUCKET_ID])]
|
| + component_match = get_component(policy, bucket)
|
| + sizes[component_match] += int(words[COMMITTED])
|
| +
|
| + if component_match[0:3] == 'tc-':
|
| + sizes['tc-total-log'] += int(words[COMMITTED])
|
| + elif component_match[0:5] == 'mmap-':
|
| + sizes['mmap-total-log'] += int(words[COMMITTED])
|
| + else:
|
| + sizes['other-total-log'] += int(words[COMMITTED])
|
| +
|
| + sizes['mmap-no-log'] = self.mmap_committed - sizes['mmap-total-log']
|
| + sizes['mmap-total-record'] = self.mmap_committed
|
| + sizes['mmap-total-record-vm'] = self.mmap_virtual
|
| +
|
| + sizes['tc-no-log'] = self.tcmalloc_committed - sizes['tc-total-log']
|
| + sizes['tc-total-record'] = self.tcmalloc_committed
|
| + sizes['tc-unused'] = sizes['mmap-tcmalloc'] - self.tcmalloc_committed
|
| + sizes['tc-total'] = sizes['mmap-tcmalloc']
|
| +
|
| + if sizes.has_key('total'):
|
| + sizes['total'] = self.total_committed
|
| + if sizes.has_key('filemapped'):
|
| + sizes['filemapped'] = self.filemapped_committed
|
| + if sizes.has_key('anonymous'):
|
| + sizes['anonymous'] = self.anonymous_committed
|
| + if sizes.has_key('other'):
|
| + sizes['other'] = self.other_committed
|
| + if sizes.has_key('total-vm'):
|
| + sizes['total-vm'] = self.total_virtual
|
| + if sizes.has_key('filemapped-vm'):
|
| + sizes['filemapped-vm'] = self.filemapped_virtual
|
| + if sizes.has_key('anonymous-vm'):
|
| + sizes['anonymous-vm'] = self.anonymous_virtual
|
| + if sizes.has_key('other-vm'):
|
| + sizes['other-vm'] = self.other_virtual
|
| + if sizes.has_key('unknown'):
|
| + sizes['unknown'] = self.total_committed - self.mmap_committed
|
| + if sizes.has_key('total-exclude-profiler'):
|
| + sizes['total-exclude-profiler'] = self.total_committed - sizes['mmap-profiler']
|
| +
|
| + if sizes.has_key('hour'):
|
| + sizes['hour'] = (self.log_time - logs[0].log_time)/60.0/60.0
|
| + if sizes.has_key('minute'):
|
| + sizes['minute'] = (self.log_time - logs[0].log_time)/60.0
|
| + if sizes.has_key('second'):
|
| + sizes['second'] = self.log_time - logs[0].log_time
|
| +
|
| + return sizes
|
| +
|
| + def expand(self, policy, buckets, com, depth):
|
| + sizes = dict()
|
| +
|
| + for l in self.stacktrace_lines:
|
| + words = l.split()
|
| + bucket = buckets[int(words[BUCKET_ID])]
|
| + component_match = get_component(policy, bucket)
|
| + if component_match == com:
|
| + a = ''
|
| + for addr in bucket.stacktrace[1 : min(len(bucket.stacktrace), 1 + depth)]:
|
| + a += addr_symbol_dict[addr] + ' '
|
| + if sizes.has_key(a) == False:
|
| + sizes[a] = 0
|
| + sizes[a] += int(words[COMMITTED])
|
| +
|
| + s = sizes.items()
|
| + s.sort(key=get_val,reverse=True)
|
| + total = 0
|
| + for l in s:
|
| + sys.stdout.write('%10d %s\n' % (l[1], l[0]))
|
| + total += l[1]
|
| + sys.stderr.write('total: %d\n' % (total))
|
| +
|
| +
|
| +def get_symbols(symbol_path, mapping_lines):
|
| + symbol_f = open(symbol_path, 'a+')
|
| + symbol_lines = symbol_f.readlines()
|
| +
|
| + if(len(symbol_lines) == 0):
|
| + pprof_in = open("/tmp/maps", 'w+')
|
| + pprof_out = open("/tmp/symbols", 'w+')
|
| +
|
| + for l in mapping_lines:
|
| + pprof_in.write(l)
|
| +
|
| + addr_list = addr_symbol_dict.keys()
|
| + addr_list.sort()
|
| + for key in addr_list:
|
| + pprof_in.write(key + "\n")
|
| +
|
| + pprof_in.seek(0)
|
| +
|
| + p = subprocess.Popen(
|
| + 'pprof --symbols %s' % (chrome_path),
|
| + shell='/usr/bash', stdin=pprof_in, stdout=pprof_out)
|
| + p.wait()
|
| +
|
| + pprof_out.seek(0)
|
| + symbols = pprof_out.readlines()
|
| + i = 0
|
| + for key in addr_list:
|
| + addr_symbol_dict[key] = symbols[i].strip()
|
| + i += 1
|
| +
|
| + pprof_in.close()
|
| + pprof_out.close()
|
| +
|
| + for a in addr_symbol_dict.items():
|
| + symbol_f.write(a[0] + ' ' + a[1] + '\n')
|
| + else:
|
| + for l in symbol_lines:
|
| + addr_symbol_dict[l.split()[0]] = l.split()[1]
|
| +
|
| + symbol_f.close()
|
| +
|
| +
|
| +def parse_policy(policy_path):
|
| + """ Parses policy file
|
| +
|
| + A policy file contains component's names and their
|
| + stacktrace pattern written in regular expression.
|
| + Those patterns are matched against each symbols of
|
| + each stacktraces in the order written in the policy file
|
| +
|
| + Args:
|
| + policy file path
|
| + Returns:
|
| + A list containing component's name and its regex object
|
| + """
|
| + policy_f = open(policy_path, mode='r')
|
| + policy_lines = policy_f.readlines();
|
| + policy = list()
|
| + for l in policy_lines:
|
| + name = l.split()[0]
|
| + if name[0] == '#':
|
| + continue
|
| + pattern = l[len(name) : len(l)].strip()
|
| + if pattern != 'default':
|
| + policy.append([name, re.compile(pattern + r'\Z')])
|
| + if components.count(name) == 0:
|
| + components.append(name)
|
| +
|
| + return policy
|
| +
|
| +action = sys.argv[1]
|
| +
|
| +if (action in ['--csv','--expand','--list','--stacktrace','--pprof']) == False:
|
| + sys.stderr.write(
|
| +"""Usage:
|
| +%s [options] <chrome-binary-path> <policy-file> <profile> [component-name] [depth]
|
| +
|
| +Options:
|
| + --csv Output result in csv format
|
| + --stacktrace Convert raw address to symbol names
|
| + --list Lists components and their sizes
|
| + --expand Show all stacktraces in the specified component
|
| + of given depth with their sizes
|
| + --pprof Format the profile file so it can be processed by pprof
|
| +
|
| +Examples:
|
| +dmprof --csv out/Debug/chrome ./policy o1211/heap.hprof.01221.0001.heap > renderer.csv
|
| +dmprof --list out/Debug/chrome ./policy o1211/heap.hprof.01221.0101.heap
|
| +dmprof --expand out/Debug/chrome ./policy o1211/heap.hprof.01221.0101.heap tc-webkit 4
|
| +dmprof --pprof out/Debug/chrome ./policy o1211/heap.hprof.01221.0101.heap > for_pprof
|
| +
|
| +
|
| +""" % (sys.argv[0]))
|
| + sys.exit(1)
|
| +
|
| +chrome_path = sys.argv[2]
|
| +policy_path = sys.argv[3]
|
| +log_path = sys.argv[4]
|
| +
|
| +sys.stderr.write('parsing a policy file\n')
|
| +policy = parse_policy(policy_path)
|
| +
|
| +p = re.compile('\.[0-9][0-9][0-9][0-9]\.heap')
|
| +prefix = p.sub('',log_path)
|
| +symbol_path = prefix + '.symbols'
|
| +
|
| +sys.stderr.write('parsing the maps file\n')
|
| +maps_path = prefix + '.maps'
|
| +maps_f = open(maps_path, mode='r')
|
| +maps_lines = maps_f.readlines()
|
| +
|
| +# Reading buckets
|
| +sys.stderr.write('parsing the bucket file\n')
|
| +buckets = [None for i in range(0, 10000000)]
|
| +bucket_count = 0
|
| +#n = int(log_path[len(log_path) - 9 : len(log_path) - 5])
|
| +n = 0
|
| +while True:
|
| + buckets_path = '%s.%04d.buckets'% (prefix, n)
|
| + if os.path.exists(buckets_path) == False:
|
| + if n > 10:
|
| + break
|
| + else:
|
| + n+=1
|
| + continue
|
| + sys.stderr.write('reading buckets from %s\n' % (buckets_path))
|
| + buckets_f = open(buckets_path, mode='r')
|
| + for l in buckets_f.readlines():
|
| + words = l.split()
|
| + st = list()
|
| + for i in range(1, len(words)):
|
| + st.append(words[i])
|
| + buckets[int(words[0])] = Bucket(st)
|
| + bucket_count+=1
|
| + buckets_f.close()
|
| + n+=1
|
| +
|
| +sys.stderr.write('the number buckets: %d\n' % (bucket_count))
|
| +
|
| +log_path_list = list()
|
| +log_path_list.append(log_path)
|
| +
|
| +if action == '--csv':
|
| + # search for the sequence of files
|
| + n = int(log_path[len(log_path) - 9 : len(log_path) - 5])
|
| + n += 1 # skip current file
|
| + while True:
|
| + p = '%s.%04d.heap'% (prefix, n)
|
| + if os.path.exists(p):
|
| + log_path_list.append(p)
|
| + else:
|
| + break
|
| + n += 1
|
| +
|
| +logs = list()
|
| +for path in log_path_list:
|
| + logs.append(Log(path, buckets))
|
| +
|
| +sys.stderr.write('getting symbols\n')
|
| +get_symbols(symbol_path, maps_lines)
|
| +
|
| +if action == '--stacktrace':
|
| + logs[0].dump_stacktrace(buckets)
|
| +
|
| +elif action == '--csv':
|
| + sys.stdout.write(','.join(components))
|
| + sys.stdout.write('\n')
|
| +
|
| + for log in logs:
|
| + component_sizes = log.apply_policy(policy, buckets)
|
| + s = list()
|
| + for c in components:
|
| + if c in ['hour', 'minute', 'second']:
|
| + s.append('%05.5f' % (component_sizes[c]))
|
| + else:
|
| + s.append('%05.5f' % (component_sizes[c]/1024./1024.))
|
| + sys.stdout.write(','.join(s))
|
| + sys.stdout.write('\n')
|
| +
|
| +elif action == '--list':
|
| + component_sizes = logs[0].apply_policy(policy, buckets)
|
| + for c in components:
|
| + if c in ['hour', 'minute', 'second']:
|
| + sys.stdout.write('%30s %10.3f\n' % (c, component_sizes[c]))
|
| + else:
|
| + sys.stdout.write('%30s %10.3f\n' % (c, component_sizes[c]/1024./1024.))
|
| +elif action == '--expand':
|
| + com_name = sys.argv[5]
|
| + depth = sys.argv[6]
|
| + logs[0].expand(policy, buckets, com_name, int(depth))
|
| +elif action == '--pprof':
|
| + if len(sys.argv) > 5:
|
| + logs[0].dump_for_pprof(policy, buckets, maps_lines, sys.argv[5])
|
| + else:
|
| + logs[0].dump_for_pprof(policy, buckets, maps_lines, None)
|
|
|