1#!/usr/bin/env python 2# 3# Copyright 2015 the V8 project authors. All rights reserved. 4# Use of this source code is governed by a BSD-style license that can be 5# found in the LICENSE file. 6 7"""This script is used to analyze GCTracer's NVP output.""" 8 9 10from argparse import ArgumentParser 11from copy import deepcopy 12from gc_nvp_common import split_nvp 13from math import ceil,log 14from sys import stdin 15 16 17class LinearBucket: 18 def __init__(self, granularity): 19 self.granularity = granularity 20 21 def value_to_bucket(self, value): 22 return int(value / self.granularity) 23 24 def bucket_to_range(self, bucket): 25 return (bucket * self.granularity, (bucket + 1) * self.granularity) 26 27 28class Log2Bucket: 29 def __init__(self, start): 30 self.start = int(log(start, 2)) - 1 31 32 def value_to_bucket(self, value): 33 index = int(log(value, 2)) 34 index -= self.start 35 if index < 0: 36 index = 0 37 return index 38 39 def bucket_to_range(self, bucket): 40 if bucket == 0: 41 return (0, 2 ** (self.start + 1)) 42 bucket += self.start 43 return (2 ** bucket, 2 ** (bucket + 1)) 44 45 46class Histogram: 47 def __init__(self, bucket_trait, fill_empty): 48 self.histogram = {} 49 self.fill_empty = fill_empty 50 self.bucket_trait = bucket_trait 51 52 def add(self, key): 53 index = self.bucket_trait.value_to_bucket(key) 54 if index not in self.histogram: 55 self.histogram[index] = 0 56 self.histogram[index] += 1 57 58 def __str__(self): 59 ret = [] 60 keys = self.histogram.keys() 61 keys.sort() 62 last = keys[len(keys) - 1] 63 for i in range(0, last + 1): 64 (min_value, max_value) = self.bucket_trait.bucket_to_range(i) 65 if i == keys[0]: 66 keys.pop(0) 67 ret.append(" [{0},{1}[: {2}".format( 68 str(min_value), str(max_value), self.histogram[i])) 69 else: 70 if self.fill_empty: 71 ret.append(" [{0},{1}[: {2}".format( 72 str(min_value), str(max_value), 0)) 73 return "\n".join(ret) 74 75 76class Category: 77 def __init__(self, key, histogram, csv, percentiles): 78 self.key = key 79 self.values = [] 80 self.histogram = histogram 81 self.csv = csv 82 self.percentiles = percentiles 83 84 def process_entry(self, entry): 85 if self.key in entry: 86 self.values.append(float(entry[self.key])) 87 if self.histogram: 88 self.histogram.add(float(entry[self.key])) 89 90 def min(self): 91 return min(self.values) 92 93 def max(self): 94 return max(self.values) 95 96 def avg(self): 97 if len(self.values) == 0: 98 return 0.0 99 return sum(self.values) / len(self.values) 100 101 def empty(self): 102 return len(self.values) == 0 103 104 def _compute_percentiles(self): 105 ret = [] 106 if len(self.values) == 0: 107 return ret 108 sorted_values = sorted(self.values) 109 for percentile in self.percentiles: 110 index = int(ceil((len(self.values) - 1) * percentile / 100)) 111 ret.append(" {0}%: {1}".format(percentile, sorted_values[index])) 112 return ret 113 114 def __str__(self): 115 if self.csv: 116 ret = [self.key] 117 ret.append(len(self.values)) 118 ret.append(self.min()) 119 ret.append(self.max()) 120 ret.append(self.avg()) 121 ret = [str(x) for x in ret] 122 return ",".join(ret) 123 else: 124 ret = [self.key] 125 ret.append(" len: {0}".format(len(self.values))) 126 if len(self.values) > 0: 127 ret.append(" min: {0}".format(self.min())) 128 ret.append(" max: {0}".format(self.max())) 129 ret.append(" avg: {0}".format(self.avg())) 130 if self.histogram: 131 ret.append(str(self.histogram)) 132 if self.percentiles: 133 ret.append("\n".join(self._compute_percentiles())) 134 return "\n".join(ret) 135 136 def __repr__(self): 137 return "<Category: {0}>".format(self.key) 138 139 140def make_key_func(cmp_metric): 141 def key_func(a): 142 return getattr(a, cmp_metric)() 143 return key_func 144 145 146def main(): 147 parser = ArgumentParser(description="Process GCTracer's NVP output") 148 parser.add_argument('keys', metavar='KEY', type=str, nargs='+', 149 help='the keys of NVPs to process') 150 parser.add_argument('--histogram-type', metavar='<linear|log2>', 151 type=str, nargs='?', default="linear", 152 help='histogram type to use (default: linear)') 153 linear_group = parser.add_argument_group('linear histogram specific') 154 linear_group.add_argument('--linear-histogram-granularity', 155 metavar='GRANULARITY', type=int, nargs='?', 156 default=5, 157 help='histogram granularity (default: 5)') 158 log2_group = parser.add_argument_group('log2 histogram specific') 159 log2_group.add_argument('--log2-histogram-init-bucket', metavar='START', 160 type=int, nargs='?', default=64, 161 help='initial buck size (default: 64)') 162 parser.add_argument('--histogram-omit-empty-buckets', 163 dest='histogram_omit_empty', 164 action='store_true', 165 help='omit empty histogram buckets') 166 parser.add_argument('--no-histogram', dest='histogram', 167 action='store_false', help='do not print histogram') 168 parser.set_defaults(histogram=True) 169 parser.set_defaults(histogram_omit_empty=False) 170 parser.add_argument('--rank', metavar='<no|min|max|avg>', 171 type=str, nargs='?', 172 default="no", 173 help="rank keys by metric (default: no)") 174 parser.add_argument('--csv', dest='csv', 175 action='store_true', help='provide output as csv') 176 parser.add_argument('--percentiles', dest='percentiles', 177 type=str, default="", 178 help='comma separated list of percentiles') 179 args = parser.parse_args() 180 181 histogram = None 182 if args.histogram: 183 bucket_trait = None 184 if args.histogram_type == "log2": 185 bucket_trait = Log2Bucket(args.log2_histogram_init_bucket) 186 else: 187 bucket_trait = LinearBucket(args.linear_histogram_granularity) 188 histogram = Histogram(bucket_trait, not args.histogram_omit_empty) 189 190 percentiles = [] 191 for percentile in args.percentiles.split(','): 192 try: 193 percentiles.append(float(percentile)) 194 except ValueError: 195 pass 196 197 categories = [ Category(key, deepcopy(histogram), args.csv, percentiles) 198 for key in args.keys ] 199 200 while True: 201 line = stdin.readline() 202 if not line: 203 break 204 obj = split_nvp(line) 205 for category in categories: 206 category.process_entry(obj) 207 208 # Filter out empty categories. 209 categories = [x for x in categories if not x.empty()] 210 211 if args.rank != "no": 212 categories = sorted(categories, key=make_key_func(args.rank), reverse=True) 213 214 for category in categories: 215 print(category) 216 217 218if __name__ == '__main__': 219 main() 220