xref: /aosp_15_r20/external/perfetto/src/trace_processor/importers/proto/content_analyzer.cc (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
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