blob: 0c5e74fe47f8a5a822df078c86362631d5b64e5d [file] [log] [blame]
/*
* Copyright (C) 2021 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.
*/
#ifndef SRC_PROTOZERO_FILTERING_FILTER_UTIL_H_
#define SRC_PROTOZERO_FILTERING_FILTER_UTIL_H_
#include <stdint.h>
#include <list>
#include <map>
#include <optional>
#include <string>
// We include this intentionally instead of forward declaring to allow
// for an easy find/replace transformation when moving to Google3.
#include <google/protobuf/descriptor.h>
namespace protozero {
// Parses a .proto message definition, recursing into its sub-messages, and
// builds up a set of Messages and Field definitions.
// Depends on libprotobuf-full and should be used only in host tools.
// See the //tools/proto_filter for an executable that wraps this class with
// a cmdline interface.
class FilterUtil {
public:
FilterUtil();
~FilterUtil();
// Loads a message schema from a .proto file, recursing into nested types.
// Args:
// proto_file: path to the .proto file.
// root_message: fully qualified message name (e.g., perfetto.protos.Trace).
// If empty, the first message in the file will be used.
// proto_dir_path: the root for .proto includes. If empty uses CWD.
bool LoadMessageDefinition(const std::string& proto_file,
const std::string& root_message,
const std::string& proto_dir_path);
// Deduplicates leaf messages having the same sets of field ids.
// It changes the internal state and affects the behavior of next calls to
// GenerateFilterBytecode() and PrintAsText().
void Dedupe();
// Generates the filter bytecode for the root message previously loaded by
// LoadMessageDefinition() using FilterBytecodeGenerator.
// The returned string is a binary-encoded proto message of type
// perfetto.protos.ProtoFilter (see proto_filter.proto).
std::string GenerateFilterBytecode();
// Prints the list of messages and fields onto stdout in a diff-friendly text
// format. Example:
// PowerRails 2 message energy_data PowerRails.EnergyData
// PowerRails.RailDescriptor 1 uint32 index
// If the optional bytecode filter is given, only the fields allowed by the
// bytecode are printed.
void PrintAsText(std::optional<std::string> filter_bytecode = {});
// Resolves an array of field ids into a dot-concatenated field names.
// E.g., [2,5,1] -> ".trace.packet.timestamp".
std::string LookupField(const uint32_t* field_ids, size_t num_fields);
// Like the above but the array of field is passed as a buffer containing
// varints, e.g. "\x02\x05\0x01".
std::string LookupField(const std::string& varint_encoded_path);
void set_print_stream_for_testing(FILE* stream) { print_stream_ = stream; }
private:
struct Message {
struct Field {
std::string name;
std::string type; // "uint32", "string", "message"
// Only when type == "message". Note that when using Dedupe() this can
// be aliased against a different submessage which happens to have the
// same set of field ids.
Message* nested_type = nullptr;
bool is_simple() const { return nested_type == nullptr; }
};
std::string full_name; // e.g., "perfetto.protos.Foo.Bar";
std::map<uint32_t /*field_id*/, Field> fields;
// True if at least one field has a non-null |nestd_type|.
bool has_nested_fields = false;
};
using DescriptorsByNameMap = std::map<std::string, Message*>;
Message* ParseProtoDescriptor(const google::protobuf::Descriptor*,
DescriptorsByNameMap*);
// list<> because pointers need to be stable.
std::list<Message> descriptors_;
FILE* print_stream_ = stdout;
};
} // namespace protozero
#endif // SRC_PROTOZERO_FILTERING_FILTER_UTIL_H_