• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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