Commit 453656aa authored by Steven Holte's avatar Steven Holte Committed by Commit Bot

Refactor ukm codegen templates.

Splits decoder logic into a seperate generated file, and breaks
logic into several templates with the same parameters.

Bug: 
Change-Id: I8d6bf188ec73a475428cc49991bdace36458a468
Reviewed-on: https://chromium-review.googlesource.com/721849Reviewed-by: default avatarRobert Kaplow <rkaplow@chromium.org>
Commit-Queue: Steven Holte <holte@chromium.org>
Cr-Commit-Position: refs/heads/master@{#509608}
parent c452aca7
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
#include "base/strings/stringprintf.h" #include "base/strings/stringprintf.h"
#include "components/ukm/ukm_service.h" #include "components/ukm/ukm_service.h"
#include "components/ukm/ukm_source.h" #include "components/ukm/ukm_source.h"
#include "services/metrics/public/cpp/ukm_builders.h" #include "services/metrics/public/cpp/ukm_decode.h"
#include "url/gurl.h" #include "url/gurl.h"
namespace ukm { namespace ukm {
......
...@@ -44,6 +44,8 @@ action("gen_ukm_builders") { ...@@ -44,6 +44,8 @@ action("gen_ukm_builders") {
outputs = [ outputs = [
outdir + "/ukm_builders.cc", outdir + "/ukm_builders.cc",
outdir + "/ukm_builders.h", outdir + "/ukm_builders.h",
outdir + "/ukm_decode.cc",
outdir + "/ukm_decode.h",
] ]
args = [ args = [
......
# Copyright 2017 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Templates for generating builder classes for UKM entries."""
import codegen
HEADER = codegen.Template(
basename="ukm_builders.h",
file_template="""
// Generated from gen_builders.py. DO NOT EDIT!
// source: ukm.xml
#ifndef {file.guard_path}
#define {file.guard_path}
#include <cstdint>
#include "services/metrics/public/cpp/ukm_entry_builder_base.h"
namespace ukm {{
namespace builders {{
{event_code}
}} // namespace builders
}} // namespace ukm
#endif // {file.guard_path}
""",
event_template="""
class {event.name} : public ::ukm::internal::UkmEntryBuilderBase {{
public:
{event.name}(ukm::SourceId source_id);
~{event.name}() override;
static const char kEntryName[];
static const uint64_t kEntryNameHash = UINT64_C({event.hash});
{metric_code}
}};
""",
metric_template="""
static const char k{metric.name}Name[];
static const uint64_t k{metric.name}NameHash = UINT64_C({metric.hash});
{event.name}& Set{metric.name}(int64_t value);
""")
IMPL = codegen.Template(
basename="ukm_builders.cc",
file_template="""
// Generated from gen_builders.py. DO NOT EDIT!
// source: ukm.xml
#include "{file.dir_path}/ukm_builders.h"
namespace ukm {{
namespace builders {{
{event_code}
}} // namespace builders
}} // namespace ukm
""",
event_template="""
const char {event.name}::kEntryName[] = "{event.raw_name}";
{event.name}::{event.name}(ukm::SourceId source_id) :
::ukm::internal::UkmEntryBuilderBase(source_id, kEntryNameHash) {{
}}
{event.name}::~{event.name}() = default;
{metric_code}
""",
metric_template="""
const char {event.name}::k{metric.name}Name[] = "{metric.raw_name}";
{event.name}& {event.name}::Set{metric.name}(int64_t value) {{
AddMetric(k{metric.name}NameHash, value);
return *this;
}}
""")
def WriteFiles(outdir, relpath, data):
HEADER.WriteFile(outdir, relpath, data)
IMPL.WriteFile(outdir, relpath, data)
# Copyright 2017 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Objects for describing template code that can be generated from ukm.xml."""
import hashlib
import os
import re
import struct
def sanitize_name(name):
s = re.sub('[^0-9a-zA-Z_]', '_', name)
return s
def HashName(name):
# This must match the hash function in base/metrics/metric_hashes.cc
# >Q: 8 bytes, big endian.
return struct.unpack('>Q', hashlib.md5(name).digest()[:8])[0]
class FileInfo(object):
def __init__(self, relpath, basename):
self.dir_path = relpath
self.guard_path = sanitize_name(os.path.join(relpath, basename)).upper()
class EventInfo(object):
def __init__(self, json_obj):
self.raw_name = json_obj['name']
self.name = sanitize_name(json_obj['name'])
self.hash = HashName(json_obj['name'])
class MetricInfo(object):
def __init__(self, json_obj):
self.raw_name = json_obj['name']
self.name = sanitize_name(json_obj['name'])
self.hash = HashName(json_obj['name'])
class Template(object):
"""Template for producing code from ukm.xml."""
def __init__(self, basename, file_template, event_template, metric_template):
self.basename = basename
self.file_template = file_template
self.event_template = event_template
self.metric_template = metric_template
def _StampMetricCode(self, file_info, event_info, metric):
return self.metric_template.format(
file=file_info,
event=event_info,
metric=MetricInfo(metric))
def _StampEventCode(self, file_info, event):
event_info = EventInfo(event)
metric_code = "".join(self._StampMetricCode(file_info, event_info, metric)
for metric in event['metrics'])
return self.event_template.format(
file=file_info,
event=event_info,
metric_code=metric_code)
def _StampFileCode(self, relpath, data):
file_info = FileInfo(relpath, self.basename)
event_code = "".join(self._StampEventCode(file_info, event)
for event in data['events'])
return self.file_template.format(
file=file_info,
event_code=event_code)
def WriteFile(self, outdir, relpath, data):
"""Generates code and writes it to a file.
Args:
relpath: The path to the file in the source tree.
rootdir: The root of the path the file should be written to.
data: The parsed ukm.xml data.
"""
output = open(os.path.join(outdir, self.basename), 'w')
output.write(self._StampFileCode(relpath, data))
output.close()
# Copyright 2017 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""A template for generating hash decoding code."""
import codegen
HEADER = codegen.Template(
basename="ukm_decode.h",
file_template="""
// Generated from gen_builders.py. DO NOT EDIT!
// source: ukm.xml
#ifndef {file.guard_path}
#define {file.guard_path}
#include <cstdint>
#include <map>
namespace ukm {{
namespace builders {{
{event_code}
typedef std::map<uint64_t, const char*> DecodeMap;
DecodeMap CreateDecodeMap();
}} // namespace builders
}} // namespace ukm
#endif // {file.guard_path}
""",
event_template="",
metric_template="")
IMPL = codegen.Template(
basename="ukm_decode.cc",
file_template="""
// Generated from gen_builders.py. DO NOT EDIT!
// source: ukm.xml
#include "{file.dir_path}/ukm_decode.h"
#include "{file.dir_path}/ukm_builders.h"
namespace ukm {{
namespace builders {{
std::map<uint64_t, const char*> CreateDecodeMap() {{
return {{
{event_code}
}};
}}
}} // namespace builders
}} // namespace ukm
""",
event_template="""
{{{event.name}::kEntryNameHash, {event.name}::kEntryName}},
{metric_code}
""",
metric_template="""
{{{event.name}::k{metric.name}NameHash, {event.name}::k{metric.name}Name}},
""")
def WriteFiles(outdir, relpath, data):
HEADER.WriteFile(outdir, relpath, data)
IMPL.WriteFile(outdir, relpath, data)
...@@ -11,188 +11,25 @@ entries and metrics. ...@@ -11,188 +11,25 @@ entries and metrics.
""" """
import argparse import argparse
import hashlib
import logging
import os
import re
import struct
import sys import sys
import model import model
import builders_template
import decode_template
HEADER = """
// Generated from gen_builders.py. DO NOT EDIT!
// source: ukm.xml
#ifndef SERVICES_METRICS_PUBLIC_CPP_UKM_BUILDERS_H
#define SERVICES_METRICS_PUBLIC_CPP_UKM_BUILDERS_H
#include <cstdint>
#include <map>
#include "services/metrics/public/cpp/ukm_entry_builder_base.h"
namespace ukm {{
namespace builders {{
{decls}
typedef std::map<uint64_t, const char*> DecodeMap;
DecodeMap CreateDecodeMap();
}} // namespace builders
}} // namespace ukm
#endif // SERVICES_METRICS_PUBLIC_CPP_UKM_BUILDERS_H
"""
BODY = """
// Generated from gen_builders.py. DO NOT EDIT!
// source: ukm.xml
#include "services/metrics/public/cpp/ukm_builders.h"
namespace ukm {{
namespace builders {{
{impls}
std::map<uint64_t, const char*> CreateDecodeMap() {{
return {{
{decodes}
}};
}}
}} // namespace builders
}} // namespace ukm
"""
BUILDER_DECL = """
class {name} : public ::ukm::internal::UkmEntryBuilderBase {{
public:
{name}(ukm::SourceId source_id);
~{name}() override;
static const char kEntryName[];
static const uint64_t kEntryNameHash = UINT64_C({hash});
{setters}
}};
"""
SETTER_DECL = """
static const char k{metric}Name[];
static const uint64_t k{metric}NameHash = UINT64_C({hash});
{name}& Set{metric}(int64_t value);
"""
BUILDER_IMPL = """
const char {name}::kEntryName[] = "{raw}";
{name}::{name}(ukm::SourceId source_id) :
::ukm::internal::UkmEntryBuilderBase(source_id, kEntryNameHash) {{
}}
{name}::~{name}() = default;
{setters}
"""
SETTER_IMPL = """
const char {name}::k{metric}Name[] = "{raw}";
{name}& {name}::Set{metric}(int64_t value) {{
AddMetric(k{metric}NameHash, value);
return *this;
}}
"""
ENTRY_DECODE = """
{{{name}::kEntryNameHash, {name}::kEntryName}},
{metric_decodes}
"""
METRIC_DECODE = """
{{{name}::k{metric}NameHash, {name}::k{metric}Name}},
"""
parser = argparse.ArgumentParser(description='Generate UKM entry builders') parser = argparse.ArgumentParser(description='Generate UKM entry builders')
parser.add_argument('--input', help='Path to ukm.xml') parser.add_argument('--input', help='Path to ukm.xml')
parser.add_argument('--output', help='Path to generated directory') parser.add_argument('--output', help='Path to generated files.')
def sanitize_name(name):
s = re.sub('[^0-9a-zA-Z_]', '_', name)
return s
def HashName(name):
# This must match the hash function in base/metrics/metric_hashes.cc
# >Q: 8 bytes, big endian.
return struct.unpack('>Q', hashlib.md5(name).digest()[:8])[0]
def GetSetterDecl(builder_name, metric):
metric_name = sanitize_name(metric['name'])
return SETTER_DECL.format(name=builder_name,
metric=metric_name,
hash=HashName(metric['name']))
def GetBuilderDecl(event):
builder_name = sanitize_name(event['name'])
setters = "".join(GetSetterDecl(builder_name, metric)
for metric in event['metrics'])
return BUILDER_DECL.format(name=builder_name,
hash=HashName(event['name']),
setters=setters)
def GetHeader(data):
decls = "\n".join(GetBuilderDecl(event) for event in data['events'])
return HEADER.format(decls=decls)
def WriteHeader(outdir, data):
output = open(os.path.join(outdir, "ukm_builders.h"), 'w')
output.write(GetHeader(data))
def GetSetterImpl(builder_name, metric):
metric_name = sanitize_name(metric['name'])
return SETTER_IMPL.format(name=builder_name,
metric=metric_name,
raw=metric['name'])
def GetBuilderImpl(event):
builder_name = sanitize_name(event['name'])
setters = "\n".join(GetSetterImpl(builder_name, metric)
for metric in event['metrics'])
return BUILDER_IMPL.format(name=builder_name,
raw=event['name'],
setters=setters)
def WriteBody(outdir, data):
output = open(os.path.join(outdir, "ukm_builders.cc"), 'w')
output.write(GetBody(data))
def GetMetricDecode(builder_name, metric):
metric_name = sanitize_name(metric['name'])
return METRIC_DECODE.format(name=builder_name, metric=metric_name)
def GetEntryDecode(event):
builder_name = sanitize_name(event['name'])
metric_decodes = "\n".join(GetMetricDecode(builder_name, metric)
for metric in event['metrics'])
return ENTRY_DECODE.format(name=builder_name,
metric_decodes=metric_decodes)
def GetBody(data):
impls = "\n".join(GetBuilderImpl(event) for event in data['events'])
decodes = "\n".join(GetEntryDecode(event) for event in data['events'])
return BODY.format(impls=impls, decodes=decodes)
def main(argv): def main(argv):
args = parser.parse_args() args = parser.parse_args()
data = model.UKM_XML_TYPE.Parse(open(args.input).read()) data = model.UKM_XML_TYPE.Parse(open(args.input).read())
WriteHeader(args.output, data) relpath = 'services/metrics/public/cpp/'
WriteBody(args.output, data) builders_template.WriteFiles(args.output, relpath, data)
decode_template.WriteFiles(args.output, relpath, data)
return 0 return 0
if '__main__' == __name__: if '__main__' == __name__:
sys.exit(main(sys.argv)) sys.exit(main(sys.argv))
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment