toolkit/components/telemetry/gen-histogram-data.py

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/toolkit/components/telemetry/gen-histogram-data.py	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,194 @@
     1.4 +# This Source Code Form is subject to the terms of the Mozilla Public
     1.5 +# License, v. 2.0. If a copy of the MPL was not distributed with this
     1.6 +# file, You can obtain one at http://mozilla.org/MPL/2.0/.
     1.7 +
     1.8 +# Write out histogram information for C++.  The histograms are defined
     1.9 +# in a file provided as a command-line argument.
    1.10 +
    1.11 +from __future__ import with_statement
    1.12 +
    1.13 +import sys
    1.14 +import histogram_tools
    1.15 +import itertools
    1.16 +
    1.17 +banner = """/* This file is auto-generated, see gen-histogram-data.py.  */
    1.18 +"""
    1.19 +
    1.20 +# Write out the gHistograms array.
    1.21 +
    1.22 +class StringTable:
    1.23 +    def __init__(self):
    1.24 +        self.current_index = 0;
    1.25 +        self.table = {}
    1.26 +
    1.27 +    def c_strlen(self, string):
    1.28 +        return len(string) + 1
    1.29 +
    1.30 +    def stringIndex(self, string):
    1.31 +        if string in self.table:
    1.32 +            return self.table[string]
    1.33 +        else:
    1.34 +            result = self.current_index
    1.35 +            self.table[string] = result
    1.36 +            self.current_index += self.c_strlen(string)
    1.37 +            return result
    1.38 +
    1.39 +    def writeDefinition(self, f, name):
    1.40 +        entries = self.table.items()
    1.41 +        entries.sort(key=lambda x:x[1])
    1.42 +        # Avoid null-in-string warnings with GCC and potentially
    1.43 +        # overlong string constants; write everything out the long way.
    1.44 +        def explodeToCharArray(string):
    1.45 +            def toCChar(s):
    1.46 +                if s == "'":
    1.47 +                    return "'\\''"
    1.48 +                else:
    1.49 +                    return "'%s'" % s
    1.50 +            return ", ".join(map(toCChar, string))
    1.51 +        f.write("const char %s[] = {\n" % name)
    1.52 +        for (string, offset) in entries[:-1]:
    1.53 +            e = explodeToCharArray(string)
    1.54 +            if e:
    1.55 +                f.write("  /* %5d */ %s, '\\0',\n"
    1.56 +                        % (offset, explodeToCharArray(string)))
    1.57 +            else:
    1.58 +                f.write("  /* %5d */ '\\0',\n" % offset)
    1.59 +        f.write("  /* %5d */ %s, '\\0' };\n\n"
    1.60 +                % (entries[-1][1], explodeToCharArray(entries[-1][0])))
    1.61 +
    1.62 +def print_array_entry(histogram, name_index, exp_index):
    1.63 +    cpp_guard = histogram.cpp_guard()
    1.64 +    if cpp_guard:
    1.65 +        print "#if defined(%s)" % cpp_guard
    1.66 +    print "  { %s, %s, %s, %s, %d, %d, %s }," \
    1.67 +        % (histogram.low(), histogram.high(),
    1.68 +           histogram.n_buckets(), histogram.nsITelemetry_kind(),
    1.69 +           name_index, exp_index,
    1.70 +           "true" if histogram.extended_statistics_ok() else "false")
    1.71 +    if cpp_guard:
    1.72 +        print "#endif"
    1.73 +
    1.74 +def write_histogram_table(histograms):
    1.75 +    table = StringTable()
    1.76 +
    1.77 +    print "const TelemetryHistogram gHistograms[] = {"
    1.78 +    for histogram in histograms:
    1.79 +        name_index = table.stringIndex(histogram.name())
    1.80 +        exp_index = table.stringIndex(histogram.expiration())
    1.81 +        print_array_entry(histogram, name_index, exp_index)
    1.82 +    print "};"
    1.83 +
    1.84 +    strtab_name = "gHistogramStringTable"
    1.85 +    table.writeDefinition(sys.stdout, strtab_name)
    1.86 +    static_assert("sizeof(%s) <= UINT32_MAX" % strtab_name,
    1.87 +                  "index overflow")
    1.88 +
    1.89 +# Write out static asserts for histogram data.  We'd prefer to perform
    1.90 +# these checks in this script itself, but since several histograms
    1.91 +# (generally enumerated histograms) use compile-time constants for
    1.92 +# their upper bounds, we have to let the compiler do the checking.
    1.93 +
    1.94 +def static_assert(expression, message):
    1.95 +    print "static_assert(%s, \"%s\");" % (expression, message)
    1.96 +
    1.97 +def static_asserts_for_boolean(histogram):
    1.98 +    pass
    1.99 +
   1.100 +def static_asserts_for_flag(histogram):
   1.101 +    pass
   1.102 +
   1.103 +def static_asserts_for_enumerated(histogram):
   1.104 +    n_values = histogram.high()
   1.105 +    static_assert("%s > 2" % n_values,
   1.106 +                  "Not enough values for %s" % histogram.name())
   1.107 +
   1.108 +def shared_static_asserts(histogram):
   1.109 +    name = histogram.name()
   1.110 +    low = histogram.low()
   1.111 +    high = histogram.high()
   1.112 +    n_buckets = histogram.n_buckets()
   1.113 +    static_assert("%s < %s" % (low, high), "low >= high for %s" % name)
   1.114 +    static_assert("%s > 2" % n_buckets, "Not enough values for %s" % name)
   1.115 +    static_assert("%s >= 1" % low, "Incorrect low value for %s" % name)
   1.116 +    static_assert("%s > %s" % (high, n_buckets),
   1.117 +                  "high must be > number of buckets for %s; you may want an enumerated histogram" % name)
   1.118 +
   1.119 +def static_asserts_for_linear(histogram):
   1.120 +    shared_static_asserts(histogram)
   1.121 +
   1.122 +def static_asserts_for_exponential(histogram):
   1.123 +    shared_static_asserts(histogram)
   1.124 +
   1.125 +def write_histogram_static_asserts(histograms):
   1.126 +    print """
   1.127 +// Perform the checks at the beginning of HistogramGet at
   1.128 +// compile time, so that incorrect histogram definitions
   1.129 +// give compile-time errors, not runtime errors."""
   1.130 +
   1.131 +    table = {
   1.132 +        'boolean' : static_asserts_for_boolean,
   1.133 +        'flag' : static_asserts_for_flag,
   1.134 +        'enumerated' : static_asserts_for_enumerated,
   1.135 +        'linear' : static_asserts_for_linear,
   1.136 +        'exponential' : static_asserts_for_exponential,
   1.137 +        }
   1.138 +
   1.139 +    for histogram in histograms:
   1.140 +        histogram_tools.table_dispatch(histogram.kind(), table,
   1.141 +                                       lambda f: f(histogram))
   1.142 +
   1.143 +def write_debug_histogram_ranges(histograms):
   1.144 +    ranges_lengths = []
   1.145 +
   1.146 +    # Collect all the range information from individual histograms.
   1.147 +    # Write that information out as well.
   1.148 +    print "#ifdef DEBUG"
   1.149 +    print "const int gBucketLowerBounds[] = {"
   1.150 +    for histogram in histograms:
   1.151 +        ranges = []
   1.152 +        try:
   1.153 +            ranges = histogram.ranges()
   1.154 +        except histogram_tools.DefinitionException:
   1.155 +            pass
   1.156 +        ranges_lengths.append(len(ranges))
   1.157 +        # Note that we do not test cpp_guard here.  We do this so we
   1.158 +        # will have complete information about all the histograms in
   1.159 +        # this array.  Just having information about the ranges of
   1.160 +        # histograms is not platform-specific; if there are histograms
   1.161 +        # that have platform-specific constants in their definitions,
   1.162 +        # those histograms will fail in the .ranges() call above and
   1.163 +        # we'll have a zero-length array to deal with here.
   1.164 +        if len(ranges) > 0:
   1.165 +            print ','.join(map(str, ranges)), ','
   1.166 +        else:
   1.167 +            print '/* Skipping %s */' % histogram.name()
   1.168 +    print "};"
   1.169 +
   1.170 +    # Write the offsets into gBucketLowerBounds.
   1.171 +    print "struct bounds { int offset; int length; };"
   1.172 +    print "const struct bounds gBucketLowerBoundIndex[] = {"
   1.173 +    offset = 0
   1.174 +    for (histogram, range_length) in itertools.izip(histograms, ranges_lengths):
   1.175 +        cpp_guard = histogram.cpp_guard()
   1.176 +        # We do test cpp_guard here, so that histogram IDs are valid
   1.177 +        # indexes into this array.
   1.178 +        if cpp_guard:
   1.179 +            print "#if defined(%s)" % cpp_guard
   1.180 +        print "{ %d, %d }," % (offset, range_length)
   1.181 +        if cpp_guard:
   1.182 +            print "#endif"
   1.183 +        offset += range_length
   1.184 +    print "};"
   1.185 +    print "#endif"
   1.186 +
   1.187 +def main(argv):
   1.188 +    filename = argv[0]
   1.189 +
   1.190 +    histograms = list(histogram_tools.from_file(filename))
   1.191 +
   1.192 +    print banner
   1.193 +    write_histogram_table(histograms)
   1.194 +    write_histogram_static_asserts(histograms)
   1.195 +    write_debug_histogram_ranges(histograms)
   1.196 +
   1.197 +main(sys.argv[1:])

mercurial