292 lines
9.8 KiB
Python
292 lines
9.8 KiB
Python
# 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.
|
|
|
|
import sys
|
|
from os import path
|
|
|
|
import buildconfig
|
|
from mozparsers import parse_histograms
|
|
from mozparsers.shared_telemetry_utils import ParserError, static_assert
|
|
|
|
COMPONENTS_PATH = path.abspath(
|
|
path.join(path.dirname(__file__), path.pardir, path.pardir)
|
|
)
|
|
sys.path.append(
|
|
path.join(COMPONENTS_PATH, "glean", "build_scripts", "glean_parser_ext")
|
|
)
|
|
from string_table import StringTable
|
|
|
|
banner = """/* This file is auto-generated, see gen_histogram_data.py. */
|
|
"""
|
|
|
|
|
|
def print_array_entry(
|
|
output,
|
|
histogram,
|
|
name_index,
|
|
exp_index,
|
|
label_index,
|
|
label_count,
|
|
key_index,
|
|
key_count,
|
|
store_index,
|
|
store_count,
|
|
):
|
|
if histogram.record_on_os(buildconfig.substs["OS_TARGET"]):
|
|
print(
|
|
" { %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %s, %s, %s, %s, %s, %s },"
|
|
% (
|
|
histogram.low(),
|
|
histogram.high(),
|
|
histogram.n_buckets(),
|
|
name_index,
|
|
exp_index,
|
|
label_count,
|
|
key_count,
|
|
store_count,
|
|
label_index,
|
|
key_index,
|
|
store_index,
|
|
" | ".join(histogram.record_in_processes_enum()),
|
|
"true" if histogram.keyed() else "false",
|
|
histogram.nsITelemetry_kind(),
|
|
histogram.dataset(),
|
|
" | ".join(histogram.products_enum()),
|
|
),
|
|
file=output,
|
|
)
|
|
|
|
|
|
def write_histogram_table(output, histograms):
|
|
string_table = StringTable()
|
|
|
|
label_table = []
|
|
label_count = 0
|
|
keys_table = []
|
|
keys_count = 0
|
|
store_table = []
|
|
total_store_count = 0
|
|
|
|
print("constexpr HistogramInfo gHistogramInfos[] = {", file=output)
|
|
for histogram in histograms:
|
|
name_index = string_table.stringIndex(histogram.name())
|
|
exp_index = string_table.stringIndex(histogram.expiration())
|
|
|
|
labels = histogram.labels()
|
|
label_index = 0
|
|
if len(labels) > 0:
|
|
label_index = label_count
|
|
label_table.append((histogram.name(), string_table.stringIndexes(labels)))
|
|
label_count += len(labels)
|
|
|
|
keys = histogram.keys()
|
|
key_index = 0
|
|
if len(keys) > 0:
|
|
key_index = keys_count
|
|
keys_table.append((histogram.name(), string_table.stringIndexes(keys)))
|
|
keys_count += len(keys)
|
|
|
|
stores = histogram.record_into_store()
|
|
store_index = 0
|
|
if stores == ["main"]:
|
|
# if count == 1 && offset == UINT16_MAX -> only main store
|
|
store_index = "UINT16_MAX"
|
|
else:
|
|
store_index = total_store_count
|
|
store_table.append((histogram.name(), string_table.stringIndexes(stores)))
|
|
total_store_count += len(stores)
|
|
|
|
print_array_entry(
|
|
output,
|
|
histogram,
|
|
name_index,
|
|
exp_index,
|
|
label_index,
|
|
len(labels),
|
|
key_index,
|
|
len(keys),
|
|
store_index,
|
|
len(stores),
|
|
)
|
|
print("};\n", file=output)
|
|
|
|
strtab_name = "gHistogramStringTable"
|
|
string_table.writeDefinition(output, strtab_name)
|
|
static_assert(output, "sizeof(%s) <= UINT32_MAX" % strtab_name, "index overflow")
|
|
|
|
print("\n#if defined(_MSC_VER) && !defined(__clang__)", file=output)
|
|
print("const uint32_t gHistogramLabelTable[] = {", file=output)
|
|
print("#else", file=output)
|
|
print("constexpr uint32_t gHistogramLabelTable[] = {", file=output)
|
|
print("#endif", file=output)
|
|
for name, indexes in label_table:
|
|
print("/* %s */ %s," % (name, ", ".join(map(str, indexes))), file=output)
|
|
print("};", file=output)
|
|
static_assert(
|
|
output, "sizeof(gHistogramLabelTable) <= UINT16_MAX", "index overflow"
|
|
)
|
|
|
|
print("\n#if defined(_MSC_VER) && !defined(__clang__)", file=output)
|
|
print("const uint32_t gHistogramKeyTable[] = {", file=output)
|
|
print("#else", file=output)
|
|
print("constexpr uint32_t gHistogramKeyTable[] = {", file=output)
|
|
print("#endif", file=output)
|
|
for name, indexes in keys_table:
|
|
print("/* %s */ %s," % (name, ", ".join(map(str, indexes))), file=output)
|
|
print("};", file=output)
|
|
static_assert(output, "sizeof(gHistogramKeyTable) <= UINT16_MAX", "index overflow")
|
|
|
|
store_table_name = "gHistogramStoresTable"
|
|
print("\n#if defined(_MSC_VER) && !defined(__clang__)", file=output)
|
|
print(f"const uint32_t {store_table_name}[] = {{", file=output)
|
|
print("#else", file=output)
|
|
print(f"constexpr uint32_t {store_table_name}[] = {{", file=output)
|
|
print("#endif", file=output)
|
|
for name, indexes in store_table:
|
|
print("/* %s */ %s," % (name, ", ".join(map(str, indexes))), file=output)
|
|
print("};", file=output)
|
|
static_assert(
|
|
output, "sizeof(%s) <= UINT16_MAX" % store_table_name, "index overflow"
|
|
)
|
|
|
|
|
|
# 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_asserts_for_boolean(output, histogram):
|
|
pass
|
|
|
|
|
|
def static_asserts_for_count(output, histogram):
|
|
pass
|
|
|
|
|
|
def static_asserts_for_enumerated(output, histogram):
|
|
n_values = histogram.high()
|
|
static_assert(
|
|
output, "%s > 2" % n_values, "Not enough values for %s" % histogram.name()
|
|
)
|
|
|
|
|
|
def shared_static_asserts(output, histogram):
|
|
name = histogram.name()
|
|
low = histogram.low()
|
|
high = histogram.high()
|
|
n_buckets = histogram.n_buckets()
|
|
static_assert(output, "%s < %s" % (low, high), "low >= high for %s" % name)
|
|
static_assert(output, "%s > 2" % n_buckets, "Not enough values for %s" % name)
|
|
static_assert(output, "%s >= 1" % low, "Incorrect low value for %s" % name)
|
|
static_assert(
|
|
output,
|
|
"%s > %s" % (high, n_buckets),
|
|
"high must be > number of buckets for %s;"
|
|
" you may want an enumerated histogram" % name,
|
|
)
|
|
|
|
|
|
def static_asserts_for_linear(output, histogram):
|
|
shared_static_asserts(output, histogram)
|
|
|
|
|
|
def static_asserts_for_exponential(output, histogram):
|
|
shared_static_asserts(output, histogram)
|
|
|
|
|
|
def write_histogram_static_asserts(output, 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.""",
|
|
file=output,
|
|
)
|
|
|
|
table = {
|
|
"boolean": static_asserts_for_boolean,
|
|
"count": static_asserts_for_count,
|
|
"enumerated": static_asserts_for_enumerated,
|
|
"categorical": static_asserts_for_enumerated,
|
|
"linear": static_asserts_for_linear,
|
|
"exponential": static_asserts_for_exponential,
|
|
}
|
|
|
|
target_os = buildconfig.substs["OS_TARGET"]
|
|
for histogram in histograms:
|
|
kind = histogram.kind()
|
|
if not histogram.record_on_os(target_os):
|
|
continue
|
|
|
|
if kind not in table:
|
|
raise Exception(
|
|
'Unknown kind "%s" for histogram "%s".' % (kind, histogram.name())
|
|
)
|
|
fn = table[kind]
|
|
fn(output, histogram)
|
|
|
|
|
|
def write_histogram_ranges(output, histograms):
|
|
# This generates static data to avoid costly initialization of histograms
|
|
# (especially exponential ones which require log and exp calls) at runtime.
|
|
# The format must exactly match that required in histogram.cc, which is
|
|
# 0, buckets..., INT_MAX. Additionally, the list ends in a 0 to aid asserts
|
|
# that validate that the length of the ranges list is correct.U cache miss.
|
|
print("#if defined(_MSC_VER) && !defined(__clang__)", file=output)
|
|
print("const int gHistogramBucketLowerBounds[] = {", file=output)
|
|
print("#else", file=output)
|
|
print("constexpr int gHistogramBucketLowerBounds[] = {", file=output)
|
|
print("#endif", file=output)
|
|
|
|
# Print the dummy buckets for expired histograms, and set the offset to match.
|
|
print("0,1,2,INT_MAX,", file=output)
|
|
offset = 4
|
|
ranges_offsets = {}
|
|
|
|
for histogram in histograms:
|
|
ranges = tuple(histogram.ranges())
|
|
if ranges not in ranges_offsets:
|
|
ranges_offsets[ranges] = offset
|
|
# Suffix each ranges listing with INT_MAX, to match histogram.cc's
|
|
# expected format.
|
|
offset += len(ranges) + 1
|
|
print(",".join(map(str, ranges)), ",INT_MAX,", file=output)
|
|
print("0};", file=output)
|
|
|
|
if offset > 32767:
|
|
raise Exception("Histogram offsets exceeded maximum value for an int16_t.")
|
|
|
|
target_os = buildconfig.substs["OS_TARGET"]
|
|
print("#if defined(_MSC_VER) && !defined(__clang__)", file=output)
|
|
print("const int16_t gHistogramBucketLowerBoundIndex[] = {", file=output)
|
|
print("#else", file=output)
|
|
print("constexpr int16_t gHistogramBucketLowerBoundIndex[] = {", file=output)
|
|
print("#endif", file=output)
|
|
for histogram in histograms:
|
|
if histogram.record_on_os(target_os):
|
|
our_offset = ranges_offsets[tuple(histogram.ranges())]
|
|
print("%d," % our_offset, file=output)
|
|
|
|
print("};", file=output)
|
|
|
|
|
|
def main(output, *filenames):
|
|
try:
|
|
histograms = list(parse_histograms.from_files(filenames))
|
|
except ParserError as ex:
|
|
print("\nError processing histograms:\n" + str(ex) + "\n")
|
|
sys.exit(1)
|
|
|
|
print(banner, file=output)
|
|
write_histogram_table(output, histograms)
|
|
write_histogram_ranges(output, histograms)
|
|
write_histogram_static_asserts(output, histograms)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main(sys.stdout, *sys.argv[1:])
|