• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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