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