1 /*
2 * Copyright (C) 2021 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/metadata_module.h"
18
19 #include "perfetto/ext/base/string_utils.h"
20 #include "src/trace_processor/importers/common/slice_tracker.h"
21 #include "src/trace_processor/importers/common/track_tracker.h"
22 #include "src/trace_processor/importers/proto/metadata_tracker.h"
23
24 #include "protos/perfetto/trace/chrome/chrome_benchmark_metadata.pbzero.h"
25 #include "protos/perfetto/trace/chrome/chrome_metadata.pbzero.h"
26 #include "protos/perfetto/trace/trigger.pbzero.h"
27
28 namespace perfetto {
29 namespace trace_processor {
30
31 using perfetto::protos::pbzero::TracePacket;
32
MetadataModule(TraceProcessorContext * context)33 MetadataModule::MetadataModule(TraceProcessorContext* context)
34 : context_(context) {
35 RegisterForField(TracePacket::kUiStateFieldNumber, context);
36 RegisterForField(TracePacket::kChromeMetadataFieldNumber, context);
37 RegisterForField(TracePacket::kChromeBenchmarkMetadataFieldNumber, context);
38 RegisterForField(TracePacket::kTriggerFieldNumber, context);
39 }
40
TokenizePacket(const protos::pbzero::TracePacket::Decoder & decoder,TraceBlobView *,int64_t,PacketSequenceState *,uint32_t field_id)41 ModuleResult MetadataModule::TokenizePacket(
42 const protos::pbzero::TracePacket::Decoder& decoder,
43 TraceBlobView*,
44 int64_t,
45 PacketSequenceState*,
46 uint32_t field_id) {
47 switch (field_id) {
48 case TracePacket::kUiStateFieldNumber: {
49 auto ui_state = decoder.ui_state();
50 std::string base64 = base::Base64Encode(ui_state.data, ui_state.size);
51 StringId id = context_->storage->InternString(base::StringView(base64));
52 context_->metadata_tracker->SetMetadata(metadata::ui_state,
53 Variadic::String(id));
54 return ModuleResult::Handled();
55 }
56 case TracePacket::kChromeMetadataFieldNumber: {
57 ParseChromeMetadataPacket(decoder.chrome_metadata());
58 // Metadata packets may also contain untyped metadata due to a bug in
59 // Chrome <M92.
60 // TODO(crbug.com/1194914): Replace this with Handled() once the
61 // Chrome-side fix has propagated into all release channels.
62 return ModuleResult::Ignored();
63 }
64 case TracePacket::kChromeBenchmarkMetadataFieldNumber: {
65 ParseChromeBenchmarkMetadata(decoder.chrome_benchmark_metadata());
66 return ModuleResult::Handled();
67 }
68 }
69 return ModuleResult::Ignored();
70 }
71
ParsePacket(const protos::pbzero::TracePacket::Decoder & decoder,const TimestampedTracePiece & ttp,uint32_t field_id)72 void MetadataModule::ParsePacket(
73 const protos::pbzero::TracePacket::Decoder& decoder,
74 const TimestampedTracePiece& ttp,
75 uint32_t field_id) {
76 switch (field_id) {
77 case TracePacket::kTriggerFieldNumber:
78 // We handle triggers at parse time rather at tokenization because
79 // we add slices to tables which need to happen post-sorting.
80 ParseTrigger(ttp.timestamp, decoder.trigger());
81 break;
82 }
83 }
84
ParseChromeBenchmarkMetadata(ConstBytes blob)85 void MetadataModule::ParseChromeBenchmarkMetadata(ConstBytes blob) {
86 TraceStorage* storage = context_->storage.get();
87 MetadataTracker* metadata = context_->metadata_tracker.get();
88
89 protos::pbzero::ChromeBenchmarkMetadata::Decoder packet(blob.data, blob.size);
90 if (packet.has_benchmark_name()) {
91 auto benchmark_name_id = storage->InternString(packet.benchmark_name());
92 metadata->SetMetadata(metadata::benchmark_name,
93 Variadic::String(benchmark_name_id));
94 }
95 if (packet.has_benchmark_description()) {
96 auto benchmark_description_id =
97 storage->InternString(packet.benchmark_description());
98 metadata->SetMetadata(metadata::benchmark_description,
99 Variadic::String(benchmark_description_id));
100 }
101 if (packet.has_label()) {
102 auto label_id = storage->InternString(packet.label());
103 metadata->SetMetadata(metadata::benchmark_label,
104 Variadic::String(label_id));
105 }
106 if (packet.has_story_name()) {
107 auto story_name_id = storage->InternString(packet.story_name());
108 metadata->SetMetadata(metadata::benchmark_story_name,
109 Variadic::String(story_name_id));
110 }
111 for (auto it = packet.story_tags(); it; ++it) {
112 auto story_tag_id = storage->InternString(*it);
113 metadata->AppendMetadata(metadata::benchmark_story_tags,
114 Variadic::String(story_tag_id));
115 }
116 if (packet.has_benchmark_start_time_us()) {
117 metadata->SetMetadata(metadata::benchmark_start_time_us,
118 Variadic::Integer(packet.benchmark_start_time_us()));
119 }
120 if (packet.has_story_run_time_us()) {
121 metadata->SetMetadata(metadata::benchmark_story_run_time_us,
122 Variadic::Integer(packet.story_run_time_us()));
123 }
124 if (packet.has_story_run_index()) {
125 metadata->SetMetadata(metadata::benchmark_story_run_index,
126 Variadic::Integer(packet.story_run_index()));
127 }
128 if (packet.has_had_failures()) {
129 metadata->SetMetadata(metadata::benchmark_had_failures,
130 Variadic::Integer(packet.had_failures()));
131 }
132 }
133
ParseChromeMetadataPacket(ConstBytes blob)134 void MetadataModule::ParseChromeMetadataPacket(ConstBytes blob) {
135 TraceStorage* storage = context_->storage.get();
136 MetadataTracker* metadata = context_->metadata_tracker.get();
137
138 // Typed chrome metadata proto. The untyped metadata is parsed below in
139 // ParseChromeEvents().
140 protos::pbzero::ChromeMetadataPacket::Decoder packet(blob.data, blob.size);
141
142 if (packet.has_chrome_version_code()) {
143 metadata->SetDynamicMetadata(
144 storage->InternString("cr-playstore_version_code"),
145 Variadic::Integer(packet.chrome_version_code()));
146 }
147 if (packet.has_enabled_categories()) {
148 auto categories_id = storage->InternString(packet.enabled_categories());
149 metadata->SetDynamicMetadata(storage->InternString("cr-enabled_categories"),
150 Variadic::String(categories_id));
151 }
152 }
153
ParseTrigger(int64_t ts,ConstBytes blob)154 void MetadataModule::ParseTrigger(int64_t ts, ConstBytes blob) {
155 protos::pbzero::Trigger::Decoder trigger(blob.data, blob.size);
156 StringId cat_id = kNullStringId;
157 TrackId track_id = context_->track_tracker->GetOrCreateTriggerTrack();
158 StringId name_id = context_->storage->InternString(trigger.trigger_name());
159 context_->slice_tracker->Scoped(
160 ts, track_id, cat_id, name_id,
161 /* duration = */ 0,
162 [&trigger, this](ArgsTracker::BoundInserter* args_table) {
163 StringId producer_name_key =
164 context_->storage->InternString("producer_name");
165 args_table->AddArg(producer_name_key,
166 Variadic::String(context_->storage->InternString(
167 trigger.producer_name())));
168 StringId trusted_producer_uid_key =
169 context_->storage->InternString("trusted_producer_uid");
170 args_table->AddArg(trusted_producer_uid_key,
171 Variadic::Integer(trigger.trusted_producer_uid()));
172 });
173 }
174
175 } // namespace trace_processor
176 } // namespace perfetto
177