|
1 # This Source Code Form is subject to the terms of the Mozilla Public |
|
2 # License, v. 2.0. If a copy of the MPL was not distributed with this |
|
3 # file, You can obtain one at http://mozilla.org/MPL/2.0/. |
|
4 |
|
5 # Write out histogram information for C++. The histograms are defined |
|
6 # in a file provided as a command-line argument. |
|
7 |
|
8 from __future__ import with_statement |
|
9 |
|
10 import sys |
|
11 import histogram_tools |
|
12 import itertools |
|
13 |
|
14 banner = """/* This file is auto-generated, see gen-histogram-data.py. */ |
|
15 """ |
|
16 |
|
17 # Write out the gHistograms array. |
|
18 |
|
19 class StringTable: |
|
20 def __init__(self): |
|
21 self.current_index = 0; |
|
22 self.table = {} |
|
23 |
|
24 def c_strlen(self, string): |
|
25 return len(string) + 1 |
|
26 |
|
27 def stringIndex(self, string): |
|
28 if string in self.table: |
|
29 return self.table[string] |
|
30 else: |
|
31 result = self.current_index |
|
32 self.table[string] = result |
|
33 self.current_index += self.c_strlen(string) |
|
34 return result |
|
35 |
|
36 def writeDefinition(self, f, name): |
|
37 entries = self.table.items() |
|
38 entries.sort(key=lambda x:x[1]) |
|
39 # Avoid null-in-string warnings with GCC and potentially |
|
40 # overlong string constants; write everything out the long way. |
|
41 def explodeToCharArray(string): |
|
42 def toCChar(s): |
|
43 if s == "'": |
|
44 return "'\\''" |
|
45 else: |
|
46 return "'%s'" % s |
|
47 return ", ".join(map(toCChar, string)) |
|
48 f.write("const char %s[] = {\n" % name) |
|
49 for (string, offset) in entries[:-1]: |
|
50 e = explodeToCharArray(string) |
|
51 if e: |
|
52 f.write(" /* %5d */ %s, '\\0',\n" |
|
53 % (offset, explodeToCharArray(string))) |
|
54 else: |
|
55 f.write(" /* %5d */ '\\0',\n" % offset) |
|
56 f.write(" /* %5d */ %s, '\\0' };\n\n" |
|
57 % (entries[-1][1], explodeToCharArray(entries[-1][0]))) |
|
58 |
|
59 def print_array_entry(histogram, name_index, exp_index): |
|
60 cpp_guard = histogram.cpp_guard() |
|
61 if cpp_guard: |
|
62 print "#if defined(%s)" % cpp_guard |
|
63 print " { %s, %s, %s, %s, %d, %d, %s }," \ |
|
64 % (histogram.low(), histogram.high(), |
|
65 histogram.n_buckets(), histogram.nsITelemetry_kind(), |
|
66 name_index, exp_index, |
|
67 "true" if histogram.extended_statistics_ok() else "false") |
|
68 if cpp_guard: |
|
69 print "#endif" |
|
70 |
|
71 def write_histogram_table(histograms): |
|
72 table = StringTable() |
|
73 |
|
74 print "const TelemetryHistogram gHistograms[] = {" |
|
75 for histogram in histograms: |
|
76 name_index = table.stringIndex(histogram.name()) |
|
77 exp_index = table.stringIndex(histogram.expiration()) |
|
78 print_array_entry(histogram, name_index, exp_index) |
|
79 print "};" |
|
80 |
|
81 strtab_name = "gHistogramStringTable" |
|
82 table.writeDefinition(sys.stdout, strtab_name) |
|
83 static_assert("sizeof(%s) <= UINT32_MAX" % strtab_name, |
|
84 "index overflow") |
|
85 |
|
86 # Write out static asserts for histogram data. We'd prefer to perform |
|
87 # these checks in this script itself, but since several histograms |
|
88 # (generally enumerated histograms) use compile-time constants for |
|
89 # their upper bounds, we have to let the compiler do the checking. |
|
90 |
|
91 def static_assert(expression, message): |
|
92 print "static_assert(%s, \"%s\");" % (expression, message) |
|
93 |
|
94 def static_asserts_for_boolean(histogram): |
|
95 pass |
|
96 |
|
97 def static_asserts_for_flag(histogram): |
|
98 pass |
|
99 |
|
100 def static_asserts_for_enumerated(histogram): |
|
101 n_values = histogram.high() |
|
102 static_assert("%s > 2" % n_values, |
|
103 "Not enough values for %s" % histogram.name()) |
|
104 |
|
105 def shared_static_asserts(histogram): |
|
106 name = histogram.name() |
|
107 low = histogram.low() |
|
108 high = histogram.high() |
|
109 n_buckets = histogram.n_buckets() |
|
110 static_assert("%s < %s" % (low, high), "low >= high for %s" % name) |
|
111 static_assert("%s > 2" % n_buckets, "Not enough values for %s" % name) |
|
112 static_assert("%s >= 1" % low, "Incorrect low value for %s" % name) |
|
113 static_assert("%s > %s" % (high, n_buckets), |
|
114 "high must be > number of buckets for %s; you may want an enumerated histogram" % name) |
|
115 |
|
116 def static_asserts_for_linear(histogram): |
|
117 shared_static_asserts(histogram) |
|
118 |
|
119 def static_asserts_for_exponential(histogram): |
|
120 shared_static_asserts(histogram) |
|
121 |
|
122 def write_histogram_static_asserts(histograms): |
|
123 print """ |
|
124 // Perform the checks at the beginning of HistogramGet at |
|
125 // compile time, so that incorrect histogram definitions |
|
126 // give compile-time errors, not runtime errors.""" |
|
127 |
|
128 table = { |
|
129 'boolean' : static_asserts_for_boolean, |
|
130 'flag' : static_asserts_for_flag, |
|
131 'enumerated' : static_asserts_for_enumerated, |
|
132 'linear' : static_asserts_for_linear, |
|
133 'exponential' : static_asserts_for_exponential, |
|
134 } |
|
135 |
|
136 for histogram in histograms: |
|
137 histogram_tools.table_dispatch(histogram.kind(), table, |
|
138 lambda f: f(histogram)) |
|
139 |
|
140 def write_debug_histogram_ranges(histograms): |
|
141 ranges_lengths = [] |
|
142 |
|
143 # Collect all the range information from individual histograms. |
|
144 # Write that information out as well. |
|
145 print "#ifdef DEBUG" |
|
146 print "const int gBucketLowerBounds[] = {" |
|
147 for histogram in histograms: |
|
148 ranges = [] |
|
149 try: |
|
150 ranges = histogram.ranges() |
|
151 except histogram_tools.DefinitionException: |
|
152 pass |
|
153 ranges_lengths.append(len(ranges)) |
|
154 # Note that we do not test cpp_guard here. We do this so we |
|
155 # will have complete information about all the histograms in |
|
156 # this array. Just having information about the ranges of |
|
157 # histograms is not platform-specific; if there are histograms |
|
158 # that have platform-specific constants in their definitions, |
|
159 # those histograms will fail in the .ranges() call above and |
|
160 # we'll have a zero-length array to deal with here. |
|
161 if len(ranges) > 0: |
|
162 print ','.join(map(str, ranges)), ',' |
|
163 else: |
|
164 print '/* Skipping %s */' % histogram.name() |
|
165 print "};" |
|
166 |
|
167 # Write the offsets into gBucketLowerBounds. |
|
168 print "struct bounds { int offset; int length; };" |
|
169 print "const struct bounds gBucketLowerBoundIndex[] = {" |
|
170 offset = 0 |
|
171 for (histogram, range_length) in itertools.izip(histograms, ranges_lengths): |
|
172 cpp_guard = histogram.cpp_guard() |
|
173 # We do test cpp_guard here, so that histogram IDs are valid |
|
174 # indexes into this array. |
|
175 if cpp_guard: |
|
176 print "#if defined(%s)" % cpp_guard |
|
177 print "{ %d, %d }," % (offset, range_length) |
|
178 if cpp_guard: |
|
179 print "#endif" |
|
180 offset += range_length |
|
181 print "};" |
|
182 print "#endif" |
|
183 |
|
184 def main(argv): |
|
185 filename = argv[0] |
|
186 |
|
187 histograms = list(histogram_tools.from_file(filename)) |
|
188 |
|
189 print banner |
|
190 write_histogram_table(histograms) |
|
191 write_histogram_static_asserts(histograms) |
|
192 write_debug_histogram_ranges(histograms) |
|
193 |
|
194 main(sys.argv[1:]) |