xref: /aosp_15_r20/external/perfetto/src/protozero/filtering/filter_util.h (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
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