Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 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 | |
| 10 | from argparse import ArgumentParser |
| 11 | from copy import deepcopy |
| 12 | from gc_nvp_common import split_nvp |
Ben Murdoch | c561043 | 2016-08-08 18:44:38 +0100 | [diff] [blame] | 13 | from math import ceil,log |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 14 | from sys import stdin |
| 15 | |
| 16 | |
| 17 | class 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 | |
| 28 | class 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 | |
| 46 | class 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 | |
| 76 | class Category: |
Ben Murdoch | c561043 | 2016-08-08 18:44:38 +0100 | [diff] [blame] | 77 | def __init__(self, key, histogram, csv, percentiles): |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 78 | self.key = key |
| 79 | self.values = [] |
| 80 | self.histogram = histogram |
Ben Murdoch | da12d29 | 2016-06-02 14:46:10 +0100 | [diff] [blame] | 81 | self.csv = csv |
Ben Murdoch | c561043 | 2016-08-08 18:44:38 +0100 | [diff] [blame] | 82 | self.percentiles = percentiles |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 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): |
Ben Murdoch | da12d29 | 2016-06-02 14:46:10 +0100 | [diff] [blame] | 97 | if len(self.values) == 0: |
| 98 | return 0.0 |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 99 | return sum(self.values) / len(self.values) |
| 100 | |
Ben Murdoch | da12d29 | 2016-06-02 14:46:10 +0100 | [diff] [blame] | 101 | def empty(self): |
| 102 | return len(self.values) == 0 |
| 103 | |
Ben Murdoch | c561043 | 2016-08-08 18:44:38 +0100 | [diff] [blame] | 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 | |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 114 | def __str__(self): |
Ben Murdoch | da12d29 | 2016-06-02 14:46:10 +0100 | [diff] [blame] | 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)) |
Ben Murdoch | c561043 | 2016-08-08 18:44:38 +0100 | [diff] [blame] | 132 | if self.percentiles: |
| 133 | ret.append("\n".join(self._compute_percentiles())) |
Ben Murdoch | da12d29 | 2016-06-02 14:46:10 +0100 | [diff] [blame] | 134 | return "\n".join(ret) |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 135 | |
| 136 | def __repr__(self): |
| 137 | return "<Category: {0}>".format(self.key) |
| 138 | |
| 139 | |
| 140 | def make_key_func(cmp_metric): |
| 141 | def key_func(a): |
| 142 | return getattr(a, cmp_metric)() |
| 143 | return key_func |
| 144 | |
| 145 | |
| 146 | def 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 Murdoch | da12d29 | 2016-06-02 14:46:10 +0100 | [diff] [blame] | 174 | parser.add_argument('--csv', dest='csv', |
| 175 | action='store_true', help='provide output as csv') |
Ben Murdoch | c561043 | 2016-08-08 18:44:38 +0100 | [diff] [blame] | 176 | parser.add_argument('--percentiles', dest='percentiles', |
| 177 | type=str, default="", |
| 178 | help='comma separated list of percentiles') |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 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 | |
Ben Murdoch | c561043 | 2016-08-08 18:44:38 +0100 | [diff] [blame] | 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) |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 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 | |
Ben Murdoch | da12d29 | 2016-06-02 14:46:10 +0100 | [diff] [blame] | 208 | # Filter out empty categories. |
| 209 | categories = [x for x in categories if not x.empty()] |
| 210 | |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 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 | |
| 218 | if __name__ == '__main__': |
| 219 | main() |