3 // Copyright 2004-present Facebook. All Rights Reserved.
5 function usage($script_name) {
9 perf script -f comm,ip,sym | $script_name
14 // If $stack has any frames in the translation cache, return the C++ frame
15 // directly called by the deepest translation cache frame. If $stack has no TC
16 // frames, return null.
17 function tc_callee($stack) {
18 for ($i = 0; $i < count($stack); ++
$i) {
19 // Normal translations starts with PHP::.
20 if (!starts_with($stack[$i], 'PHP::')) {
25 if ($stack[$i] === '[unknown]') continue;
33 // If $container is an int, return it. Otherwise, assume $container is a
34 // Map<string, int> and return the sum of its values.
35 function count_leaves($container) {
36 if (is_int($container)) return $container;
39 foreach ($container as $value) {
45 // Print a summary of the data in $map, using keys as categories and values
46 // as sample counts. If the values in $map are more Maps, sample counts will be
47 // obtained by counting leaves, allowing for multiple levels of categorization.
48 function print_map($header, $map, $total) {
49 printf("%s\n", $header);
50 uasort(inout
$map, ($a, $b) ==> count_leaves($b) - count_leaves($a));
52 foreach ($map as $name => $samples) {
53 $count = count_leaves($samples);
54 $pct_total = $count * 100.0 / $total;
55 if ($pct_total < 0.10) break;
56 printf(" %5.2f%% - %s\n", $pct_total, $name);
61 // Increment $map[$key] by 1, setting it to 0 first if it doesn't exist.
62 function increment_key($map, $key) {
63 if (!isset($map[$key])) $map[$key] = 0;
67 // Increment $map[$key1][$key2] by 1, setting it to 0 first if it doesn't exist.
68 function increment_key2($map, $key1, $key2) {
69 if (!isset($map[$key1])) $map[$key1] = Map
{};
70 increment_key($map[$key1], $key2);
74 function get_categories() {
76 'InterpOne' => Vector
{
79 'Iterators' => Vector
{
84 '/AsioBlockableChain/',
87 'CacheClient' => Vector
{
94 '/(Mixed|Packed|Bespoke)Array/',
95 '/Vanilla(Dict|Vec)/',
98 '/^HPHP::f_(.?.?sort|array)/',
101 '/MInstrHelpers::dict/',
102 '/MInstrHelpers::keyset/',
104 'Objects' => Vector
{
108 '/lookupClsMethodHelper/',
111 '/::closureInstance.tor/',
113 'Strings' => Vector
{
116 '/^HPHP::f_(explode|str|mb_strtolower|substr|preg_)/',
119 'Memoization' => Vector
{
120 '/^HPHP::memoCache/',
126 'Extensions' => Vector
{
128 '/^HPHP::thrift::f_/',
129 '/^HPHP::math_mt_rand/',
130 '/^HPHP::\(anonymous namespace\)::f_/',
132 'Collections' => Vector
{
141 'UniqueStubs' => Vector
{
147 function categorize_helper($func) {
148 // Order is important in this Map: we return the first category with a match,
149 // even though a later category may also match.
150 $categories = get_categories();
152 foreach ($categories as $cat => $regexes) {
153 foreach ($regexes as $regex) {
154 if (preg_match($regex, $func) === 1) return $cat;
157 return 'Uncategorized';
162 require(__DIR__
.'/perf-lib.php');
164 $argv = $_SERVER['argv'];
165 ini_set('memory_limit', '64G');
166 if (posix_isatty(STDIN
)) {
171 fprintf(STDERR
, ">> Parsing samples from stdin...\n");
172 $samples = read_perf_samples(STDIN
);
176 fprintf(STDERR
, ">> Categorizing samples...\n");
177 foreach ($samples as $stack) {
178 if (starts_with($stack[0], 'PHP::')) {
179 increment_key($groups, 'TC');
180 } else if (starts_with($stack[0], 'HHVM::pcre_jit::')) {
181 increment_key($groups, 'PCRE JIT');
182 } else if ($callee = tc_callee($stack)) {
183 increment_key($groups, 'TC helpers');
184 $category = categorize_helper($callee);
185 increment_key2($helpers, $category, $callee);
187 increment_key($groups, 'Other');
191 $total_samples = count($samples);
192 print_map('Summary', $groups, $total_samples);
193 print_map('Helper categories', $helpers, $total_samples);
194 foreach ($helpers as $category => $map) {
195 print_map($category, $map, $total_samples);