1 /*
2 * Copyright (C) 2024 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/pixel_modem_parser.h"
18
19 #include <cstddef>
20 #include <cstdint>
21 #include <string>
22 #include <string_view>
23 #include <variant>
24 #include <vector>
25
26 #include "perfetto/base/status.h"
27 #include "perfetto/ext/base/flat_hash_map.h"
28 #include "perfetto/ext/base/string_utils.h"
29 #include "perfetto/ext/base/string_view.h"
30 #include "perfetto/protozero/field.h"
31 #include "src/trace_processor/importers/common/slice_tracker.h"
32 #include "src/trace_processor/importers/common/track_tracker.h"
33 #include "src/trace_processor/importers/common/tracks.h"
34 #include "src/trace_processor/importers/proto/pigweed_detokenizer.h"
35 #include "src/trace_processor/storage/trace_storage.h"
36 #include "src/trace_processor/types/variadic.h"
37 #include "src/trace_processor/util/status_macros.h"
38
39 namespace perfetto::trace_processor {
40
41 namespace {
42
43 constexpr std::string_view kKeyDelimiterStart = "\u25A0";
44 constexpr std::string_view kKeyDelimiterEnd = "\u2666";
45 constexpr std::string_view kKeyDomain = "domain";
46 constexpr std::string_view kKeyFormat = "format";
47 constexpr std::string_view kModemNamePrefix = "Pixel Modem Events: ";
48 constexpr std::string_view kModemName = "Pixel Modem Events";
49
50 // Modem inputs in particular have this key-value encoding. It's not a Pigweed
51 // thing.
SplitUpModemString(const std::string & input)52 base::FlatHashMap<std::string, std::string> SplitUpModemString(
53 const std::string& input) {
54 auto delimStart = std::string(kKeyDelimiterStart);
55 auto delimEnd = std::string(kKeyDelimiterEnd);
56
57 base::FlatHashMap<std::string, std::string> result;
58 std::vector<std::string> pairs = base::SplitString(input, delimStart);
59 for (auto& it : pairs) {
60 std::vector<std::string> pair = base::SplitString(it, delimEnd);
61 if (pair.size() >= 2) {
62 result.Insert(pair[0], pair[1]);
63 }
64 }
65 return result;
66 }
67
68 } // namespace
69
PixelModemParser(TraceProcessorContext * context)70 PixelModemParser::PixelModemParser(TraceProcessorContext* context)
71 : context_(context),
72 detokenizer_(pigweed::CreateNullDetokenizer()),
73 template_id_(context->storage->InternString("raw_template")),
74 token_id_(context->storage->InternString("token_id")),
75 token_id_hex_(context->storage->InternString("token_id_hex")),
76 packet_timestamp_id_(context->storage->InternString("packet_ts")) {}
77
78 PixelModemParser::~PixelModemParser() = default;
79
SetDatabase(protozero::ConstBytes blob)80 base::Status PixelModemParser::SetDatabase(protozero::ConstBytes blob) {
81 ASSIGN_OR_RETURN(detokenizer_, pigweed::CreateDetokenizer(blob));
82 return base::OkStatus();
83 }
84
ParseEvent(int64_t ts,uint64_t trace_packet_ts,protozero::ConstBytes blob)85 base::Status PixelModemParser::ParseEvent(int64_t ts,
86 uint64_t trace_packet_ts,
87 protozero::ConstBytes blob) {
88 ASSIGN_OR_RETURN(pigweed::DetokenizedString detokenized_str,
89 detokenizer_.Detokenize(blob));
90
91 std::string event = detokenized_str.Format();
92
93 base::FlatHashMap<std::string, std::string> map = SplitUpModemString(event);
94 std::string* domain = map.Find(std::string(kKeyDomain));
95 std::string* format = map.Find(std::string(kKeyFormat));
96
97 static constexpr auto kBlueprint = tracks::SliceBlueprint(
98 "pixel_modem_event",
99 tracks::DimensionBlueprints(
100 tracks::StringDimensionBlueprint("modem_domain")),
101 tracks::FnNameBlueprint([](base::StringView domain) {
102 if (domain.empty()) {
103 return base::StackString<1024>("%.*s", int(kModemName.size()),
104 kModemName.data());
105 }
106 return base::StackString<1024>("%.*s%.*s", int(kModemNamePrefix.size()),
107 kModemNamePrefix.data(),
108 int(domain.size()), domain.data());
109 }));
110
111 const std::string& slice_name = format ? *format : event;
112
113 StringId slice_name_id = context_->storage->InternString(slice_name.c_str());
114 TrackId id = context_->track_tracker->InternTrack(
115 kBlueprint, tracks::Dimensions(domain ? base::StringView(*domain)
116 : base::StringView()));
117 context_->slice_tracker->Scoped(
118 ts, id, kNullStringId, slice_name_id, 0,
119 [this, &detokenized_str,
120 trace_packet_ts](ArgsTracker::BoundInserter* inserter) {
121 inserter->AddArg(template_id_,
122 Variadic::String(context_->storage->InternString(
123 detokenized_str.template_str().c_str())));
124 uint32_t token = detokenized_str.token();
125 inserter->AddArg(token_id_, Variadic::Integer(token));
126 inserter->AddArg(token_id_hex_,
127 Variadic::String(context_->storage->InternString(
128 base::IntToHexString(token).c_str())));
129 inserter->AddArg(packet_timestamp_id_,
130 Variadic::UnsignedInteger(trace_packet_ts));
131 auto pw_args = detokenized_str.args();
132 for (size_t i = 0; i < pw_args.size(); i++) {
133 StringId arg_name = context_->storage->InternString(
134 ("pw_token_" + std::to_string(token) + ".arg_" +
135 std::to_string(i))
136 .c_str());
137 auto arg = pw_args[i];
138 if (auto* int_arg = std::get_if<int64_t>(&arg)) {
139 inserter->AddArg(arg_name, Variadic::Integer(*int_arg));
140 } else if (auto* uint_arg = std::get_if<uint64_t>(&arg)) {
141 inserter->AddArg(arg_name, Variadic::UnsignedInteger(*uint_arg));
142 } else {
143 inserter->AddArg(arg_name, Variadic::Real(std::get<double>(arg)));
144 }
145 }
146 });
147 return base::OkStatus();
148 }
149
150 } // namespace perfetto::trace_processor
151