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