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