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/.
4
5# Write out a C++ enum definition whose members are the names of
6# histograms as well as the following other members:
7#
8#   - HistogramCount
9#   - HistogramFirstUseCounter
10#   - HistogramLastUseCounter
11#   - HistogramUseCounterCount
12#
13# The histograms are defined in files provided as command-line arguments.
14
15from __future__ import print_function
16from mozparsers.shared_telemetry_utils import ParserError
17from mozparsers import parse_histograms
18
19import itertools
20import sys
21import buildconfig
22
23
24banner = """/* This file is auto-generated, see gen_histogram_enum.py.  */
25"""
26
27header = """
28#ifndef mozilla_TelemetryHistogramEnums_h
29#define mozilla_TelemetryHistogramEnums_h
30
31#include <cstdint>
32#include <type_traits>
33
34namespace mozilla {
35namespace Telemetry {
36"""
37
38footer = """
39} // namespace mozilla
40} // namespace Telemetry
41#endif // mozilla_TelemetryHistogramEnums_h"""
42
43
44def get_histogram_typename(histogram):
45    name = histogram.name()
46    if name.startswith("USE_COUNTER2_"):
47        return "UseCounterWorker" if name.endswith("_WORKER") else "UseCounter"
48    return None
49
50
51def main(output, *filenames):
52    # Print header.
53    print(banner, file=output)
54    print(header, file=output)
55
56    # Load the histograms.
57    try:
58        all_histograms = list(parse_histograms.from_files(filenames))
59    except ParserError as ex:
60        print("\nError processing histograms:\n" + str(ex) + "\n")
61        sys.exit(1)
62
63    groups = itertools.groupby(all_histograms, get_histogram_typename)
64
65    # Print the histogram enums.
66    # Note that parse_histograms.py guarantees that all of the
67    # USE_COUNTER2_*_WORKER and USE_COUNTER2_* histograms are both defined in a
68    # contiguous block.
69    print("enum HistogramID : uint32_t {", file=output)
70    seen_group_types = {"UseCounter": False, "UseCounterWorker": False}
71    for (group_type, histograms) in groups:
72        if group_type is not None:
73            assert isinstance(group_type, str)
74            assert group_type in seen_group_types.keys()
75            assert not seen_group_types[group_type]
76            seen_group_types[group_type] = True
77            # The Histogram*DUMMY enum variables are used to make the computation
78            # of Histogram{First,Last}* easier.  Otherwise, we'd have to special
79            # case the first and last histogram in the group.
80            print("  HistogramFirst%s," % group_type, file=output)
81            print(
82                "  Histogram{0}DUMMY1 = HistogramFirst{0} - 1,".format(group_type),
83                file=output,
84            )
85
86        for histogram in histograms:
87            if histogram.record_on_os(buildconfig.substs["OS_TARGET"]):
88                print("  %s," % histogram.name(), file=output)
89
90        if group_type is not None:
91            assert isinstance(group_type, str)
92            print("  Histogram%sDUMMY2," % group_type, file=output)
93            print(
94                "  HistogramLast{0} = Histogram{0}DUMMY2 - 1,".format(group_type),
95                file=output,
96            )
97
98    print("  HistogramCount,", file=output)
99
100    for (key, value) in sorted(seen_group_types.items()):
101        if value:
102            print(
103                "  Histogram{0}Count = HistogramLast{0} - HistogramFirst{0} + 1,".format(
104                    key
105                ),
106                file=output,
107            )
108        else:
109            print("  HistogramFirst%s = 0," % key, file=output)
110            print("  HistogramLast%s = 0," % key, file=output)
111            print("  Histogram%sCount = 0," % key, file=output)
112
113    print("};", file=output)
114
115    # Write categorical label enums.
116    categorical = filter(lambda h: h.kind() == "categorical", all_histograms)
117    categorical = filter(
118        lambda h: h.record_on_os(buildconfig.substs["OS_TARGET"]), categorical
119    )
120    enums = [("LABELS_" + h.name(), h.labels(), h.name()) for h in categorical]
121    for name, labels, _ in enums:
122        print("\nenum class %s : uint32_t {" % name, file=output)
123        print("  %s" % ",\n  ".join(labels), file=output)
124        print("};", file=output)
125
126    print(
127        "\ntemplate<class T> struct IsCategoricalLabelEnum : std::false_type {};",
128        file=output,
129    )
130    for name, _, _ in enums:
131        print(
132            "template<> struct IsCategoricalLabelEnum<%s> : std::true_type {};" % name,
133            file=output,
134        )
135
136    print("\ntemplate<class T> struct CategoricalLabelId {};", file=output)
137    for name, _, id in enums:
138        print(
139            "template<> struct CategoricalLabelId<%s> : "
140            "std::integral_constant<uint32_t, %s> {};" % (name, id),
141            file=output,
142        )
143
144    # Footer.
145    print(footer, file=output)
146
147
148if __name__ == "__main__":
149    main(sys.stdout, *sys.argv[1:])
150