michael@0: # This Source Code Form is subject to the terms of the Mozilla Public michael@0: # License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: # file, You can obtain one at http://mozilla.org/MPL/2.0/. michael@0: michael@0: # Write out histogram information for C++. The histograms are defined michael@0: # in a file provided as a command-line argument. michael@0: michael@0: from __future__ import with_statement michael@0: michael@0: import sys michael@0: import histogram_tools michael@0: import itertools michael@0: michael@0: banner = """/* This file is auto-generated, see gen-histogram-data.py. */ michael@0: """ michael@0: michael@0: # Write out the gHistograms array. michael@0: michael@0: class StringTable: michael@0: def __init__(self): michael@0: self.current_index = 0; michael@0: self.table = {} michael@0: michael@0: def c_strlen(self, string): michael@0: return len(string) + 1 michael@0: michael@0: def stringIndex(self, string): michael@0: if string in self.table: michael@0: return self.table[string] michael@0: else: michael@0: result = self.current_index michael@0: self.table[string] = result michael@0: self.current_index += self.c_strlen(string) michael@0: return result michael@0: michael@0: def writeDefinition(self, f, name): michael@0: entries = self.table.items() michael@0: entries.sort(key=lambda x:x[1]) michael@0: # Avoid null-in-string warnings with GCC and potentially michael@0: # overlong string constants; write everything out the long way. michael@0: def explodeToCharArray(string): michael@0: def toCChar(s): michael@0: if s == "'": michael@0: return "'\\''" michael@0: else: michael@0: return "'%s'" % s michael@0: return ", ".join(map(toCChar, string)) michael@0: f.write("const char %s[] = {\n" % name) michael@0: for (string, offset) in entries[:-1]: michael@0: e = explodeToCharArray(string) michael@0: if e: michael@0: f.write(" /* %5d */ %s, '\\0',\n" michael@0: % (offset, explodeToCharArray(string))) michael@0: else: michael@0: f.write(" /* %5d */ '\\0',\n" % offset) michael@0: f.write(" /* %5d */ %s, '\\0' };\n\n" michael@0: % (entries[-1][1], explodeToCharArray(entries[-1][0]))) michael@0: michael@0: def print_array_entry(histogram, name_index, exp_index): michael@0: cpp_guard = histogram.cpp_guard() michael@0: if cpp_guard: michael@0: print "#if defined(%s)" % cpp_guard michael@0: print " { %s, %s, %s, %s, %d, %d, %s }," \ michael@0: % (histogram.low(), histogram.high(), michael@0: histogram.n_buckets(), histogram.nsITelemetry_kind(), michael@0: name_index, exp_index, michael@0: "true" if histogram.extended_statistics_ok() else "false") michael@0: if cpp_guard: michael@0: print "#endif" michael@0: michael@0: def write_histogram_table(histograms): michael@0: table = StringTable() michael@0: michael@0: print "const TelemetryHistogram gHistograms[] = {" michael@0: for histogram in histograms: michael@0: name_index = table.stringIndex(histogram.name()) michael@0: exp_index = table.stringIndex(histogram.expiration()) michael@0: print_array_entry(histogram, name_index, exp_index) michael@0: print "};" michael@0: michael@0: strtab_name = "gHistogramStringTable" michael@0: table.writeDefinition(sys.stdout, strtab_name) michael@0: static_assert("sizeof(%s) <= UINT32_MAX" % strtab_name, michael@0: "index overflow") michael@0: michael@0: # Write out static asserts for histogram data. We'd prefer to perform michael@0: # these checks in this script itself, but since several histograms michael@0: # (generally enumerated histograms) use compile-time constants for michael@0: # their upper bounds, we have to let the compiler do the checking. michael@0: michael@0: def static_assert(expression, message): michael@0: print "static_assert(%s, \"%s\");" % (expression, message) michael@0: michael@0: def static_asserts_for_boolean(histogram): michael@0: pass michael@0: michael@0: def static_asserts_for_flag(histogram): michael@0: pass michael@0: michael@0: def static_asserts_for_enumerated(histogram): michael@0: n_values = histogram.high() michael@0: static_assert("%s > 2" % n_values, michael@0: "Not enough values for %s" % histogram.name()) michael@0: michael@0: def shared_static_asserts(histogram): michael@0: name = histogram.name() michael@0: low = histogram.low() michael@0: high = histogram.high() michael@0: n_buckets = histogram.n_buckets() michael@0: static_assert("%s < %s" % (low, high), "low >= high for %s" % name) michael@0: static_assert("%s > 2" % n_buckets, "Not enough values for %s" % name) michael@0: static_assert("%s >= 1" % low, "Incorrect low value for %s" % name) michael@0: static_assert("%s > %s" % (high, n_buckets), michael@0: "high must be > number of buckets for %s; you may want an enumerated histogram" % name) michael@0: michael@0: def static_asserts_for_linear(histogram): michael@0: shared_static_asserts(histogram) michael@0: michael@0: def static_asserts_for_exponential(histogram): michael@0: shared_static_asserts(histogram) michael@0: michael@0: def write_histogram_static_asserts(histograms): michael@0: print """ michael@0: // Perform the checks at the beginning of HistogramGet at michael@0: // compile time, so that incorrect histogram definitions michael@0: // give compile-time errors, not runtime errors.""" michael@0: michael@0: table = { michael@0: 'boolean' : static_asserts_for_boolean, michael@0: 'flag' : static_asserts_for_flag, michael@0: 'enumerated' : static_asserts_for_enumerated, michael@0: 'linear' : static_asserts_for_linear, michael@0: 'exponential' : static_asserts_for_exponential, michael@0: } michael@0: michael@0: for histogram in histograms: michael@0: histogram_tools.table_dispatch(histogram.kind(), table, michael@0: lambda f: f(histogram)) michael@0: michael@0: def write_debug_histogram_ranges(histograms): michael@0: ranges_lengths = [] michael@0: michael@0: # Collect all the range information from individual histograms. michael@0: # Write that information out as well. michael@0: print "#ifdef DEBUG" michael@0: print "const int gBucketLowerBounds[] = {" michael@0: for histogram in histograms: michael@0: ranges = [] michael@0: try: michael@0: ranges = histogram.ranges() michael@0: except histogram_tools.DefinitionException: michael@0: pass michael@0: ranges_lengths.append(len(ranges)) michael@0: # Note that we do not test cpp_guard here. We do this so we michael@0: # will have complete information about all the histograms in michael@0: # this array. Just having information about the ranges of michael@0: # histograms is not platform-specific; if there are histograms michael@0: # that have platform-specific constants in their definitions, michael@0: # those histograms will fail in the .ranges() call above and michael@0: # we'll have a zero-length array to deal with here. michael@0: if len(ranges) > 0: michael@0: print ','.join(map(str, ranges)), ',' michael@0: else: michael@0: print '/* Skipping %s */' % histogram.name() michael@0: print "};" michael@0: michael@0: # Write the offsets into gBucketLowerBounds. michael@0: print "struct bounds { int offset; int length; };" michael@0: print "const struct bounds gBucketLowerBoundIndex[] = {" michael@0: offset = 0 michael@0: for (histogram, range_length) in itertools.izip(histograms, ranges_lengths): michael@0: cpp_guard = histogram.cpp_guard() michael@0: # We do test cpp_guard here, so that histogram IDs are valid michael@0: # indexes into this array. michael@0: if cpp_guard: michael@0: print "#if defined(%s)" % cpp_guard michael@0: print "{ %d, %d }," % (offset, range_length) michael@0: if cpp_guard: michael@0: print "#endif" michael@0: offset += range_length michael@0: print "};" michael@0: print "#endif" michael@0: michael@0: def main(argv): michael@0: filename = argv[0] michael@0: michael@0: histograms = list(histogram_tools.from_file(filename)) michael@0: michael@0: print banner michael@0: write_histogram_table(histograms) michael@0: write_histogram_static_asserts(histograms) michael@0: write_debug_histogram_ranges(histograms) michael@0: michael@0: main(sys.argv[1:])