1 /*
2 * Copyright (C) 2022 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 #include "src/trace_processor/importers/proto/content_analyzer.h"
18
19 #include "perfetto/ext/base/string_utils.h"
20 #include "src/trace_processor/importers/common/args_tracker.h"
21 #include "src/trace_processor/importers/proto/content_analyzer.h"
22 #include "src/trace_processor/storage/trace_storage.h"
23
24 #include "src/trace_processor/importers/proto/trace.descriptor.h"
25
26 namespace perfetto {
27 namespace trace_processor {
28
ProtoContentAnalyzer(TraceProcessorContext * context)29 ProtoContentAnalyzer::ProtoContentAnalyzer(TraceProcessorContext* context)
30 : context_(context),
31 pool_([]() {
32 DescriptorPool pool;
33 base::Status status = pool.AddFromFileDescriptorSet(
34 kTraceDescriptor.data(), kTraceDescriptor.size());
35 if (!status.ok()) {
36 PERFETTO_ELOG("Could not add TracePacket proto descriptor %s",
37 status.c_message());
38 }
39 return pool;
40 }()),
41 computer_(&pool_, ".perfetto.protos.TracePacket") {}
42
43 ProtoContentAnalyzer::~ProtoContentAnalyzer() = default;
44
ProcessPacket(const TraceBlobView & packet,const SampleAnnotation & packet_annotations)45 void ProtoContentAnalyzer::ProcessPacket(
46 const TraceBlobView& packet,
47 const SampleAnnotation& packet_annotations) {
48 auto& map = aggregated_samples_[packet_annotations];
49 computer_.Reset(packet.data(), packet.length());
50 for (auto sample = computer_.GetNext(); sample.has_value();
51 sample = computer_.GetNext()) {
52 auto* value = map.Find(computer_.GetPath());
53 if (value) {
54 value->size += *sample;
55 ++value->count;
56 } else {
57 map.Insert(computer_.GetPath(), Sample{*sample, 1});
58 }
59 }
60 }
61
NotifyEndOfFile()62 void ProtoContentAnalyzer::NotifyEndOfFile() {
63 // TODO(kraskevich): consider generating a flamegraph-compatable table once
64 // Perfetto UI supports custom flamegraphs (b/227644078).
65 for (auto annotated_map = aggregated_samples_.GetIterator(); annotated_map;
66 ++annotated_map) {
67 base::FlatHashMap<util::SizeProfileComputer::FieldPath,
68 tables::ExperimentalProtoPathTable::Id,
69 util::SizeProfileComputer::FieldPathHasher>
70 path_ids;
71 for (auto sample = annotated_map.value().GetIterator(); sample; ++sample) {
72 std::string path_string;
73 std::optional<tables::ExperimentalProtoPathTable::Id> previous_path_id;
74 util::SizeProfileComputer::FieldPath path;
75 for (const auto& field : sample.key()) {
76 if (field.has_field_name()) {
77 if (!path_string.empty()) {
78 path_string += '.';
79 }
80 path_string.append(field.field_name());
81 }
82 if (!path_string.empty()) {
83 path_string += '.';
84 }
85 path_string.append(field.type_name());
86
87 path.push_back(field);
88 // Reuses existing path from |path_ids| if possible.
89 {
90 auto* path_id = path_ids.Find(path);
91 if (path_id) {
92 previous_path_id = *path_id;
93 continue;
94 }
95 }
96 // Create a new row in experimental_proto_path.
97 tables::ExperimentalProtoPathTable::Row path_row;
98 if (field.has_field_name()) {
99 path_row.field_name = context_->storage->InternString(
100 base::StringView(field.field_name()));
101 }
102 path_row.field_type = context_->storage->InternString(
103 base::StringView(field.type_name()));
104 if (previous_path_id.has_value())
105 path_row.parent_id = *previous_path_id;
106
107 auto path_id =
108 context_->storage->mutable_experimental_proto_path_table()
109 ->Insert(path_row)
110 .id;
111 if (!previous_path_id.has_value()) {
112 // Add annotations to the current row as an args set.
113 auto inserter = context_->args_tracker->AddArgsTo(path_id);
114 for (auto& annotation : annotated_map.key()) {
115 inserter.AddArg(annotation.first,
116 Variadic::String(annotation.second));
117 }
118 }
119 previous_path_id = path_id;
120 path_ids[path] = path_id;
121 }
122
123 // Add a content row referring to |previous_path_id|.
124 tables::ExperimentalProtoContentTable::Row content_row;
125 content_row.path =
126 context_->storage->InternString(base::StringView(path_string));
127 content_row.path_id = *previous_path_id;
128 content_row.total_size = static_cast<int64_t>(sample.value().size);
129 content_row.size = static_cast<int64_t>(sample.value().size);
130 content_row.count = static_cast<int64_t>(sample.value().count);
131 context_->storage->mutable_experimental_proto_content_table()->Insert(
132 content_row);
133 }
134 }
135 aggregated_samples_.Clear();
136 }
137
138 } // namespace trace_processor
139 } // namespace perfetto
140