1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #ifndef SRC_PROTOZERO_FILTERING_FILTER_UTIL_H_ 18 #define SRC_PROTOZERO_FILTERING_FILTER_UTIL_H_ 19 20 #include <stdint.h> 21 22 #include <list> 23 #include <map> 24 #include <optional> 25 #include <set> 26 #include <string> 27 28 // We include this intentionally instead of forward declaring to allow 29 // for an easy find/replace transformation when moving to Google3. 30 #include <google/protobuf/descriptor.h> 31 32 namespace protozero { 33 34 // Parses a .proto message definition, recursing into its sub-messages, and 35 // builds up a set of Messages and Field definitions. 36 // Depends on libprotobuf-full and should be used only in host tools. 37 // See the //tools/proto_filter for an executable that wraps this class with 38 // a cmdline interface. 39 class FilterUtil { 40 public: 41 FilterUtil(); 42 ~FilterUtil(); 43 44 // Loads a message schema from a .proto file, recursing into nested types. 45 // Args: 46 // proto_file: path to the .proto file. 47 // root_message: fully qualified message name (e.g., perfetto.protos.Trace). 48 // If empty, the first message in the file will be used. 49 // proto_dir_path: the root for .proto includes. If empty uses CWD. 50 // passthrough: an optional set of fields that should be transparently passed 51 // through without recursing further. 52 // Syntax: "perfetto.protos.TracePacket:trace_config" 53 // filter_string_fields: an optional set of fields that should be treated as 54 // string fields which need to be filtered. 55 // Syntax: same as passthrough 56 bool LoadMessageDefinition( 57 const std::string& proto_file, 58 const std::string& root_message, 59 const std::string& proto_dir_path, 60 const std::set<std::string>& passthrough_fields = {}, 61 const std::set<std::string>& filter_string_fields = {}); 62 63 // Deduplicates leaf messages having the same sets of field ids. 64 // It changes the internal state and affects the behavior of next calls to 65 // GenerateFilterBytecode() and PrintAsText(). 66 void Dedupe(); 67 68 // Generates the filter bytecode for the root message previously loaded by 69 // LoadMessageDefinition() using FilterBytecodeGenerator. 70 // The returned string is a binary-encoded proto message of type 71 // perfetto.protos.ProtoFilter (see proto_filter.proto). 72 std::string GenerateFilterBytecode(); 73 74 // Prints the list of messages and fields onto stdout in a diff-friendly text 75 // format. Example: 76 // PowerRails 2 message energy_data PowerRails.EnergyData 77 // PowerRails.RailDescriptor 1 uint32 index 78 // If the optional bytecode filter is given, only the fields allowed by the 79 // bytecode are printed. 80 void PrintAsText(std::optional<std::string> filter_bytecode = {}); 81 82 // Resolves an array of field ids into a dot-concatenated field names. 83 // E.g., [2,5,1] -> ".trace.packet.timestamp". 84 std::string LookupField(const uint32_t* field_ids, size_t num_fields); 85 86 // Like the above but the array of field is passed as a buffer containing 87 // varints, e.g. "\x02\x05\0x01". 88 std::string LookupField(const std::string& varint_encoded_path); 89 set_print_stream_for_testing(FILE * stream)90 void set_print_stream_for_testing(FILE* stream) { print_stream_ = stream; } 91 92 private: 93 struct Message { 94 struct Field { 95 std::string name; 96 std::string type; // "uint32", "string", "message" 97 bool filter_string; 98 // Only when type == "message". Note that when using Dedupe() this can 99 // be aliased against a different submessage which happens to have the 100 // same set of field ids. 101 Message* nested_type = nullptr; 102 is_simpleMessage::Field103 bool is_simple() const { 104 return nested_type == nullptr && !filter_string; 105 } 106 }; 107 std::string full_name; // e.g., "perfetto.protos.Foo.Bar"; 108 std::map<uint32_t /*field_id*/, Field> fields; 109 110 // True if at least one field has a non-null |nestd_type|. 111 bool has_nested_fields = false; 112 113 // True if at least one field has |filter_string|==true. 114 bool has_filter_string_fields = false; 115 }; 116 117 using DescriptorsByNameMap = std::map<std::string, Message*>; 118 Message* ParseProtoDescriptor(const google::protobuf::Descriptor*, 119 DescriptorsByNameMap*); 120 121 // list<> because pointers need to be stable. 122 std::list<Message> descriptors_; 123 std::set<std::string> passthrough_fields_; 124 std::set<std::string> filter_string_fields_; 125 126 // Used only for debugging aid, to print out an error message when the user 127 // specifies a field to pass through but it doesn't exist. 128 std::set<std::string> passthrough_fields_seen_; 129 std::set<std::string> filter_string_fields_seen_; 130 131 FILE* print_stream_ = stdout; 132 }; 133 134 } // namespace protozero 135 136 #endif // SRC_PROTOZERO_FILTERING_FILTER_UTIL_H_ 137