Remove generic comparison IR ops
[hiphop-php.git] / hphp / tools / hhvm-leak-isolator
blobcb123345d9766f30a9910dbf7bcd55e2542b3293
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 """
4 This script can help you isolate memory leaks in HHVM. It will request a heap
5 dump from HHVM at regular intervals and diff successive dumps to show you
6 which code paths are allocating memory.
8 requirements:
9 * HHVM debug symbols.
10 * a copy of libjemalloc compiled with `--enable-prof`.
11 (the script will check your libjemalloc for profiling capabilities
12 and alert you if they are missing.)
13 * Google Perf Tools (for diffing heaps).
14 * Ability to make HTTP or HTTPS requests to the HHVM admin server.
16 usage: hhvm-leak-isolator [--heap-dir HEAP_DIR] [--check-only]
17 [--admin-server URL] [--interval MINS]
19 optional arguments:
20 -h, --help show this help message and exit
21 --heap-dir HEAP_DIR Dump heaps to this directory (default: $TMP).
22 --check-only Check for heap profiling capabilities and exit.
23 --admin-server URL HHVM admin server URL (default: http://localhost:9002)
24 --interval MINS Time to sleep between heap dumps (default: 5).
26 Author: Ori Livneh
28 """
29 from __future__ import print_function
31 import argparse
32 import ctypes
33 import ctypes.util
34 import distutils.spawn
35 import os
36 import subprocess
37 import sys
38 import tempfile
39 import textwrap
40 import time
42 try:
43 from urllib import urlencode
44 from urllib2 import urlopen
45 from urlparse import urljoin
46 except ImportError:
47 from urllib.parse import urlencode, urljoin
48 from urllib.request import urlopen
51 temp_dir = tempfile.gettempdir()
52 ap = argparse.ArgumentParser(
53 description=textwrap.dedent(__doc__.split('usage')[0]),
54 formatter_class=argparse.RawDescriptionHelpFormatter)
55 ap.add_argument('--heap-dir', default=temp_dir, type=os.path.abspath,
56 help='Dump heaps to this directory (default: %s).' % temp_dir)
57 ap.add_argument('--check-only', action='store_true', default=False,
58 help='Check for heap profiling capabilities and exit.')
59 ap.add_argument('--admin-server', default='http://localhost:9002',
60 help='HHVM admin server URL (default: http://localhost:9002)',
61 metavar='URL')
62 ap.add_argument('--interval', default=5, type=int, metavar='MINS',
63 help='Time to sleep between heap dumps (default: 5).')
64 args = ap.parse_args()
67 def get_jemalloc_can_profile():
68 """Check if libjemalloc was built with `--enable-prof` and thus
69 has heap profiling capabilities."""
70 jemalloc_so = ctypes.util.find_library('jemalloc')
71 if jemalloc_so is None:
72 raise EnvironmentError('Could not find libjemalloc!')
73 jemalloc = ctypes.CDLL(jemalloc_so, use_errno=True)
75 has_prof = ctypes.c_bool(False)
76 has_prof_len = ctypes.c_size_t(ctypes.sizeof(has_prof))
77 rv = jemalloc.mallctl(b'config.prof', ctypes.byref(has_prof),
78 ctypes.byref(has_prof_len), None, None)
79 if rv != 0:
80 err = ctypes.get_errno()
81 raise OSError(err, os.strerror(err))
83 return bool(has_prof)
86 def hhvm_admin_do(endpoint, **query_params):
87 url = urljoin(args.admin_server, endpoint)
88 if query_params:
89 url += '?' + urlencode(query_params)
90 req = urlopen(url)
91 return req.read().decode('utf-8')
94 if get_jemalloc_can_profile():
95 print('OK: jemalloc built with `--enable-prof`.')
96 else:
97 print('NOT OK: your copy of libjemalloc was not built with `--enable-prof`'
98 ' and thus has no heap profiling capabilities.', file=sys.stderr)
99 sys.exit(1)
101 try:
102 stats = hhvm_admin_do('jemalloc-stats-print')
103 except EnvironmentError:
104 print('NOT OK: Could not connect to an HHVM admin server at %s.\n'
105 ' Use `--admin-server` to specify an alternate URL.'
106 % args.admin_server, file=sys.stderr)
107 sys.exit(1)
109 if 'opt.prof: true' in stats:
110 print('OK: jemalloc heap profiling is active (prof: true).')
111 else:
112 print('NOT OK: jemalloc heap profiling is off.'
113 ' Run `sudo ln -sf prof:true /etc/malloc.conf` and restart HHVM.',
114 file=sys.stderr)
115 sys.exit(1)
117 hhvm_path = distutils.spawn.find_executable('hhvm')
118 pprof_path = (distutils.spawn.find_executable('google-pprof') or
119 distutils.spawn.find_executable('pprof'))
120 if pprof_path:
121 print('OK: pprof is %s.' % pprof_path)
122 else:
123 print('WARN: Could not find `pprof` or `google-pprof` in $PATH. '
124 'pprof is required for diffing heap dumps.\n'
125 'Install the `google-perftools` package or see '
126 'https://code.google.com/p/gperftools/.', file=sys.stderr)
128 if args.check_only:
129 sys.exit(0)
132 hhvm_admin_do('jemalloc-prof-activate')
133 if 'ACTIVE' not in hhvm_admin_do('jemalloc-prof-status'):
134 print('NOT OK: failed to activate jemalloc profiling.', file=sys.stderr)
135 sys.exit(1)
137 print('I will write heap files to %s every %d minute(s).' %
138 (os.path.join(args.heap_dir, 'hhvm-heap.*'), args.interval))
140 heap_files = []
141 while 1:
142 heap_file = os.path.join(args.heap_dir, 'hhvm-heap.%d' % time.time())
143 heap_files.append(heap_file)
144 hhvm_admin_do('jemalloc-prof-dump', file=heap_file)
145 print('Wrote %s.' % heap_file)
146 if pprof_path and len(heap_files) > 1:
147 base, heap = heap_files[-2:]
148 cmd = (pprof_path, hhvm_path, '--show_bytes', '--text',
149 '--base=%s' % base, heap)
150 out = subprocess.check_output(cmd, stderr=open(os.devnull, 'w'))
151 out = out.decode('utf-8', errors='ignore')
152 if out.strip():
153 print('-' * 80, '\n', out, '\n', '-' * 80)
154 else:
155 print('-- no diff --')
156 print('Sleeping for %d minute(s).' % args.interval)
157 time.sleep(args.interval * 60)