1 /*
2 * Copyright (C) 2019 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/android_probes_parser.h"
18
19 #include "perfetto/base/logging.h"
20 #include "perfetto/ext/traced/sys_stats_counters.h"
21 #include "src/trace_processor/importers/common/args_tracker.h"
22 #include "src/trace_processor/importers/common/clock_tracker.h"
23 #include "src/trace_processor/importers/common/event_tracker.h"
24 #include "src/trace_processor/importers/common/process_tracker.h"
25 #include "src/trace_processor/importers/proto/metadata_tracker.h"
26 #include "src/trace_processor/importers/syscalls/syscall_tracker.h"
27 #include "src/trace_processor/types/trace_processor_context.h"
28
29 #include "protos/perfetto/common/android_log_constants.pbzero.h"
30 #include "protos/perfetto/common/builtin_clock.pbzero.h"
31 #include "protos/perfetto/config/trace_config.pbzero.h"
32 #include "protos/perfetto/trace/android/android_log.pbzero.h"
33 #include "protos/perfetto/trace/android/initial_display_state.pbzero.h"
34 #include "protos/perfetto/trace/android/packages_list.pbzero.h"
35 #include "protos/perfetto/trace/power/battery_counters.pbzero.h"
36 #include "protos/perfetto/trace/power/power_rails.pbzero.h"
37 #include "protos/perfetto/trace/ps/process_stats.pbzero.h"
38 #include "protos/perfetto/trace/ps/process_tree.pbzero.h"
39 #include "protos/perfetto/trace/sys_stats/sys_stats.pbzero.h"
40 #include "protos/perfetto/trace/system_info.pbzero.h"
41
42 #include "src/trace_processor/importers/proto/android_probes_tracker.h"
43
44 namespace perfetto {
45 namespace trace_processor {
46
AndroidProbesParser(TraceProcessorContext * context)47 AndroidProbesParser::AndroidProbesParser(TraceProcessorContext* context)
48 : context_(context),
49 batt_charge_id_(context->storage->InternString("batt.charge_uah")),
50 batt_capacity_id_(context->storage->InternString("batt.capacity_pct")),
51 batt_current_id_(context->storage->InternString("batt.current_ua")),
52 batt_current_avg_id_(
53 context->storage->InternString("batt.current.avg_ua")),
54 screen_state_id_(context->storage->InternString("ScreenState")) {}
55
ParseBatteryCounters(int64_t ts,ConstBytes blob)56 void AndroidProbesParser::ParseBatteryCounters(int64_t ts, ConstBytes blob) {
57 protos::pbzero::BatteryCounters::Decoder evt(blob.data, blob.size);
58 if (evt.has_charge_counter_uah()) {
59 TrackId track =
60 context_->track_tracker->InternGlobalCounterTrack(batt_charge_id_);
61 context_->event_tracker->PushCounter(ts, evt.charge_counter_uah(), track);
62 }
63 if (evt.has_capacity_percent()) {
64 TrackId track =
65 context_->track_tracker->InternGlobalCounterTrack(batt_capacity_id_);
66 context_->event_tracker->PushCounter(
67 ts, static_cast<double>(evt.capacity_percent()), track);
68 }
69 if (evt.has_current_ua()) {
70 TrackId track =
71 context_->track_tracker->InternGlobalCounterTrack(batt_current_id_);
72 context_->event_tracker->PushCounter(ts, evt.current_ua(), track);
73 }
74 if (evt.has_current_avg_ua()) {
75 TrackId track =
76 context_->track_tracker->InternGlobalCounterTrack(batt_current_avg_id_);
77 context_->event_tracker->PushCounter(ts, evt.current_avg_ua(), track);
78 }
79 }
80
ParsePowerRails(int64_t ts,ConstBytes blob)81 void AndroidProbesParser::ParsePowerRails(int64_t ts, ConstBytes blob) {
82 protos::pbzero::PowerRails::Decoder evt(blob.data, blob.size);
83 if (evt.has_rail_descriptor()) {
84 for (auto it = evt.rail_descriptor(); it; ++it) {
85 protos::pbzero::PowerRails::RailDescriptor::Decoder desc(*it);
86 uint32_t idx = desc.index();
87 if (PERFETTO_UNLIKELY(idx > 256)) {
88 PERFETTO_DLOG("Skipping excessively large power_rail index %" PRIu32,
89 idx);
90 continue;
91 }
92 if (power_rails_strs_id_.size() <= idx)
93 power_rails_strs_id_.resize(idx + 1);
94 char counter_name[255];
95 snprintf(counter_name, sizeof(counter_name), "power.%.*s_uws",
96 int(desc.rail_name().size), desc.rail_name().data);
97 power_rails_strs_id_[idx] = context_->storage->InternString(counter_name);
98 }
99 }
100
101 if (evt.has_energy_data()) {
102 // Because we have some special code in the tokenization phase, we
103 // will only every get one EnergyData message per packet. Therefore,
104 // we can just read the data directly.
105 auto it = evt.energy_data();
106 protos::pbzero::PowerRails::EnergyData::Decoder desc(*it);
107 if (desc.index() < power_rails_strs_id_.size()) {
108 // The tokenization makes sure that this field is always present and
109 // is equal to the packet's timestamp (as the packet was forged in
110 // the tokenizer).
111 PERFETTO_DCHECK(desc.has_timestamp_ms());
112 PERFETTO_DCHECK(ts / 1000000 ==
113 static_cast<int64_t>(desc.timestamp_ms()));
114
115 TrackId track = context_->track_tracker->InternGlobalCounterTrack(
116 power_rails_strs_id_[desc.index()]);
117 context_->event_tracker->PushCounter(ts, desc.energy(), track);
118 } else {
119 context_->storage->IncrementStats(stats::power_rail_unknown_index);
120 }
121
122 // DCHECK that we only got one message.
123 PERFETTO_DCHECK(!++it);
124 }
125 }
126
ParseAndroidLogPacket(ConstBytes blob)127 void AndroidProbesParser::ParseAndroidLogPacket(ConstBytes blob) {
128 protos::pbzero::AndroidLogPacket::Decoder packet(blob.data, blob.size);
129 for (auto it = packet.events(); it; ++it)
130 ParseAndroidLogEvent(*it);
131
132 if (packet.has_stats())
133 ParseAndroidLogStats(packet.stats());
134 }
135
ParseAndroidLogEvent(ConstBytes blob)136 void AndroidProbesParser::ParseAndroidLogEvent(ConstBytes blob) {
137 // TODO(primiano): Add events and non-stringified fields to the "raw" table.
138 protos::pbzero::AndroidLogPacket::LogEvent::Decoder evt(blob.data, blob.size);
139 int64_t ts = static_cast<int64_t>(evt.timestamp());
140 uint32_t pid = static_cast<uint32_t>(evt.pid());
141 uint32_t tid = static_cast<uint32_t>(evt.tid());
142 uint8_t prio = static_cast<uint8_t>(evt.prio());
143 StringId tag_id = context_->storage->InternString(
144 evt.has_tag() ? evt.tag() : base::StringView());
145 StringId msg_id = context_->storage->InternString(
146 evt.has_message() ? evt.message() : base::StringView());
147
148 char arg_msg[4096];
149 char* arg_str = &arg_msg[0];
150 *arg_str = '\0';
151 auto arg_avail = [&arg_msg, &arg_str]() {
152 return sizeof(arg_msg) - static_cast<size_t>(arg_str - arg_msg);
153 };
154 for (auto it = evt.args(); it; ++it) {
155 protos::pbzero::AndroidLogPacket::LogEvent::Arg::Decoder arg(*it);
156 if (!arg.has_name())
157 continue;
158 arg_str +=
159 snprintf(arg_str, arg_avail(),
160 " %.*s=", static_cast<int>(arg.name().size), arg.name().data);
161 if (arg.has_string_value()) {
162 arg_str += snprintf(arg_str, arg_avail(), "\"%.*s\"",
163 static_cast<int>(arg.string_value().size),
164 arg.string_value().data);
165 } else if (arg.has_int_value()) {
166 arg_str += snprintf(arg_str, arg_avail(), "%" PRId64, arg.int_value());
167 } else if (arg.has_float_value()) {
168 arg_str += snprintf(arg_str, arg_avail(), "%f",
169 static_cast<double>(arg.float_value()));
170 }
171 }
172
173 if (prio == 0)
174 prio = protos::pbzero::AndroidLogPriority::PRIO_INFO;
175
176 if (arg_str != &arg_msg[0]) {
177 PERFETTO_DCHECK(msg_id.is_null());
178 // Skip the first space char (" foo=1 bar=2" -> "foo=1 bar=2").
179 msg_id = context_->storage->InternString(&arg_msg[1]);
180 }
181 UniquePid utid = tid ? context_->process_tracker->UpdateThread(tid, pid) : 0;
182 base::Optional<int64_t> opt_trace_time = context_->clock_tracker->ToTraceTime(
183 protos::pbzero::BUILTIN_CLOCK_REALTIME, ts);
184 if (!opt_trace_time)
185 return;
186
187 // Log events are NOT required to be sorted by trace_time. The virtual table
188 // will take care of sorting on-demand.
189 context_->storage->mutable_android_log_table()->Insert(
190 {opt_trace_time.value(), utid, prio, tag_id, msg_id});
191 }
192
ParseAndroidLogStats(ConstBytes blob)193 void AndroidProbesParser::ParseAndroidLogStats(ConstBytes blob) {
194 protos::pbzero::AndroidLogPacket::Stats::Decoder evt(blob.data, blob.size);
195 if (evt.has_num_failed()) {
196 context_->storage->SetStats(stats::android_log_num_failed,
197 static_cast<int64_t>(evt.num_failed()));
198 }
199
200 if (evt.has_num_skipped()) {
201 context_->storage->SetStats(stats::android_log_num_skipped,
202 static_cast<int64_t>(evt.num_skipped()));
203 }
204
205 if (evt.has_num_total()) {
206 context_->storage->SetStats(stats::android_log_num_total,
207 static_cast<int64_t>(evt.num_total()));
208 }
209 }
210
ParseStatsdMetadata(ConstBytes blob)211 void AndroidProbesParser::ParseStatsdMetadata(ConstBytes blob) {
212 protos::pbzero::TraceConfig::StatsdMetadata::Decoder metadata(blob.data,
213 blob.size);
214 if (metadata.has_triggering_subscription_id()) {
215 context_->metadata_tracker->SetMetadata(
216 metadata::statsd_triggering_subscription_id,
217 Variadic::Integer(metadata.triggering_subscription_id()));
218 }
219 }
220
ParseAndroidPackagesList(ConstBytes blob)221 void AndroidProbesParser::ParseAndroidPackagesList(ConstBytes blob) {
222 protos::pbzero::PackagesList::Decoder pkg_list(blob.data, blob.size);
223 context_->storage->SetStats(stats::packages_list_has_read_errors,
224 pkg_list.read_error());
225 context_->storage->SetStats(stats::packages_list_has_parse_errors,
226 pkg_list.parse_error());
227
228 AndroidProbesTracker* tracker = AndroidProbesTracker::GetOrCreate(context_);
229 for (auto it = pkg_list.packages(); it; ++it) {
230 protos::pbzero::PackagesList_PackageInfo::Decoder pkg(*it);
231 std::string pkg_name = pkg.name().ToStdString();
232 if (!tracker->ShouldInsertPackage(pkg_name)) {
233 continue;
234 }
235 context_->storage->mutable_package_list_table()->Insert(
236 {context_->storage->InternString(pkg.name()),
237 static_cast<int64_t>(pkg.uid()), pkg.debuggable(),
238 pkg.profileable_from_shell(),
239 static_cast<int64_t>(pkg.version_code())});
240 tracker->InsertedPackage(std::move(pkg_name));
241 }
242 }
243
ParseInitialDisplayState(int64_t ts,ConstBytes blob)244 void AndroidProbesParser::ParseInitialDisplayState(int64_t ts,
245 ConstBytes blob) {
246 protos::pbzero::InitialDisplayState::Decoder state(blob.data, blob.size);
247
248 TrackId track =
249 context_->track_tracker->InternGlobalCounterTrack(screen_state_id_);
250 context_->event_tracker->PushCounter(ts, state.display_state(), track);
251 }
252
253 } // namespace trace_processor
254 } // namespace perfetto
255