blob: cc0f6592e6f395bcfa6f70060fb2062f0a3cbe89 [file] [log] [blame]
#include "src/trace_processor/util/protozero_to_text.h"
#include "perfetto/ext/base/string_view.h"
#include "perfetto/protozero/proto_decoder.h"
#include "perfetto/protozero/proto_utils.h"
#include "protos/perfetto/common/descriptor.pbzero.h"
#include "src/trace_processor/util/descriptors.h"
// This is the highest level that this protozero to text supports.
#include "src/trace_processor/importers/proto/track_event.descriptor.h"
namespace perfetto {
namespace trace_processor {
namespace {
// Recursively determine the size of all the string like things passed in the
// parameter pack |rest|.
size_t SizeOfStr() {
return 0;
}
template <typename T, typename... Rest>
size_t SizeOfStr(const T& first, Rest... rest) {
return base::StringView(first).size() + SizeOfStr(rest...);
}
// Append |to_add| which is something string like to |out|.
template <typename T>
void StrAppendInternal(std::string* out, const T& to_add) {
out->append(to_add);
}
template <typename T, typename... strings>
void StrAppendInternal(std::string* out, const T& first, strings... values) {
StrAppendInternal(out, first);
StrAppendInternal(out, values...);
}
// Append |to_add| which is something string like to |out|.
template <typename T>
void StrAppend(std::string* out, const T& to_add) {
out->reserve(out->size() + base::StringView(to_add).size());
out->append(to_add);
}
template <typename T, typename... strings>
void StrAppend(std::string* out, const T& first, strings... values) {
out->reserve(out->size() + SizeOfStr(values...));
StrAppendInternal(out, first);
StrAppendInternal(out, values...);
}
void ConvertProtoTypeToFieldAndValueString(const FieldDescriptor& fd,
const protozero::Field& field,
const std::string& separator,
const std::string& indent,
DescriptorPool* pool,
std::string* out) {
using FieldDescriptorProto = protos::pbzero::FieldDescriptorProto;
switch (fd.type()) {
case FieldDescriptorProto::TYPE_INT32:
case FieldDescriptorProto::TYPE_SFIXED32:
case FieldDescriptorProto::TYPE_FIXED32:
StrAppend(out, separator, indent, fd.name(), ": ",
std::to_string(field.as_int32()));
return;
case FieldDescriptorProto::TYPE_SINT32:
StrAppend(out, separator, indent, fd.name(), ": ",
std::to_string(field.as_sint32()));
return;
case FieldDescriptorProto::TYPE_INT64:
case FieldDescriptorProto::TYPE_SFIXED64:
case FieldDescriptorProto::TYPE_FIXED64:
StrAppend(out, separator, indent, fd.name(), ": ",
std::to_string(field.as_int64()));
return;
case FieldDescriptorProto::TYPE_SINT64:
StrAppend(out, separator, indent, fd.name(), ": ",
std::to_string(field.as_sint64()));
return;
case FieldDescriptorProto::TYPE_UINT32:
StrAppend(out, separator, indent, fd.name(), ": ",
std::to_string(field.as_uint32()));
return;
case FieldDescriptorProto::TYPE_UINT64:
StrAppend(out, separator, indent, fd.name(), ": ",
std::to_string(field.as_uint64()));
return;
case FieldDescriptorProto::TYPE_BOOL:
StrAppend(out, separator, indent, fd.name(), ": ",
field.as_bool() ? "true" : "false");
return;
case FieldDescriptorProto::TYPE_DOUBLE:
StrAppend(out, separator, indent, fd.name(), ": ",
std::to_string(field.as_double()));
return;
case FieldDescriptorProto::TYPE_FLOAT:
StrAppend(out, separator, indent, fd.name(), ": ",
std::to_string(field.as_float()));
return;
case FieldDescriptorProto::TYPE_STRING:
StrAppend(out, separator, indent, fd.name(), ": ", field.as_std_string());
return;
case FieldDescriptorProto::TYPE_ENUM: {
auto opt_enum_descriptor_idx =
pool->FindDescriptorIdx(fd.resolved_type_name());
PERFETTO_DCHECK(opt_enum_descriptor_idx);
auto opt_enum_string =
pool->descriptors()[*opt_enum_descriptor_idx].FindEnumString(
field.as_int32());
PERFETTO_DCHECK(opt_enum_string);
StrAppend(out, separator, indent, fd.name(), ": ", *opt_enum_string);
return;
}
default: {
PERFETTO_FATAL(
"Tried to write value of type field %s (in proto type "
"%s) which has type enum %d",
fd.name().c_str(), fd.resolved_type_name().c_str(), fd.type());
}
}
return;
}
void IncreaseIndents(std::string* out) {
StrAppend(out, " ");
}
void DecreaseIndents(std::string* out) {
PERFETTO_DCHECK(out->size() >= 2);
out->erase(out->size() - 2);
}
// Recursive case function, Will parse |protobytes| assuming it is a proto of
// |type| and will use |pool| to look up the |type|. All output will be placed
// in |output| and between fields |separator| will be placed. When called for
// |indents| will be increased by 2 spaces to improve readability.
void ProtozeroToText(const std::string& type,
protozero::ConstBytes protobytes,
bool include_new_lines,
DescriptorPool* pool,
std::string* indents,
std::string* output) {
auto opt_proto_descriptor_idx = pool->FindDescriptorIdx(type);
PERFETTO_DCHECK(opt_proto_descriptor_idx);
auto& proto_descriptor = pool->descriptors()[*opt_proto_descriptor_idx];
protozero::ProtoDecoder decoder(protobytes.data, protobytes.size);
for (auto field = decoder.ReadField(); field.valid();
field = decoder.ReadField()) {
// Since this is only used in debugging or tests we should always have a
// valid compiled in binary descriptor.
auto opt_field_descriptor_idx =
proto_descriptor.FindFieldIdxByTag(field.id());
PERFETTO_DCHECK(opt_field_descriptor_idx);
const auto& field_descriptor =
proto_descriptor.fields()[*opt_field_descriptor_idx];
if (field_descriptor.type() ==
protos::pbzero::FieldDescriptorProto::TYPE_MESSAGE) {
if (include_new_lines) {
StrAppend(output, output->empty() ? "" : "\n", *indents,
field_descriptor.name(), ": {");
IncreaseIndents(indents);
} else {
StrAppend(output, output->empty() ? "" : " ", field_descriptor.name(),
": {");
}
ProtozeroToText(field_descriptor.resolved_type_name(), field.as_bytes(),
include_new_lines, pool, indents, output);
if (include_new_lines) {
DecreaseIndents(indents);
StrAppend(output, "\n", *indents, "}");
} else {
StrAppend(output, " }");
}
} else {
ConvertProtoTypeToFieldAndValueString(
field_descriptor, field,
output->empty() ? "" : include_new_lines ? "\n" : " ", *indents, pool,
output);
}
}
PERFETTO_DCHECK(decoder.bytes_left() == 0);
}
std::string ProtozeroToText(const std::string& type,
protozero::ConstBytes protobytes,
bool include_new_lines) {
std::string indent = "";
std::string final_result;
DescriptorPool pool;
auto status = pool.AddFromFileDescriptorSet(kTrackEventDescriptor.data(),
kTrackEventDescriptor.size());
PERFETTO_DCHECK(status.ok());
ProtozeroToText(type, protobytes, include_new_lines, &pool, &indent,
&final_result);
return final_result;
}
} // namespace
std::string DebugProtozeroToText(const std::string& type,
protozero::ConstBytes protobytes) {
return ProtozeroToText(type, protobytes, /* include_new_lines = */ true);
}
std::string ShortDebugProtozeroToText(const std::string& type,
protozero::ConstBytes protobytes) {
return ProtozeroToText(type, protobytes, /* include_new_lines = */ false);
}
std::string ProtozeroEnumToText(const std::string& type, int32_t enum_value) {
DescriptorPool pool;
auto status = pool.AddFromFileDescriptorSet(kTrackEventDescriptor.data(),
kTrackEventDescriptor.size());
PERFETTO_DCHECK(status.ok());
auto opt_enum_descriptor_idx = pool.FindDescriptorIdx(type);
if (!opt_enum_descriptor_idx) {
// Fall back to the integer representation of the field.
return std::to_string(enum_value);
}
auto opt_enum_string =
pool.descriptors()[*opt_enum_descriptor_idx].FindEnumString(enum_value);
if (!opt_enum_string) {
// Fall back to the integer representation of the field.
return std::to_string(enum_value);
}
return *opt_enum_string;
}
} // namespace trace_processor
} // namespace perfetto