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