/* * 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. */ #include "src/tools/ftrace_proto_gen/ftrace_proto_gen.h" #include #include #include #include "perfetto/base/logging.h" #include "perfetto/ext/base/file_utils.h" #include "perfetto/ext/base/pipe.h" #include "perfetto/ext/base/string_splitter.h" #include "perfetto/ext/base/string_utils.h" namespace perfetto { namespace { const char kCopyrightHeader[] = R"(/* * 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. */ )"; } // namespace using base::Contains; using base::StartsWith; std::string EventNameToProtoFieldName(const std::string& group, const std::string& name) { std::string event_name = (name == "0") ? "zero" : name; // These groups have events where the name alone conflicts with an existing // proto: if (group == "sde" || group == "g2d" || group == "dpu" || group == "mali" || group == "lwis" || group == "samsung" || group == "kgsl") { event_name = group + "_" + event_name; } return event_name; } std::string EventNameToProtoName(const std::string& group, const std::string& name) { return ToCamelCase(EventNameToProtoFieldName(group, name)) + "FtraceEvent"; } std::vector ReadAllowList(const std::string& filename) { std::string line; std::vector lines; std::ifstream fin(filename, std::ios::in); if (!fin) { fprintf(stderr, "failed to open event list %s\n", filename.c_str()); return lines; } while (std::getline(fin, line)) { if (!StartsWith(line, "#")) lines.emplace_back(FtraceEventName(line)); } return lines; } std::vector ToProtoFields(const FtraceEvent& format) { std::vector ret; for (size_t i = 0; i < format.fields.size(); i++) { const FtraceEvent::Field& field = format.fields[i]; std::string name = GetNameFromTypeAndName(field.type_and_name); // Skip tracepoint fields whose names cannot be used as an identifier in the // generated C++ code due to stdlib header conflicts. if (name == "" || name == "sa_handler" || name == "errno") continue; ProtoType type = InferProtoType(field); if (type.type == ProtoType::INVALID) continue; ret.push_back({type, std::move(name), static_cast(i)}); } return ret; } void GenerateFtraceEventProto(const std::vector& raw_eventlist, const std::set& groups, std::ostream* fout) { *fout << kCopyrightHeader; *fout << "// Autogenerated by:\n"; *fout << std::string("// ") + __FILE__ + "\n"; *fout << "// Do not edit.\n\n"; *fout << R"(syntax = "proto2";)" << "\n\n"; for (const std::string& group : groups) { *fout << R"(import "protos/perfetto/trace/ftrace/)" << group << R"(.proto";)" << "\n"; } *fout << "import \"protos/perfetto/trace/ftrace/generic.proto\";\n"; *fout << "\n"; *fout << "package perfetto.protos;\n\n"; *fout << R"(message FtraceEvent { // Timestamp in nanoseconds using .../tracing/trace_clock. optional uint64 timestamp = 1; // Kernel pid (do not confuse with userspace pid aka tgid). optional uint32 pid = 2; // Not populated in actual traces. Wire format might change. // Placeholder declaration so that the ftrace parsing code accepts the // existence of this common field. If this becomes needed for all events: // consider merging with common_preempt_count to avoid extra proto tags. optional uint32 common_flags = 5; oneof event { )"; int i = 3; for (const FtraceEventName& event : raw_eventlist) { if (!event.valid()) { *fout << " // removed field with id " << i << ";\n"; ++i; continue; } std::string field_name = EventNameToProtoFieldName(event.group(), event.name()); std::string type_name = EventNameToProtoName(event.group(), event.name()); // " " (indent) + TypeName + " " + field_name + " = " + 123 + ";" if (4 + type_name.size() + 1 + field_name.size() + 3 + 3 + 1 <= 80) { // Everything fits in one line: *fout << " " << type_name << " " << field_name << " = " << i << ";\n"; } else if (4 + type_name.size() + 1 + field_name.size() + 2 <= 80) { // Everything fits except the field id: *fout << " " << type_name << " " << field_name << " =\n " << i << ";\n"; } else { // Nothing fits: *fout << " " << type_name << "\n " << field_name << " = " << i << ";\n"; } ++i; // We cannot depend on the proto file to get this number because // it would cause a dependency cycle between this generator and the // generated code. if (i == 327) { *fout << " GenericFtraceEvent generic = " << i << ";\n"; ++i; } if (i == 542) { *fout << " KprobeEvent kprobe_event = " << i << ";\n"; ++i; } } *fout << " }\n"; *fout << "}\n"; } // Generates section of event_info.cc for a single event. std::string SingleEventInfo(perfetto::Proto proto, const std::string& group, const uint32_t proto_field_id) { std::string s = "{"; s += "\"" + proto.event_name + "\", "; s += "\"" + group + "\", "; // Vector of fields. s += "{ "; for (const auto& field : proto.SortedFields()) { // Ignore the "ip" field from print events. This field has not proven // particularly useful and takes up a large amount of space (30% of total // PrintFtraceEvent size and up to 12% of the entire trace in some // configurations) if (group == "ftrace" && proto.event_name == "print" && field->name == "ip") continue; // Ignore the "nid" field. On new kernels, this field has a type that we // don't know how to parse. See b/281660544 if (group == "f2fs" && proto.event_name == "f2fs_truncate_partial_nodes" && field->name == "nid") continue; s += "{"; s += "kUnsetOffset, "; s += "kUnsetSize, "; s += "FtraceFieldType::kInvalidFtraceFieldType, "; s += "\"" + field->name + "\", "; s += std::to_string(field->number) + ", "; s += "ProtoSchemaType::k" + ToCamelCase(field->type.ToString()) + ", "; s += "TranslationStrategy::kInvalidTranslationStrategy"; s += "}, "; } s += "}, "; s += "kUnsetFtraceId, "; s += std::to_string(proto_field_id) + ", "; s += "kUnsetSize"; s += "}"; return s; } // This will generate the event_info.cc file for the listed protos. void GenerateEventInfo(const std::vector& events_info, std::ostream* fout) { std::string s = kCopyrightHeader; s += "// Autogenerated by:\n"; s += std::string("// ") + __FILE__ + "\n"; s += "// Do not edit.\n"; s += R"( #include "src/traced/probes/ftrace/event_info.h" #include "perfetto/protozero/proto_utils.h" namespace perfetto { using protozero::proto_utils::ProtoSchemaType; std::vector GetStaticEventInfo() { static constexpr uint16_t kUnsetOffset = 0; static constexpr uint16_t kUnsetSize = 0; static constexpr uint16_t kUnsetFtraceId = 0; return )"; s += " {"; for (const auto& event : events_info) { s += event; s += ",\n"; } s += "};"; s += R"( } } // namespace perfetto )"; *fout << s; } std::string ProtoHeader() { std::string s = "// Autogenerated by:\n"; s += std::string("// ") + __FILE__ + "\n"; s += "// Do not edit.\n"; s += R"( syntax = "proto2"; package perfetto.protos; )"; return s; } } // namespace perfetto