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 <string>
25 
26 namespace google {
27 namespace protobuf {
28 class Descriptor;
29 }
30 }  // namespace google
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   bool LoadMessageDefinition(const std::string& proto_file,
51                              const std::string& root_message,
52                              const std::string& proto_dir_path);
53 
54   // Deduplicates leaf messages having the same sets of field ids.
55   // It changes the internal state and affects the behavior of next calls to
56   // GenerateFilterBytecode() and PrintAsText().
57   void Dedupe();
58 
59   // Generates the filter bytecode for the root message previously loaded by
60   // LoadMessageDefinition() using FilterBytecodeGenerator.
61   // The returned string is a binary-encoded proto message of type
62   // perfetto.protos.ProtoFilter (see proto_filter.proto).
63   std::string GenerateFilterBytecode();
64 
65   // Prints the list of messages and fields onto stdout in a diff-friendly text
66   // format. Example:
67   // PowerRails                 2 message  energy_data     PowerRails.EnergyData
68   // PowerRails.RailDescriptor  1 uint32   index
69   void PrintAsText();
70 
71   // Resolves an array of field ids into a dot-concatenated field names.
72   // E.g., [2,5,1] -> ".trace.packet.timestamp".
73   std::string LookupField(const uint32_t* field_ids, size_t num_fields);
74 
75   // Like the above but the array of field is passed as a buffer containing
76   // varints, e.g. "\x02\x05\0x01".
77   std::string LookupField(const std::string& varint_encoded_path);
78 
79  private:
80   struct Message {
81     struct Field {
82       std::string name;
83       std::string type;  // "uint32", "string", "message"
84       // Only when type == "message". Note that when using Dedupe() this can
85       // be aliased against a different submessage which happens to have the
86       // same set of field ids.
87       Message* nested_type = nullptr;
is_simpleMessage::Field88       bool is_simple() const { return nested_type == nullptr; }
89     };
90     std::string full_name;  // e.g., "perfetto.protos.Foo.Bar";
91     std::map<uint32_t /*field_id*/, Field> fields;
92 
93     // True if at least one field has a non-null |nestd_type|.
94     bool has_nested_fields = false;
95   };
96 
97   using DescriptorsByNameMap = std::map<std::string, Message*>;
98   Message* ParseProtoDescriptor(const google::protobuf::Descriptor*,
99                                 DescriptorsByNameMap*);
100 
101   // list<> because pointers need to be stable.
102   std::list<Message> descriptors_;
103 };
104 
105 }  // namespace protozero
106 
107 #endif  // SRC_PROTOZERO_FILTERING_FILTER_UTIL_H_
108