blob: 25afe8e4f0a74a6aed84524e85ec6c66f0bfeb91 [file] [log] [blame]
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001#!/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
Ben Murdochc5610432016-08-08 18:44:38 +010013from math import ceil,log
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000014from 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:
Ben Murdochc5610432016-08-08 18:44:38 +010077 def __init__(self, key, histogram, csv, percentiles):
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000078 self.key = key
79 self.values = []
80 self.histogram = histogram
Ben Murdochda12d292016-06-02 14:46:10 +010081 self.csv = csv
Ben Murdochc5610432016-08-08 18:44:38 +010082 self.percentiles = percentiles
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000083
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):
Ben Murdochda12d292016-06-02 14:46:10 +010097 if len(self.values) == 0:
98 return 0.0
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000099 return sum(self.values) / len(self.values)
100
Ben Murdochda12d292016-06-02 14:46:10 +0100101 def empty(self):
102 return len(self.values) == 0
103
Ben Murdochc5610432016-08-08 18:44:38 +0100104 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
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000114 def __str__(self):
Ben Murdochda12d292016-06-02 14:46:10 +0100115 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))
Ben Murdochc5610432016-08-08 18:44:38 +0100132 if self.percentiles:
133 ret.append("\n".join(self._compute_percentiles()))
Ben Murdochda12d292016-06-02 14:46:10 +0100134 return "\n".join(ret)
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000135
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)")
Ben Murdochda12d292016-06-02 14:46:10 +0100174 parser.add_argument('--csv', dest='csv',
175 action='store_true', help='provide output as csv')
Ben Murdochc5610432016-08-08 18:44:38 +0100176 parser.add_argument('--percentiles', dest='percentiles',
177 type=str, default="",
178 help='comma separated list of percentiles')
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000179 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
Ben Murdochc5610432016-08-08 18:44:38 +0100190 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)
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000198 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
Ben Murdochda12d292016-06-02 14:46:10 +0100208 # Filter out empty categories.
209 categories = [x for x in categories if not x.empty()]
210
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000211 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()