xref: /aosp_15_r20/external/perfetto/src/traced/probes/android_log/android_log_data_source.cc (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1 /*
2  * Copyright (C) 2018 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/traced/probes/android_log/android_log_data_source.h"
18 #include <optional>
19 
20 #include "perfetto/base/logging.h"
21 #include "perfetto/base/task_runner.h"
22 #include "perfetto/base/time.h"
23 #include "perfetto/ext/base/file_utils.h"
24 #include "perfetto/ext/base/scoped_file.h"
25 #include "perfetto/ext/base/string_splitter.h"
26 #include "perfetto/ext/base/string_view.h"
27 #include "perfetto/ext/base/unix_socket.h"
28 #include "perfetto/ext/tracing/core/trace_packet.h"
29 #include "perfetto/ext/tracing/core/trace_writer.h"
30 #include "perfetto/tracing/core/data_source_config.h"
31 
32 #include "protos/perfetto/common/android_log_constants.pbzero.h"
33 #include "protos/perfetto/config/android/android_log_config.pbzero.h"
34 #include "protos/perfetto/trace/android/android_log.pbzero.h"
35 #include "protos/perfetto/trace/trace_packet.pbzero.h"
36 
37 namespace perfetto {
38 
39 namespace {
40 
41 using protos::pbzero::AndroidLogConfig;
42 using protos::pbzero::AndroidLogId;
43 
44 constexpr size_t kBufSize = 4096;
45 const char kLogTagsPath[] = "/system/etc/event-log-tags";
46 const char kLogdrSocket[] = "/dev/socket/logdr";
47 
48 // From AOSP's liblog/include/log/log_read.h .
49 // Android Log is supposed to be an ABI as it's exposed also by logcat -b (and
50 // in practice never changed in backwards-incompatible ways in the past).
51 // Note: the casing doesn't match the style guide and instead matches the
52 // original name in liblog for the sake of code-searcheability.
53 struct logger_entry_v4 {
54   uint16_t len;       // length of the payload.
55   uint16_t hdr_size;  // sizeof(struct logger_entry_v4).
56   int32_t pid;        // generating process's pid.
57   uint32_t tid;       // generating process's tid.
58   uint32_t sec;       // seconds since Epoch.
59   uint32_t nsec;      // nanoseconds.
60   uint32_t lid;       // log id of the payload, bottom 4 bits currently.
61   uint32_t uid;       // generating process's uid.
62 };
63 
64 // Event types definition in the binary encoded format, from
65 // liblog/include/log/log.h .
66 // Note that these values don't match the textual definitions of
67 // system/core/android_logevent.logtags (which are not used by perfetto).
68 // The latter are off by one (INT = 1, LONG=2 and so on).
69 enum AndroidEventLogType {
70   EVENT_TYPE_INT = 0,
71   EVENT_TYPE_LONG = 1,
72   EVENT_TYPE_STRING = 2,
73   EVENT_TYPE_LIST = 3,
74   EVENT_TYPE_FLOAT = 4,
75 };
76 
77 template <typename T>
ReadAndAdvance(const char ** ptr,const char * end,T * out)78 inline bool ReadAndAdvance(const char** ptr, const char* end, T* out) {
79   if (*ptr > end - sizeof(T))
80     return false;
81   memcpy(reinterpret_cast<void*>(out), *ptr, sizeof(T));
82   *ptr += sizeof(T);
83   return true;
84 }
85 
86 }  // namespace
87 
88 // static
89 const ProbesDataSource::Descriptor AndroidLogDataSource::descriptor = {
90     /*name*/ "android.log",
91     /*flags*/ Descriptor::kFlagsNone,
92     /*fill_descriptor_func*/ nullptr,
93 };
94 
AndroidLogDataSource(DataSourceConfig ds_config,base::TaskRunner * task_runner,TracingSessionID session_id,std::unique_ptr<TraceWriter> writer)95 AndroidLogDataSource::AndroidLogDataSource(DataSourceConfig ds_config,
96                                            base::TaskRunner* task_runner,
97                                            TracingSessionID session_id,
98                                            std::unique_ptr<TraceWriter> writer)
99     : ProbesDataSource(session_id, &descriptor),
100       task_runner_(task_runner),
101       writer_(std::move(writer)),
102       weak_factory_(this) {
103   AndroidLogConfig::Decoder cfg(ds_config.android_log_config_raw());
104 
105   std::vector<uint32_t> log_ids;
106   for (auto id = cfg.log_ids(); id; ++id)
107     log_ids.push_back(static_cast<uint32_t>(*id));
108 
109   if (log_ids.empty()) {
110     // If no log id is specified, add the most popular ones.
111     log_ids.push_back(AndroidLogId::LID_DEFAULT);
112     log_ids.push_back(AndroidLogId::LID_EVENTS);
113     log_ids.push_back(AndroidLogId::LID_SYSTEM);
114     log_ids.push_back(AndroidLogId::LID_CRASH);
115     log_ids.push_back(AndroidLogId::LID_KERNEL);
116   }
117 
118   // Build the command string that will be sent to the logdr socket on Start(),
119   // which looks like "stream lids=1,2,3,4" (lids == log buffer id(s)).
120   mode_ = "stream tail=1 lids";
121   for (auto it = log_ids.begin(); it != log_ids.end(); it++) {
122     mode_ += it == log_ids.begin() ? "=" : ",";
123     mode_ += std::to_string(*it);
124   }
125 
126   // Build a linear vector out of the tag filters and keep track of the string
127   // boundaries. Once done, derive StringView(s) out of the vector. This is
128   // to create a set<StringView> which is backed by contiguous chars that can be
129   // used to lookup StringView(s) from the parsed buffer.
130   // This is to avoid copying strings of tags for the only sake of checking for
131   // their existence in the set.
132   std::vector<std::pair<size_t, size_t>> tag_boundaries;
133   for (auto it = cfg.filter_tags(); it; ++it) {
134     base::StringView tag(*it);
135     const size_t begin = filter_tags_strbuf_.size();
136     filter_tags_strbuf_.insert(filter_tags_strbuf_.end(), tag.begin(),
137                                tag.end());
138     const size_t end = filter_tags_strbuf_.size();
139     tag_boundaries.emplace_back(begin, end - begin);
140   }
141   filter_tags_strbuf_.shrink_to_fit();
142   // At this point pointers to |filter_tags_strbuf_| are stable.
143   for (const auto& it : tag_boundaries)
144     filter_tags_.emplace(&filter_tags_strbuf_[it.first], it.second);
145 
146   min_prio_ = cfg.min_prio();
147   buf_ = base::PagedMemory::Allocate(kBufSize);
148 }
149 
~AndroidLogDataSource()150 AndroidLogDataSource::~AndroidLogDataSource() {
151   if (logdr_sock_) {
152     EnableSocketWatchTask(false);
153     logdr_sock_.Shutdown();
154   }
155 }
156 
ConnectLogdrSocket()157 base::UnixSocketRaw AndroidLogDataSource::ConnectLogdrSocket() {
158   auto socket = base::UnixSocketRaw::CreateMayFail(base::SockFamily::kUnix,
159                                                    base::SockType::kSeqPacket);
160   if (!socket || !socket.Connect(kLogdrSocket)) {
161     PERFETTO_PLOG("Failed to connect to %s", kLogdrSocket);
162     return base::UnixSocketRaw();
163   }
164   return socket;
165 }
166 
Start()167 void AndroidLogDataSource::Start() {
168   ParseEventLogDefinitions();
169 
170   if (!(logdr_sock_ = ConnectLogdrSocket()))
171     return;
172 
173   PERFETTO_DLOG("Starting Android log data source: %s", mode_.c_str());
174   if (logdr_sock_.Send(mode_.data(), mode_.size()) <= 0) {
175     PERFETTO_PLOG("send() failed on logdr socket %s", kLogdrSocket);
176     return;
177   }
178   logdr_sock_.SetBlocking(false);
179   EnableSocketWatchTask(true);
180 }
181 
EnableSocketWatchTask(bool enable)182 void AndroidLogDataSource::EnableSocketWatchTask(bool enable) {
183   if (fd_watch_task_enabled_ == enable)
184     return;
185 
186   if (enable) {
187     auto weak_this = weak_factory_.GetWeakPtr();
188     task_runner_->AddFileDescriptorWatch(logdr_sock_.fd(), [weak_this] {
189       if (weak_this)
190         weak_this->OnSocketDataAvailable();
191     });
192   } else {
193     task_runner_->RemoveFileDescriptorWatch(logdr_sock_.fd());
194   }
195 
196   fd_watch_task_enabled_ = enable;
197 }
198 
OnSocketDataAvailable()199 void AndroidLogDataSource::OnSocketDataAvailable() {
200   PERFETTO_DCHECK(fd_watch_task_enabled_);
201   auto now_ms = base::GetWallTimeMs().count();
202 
203   // Disable the FD watch until the delayed read happens, otherwise we get a
204   // storm of OnSocketDataAvailable() until the delayed ReadLogSocket() happens.
205   EnableSocketWatchTask(false);
206 
207   // Delay the read by (at most) 100 ms so we get a chance to batch reads and
208   // don't cause too many context switches in cases of logging storms. The
209   // modulo is to increase the chance that the wakeup is packed together with
210   // some other wakeup task of traced_probes.
211   const uint32_t kBatchMs = 100;
212   uint32_t delay_ms = kBatchMs - static_cast<uint32_t>(now_ms % kBatchMs);
213   auto weak_this = weak_factory_.GetWeakPtr();
214   task_runner_->PostDelayedTask(
215       [weak_this] {
216         if (weak_this) {
217           weak_this->ReadLogSocket();
218           weak_this->EnableSocketWatchTask(true);
219         }
220       },
221       delay_ms);
222 }
223 
ReadLogSocket()224 void AndroidLogDataSource::ReadLogSocket() {
225   TraceWriter::TracePacketHandle packet;
226   protos::pbzero::AndroidLogPacket* log_packet = nullptr;
227   size_t num_events = 0;
228   bool stop = false;
229   ssize_t rsize;
230   while (!stop && (rsize = logdr_sock_.Receive(buf_.Get(), kBufSize)) > 0) {
231     num_events++;
232     stats_.num_total++;
233     // Don't hold the message loop for too long. If there are so many events
234     // in the queue, stop at some point and parse the remaining ones in another
235     // task (posted after this while loop).
236     if (num_events > 500) {
237       stop = true;
238       auto weak_this = weak_factory_.GetWeakPtr();
239       task_runner_->PostTask([weak_this] {
240         if (weak_this)
241           weak_this->ReadLogSocket();
242       });
243     }
244     char* buf = reinterpret_cast<char*>(buf_.Get());
245     PERFETTO_DCHECK(reinterpret_cast<uintptr_t>(buf) % 16 == 0);
246     size_t payload_size = reinterpret_cast<logger_entry_v4*>(buf)->len;
247     size_t hdr_size = reinterpret_cast<logger_entry_v4*>(buf)->hdr_size;
248     if (payload_size + hdr_size > static_cast<size_t>(rsize)) {
249       PERFETTO_DLOG(
250           "Invalid Android log frame (hdr: %zu, payload: %zu, rsize: %zd)",
251           hdr_size, payload_size, rsize);
252       stats_.num_failed++;
253       continue;
254     }
255     char* const end = buf + hdr_size + payload_size;
256 
257     // In older versions of Android the logger_entry struct can contain less
258     // fields. Copy that in a temporary struct, so that unset fields are
259     // always zero-initialized.
260     logger_entry_v4 entry{};
261     memcpy(&entry, buf, std::min(hdr_size, sizeof(entry)));
262     buf += hdr_size;
263 
264     if (!packet) {
265       // Lazily add the packet on the first event. This is to avoid creating
266       // empty packets if there are no events in a task.
267       packet = writer_->NewTracePacket();
268       packet->set_timestamp(
269           static_cast<uint64_t>(base::GetBootTimeNs().count()));
270       log_packet = packet->set_android_log();
271     }
272 
273     protos::pbzero::AndroidLogPacket::LogEvent* evt = nullptr;
274 
275     if (entry.lid == AndroidLogId::LID_EVENTS) {
276       // Entries in the EVENTS buffer are special, they are binary encoded.
277       // See https://developer.android.com/reference/android/util/EventLog.
278       if (!ParseBinaryEvent(buf, end, log_packet, &evt)) {
279         PERFETTO_DLOG("Failed to parse Android log binary event");
280         stats_.num_failed++;
281         continue;
282       }
283     } else {
284       if (!ParseTextEvent(buf, end, log_packet, &evt)) {
285         PERFETTO_DLOG("Failed to parse Android log text event");
286         stats_.num_failed++;
287         continue;
288       }
289     }
290     if (!evt) {
291       // Parsing succeeded but the event was skipped due to filters.
292       stats_.num_skipped++;
293       continue;
294     }
295 
296     // Add the common fields to the event.
297     uint64_t ts = entry.sec * 1000000000ULL + entry.nsec;
298     evt->set_timestamp(ts);
299     evt->set_log_id(static_cast<protos::pbzero::AndroidLogId>(entry.lid));
300     evt->set_pid(entry.pid);
301     evt->set_tid(static_cast<int32_t>(entry.tid));
302     evt->set_uid(static_cast<int32_t>(entry.uid));
303   }  // while(logdr_sock_.Receive())
304 
305   // Only print the log message if we have seen a bunch of events. This is to
306   // avoid that we keep re-triggering the log socket by writing into the log
307   // buffer ourselves.
308   if (num_events > 3)
309     PERFETTO_DLOG("Seen %zu Android log events", num_events);
310 }
311 
ParseTextEvent(const char * start,const char * end,protos::pbzero::AndroidLogPacket * packet,protos::pbzero::AndroidLogPacket::LogEvent ** out_evt)312 bool AndroidLogDataSource::ParseTextEvent(
313     const char* start,
314     const char* end,
315     protos::pbzero::AndroidLogPacket* packet,
316     protos::pbzero::AndroidLogPacket::LogEvent** out_evt) {
317   // Format: [Priority 1 byte] [ tag ] [ NUL ] [ message ]
318   const char* buf = start;
319   int8_t prio;
320   if (!ReadAndAdvance(&buf, end, &prio))
321     return false;
322 
323   if (prio > 10) {
324     PERFETTO_DLOG("Skipping log event with suspiciously high priority %d",
325                   prio);
326     return false;
327   }
328 
329   // Skip if the user specified a min-priority filter in the config.
330   if (prio < min_prio_)
331     return true;
332 
333   // Find the null terminator that separates |tag| from |message|.
334   const char* str_end;
335   for (str_end = buf; str_end < end && *str_end; str_end++) {
336   }
337   if (str_end >= end - 2)
338     return false;
339 
340   auto tag = base::StringView(buf, static_cast<size_t>(str_end - buf));
341   if (!filter_tags_.empty() && filter_tags_.count(tag) == 0)
342     return true;
343 
344   auto* evt = packet->add_events();
345   *out_evt = evt;
346   evt->set_prio(static_cast<protos::pbzero::AndroidLogPriority>(prio));
347   evt->set_tag(tag.data(), tag.size());
348 
349   buf = str_end + 1;  // Move |buf| to the start of the message.
350   size_t msg_len = static_cast<size_t>(end - buf);
351 
352   // Protobuf strings don't need the null terminator. If the string is
353   // null terminated, omit the terminator.
354   if (msg_len > 0 && *(end - 1) == '\0')
355     msg_len--;
356 
357   evt->set_message(buf, msg_len);
358   return true;
359 }
360 
ParseBinaryEvent(const char * start,const char * end,protos::pbzero::AndroidLogPacket * packet,protos::pbzero::AndroidLogPacket::LogEvent ** out_evt)361 bool AndroidLogDataSource::ParseBinaryEvent(
362     const char* start,
363     const char* end,
364     protos::pbzero::AndroidLogPacket* packet,
365     protos::pbzero::AndroidLogPacket::LogEvent** out_evt) {
366   const char* buf = start;
367   int32_t eid;
368   if (!ReadAndAdvance(&buf, end, &eid))
369     return false;
370 
371   const EventFormat* fmt = GetEventFormat(eid);
372   if (!fmt) {
373     // We got an event which doesn't have a corresponding entry in
374     // /system/etc/event-log-tags. In most cases this is a bug in the App that
375     // produced the event, which forgot to update the log tags dictionary.
376     return false;
377   }
378 
379   if (!filter_tags_.empty() &&
380       filter_tags_.count(base::StringView(fmt->name)) == 0) {
381     return true;
382   }
383 
384   auto* evt = packet->add_events();
385   *out_evt = evt;
386   evt->set_tag(fmt->name.c_str());
387   size_t field_num = 0;
388   while (buf < end) {
389     char type = *(buf++);
390     if (field_num >= fmt->fields.size())
391       return true;
392     const char* field_name = fmt->fields[field_num].c_str();
393     switch (type) {
394       case EVENT_TYPE_INT: {
395         int32_t value;
396         if (!ReadAndAdvance(&buf, end, &value))
397           return false;
398         auto* arg = evt->add_args();
399         arg->set_name(field_name);
400         arg->set_int_value(value);
401         field_num++;
402         break;
403       }
404       case EVENT_TYPE_LONG: {
405         int64_t value;
406         if (!ReadAndAdvance(&buf, end, &value))
407           return false;
408         auto* arg = evt->add_args();
409         arg->set_name(field_name);
410         arg->set_int_value(value);
411         field_num++;
412         break;
413       }
414       case EVENT_TYPE_FLOAT: {
415         float value;
416         if (!ReadAndAdvance(&buf, end, &value))
417           return false;
418         auto* arg = evt->add_args();
419         arg->set_name(field_name);
420         arg->set_float_value(value);
421         field_num++;
422         break;
423       }
424       case EVENT_TYPE_STRING: {
425         uint32_t len;
426         if (!ReadAndAdvance(&buf, end, &len) || buf + len > end)
427           return false;
428         auto* arg = evt->add_args();
429         arg->set_name(field_name);
430         arg->set_string_value(buf, len);
431         buf += len;
432         field_num++;
433         break;
434       }
435       case EVENT_TYPE_LIST: {
436         buf++;  // EVENT_TYPE_LIST has one byte payload containing the list len.
437         if (field_num > 0) {
438           // Lists are supported only as a top-level node. We stop parsing when
439           // encountering a list as an inner field. The very few of them are not
440           // interesting enough to warrant the extra complexity.
441           return true;
442         }
443         break;
444       }
445       default:
446         PERFETTO_DLOG(
447             "Skipping unknown Android log binary event of type %d for %s at pos"
448             " %zd after parsing %zu fields",
449             static_cast<int>(type), fmt->name.c_str(), buf - start, field_num);
450         return true;
451     }  // switch(type)
452   }    // while(buf < end)
453   return true;
454 }
455 
Flush(FlushRequestID,std::function<void ()> callback)456 void AndroidLogDataSource::Flush(FlushRequestID,
457                                  std::function<void()> callback) {
458   // Grab most recent entries.
459   if (logdr_sock_)
460     ReadLogSocket();
461 
462   // Emit stats.
463   {
464     auto packet = writer_->NewTracePacket();
465     packet->set_timestamp(static_cast<uint64_t>(base::GetBootTimeNs().count()));
466     auto* stats = packet->set_android_log()->set_stats();
467     stats->set_num_total(stats_.num_total);
468     stats->set_num_skipped(stats_.num_skipped);
469     stats->set_num_failed(stats_.num_failed);
470   }
471 
472   writer_->Flush(callback);
473 }
474 
ParseEventLogDefinitions()475 void AndroidLogDataSource::ParseEventLogDefinitions() {
476   std::string event_log_tags = ReadEventLogDefinitions();
477   for (base::StringSplitter ss(std::move(event_log_tags), '\n'); ss.Next();) {
478     if (!ParseEventLogDefinitionLine(ss.cur_token(), ss.cur_token_size() + 1)) {
479       PERFETTO_DLOG("Could not parse event log format: %s", ss.cur_token());
480     }
481   }
482 }
483 
ParseEventLogDefinitionLine(char * line,size_t len)484 bool AndroidLogDataSource::ParseEventLogDefinitionLine(char* line, size_t len) {
485   base::StringSplitter tok(line, len, ' ');
486   if (!tok.Next())
487     return false;
488   auto id = static_cast<uint32_t>(std::strtoul(tok.cur_token(), nullptr, 10));
489   if (!tok.Next())
490     return false;
491   std::string name(tok.cur_token(), tok.cur_token_size());
492   auto it = event_formats_.emplace(id, EventFormat{std::move(name), {}}).first;
493   char* format = tok.cur_token() + tok.cur_token_size() + 1;
494   if (format >= line + len || !*format || *format == '\n') {
495     return true;
496   }
497   size_t format_len = len - static_cast<size_t>(format - line);
498 
499   // Parse the arg formats, e.g.:
500   // (status|1|5),(health|1|5),(present|1|5),(plugged|1|5),(technology|3).
501   // We don't really care neither about the field type nor its unit (the two
502   // numbers after the |). The binary format re-states the type and we don't
503   // currently propagate the unit at all.
504   bool parsed_all_tokens = true;
505   for (base::StringSplitter field(format, format_len, ','); field.Next();) {
506     if (field.cur_token_size() <= 2) {
507       parsed_all_tokens = false;
508       continue;
509     }
510     char* start = field.cur_token() + 1;
511     base::StringSplitter parts(start, field.cur_token_size() - 1, '|');
512     if (!parts.Next()) {
513       parsed_all_tokens = false;
514       continue;
515     }
516     std::string field_name(parts.cur_token(), parts.cur_token_size());
517     it->second.fields.emplace_back(std::move(field_name));
518   }
519   return parsed_all_tokens;
520 }
521 
ReadEventLogDefinitions()522 std::string AndroidLogDataSource::ReadEventLogDefinitions() {
523   std::string contents;
524   if (!base::ReadFile(kLogTagsPath, &contents)) {
525     PERFETTO_PLOG("Failed to read %s", kLogTagsPath);
526     return "";
527   }
528   return contents;
529 }
530 
GetEventFormat(int id) const531 const AndroidLogDataSource::EventFormat* AndroidLogDataSource::GetEventFormat(
532     int id) const {
533   auto it = event_formats_.find(id);
534   return it == event_formats_.end() ? nullptr : &it->second;
535 }
536 
537 }  // namespace perfetto
538