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