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