xref: /aosp_15_r20/external/perfetto/src/trace_processor/importers/proto/winscope/protolog_parser.cc (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1 /*
2  * Copyright (C) 2023 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/winscope/protolog_parser.h"
18 
19 #include <cinttypes>
20 #include <cstddef>
21 #include <cstdint>
22 #include <optional>
23 #include <string>
24 #include <utility>
25 #include <vector>
26 
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 "protos/perfetto/trace/android/protolog.pbzero.h"
32 #include "protos/perfetto/trace/interned_data/interned_data.pbzero.h"
33 #include "protos/perfetto/trace/profiling/profile_common.pbzero.h"
34 #include "protos/perfetto/trace/profiling/profile_packet.pbzero.h"
35 #include "src/trace_processor/containers/string_pool.h"
36 #include "src/trace_processor/importers/proto/packet_sequence_state_generation.h"
37 #include "src/trace_processor/importers/proto/winscope/protolog_message_decoder.h"
38 #include "src/trace_processor/storage/stats.h"
39 #include "src/trace_processor/storage/trace_storage.h"
40 #include "src/trace_processor/tables/winscope_tables_py.h"
41 #include "src/trace_processor/types/trace_processor_context.h"
42 
43 namespace perfetto::trace_processor {
44 
ProtoLogParser(TraceProcessorContext * context)45 ProtoLogParser::ProtoLogParser(TraceProcessorContext* context)
46     : context_(context),
47       args_parser_{*context_->descriptor_pool_},
48       log_level_debug_string_id_(context->storage->InternString("DEBUG")),
49       log_level_verbose_string_id_(context->storage->InternString("VERBOSE")),
50       log_level_info_string_id_(context->storage->InternString("INFO")),
51       log_level_warn_string_id_(context->storage->InternString("WARN")),
52       log_level_error_string_id_(context->storage->InternString("ERROR")),
53       log_level_wtf_string_id_(context->storage->InternString("WTF")),
54       log_level_unknown_string_id_(context_->storage->InternString("UNKNOWN")) {
55 }
56 
ParseProtoLogMessage(PacketSequenceStateGeneration * sequence_state,protozero::ConstBytes blob,int64_t timestamp)57 void ProtoLogParser::ParseProtoLogMessage(
58     PacketSequenceStateGeneration* sequence_state,
59     protozero::ConstBytes blob,
60     int64_t timestamp) {
61   protos::pbzero::ProtoLogMessage::Decoder protolog_message(blob);
62 
63   std::vector<int64_t> sint64_params;
64   for (auto it = protolog_message.sint64_params(); it; ++it) {
65     sint64_params.emplace_back(it->as_sint64());
66   }
67 
68   std::vector<double> double_params;
69   for (auto it = protolog_message.double_params(); it; ++it) {
70     double_params.emplace_back(*it);
71   }
72 
73   std::vector<bool> boolean_params;
74   for (auto it = protolog_message.boolean_params(); it; ++it) {
75     boolean_params.emplace_back(*it);
76   }
77 
78   std::vector<std::string> string_params;
79   if (protolog_message.has_str_param_iids()) {
80     for (auto it = protolog_message.str_param_iids(); it; ++it) {
81       auto* decoder = sequence_state->LookupInternedMessage<
82           protos::pbzero::InternedData::kProtologStringArgsFieldNumber,
83           protos::pbzero::InternedString>(it.field().as_uint32());
84       if (!decoder) {
85         // This shouldn't happen since we already checked the incremental
86         // state is valid.
87         string_params.emplace_back("<ERROR>");
88         context_->storage->IncrementStats(
89             stats::winscope_protolog_missing_interned_arg_parse_errors);
90         continue;
91       }
92       string_params.emplace_back(decoder->str().ToStdString());
93     }
94   }
95 
96   std::optional<StringId> stacktrace = std::nullopt;
97   if (protolog_message.has_stacktrace_iid()) {
98     auto* stacktrace_decoder = sequence_state->LookupInternedMessage<
99         protos::pbzero::InternedData::kProtologStacktraceFieldNumber,
100         protos::pbzero::InternedString>(protolog_message.stacktrace_iid());
101 
102     if (!stacktrace_decoder) {
103       // This shouldn't happen since we already checked the incremental
104       // state is valid.
105       string_params.emplace_back("<ERROR>");
106       context_->storage->IncrementStats(
107           stats::winscope_protolog_missing_interned_stacktrace_parse_errors);
108     } else {
109       stacktrace = context_->storage->InternString(
110           base::StringView(stacktrace_decoder->str().ToStdString()));
111     }
112   }
113 
114   auto* protolog_table = context_->storage->mutable_protolog_table();
115 
116   tables::ProtoLogTable::Row row;
117   row.ts = timestamp;
118   auto row_id = protolog_table->Insert(row).id;
119 
120   auto* protolog_message_decoder =
121       ProtoLogMessageDecoder::GetOrCreate(context_);
122 
123   auto decoded_message_opt = protolog_message_decoder->Decode(
124       protolog_message.message_id(), sint64_params, double_params,
125       boolean_params, string_params);
126   if (decoded_message_opt.has_value()) {
127     auto decoded_message = decoded_message_opt.value();
128     std::optional<std::string> location = decoded_message.location;
129     PopulateReservedRowWithMessage(
130         row_id, decoded_message.log_level, decoded_message.group_tag,
131         decoded_message.message, stacktrace, location);
132   } else {
133     // Failed to fully decode the message.
134     // This shouldn't happen since we should have processed all viewer config
135     // messages in the tokenization state, and process the protolog messages
136     // only in the parsing state.
137     context_->storage->IncrementStats(
138         stats::winscope_protolog_message_decoding_failed);
139   }
140 }
141 
ParseAndAddViewerConfigToMessageDecoder(protozero::ConstBytes blob)142 void ProtoLogParser::ParseAndAddViewerConfigToMessageDecoder(
143     protozero::ConstBytes blob) {
144   protos::pbzero::ProtoLogViewerConfig::Decoder protolog_viewer_config(blob);
145 
146   auto* protolog_message_decoder =
147       ProtoLogMessageDecoder::GetOrCreate(context_);
148 
149   for (auto it = protolog_viewer_config.groups(); it; ++it) {
150     protos::pbzero::ProtoLogViewerConfig::Group::Decoder group(*it);
151     protolog_message_decoder->TrackGroup(group.id(), group.tag().ToStdString());
152   }
153 
154   for (auto it = protolog_viewer_config.messages(); it; ++it) {
155     protos::pbzero::ProtoLogViewerConfig::MessageData::Decoder message_data(
156         *it);
157 
158     std::optional<std::string> location = std::nullopt;
159     if (message_data.has_location()) {
160       location = message_data.location().ToStdString();
161     }
162 
163     protolog_message_decoder->TrackMessage(
164         message_data.message_id(),
165         static_cast<ProtoLogLevel>(message_data.level()),
166         message_data.group_id(), message_data.message().ToStdString(),
167         location);
168   }
169 }
170 
PopulateReservedRowWithMessage(tables::ProtoLogTable::Id table_row_id,ProtoLogLevel log_level,std::string & group_tag,std::string & message,std::optional<StringId> stacktrace,std::optional<std::string> & location)171 void ProtoLogParser::PopulateReservedRowWithMessage(
172     tables::ProtoLogTable::Id table_row_id,
173     ProtoLogLevel log_level,
174     std::string& group_tag,
175     std::string& message,
176     std::optional<StringId> stacktrace,
177     std::optional<std::string>& location) {
178   auto* protolog_table = context_->storage->mutable_protolog_table();
179   auto row = protolog_table->FindById(table_row_id).value();
180 
181   StringPool::Id level;
182   switch (log_level) {
183     case ProtoLogLevel::DEBUG:
184       level = log_level_debug_string_id_;
185       break;
186     case ProtoLogLevel::VERBOSE:
187       level = log_level_verbose_string_id_;
188       break;
189     case ProtoLogLevel::INFO:
190       level = log_level_info_string_id_;
191       break;
192     case ProtoLogLevel::WARN:
193       level = log_level_warn_string_id_;
194       break;
195     case ProtoLogLevel::ERROR:
196       level = log_level_error_string_id_;
197       break;
198     case ProtoLogLevel::WTF:
199       level = log_level_wtf_string_id_;
200       break;
201   }
202   row.set_level(level);
203 
204   auto tag = context_->storage->InternString(base::StringView(group_tag));
205   row.set_tag(tag);
206 
207   auto message_string_id =
208       context_->storage->InternString(base::StringView(message));
209   row.set_message(message_string_id);
210 
211   if (stacktrace.has_value()) {
212     row.set_stacktrace(stacktrace.value());
213   }
214 
215   if (location.has_value()) {
216     auto location_string_id =
217         context_->storage->InternString(base::StringView(location.value()));
218     row.set_location(location_string_id);
219   }
220 }
221 
222 }  // namespace perfetto::trace_processor
223