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/.
5 # Write out histogram information for C++. The histograms are defined
6 # in a file provided as a command-line argument.
12 from mozparsers
import parse_histograms
13 from mozparsers
.shared_telemetry_utils
import ParserError
, static_assert
15 COMPONENTS_PATH
= path
.abspath(
16 path
.join(path
.dirname(__file__
), path
.pardir
, path
.pardir
)
19 path
.join(COMPONENTS_PATH
, "glean", "build_scripts", "glean_parser_ext")
21 from string_table
import StringTable
23 banner
= """/* This file is auto-generated, see gen_histogram_data.py. */
27 def print_array_entry(
39 if histogram
.record_on_os(buildconfig
.substs
["OS_TARGET"]):
41 " { %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %s, %s, %s, %s, %s, %s },"
45 histogram
.n_buckets(),
54 " | ".join(histogram
.record_in_processes_enum()),
55 "true" if histogram
.keyed() else "false",
56 histogram
.nsITelemetry_kind(),
58 " | ".join(histogram
.products_enum()),
64 def write_histogram_table(output
, histograms
):
65 string_table
= StringTable()
74 print("constexpr HistogramInfo gHistogramInfos[] = {", file=output
)
75 for histogram
in histograms
:
76 name_index
= string_table
.stringIndex(histogram
.name())
77 exp_index
= string_table
.stringIndex(histogram
.expiration())
79 labels
= histogram
.labels()
82 label_index
= label_count
83 label_table
.append((histogram
.name(), string_table
.stringIndexes(labels
)))
84 label_count
+= len(labels
)
86 keys
= histogram
.keys()
89 key_index
= keys_count
90 keys_table
.append((histogram
.name(), string_table
.stringIndexes(keys
)))
91 keys_count
+= len(keys
)
93 stores
= histogram
.record_into_store()
95 if stores
== ["main"]:
96 # if count == 1 && offset == UINT16_MAX -> only main store
97 store_index
= "UINT16_MAX"
99 store_index
= total_store_count
100 store_table
.append((histogram
.name(), string_table
.stringIndexes(stores
)))
101 total_store_count
+= len(stores
)
115 print("};\n", file=output
)
117 strtab_name
= "gHistogramStringTable"
118 string_table
.writeDefinition(output
, strtab_name
)
119 static_assert(output
, "sizeof(%s) <= UINT32_MAX" % strtab_name
, "index overflow")
121 print("\n#if defined(_MSC_VER) && !defined(__clang__)", file=output
)
122 print("const uint32_t gHistogramLabelTable[] = {", file=output
)
123 print("#else", file=output
)
124 print("constexpr uint32_t gHistogramLabelTable[] = {", file=output
)
125 print("#endif", file=output
)
126 for name
, indexes
in label_table
:
127 print("/* %s */ %s," % (name
, ", ".join(map(str, indexes
))), file=output
)
128 print("};", file=output
)
130 output
, "sizeof(gHistogramLabelTable) <= UINT16_MAX", "index overflow"
133 print("\n#if defined(_MSC_VER) && !defined(__clang__)", file=output
)
134 print("const uint32_t gHistogramKeyTable[] = {", file=output
)
135 print("#else", file=output
)
136 print("constexpr uint32_t gHistogramKeyTable[] = {", file=output
)
137 print("#endif", file=output
)
138 for name
, indexes
in keys_table
:
139 print("/* %s */ %s," % (name
, ", ".join(map(str, indexes
))), file=output
)
140 print("};", file=output
)
141 static_assert(output
, "sizeof(gHistogramKeyTable) <= UINT16_MAX", "index overflow")
143 store_table_name
= "gHistogramStoresTable"
144 print("\n#if defined(_MSC_VER) && !defined(__clang__)", file=output
)
145 print("const uint32_t {}[] = {{".format(store_table_name
), file=output
)
146 print("#else", file=output
)
147 print("constexpr uint32_t {}[] = {{".format(store_table_name
), file=output
)
148 print("#endif", file=output
)
149 for name
, indexes
in store_table
:
150 print("/* %s */ %s," % (name
, ", ".join(map(str, indexes
))), file=output
)
151 print("};", file=output
)
153 output
, "sizeof(%s) <= UINT16_MAX" % store_table_name
, "index overflow"
157 # Write out static asserts for histogram data. We'd prefer to perform
158 # these checks in this script itself, but since several histograms
159 # (generally enumerated histograms) use compile-time constants for
160 # their upper bounds, we have to let the compiler do the checking.
163 def static_asserts_for_boolean(output
, histogram
):
167 def static_asserts_for_flag(output
, histogram
):
171 def static_asserts_for_count(output
, histogram
):
175 def static_asserts_for_enumerated(output
, histogram
):
176 n_values
= histogram
.high()
178 output
, "%s > 2" % n_values
, "Not enough values for %s" % histogram
.name()
182 def shared_static_asserts(output
, histogram
):
183 name
= histogram
.name()
184 low
= histogram
.low()
185 high
= histogram
.high()
186 n_buckets
= histogram
.n_buckets()
187 static_assert(output
, "%s < %s" % (low
, high
), "low >= high for %s" % name
)
188 static_assert(output
, "%s > 2" % n_buckets
, "Not enough values for %s" % name
)
189 static_assert(output
, "%s >= 1" % low
, "Incorrect low value for %s" % name
)
192 "%s > %s" % (high
, n_buckets
),
193 "high must be > number of buckets for %s;"
194 " you may want an enumerated histogram" % name
,
198 def static_asserts_for_linear(output
, histogram
):
199 shared_static_asserts(output
, histogram
)
202 def static_asserts_for_exponential(output
, histogram
):
203 shared_static_asserts(output
, histogram
)
206 def write_histogram_static_asserts(output
, histograms
):
209 // Perform the checks at the beginning of HistogramGet at
210 // compile time, so that incorrect histogram definitions
211 // give compile-time errors, not runtime errors.""",
216 "boolean": static_asserts_for_boolean
,
217 "flag": static_asserts_for_flag
,
218 "count": static_asserts_for_count
,
219 "enumerated": static_asserts_for_enumerated
,
220 "categorical": static_asserts_for_enumerated
,
221 "linear": static_asserts_for_linear
,
222 "exponential": static_asserts_for_exponential
,
225 target_os
= buildconfig
.substs
["OS_TARGET"]
226 for histogram
in histograms
:
227 kind
= histogram
.kind()
228 if not histogram
.record_on_os(target_os
):
231 if kind
not in table
:
233 'Unknown kind "%s" for histogram "%s".' % (kind
, histogram
.name())
236 fn(output
, histogram
)
239 def write_histogram_ranges(output
, histograms
):
240 # This generates static data to avoid costly initialization of histograms
241 # (especially exponential ones which require log and exp calls) at runtime.
242 # The format must exactly match that required in histogram.cc, which is
243 # 0, buckets..., INT_MAX. Additionally, the list ends in a 0 to aid asserts
244 # that validate that the length of the ranges list is correct.U cache miss.
245 print("#if defined(_MSC_VER) && !defined(__clang__)", file=output
)
246 print("const int gHistogramBucketLowerBounds[] = {", file=output
)
247 print("#else", file=output
)
248 print("constexpr int gHistogramBucketLowerBounds[] = {", file=output
)
249 print("#endif", file=output
)
251 # Print the dummy buckets for expired histograms, and set the offset to match.
252 print("0,1,2,INT_MAX,", file=output
)
256 for histogram
in histograms
:
257 ranges
= tuple(histogram
.ranges())
258 if ranges
not in ranges_offsets
:
259 ranges_offsets
[ranges
] = offset
260 # Suffix each ranges listing with INT_MAX, to match histogram.cc's
262 offset
+= len(ranges
) + 1
263 print(",".join(map(str, ranges
)), ",INT_MAX,", file=output
)
264 print("0};", file=output
)
267 raise Exception("Histogram offsets exceeded maximum value for an int16_t.")
269 target_os
= buildconfig
.substs
["OS_TARGET"]
270 print("#if defined(_MSC_VER) && !defined(__clang__)", file=output
)
271 print("const int16_t gHistogramBucketLowerBoundIndex[] = {", file=output
)
272 print("#else", file=output
)
273 print("constexpr int16_t gHistogramBucketLowerBoundIndex[] = {", file=output
)
274 print("#endif", file=output
)
275 for histogram
in histograms
:
276 if histogram
.record_on_os(target_os
):
277 our_offset
= ranges_offsets
[tuple(histogram
.ranges())]
278 print("%d," % our_offset
, file=output
)
280 print("};", file=output
)
283 def main(output
, *filenames
):
285 histograms
= list(parse_histograms
.from_files(filenames
))
286 except ParserError
as ex
:
287 print("\nError processing histograms:\n" + str(ex
) + "\n")
290 print(banner
, file=output
)
291 write_histogram_table(output
, histograms
)
292 write_histogram_ranges(output
, histograms
)
293 write_histogram_static_asserts(output
, histograms
)
296 if __name__
== "__main__":
297 main(sys
.stdout
, *sys
.argv
[1:])