/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <getopt.h>
#include <sys/stat.h>
#include <fstream>
#include <map>
#include <memory>
#include <regex>
#include <set>
#include <sstream>
#include <string>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/descriptor.pb.h>
#include "perfetto/base/file_utils.h"
#include "perfetto/base/logging.h"
#include "src/traced/probes/ftrace/format_parser.h"
#include "tools/ftrace_proto_gen/ftrace_descriptor_gen.h"
#include "tools/ftrace_proto_gen/ftrace_proto_gen.h"
namespace {
inline std::unique_ptr<std::ostream> MakeOFStream(const std::string& filename) {
return std::unique_ptr<std::ostream>(new std::ofstream(filename));
}
inline std::unique_ptr<std::ostream> MakeVerifyStream(
const std::string& filename) {
return std::unique_ptr<std::ostream>(new perfetto::VerifyStream(filename));
}
} // namespace
int main(int argc, char** argv) {
static struct option long_options[] = {
{"whitelist_path", required_argument, nullptr, 'w'},
{"output_dir", required_argument, nullptr, 'o'},
{"proto_descriptor", required_argument, nullptr, 'd'},
{"update_build_files", no_argument, nullptr, 'b'},
{"check_only", no_argument, nullptr, 'c'},
};
int option_index;
int c;
std::string whitelist_path;
std::string output_dir;
std::string proto_descriptor;
bool update_build_files = false;
std::unique_ptr<std::ostream> (*ostream_factory)(const std::string&) =
&MakeOFStream;
while ((c = getopt_long(argc, argv, "", long_options, &option_index)) != -1) {
switch (c) {
case 'w':
whitelist_path = optarg;
break;
case 'o':
output_dir = optarg;
break;
case 'd':
proto_descriptor = optarg;
break;
case 'b':
update_build_files = true;
break;
case 'c':
ostream_factory = &MakeVerifyStream;
}
}
PERFETTO_CHECK(!whitelist_path.empty());
PERFETTO_CHECK(!output_dir.empty());
PERFETTO_CHECK(!proto_descriptor.empty());
if (optind >= argc) {
fprintf(stderr,
"Usage: ./%s -w whitelist_dir -o output_dir -d proto_descriptor "
"[--check_only] input_dir...\n",
argv[0]);
return 1;
}
std::vector<perfetto::FtraceEventName> whitelist =
perfetto::ReadWhitelist(whitelist_path);
std::vector<std::string> events_info;
google::protobuf::DescriptorPool descriptor_pool;
descriptor_pool.AllowUnknownDependencies();
{
google::protobuf::FileDescriptorSet file_descriptor_set;
std::string descriptor_bytes;
if (!perfetto::base::ReadFile(proto_descriptor, &descriptor_bytes)) {
fprintf(stderr, "Failed to open %s\n", proto_descriptor.c_str());
return 1;
}
file_descriptor_set.ParseFromString(descriptor_bytes);
for (const auto& d : file_descriptor_set.file()) {
PERFETTO_CHECK(descriptor_pool.BuildFile(d));
}
}
std::set<std::string> groups;
std::multimap<std::string, const perfetto::FtraceEventName*> group_to_event;
std::set<std::string> new_events;
for (const auto& event : whitelist) {
if (!event.valid())
continue;
groups.emplace(event.group());
group_to_event.emplace(event.group(), &event);
struct stat buf;
if (stat(
("protos/perfetto/trace/ftrace/" + event.name() + ".proto").c_str(),
&buf) == -1) {
new_events.insert(event.name());
}
}
{
std::unique_ptr<std::ostream> out =
ostream_factory(output_dir + "/ftrace_event.proto");
perfetto::GenerateFtraceEventProto(whitelist, groups, out.get());
}
if (!new_events.empty()) {
perfetto::PrintEventFormatterMain(new_events);
perfetto::PrintEventFormatterUsingStatements(new_events);
perfetto::PrintEventFormatterFunctions(new_events);
printf(
"\nAdd output to ParseInode in "
"tools/ftrace_proto_gen/ftrace_inode_handler.cc\n");
}
for (const std::string& group : groups) {
std::string proto_file_name = group + ".proto";
std::string output_path = output_dir + std::string("/") + proto_file_name;
std::unique_ptr<std::ostream> fout = ostream_factory(output_path);
if (!fout) {
fprintf(stderr, "Failed to open %s\n", output_path.c_str());
return 1;
}
*fout << perfetto::ProtoHeader();
auto range = group_to_event.equal_range(group);
for (auto it = range.first; it != range.second; ++it) {
const auto& event = *it->second;
if (!event.valid())
continue;
std::string proto_name = perfetto::EventNameToProtoName(event.name());
perfetto::Proto proto;
proto.name = proto_name;
proto.event_name = event.name();
const google::protobuf::Descriptor* d =
descriptor_pool.FindMessageTypeByName("perfetto.protos." +
proto_name);
if (d)
proto = perfetto::Proto(event.name(), *d);
else
PERFETTO_LOG("Did not find %s", proto_name.c_str());
for (int i = optind; i < argc; ++i) {
std::string input_dir = argv[i];
std::string input_path = input_dir + event.group() + "/" +
event.name() + std::string("/format");
std::string contents;
if (!perfetto::base::ReadFile(input_path, &contents)) {
continue;
}
perfetto::FtraceEvent format;
if (!perfetto::ParseFtraceEvent(contents, &format)) {
fprintf(stderr, "Could not parse file %s.\n", input_path.c_str());
return 1;
}
perfetto::Proto event_proto;
if (!perfetto::GenerateProto(format, &event_proto)) {
fprintf(stderr, "Could not generate proto for file %s\n",
input_path.c_str());
return 1;
}
proto.MergeFrom(event_proto);
}
if (!new_events.empty())
PrintInodeHandlerMain(proto.name, proto);
uint32_t i = 0;
for (; it->second != &whitelist[i]; i++)
;
// The first id used for events in FtraceEvent proto is 3.
uint32_t proto_field = i + 3;
// The generic event has field id 327 so any event with a id higher
// than that has to be incremented by 1.
if (proto_field >= 327)
proto_field++;
events_info.push_back(
perfetto::SingleEventInfo(proto, event.group(), proto_field));
*fout << proto.ToString();
PERFETTO_CHECK(!fout->fail());
}
}
{
std::unique_ptr<std::ostream> out =
ostream_factory("src/trace_processor/ftrace_descriptors.cc");
perfetto::GenerateFtraceDescriptors(descriptor_pool, out.get());
PERFETTO_CHECK(!out->fail());
}
{
std::unique_ptr<std::ostream> out =
ostream_factory("src/traced/probes/ftrace/event_info.cc");
perfetto::GenerateEventInfo(events_info, out.get());
PERFETTO_CHECK(!out->fail());
}
if (update_build_files) {
std::unique_ptr<std::ostream> f =
ostream_factory(output_dir + "/all_protos.gni");
*f << R"(# Copyright (C) 2018 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Autogenerated by ftrace_proto_gen.
ftrace_proto_names = [
"ftrace_event.proto",
"ftrace_event_bundle.proto",
"ftrace_stats.proto",
"test_bundle_wrapper.proto",
"generic.proto",
)";
for (const std::string& group : groups) {
*f << " \"" << group << ".proto\",\n";
}
*f << "]\n";
PERFETTO_CHECK(!f->fail());
}
}