Add logcat data source

Introduces a data source that allows copying logcat entries
into the perfetto userspace buffer.

Bug: 122243384
Test: perfetto_unittests
Change-Id: Iab1b32c5ee67d8b1aeec921c3e30260c9f9fe9e2
diff --git a/src/traced/probes/BUILD.gn b/src/traced/probes/BUILD.gn
index 17fe4db..827c570 100644
--- a/src/traced/probes/BUILD.gn
+++ b/src/traced/probes/BUILD.gn
@@ -38,6 +38,7 @@
     "../../base",
     "../../tracing:ipc",
     "../../tracing:tracing",
+    "android_log",
     "filesystem",
     "power",
     "ps",
@@ -69,6 +70,7 @@
     "../../../gn:default_deps",
     "../../../gn:gtest_deps",
     "../../tracing:test_support",
+    "android_log:unittests",
     "filesystem:unittests",
     "ps:unittests",
     "sys_stats:unittests",
diff --git a/src/traced/probes/android_log/BUILD.gn b/src/traced/probes/android_log/BUILD.gn
new file mode 100644
index 0000000..cbd9148
--- /dev/null
+++ b/src/traced/probes/android_log/BUILD.gn
@@ -0,0 +1,46 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+source_set("android_log") {
+  public_deps = [
+    "../../../tracing",
+  ]
+  deps = [
+    "..:data_source",
+    "../../../../gn:default_deps",
+    "../../../../include/perfetto/traced",
+    "../../../../protos/perfetto/common:zero",
+    "../../../../protos/perfetto/trace/android:zero",
+    "../../../base",
+  ]
+  sources = [
+    "android_log_data_source.cc",
+    "android_log_data_source.h",
+  ]
+}
+
+source_set("unittests") {
+  testonly = true
+  deps = [
+    ":android_log",
+    "../../../../gn:default_deps",
+    "../../../../gn:gtest_deps",
+    "../../../../protos/perfetto/trace:lite",
+    "../../../../src/base:test_support",
+    "../../../../src/tracing:test_support",
+  ]
+  sources = [
+    "android_log_data_source_unittest.cc",
+  ]
+}
diff --git a/src/traced/probes/android_log/android_log_data_source.cc b/src/traced/probes/android_log/android_log_data_source.cc
new file mode 100644
index 0000000..bb003eb
--- /dev/null
+++ b/src/traced/probes/android_log/android_log_data_source.cc
@@ -0,0 +1,488 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/traced/probes/android_log/android_log_data_source.h"
+
+#include "perfetto/base/file_utils.h"
+#include "perfetto/base/logging.h"
+#include "perfetto/base/optional.h"
+#include "perfetto/base/scoped_file.h"
+#include "perfetto/base/string_splitter.h"
+#include "perfetto/base/task_runner.h"
+#include "perfetto/base/time.h"
+#include "perfetto/base/unix_socket.h"
+#include "perfetto/tracing/core/data_source_config.h"
+#include "perfetto/tracing/core/trace_packet.h"
+#include "perfetto/tracing/core/trace_writer.h"
+
+#include "perfetto/trace/android/android_log.pbzero.h"
+#include "perfetto/trace/trace_packet.pbzero.h"
+
+namespace perfetto {
+
+namespace {
+
+constexpr uint32_t kMinPollRateMs = 100;
+constexpr uint32_t kDefaultPollRateMs = 1000;
+constexpr size_t kBufSize = base::kPageSize;
+const char kLogTagsPath[] = "/system/etc/event-log-tags";
+const char kLogdrSocket[] = "/dev/socket/logdr";
+
+// From AOSP's liblog/include/log/log_read.h .
+// Android Log is supposed to be an ABI as it's exposed also by logcat -b (and
+// in practice never changed in backwards-incompatible ways in the past).
+// Note: the casing doesn't match the style guide and instead matches the
+// original name in liblog for the sake of code-searcheability.
+struct logger_entry_v4 {
+  uint16_t len;       // length of the payload.
+  uint16_t hdr_size;  // sizeof(struct logger_entry_v4).
+  int32_t pid;        // generating process's pid.
+  uint32_t tid;       // generating process's tid.
+  uint32_t sec;       // seconds since Epoch.
+  uint32_t nsec;      // nanoseconds.
+  uint32_t lid;       // log id of the payload, bottom 4 bits currently.
+  uint32_t uid;       // generating process's uid.
+};
+
+// Event types definition in the binary encoded format, from
+// liblog/include/log/log.h .
+// Note that these values don't match the textual definitions of
+// system/core/android_logevent.logtags (which are not used by perfetto).
+// The latter are off by one (INT = 1, LONG=2 and so on).
+enum AndroidEventLogType {
+  EVENT_TYPE_INT = 0,
+  EVENT_TYPE_LONG = 1,
+  EVENT_TYPE_STRING = 2,
+  EVENT_TYPE_LIST = 3,
+  EVENT_TYPE_FLOAT = 4,
+};
+
+template <typename T>
+inline bool ReadAndAdvance(const char** ptr, const char* end, T* out) {
+  if (*ptr > end - sizeof(T))
+    return false;
+  memcpy(reinterpret_cast<void*>(out), *ptr, sizeof(T));
+  *ptr += sizeof(T);
+  return true;
+}
+
+}  // namespace
+
+AndroidLogDataSource::AndroidLogDataSource(DataSourceConfig ds_config,
+                                           base::TaskRunner* task_runner,
+                                           TracingSessionID session_id,
+                                           std::unique_ptr<TraceWriter> writer)
+    : ProbesDataSource(session_id, kTypeId),
+      task_runner_(task_runner),
+      writer_(std::move(writer)),
+      weak_factory_(this) {
+  const auto& cfg = ds_config.android_log_config();
+  poll_rate_ms_ = cfg.poll_ms() ? cfg.poll_ms() : kDefaultPollRateMs;
+  poll_rate_ms_ = std::max(kMinPollRateMs, poll_rate_ms_);
+
+  std::vector<uint32_t> log_ids;
+  for (uint32_t id : cfg.log_ids())
+    log_ids.push_back(id);
+  if (log_ids.empty()) {
+    // If no log id is specified, add the most popular ones.
+    log_ids.push_back(AndroidLogConfig::AndroidLogId::LID_DEFAULT);
+    log_ids.push_back(AndroidLogConfig::AndroidLogId::LID_EVENTS);
+    log_ids.push_back(AndroidLogConfig::AndroidLogId::LID_SYSTEM);
+    log_ids.push_back(AndroidLogConfig::AndroidLogId::LID_CRASH);
+    log_ids.push_back(AndroidLogConfig::AndroidLogId::LID_KERNEL);
+  }
+  // Build the command string that will be sent to the logdr socket on Start(),
+  // which looks like "stream lids=1,2,3,4" (lids == log buffer id(s)).
+  mode_ = "stream tail=1 lids";
+  for (auto it = log_ids.begin(); it != log_ids.end(); it++) {
+    mode_ += it == log_ids.begin() ? "=" : ",";
+    mode_ += std::to_string(*it);
+  }
+
+  // Build a linear vector out of the tag filters and keep track of the string
+  // boundaries. Once done, derive StringView(s) out of the vector. This is
+  // to create a set<StringView> which is backed by contiguous chars that can be
+  // used to lookup StringView(s) from the parsed buffer.
+  // This is to avoid copying strings of tags for the only sake of checking for
+  // their existence in the set.
+  std::vector<std::pair<size_t, size_t>> tag_boundaries;
+  for (const std::string& tag : cfg.filter_tags()) {
+    const size_t begin = filter_tags_strbuf_.size();
+    filter_tags_strbuf_.insert(filter_tags_strbuf_.end(), tag.begin(),
+                               tag.end());
+    const size_t end = filter_tags_strbuf_.size();
+    tag_boundaries.emplace_back(begin, end - begin);
+  }
+  filter_tags_strbuf_.shrink_to_fit();
+  // At this point pointers to |filter_tags_strbuf_| are stable.
+  for (const auto& it : tag_boundaries)
+    filter_tags_.emplace(&filter_tags_strbuf_[it.first], it.second);
+
+  min_prio_ = cfg.min_prio();
+  buf_ = base::PagedMemory::Allocate(kBufSize);
+}
+
+AndroidLogDataSource::~AndroidLogDataSource() = default;
+
+base::UnixSocketRaw AndroidLogDataSource::ConnectLogdrSocket() {
+  auto socket = base::UnixSocketRaw::CreateMayFail(base::SockType::kSeqPacket);
+  if (!socket || !socket.Connect(kLogdrSocket)) {
+    PERFETTO_PLOG("Failed to connect to %s", kLogdrSocket);
+    return base::UnixSocketRaw();
+  }
+  return socket;
+}
+
+void AndroidLogDataSource::Start() {
+  ParseEventLogDefinitions();
+
+  if (!(logdr_sock_ = ConnectLogdrSocket()))
+    return;
+
+  PERFETTO_DLOG("Starting Android log data source: %s", mode_.c_str());
+  if (logdr_sock_.Send(mode_.data(), mode_.size()) <= 0) {
+    PERFETTO_PLOG("send() failed on logdr socket %s", kLogdrSocket);
+    return;
+  }
+  logdr_sock_.SetBlocking(false);
+
+  auto weak_this = weak_factory_.GetWeakPtr();
+  task_runner_->PostTask([weak_this] {
+    if (weak_this)
+      weak_this->Tick(/*post_next_task=*/true);
+  });
+}
+
+void AndroidLogDataSource::Tick(bool post_next_task) {
+  auto weak_this = weak_factory_.GetWeakPtr();
+  if (post_next_task) {
+    auto now_ms = base::GetWallTimeMs().count();
+    task_runner_->PostDelayedTask(
+        [weak_this] {
+          if (weak_this)
+            weak_this->Tick(/*post_next_task=*/true);
+        },
+        poll_rate_ms_ - (now_ms % poll_rate_ms_));
+  }
+
+  TraceWriter::TracePacketHandle packet;
+  protos::pbzero::AndroidLogPacket* log_packet = nullptr;
+  size_t num_events = 0;
+  bool stop = false;
+  ssize_t rsize;
+  while (!stop && (rsize = logdr_sock_.Receive(buf_.Get(), kBufSize)) > 0) {
+    num_events++;
+    stats_.num_total++;
+    // Don't hold the message loop for too long. If there are so many events
+    // in the queue, stop at some point and parse the remaining ones in another
+    // task (posted after this while loop).
+    if (num_events > 500) {
+      stop = true;
+      task_runner_->PostTask([weak_this] {
+        if (weak_this)
+          weak_this->Tick(/*post_next_task=*/false);
+      });
+    }
+    char* buf = reinterpret_cast<char*>(buf_.Get());
+    PERFETTO_DCHECK(reinterpret_cast<uintptr_t>(buf) % 16 == 0);
+    size_t payload_size = reinterpret_cast<logger_entry_v4*>(buf)->len;
+    size_t hdr_size = reinterpret_cast<logger_entry_v4*>(buf)->hdr_size;
+    if (payload_size + hdr_size > static_cast<size_t>(rsize)) {
+      PERFETTO_DLOG(
+          "Invalid Android log frame (hdr: %zu, payload: %zu, rsize: %zd)",
+          hdr_size, payload_size, rsize);
+      stats_.num_failed++;
+      continue;
+    }
+    char* const end = buf + hdr_size + payload_size;
+
+    // In older versions of Android the logger_entry struct can contain less
+    // fields. Copy that in a temporary struct, so that unset fields are
+    // always zero-initialized.
+    logger_entry_v4 entry{};
+    memcpy(&entry, buf, std::min(hdr_size, sizeof(entry)));
+    buf += hdr_size;
+
+    if (!packet) {
+      // Lazily add the packet on the first event. This is to avoid creating
+      // empty packets if there are no events in a poll task.
+      packet = writer_->NewTracePacket();
+      packet->set_timestamp(
+          static_cast<uint64_t>(base::GetBootTimeNs().count()));
+      log_packet = packet->set_android_log();
+    }
+
+    protos::pbzero::AndroidLogPacket::LogEvent* evt = nullptr;
+
+    if (entry.lid == AndroidLogConfig::AndroidLogId::LID_EVENTS) {
+      // Entries in the EVENTS buffer are special, they are binary encoded.
+      // See https://developer.android.com/reference/android/util/EventLog.
+      if (!ParseBinaryEvent(buf, end, log_packet, &evt)) {
+        PERFETTO_DLOG("Failed to parse Android log binary event");
+        stats_.num_failed++;
+        continue;
+      }
+    } else {
+      if (!ParseTextEvent(buf, end, log_packet, &evt)) {
+        PERFETTO_DLOG("Failed to parse Android log text event");
+        stats_.num_failed++;
+        continue;
+      }
+    }
+    if (!evt) {
+      // Parsing succeeded but the event was skipped due to filters.
+      stats_.num_skipped++;
+      continue;
+    }
+
+    // Add the common fields to the event.
+    uint64_t ts = entry.sec * 1000000000ULL + entry.nsec;
+    evt->set_timestamp(ts);
+    evt->set_log_id(static_cast<protos::pbzero::AndroidLogId>(entry.lid));
+    evt->set_pid(entry.pid);
+    evt->set_tid(static_cast<int32_t>(entry.tid));
+    evt->set_uid(static_cast<int32_t>(entry.uid));
+  }  // while(logdr_sock_.Receive())
+  PERFETTO_DLOG("Seen %zu Android log events", num_events);
+}
+
+bool AndroidLogDataSource::ParseTextEvent(
+    const char* start,
+    const char* end,
+    protos::pbzero::AndroidLogPacket* packet,
+    protos::pbzero::AndroidLogPacket::LogEvent** out_evt) {
+  // Format: [Priority 1 byte] [ tag ] [ NUL ] [ message ]
+  const char* buf = start;
+  int8_t prio;
+  if (!ReadAndAdvance(&buf, end, &prio))
+    return false;
+
+  if (prio > 10) {
+    PERFETTO_DLOG("Skipping log event with suspiciously high priority %d",
+                  prio);
+    return false;
+  }
+
+  // Skip if the user specified a min-priority filter in the config.
+  if (prio < min_prio_)
+    return true;
+
+  // Find the null terminator that separates |tag| from |message|.
+  const char* str_end;
+  for (str_end = buf; str_end < end && *str_end; str_end++) {
+  }
+  if (str_end >= end - 2)
+    return false;
+
+  auto tag = base::StringView(buf, static_cast<size_t>(str_end - buf));
+  if (!filter_tags_.empty() && filter_tags_.count(tag) == 0)
+    return true;
+
+  auto* evt = packet->add_events();
+  *out_evt = evt;
+  evt->set_prio(static_cast<protos::pbzero::AndroidLogPriority>(prio));
+  evt->set_tag(tag.data(), tag.size());
+
+  buf = str_end + 1;  // Move |buf| to the start of the message.
+  size_t msg_len = static_cast<size_t>(end - buf);
+
+  // Protobuf strings don't need the null terminator. If the string is
+  // null terminated, omit the terminator.
+  if (msg_len > 0 && *(end - 1) == '\0')
+    msg_len--;
+
+  evt->set_message(buf, msg_len);
+  return true;
+}
+
+bool AndroidLogDataSource::ParseBinaryEvent(
+    const char* start,
+    const char* end,
+    protos::pbzero::AndroidLogPacket* packet,
+    protos::pbzero::AndroidLogPacket::LogEvent** out_evt) {
+  const char* buf = start;
+  int32_t eid;
+  if (!ReadAndAdvance(&buf, end, &eid))
+    return false;
+
+  // TODO test events with 0 arguments. DNS.
+
+  const EventFormat* fmt = GetEventFormat(eid);
+  if (!fmt) {
+    // We got an event which doesn't have a corresponding entry in
+    // /system/etc/event-log-tags. In most cases this is a bug in the App that
+    // produced the event, which forgot to update the log tags dictionary.
+    return false;
+  }
+
+  if (!filter_tags_.empty() &&
+      filter_tags_.count(base::StringView(fmt->name)) == 0) {
+    return true;
+  }
+
+  auto* evt = packet->add_events();
+  *out_evt = evt;
+  evt->set_tag(fmt->name.c_str());
+  size_t field_num = 0;
+  while (buf < end) {
+    char type = *(buf++);
+    if (field_num >= fmt->fields.size())
+      return true;
+    const char* field_name = fmt->fields[field_num].c_str();
+    switch (type) {
+      case EVENT_TYPE_INT: {
+        int32_t value;
+        if (!ReadAndAdvance(&buf, end, &value))
+          return false;
+        auto* arg = evt->add_args();
+        arg->set_name(field_name);
+        arg->set_int_value(value);
+        field_num++;
+        break;
+      }
+      case EVENT_TYPE_LONG: {
+        int64_t value;
+        if (!ReadAndAdvance(&buf, end, &value))
+          return false;
+        auto* arg = evt->add_args();
+        arg->set_name(field_name);
+        arg->set_int_value(value);
+        field_num++;
+        break;
+      }
+      case EVENT_TYPE_FLOAT: {
+        float value;
+        if (!ReadAndAdvance(&buf, end, &value))
+          return false;
+        auto* arg = evt->add_args();
+        arg->set_name(field_name);
+        arg->set_float_value(value);
+        field_num++;
+        break;
+      }
+      case EVENT_TYPE_STRING: {
+        uint32_t len;
+        if (!ReadAndAdvance(&buf, end, &len) || buf + len > end)
+          return false;
+        auto* arg = evt->add_args();
+        arg->set_name(field_name);
+        arg->set_string_value(buf, len);
+        buf += len;
+        field_num++;
+        break;
+      }
+      case EVENT_TYPE_LIST: {
+        buf++;  // EVENT_TYPE_LIST has one byte payload containing the list len.
+        if (field_num > 0) {
+          // Lists are supported only as a top-level node. We stop parsing when
+          // encountering a list as an inner field. The very few of them are not
+          // interesting enough to warrant the extra complexity.
+          return true;
+        }
+        break;
+      }
+      default:
+        PERFETTO_DLOG(
+            "Skipping unknown Android log binary event of type %d for %s at pos"
+            " %zd after parsing %zu fields",
+            static_cast<int>(type), fmt->name.c_str(), buf - start, field_num);
+        return true;
+    }  // switch(type)
+  }    // while(buf < end)
+  return true;
+}
+
+void AndroidLogDataSource::Flush(FlushRequestID,
+                                 std::function<void()> callback) {
+  // Grab most recent entries.
+  Tick(/*post_next_task=*/false);
+
+  // Emit stats.
+  {
+    auto packet = writer_->NewTracePacket();
+    packet->set_timestamp(static_cast<uint64_t>(base::GetBootTimeNs().count()));
+    auto* stats = packet->set_android_log()->set_stats();
+    stats->set_num_total(stats_.num_total);
+    stats->set_num_skipped(stats_.num_skipped);
+    stats->set_num_failed(stats_.num_failed);
+  }
+
+  writer_->Flush(callback);
+}
+
+void AndroidLogDataSource::ParseEventLogDefinitions() {
+  std::string event_log_tags = ReadEventLogDefinitions();
+  for (base::StringSplitter ss(std::move(event_log_tags), '\n'); ss.Next();) {
+    if (!ParseEventLogDefinitionLine(ss.cur_token(), ss.cur_token_size() + 1)) {
+      PERFETTO_DLOG("Could not parse event log format: %s", ss.cur_token());
+    }
+  }
+}
+
+bool AndroidLogDataSource::ParseEventLogDefinitionLine(char* line, size_t len) {
+  base::StringSplitter tok(line, len, ' ');
+  if (!tok.Next())
+    return false;
+  auto id = static_cast<uint32_t>(std::strtoul(tok.cur_token(), nullptr, 10));
+  if (!tok.Next())
+    return false;
+  std::string name(tok.cur_token(), tok.cur_token_size());
+  auto it = event_formats_.emplace(id, EventFormat{std::move(name), {}}).first;
+  char* format = tok.cur_token() + tok.cur_token_size() + 1;
+  if (format >= line + len || !*format || *format == '\n') {
+    return true;
+  }
+  size_t format_len = len - static_cast<size_t>(format - line);
+
+  // Parse the arg formats, e.g.:
+  // (status|1|5),(health|1|5),(present|1|5),(plugged|1|5),(technology|3).
+  // We don't really care neither about the field type nor its unit (the two
+  // numbers after the |). The binary format re-states the type and we don't
+  // currently propagate the unit at all.
+  bool parsed_all_tokens = true;
+  for (base::StringSplitter field(format, format_len, ','); field.Next();) {
+    if (field.cur_token_size() <= 2) {
+      parsed_all_tokens = false;
+      continue;
+    }
+    char* start = field.cur_token() + 1;
+    base::StringSplitter parts(start, field.cur_token_size() - 1, '|');
+    if (!parts.Next()) {
+      parsed_all_tokens = false;
+      continue;
+    }
+    std::string field_name(parts.cur_token(), parts.cur_token_size());
+    it->second.fields.emplace_back(std::move(field_name));
+  }
+  return parsed_all_tokens;
+}
+
+std::string AndroidLogDataSource::ReadEventLogDefinitions() {
+  std::string contents;
+  if (!base::ReadFile(kLogTagsPath, &contents)) {
+    PERFETTO_PLOG("Failed to read %s", kLogTagsPath);
+    return "";
+  }
+  return contents;
+}
+
+const AndroidLogDataSource::EventFormat* AndroidLogDataSource::GetEventFormat(
+    int id) const {
+  auto it = event_formats_.find(id);
+  return it == event_formats_.end() ? nullptr : &it->second;
+}
+
+}  // namespace perfetto
diff --git a/src/traced/probes/android_log/android_log_data_source.h b/src/traced/probes/android_log/android_log_data_source.h
new file mode 100644
index 0000000..540e528
--- /dev/null
+++ b/src/traced/probes/android_log/android_log_data_source.h
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SRC_TRACED_PROBES_ANDROID_LOG_ANDROID_LOG_DATA_SOURCE_H_
+#define SRC_TRACED_PROBES_ANDROID_LOG_ANDROID_LOG_DATA_SOURCE_H_
+
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+
+#include "perfetto/base/paged_memory.h"
+#include "perfetto/base/scoped_file.h"
+#include "perfetto/base/string_view.h"
+#include "perfetto/base/unix_socket.h"
+#include "perfetto/base/weak_ptr.h"
+#include "src/traced/probes/probes_data_source.h"
+
+namespace perfetto {
+
+class DataSourceConfig;
+class TraceWriter;
+namespace base {
+class TaskRunner;
+}
+
+namespace protos {
+namespace pbzero {
+class AndroidLogPacket;
+class AndroidLogPacket_LogEvent;
+}  // namespace pbzero
+}  // namespace protos
+
+class AndroidLogDataSource : public ProbesDataSource {
+ public:
+  struct Stats {
+    uint64_t num_total = 0;    // Total number of log entries received.
+    uint64_t num_failed = 0;   // Parser failures.
+    uint64_t num_skipped = 0;  // Messages skipped due to filters.
+  };
+
+  // One EventFormat == one line of /system/etc/event-log-tags.
+  struct EventFormat {
+    std::string name;
+    std::vector<std::string> fields;
+  };
+  static constexpr int kTypeId = 6;
+
+  AndroidLogDataSource(DataSourceConfig,
+                       base::TaskRunner*,
+                       TracingSessionID,
+                       std::unique_ptr<TraceWriter> writer);
+
+  ~AndroidLogDataSource() override;
+
+  // ProbesDataSource implementation.
+  void Start() override;
+  void Flush(FlushRequestID, std::function<void()> callback) override;
+
+  // Reads the contents of /system/etc/event-log-tags. Virtual for testing.
+  virtual std::string ReadEventLogDefinitions();
+
+  // Connects to the /dev/socket/logdr socket. Virtual for testing.
+  virtual base::UnixSocketRaw ConnectLogdrSocket();
+
+  // Parses the contents of ReadEventLogDefinitions().
+  void ParseEventLogDefinitions();
+
+  const EventFormat* GetEventFormat(int id) const;
+  const Stats& stats() const { return stats_; }
+  base::WeakPtr<AndroidLogDataSource> GetWeakPtr() const;
+
+ private:
+  // Periodic polling task.
+  void Tick(bool post_next_task);
+
+  // Parses one line of /system/etc/event-log-tags.
+  bool ParseEventLogDefinitionLine(char* line, size_t len);
+
+  // Parses a textual (i.e. tag + message) event, which is the majority of
+  // log events. All buffers but the LID_EVENTS contain text events.
+  // If parsing fails returns false and leaves the |out_evt| field unset.
+  // If parsing succeeds returns true and:
+  // - If the event is skipped due to filters, |out_evt| is left unset.
+  // - If a new event is aded to the packet, |out_evt| is set to that.
+  bool ParseTextEvent(const char* start,
+                      const char* end,
+                      protos::pbzero::AndroidLogPacket* packet,
+                      protos::pbzero::AndroidLogPacket_LogEvent** out_evt);
+
+  // Parses a binary event from the "events" buffer.
+  // If parsing fails returns false and leaves the |out_evt| field unset.
+  // If parsing succeeds returns true and:
+  // - If the event is skipped due to filters, |out_evt| is left unset.
+  // - If a new event is aded to the packet, |out_evt| is set to that.
+  bool ParseBinaryEvent(const char* start,
+                        const char* end,
+                        protos::pbzero::AndroidLogPacket* packet,
+                        protos::pbzero::AndroidLogPacket_LogEvent** out_evt);
+
+  base::TaskRunner* const task_runner_;
+  std::unique_ptr<TraceWriter> writer_;
+  base::UnixSocketRaw logdr_sock_;
+
+  // Config parameters coming from the data source section in the trace config.
+  uint32_t poll_rate_ms_ = 0;
+  int min_prio_ = 0;
+  std::string mode_;
+
+  // For filtering events based on tags.
+  std::unordered_set<base::StringView> filter_tags_;
+  std::vector<char> filter_tags_strbuf_;
+
+  // Lookup map for binary events (log_id=EVENTS). Translates a numeric id into
+  // a corresponding field descriptor. This is generated by parsing
+  // /system/etc/event-log-tags when starting.
+  std::unordered_map<int, EventFormat> event_formats_;
+
+  // Buffer used for parsing. It's safer (read: fails sooner) than using the
+  // stack, due to red zones around the boundaries.
+  base::PagedMemory buf_;
+  Stats stats_;
+
+  base::WeakPtrFactory<AndroidLogDataSource> weak_factory_;  // Keep last.
+};
+
+}  // namespace perfetto
+
+#endif  // SRC_TRACED_PROBES_ANDROID_LOG_ANDROID_LOG_DATA_SOURCE_H_
diff --git a/src/traced/probes/android_log/android_log_data_source_unittest.cc b/src/traced/probes/android_log/android_log_data_source_unittest.cc
new file mode 100644
index 0000000..09bf81a
--- /dev/null
+++ b/src/traced/probes/android_log/android_log_data_source_unittest.cc
@@ -0,0 +1,396 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/traced/probes/android_log/android_log_data_source.h"
+
+#include "perfetto/tracing/core/data_source_config.h"
+#include "src/base/test/test_task_runner.h"
+#include "src/tracing/core/trace_writer_for_testing.h"
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+#include "perfetto/trace/trace_packet.pb.h"
+#include "perfetto/trace/trace_packet.pbzero.h"
+
+using ::testing::Invoke;
+using ::testing::Return;
+
+namespace perfetto {
+namespace {
+
+class TestAndroidLogDataSource : public AndroidLogDataSource {
+ public:
+  TestAndroidLogDataSource(const DataSourceConfig& config,
+                           base::TaskRunner* task_runner,
+                           TracingSessionID id,
+                           std::unique_ptr<TraceWriter> writer)
+      : AndroidLogDataSource(config, task_runner, id, std::move(writer)) {}
+
+  MOCK_METHOD0(ReadEventLogDefinitions, std::string());
+  MOCK_METHOD0(ConnectLogdrSocket, base::UnixSocketRaw());
+};
+
+class AndroidLogDataSourceTest : public ::testing::Test {
+ protected:
+  AndroidLogDataSourceTest() {}
+
+  void CreateInstance(const DataSourceConfig& cfg) {
+    auto writer =
+        std::unique_ptr<TraceWriterForTesting>(new TraceWriterForTesting());
+    writer_raw_ = writer.get();
+    data_source_.reset(
+        new TestAndroidLogDataSource(cfg, &task_runner_, 0, std::move(writer)));
+  }
+
+  void StartAndSimulateLogd(
+      const std::vector<std::vector<uint8_t>>& fake_events) {
+    base::UnixSocketRaw send_sock;
+    base::UnixSocketRaw recv_sock;
+    // In theory this should be a kSeqPacket. We use kDgram here so that the
+    // test can run also on MacOS (which doesn't support SOCK_SEQPACKET).
+    const auto kSockType = base::SockType::kDgram;
+    std::tie(send_sock, recv_sock) = base::UnixSocketRaw::CreatePair(kSockType);
+    ASSERT_TRUE(send_sock);
+    ASSERT_TRUE(recv_sock);
+
+    EXPECT_CALL(*data_source_, ConnectLogdrSocket())
+        .WillOnce(Invoke([&recv_sock] { return std::move(recv_sock); }));
+
+    data_source_->Start();
+
+    char cmd[64]{};
+    EXPECT_GT(send_sock.Receive(cmd, sizeof(cmd) - 1), 0);
+    EXPECT_STREQ("stream tail=1 lids=0,2,3,4,7", cmd);
+
+    // Send back log messages emulating Android's logdr socket.
+    for (const auto& buf : fake_events)
+      send_sock.Send(buf.data(), buf.size());
+
+    auto on_flush = task_runner_.CreateCheckpoint("on_flush");
+    data_source_->Flush(1, on_flush);
+    task_runner_.RunUntilCheckpoint("on_flush");
+  }
+
+  base::TestTaskRunner task_runner_;
+  std::unique_ptr<TestAndroidLogDataSource> data_source_;
+  TraceWriterForTesting* writer_raw_;
+
+  const std::vector<std::vector<uint8_t>> kValidTextEvents{
+      // 12-29 23:13:59.679  7546  8991 I ActivityManager:
+      // Killing 11660:com.google.android.videos/u0a168 (adj 985): empty #17
+      {0x55, 0x00, 0x1c, 0x00, 0x7a, 0x1d, 0x00, 0x00, 0x1f, 0x23, 0x00, 0x00,
+       0xb7, 0xff, 0x27, 0x5c, 0xe6, 0x58, 0x7b, 0x28, 0x03, 0x00, 0x00, 0x00,
+       0xe8, 0x03, 0x00, 0x00, 0x04, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74,
+       0x79, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x00, 0x4b, 0x69, 0x6c,
+       0x6c, 0x69, 0x6e, 0x67, 0x20, 0x31, 0x31, 0x36, 0x36, 0x30, 0x3a, 0x63,
+       0x6f, 0x6d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x6e,
+       0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x73,
+       0x2f, 0x75, 0x30, 0x61, 0x31, 0x36, 0x38, 0x20, 0x28, 0x61, 0x64, 0x6a,
+       0x20, 0x39, 0x38, 0x35, 0x29, 0x3a, 0x20, 0x65, 0x6d, 0x70, 0x74, 0x79,
+       0x20, 0x23, 0x31, 0x37, 0x00},
+
+      // 12-29 23:13:59.683  7546  7570 W libprocessgroup:
+      // kill(-11660, 9) failed: No such process
+      {0x39, 0x00, 0x1c, 0x00, 0x7a, 0x1d, 0x00, 0x00, 0x92, 0x1d, 0x00,
+       0x00, 0xb7, 0xff, 0x27, 0x5c, 0x12, 0xf3, 0xbd, 0x28, 0x00, 0x00,
+       0x00, 0x00, 0xe8, 0x03, 0x00, 0x00, 0x05, 0x6c, 0x69, 0x62, 0x70,
+       0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x67, 0x72, 0x6f, 0x75, 0x70,
+       0x00, 0x6b, 0x69, 0x6c, 0x6c, 0x28, 0x2d, 0x31, 0x31, 0x36, 0x36,
+       0x30, 0x2c, 0x20, 0x39, 0x29, 0x20, 0x66, 0x61, 0x69, 0x6c, 0x65,
+       0x64, 0x3a, 0x20, 0x4e, 0x6f, 0x20, 0x73, 0x75, 0x63, 0x68, 0x20,
+       0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x00},
+
+      // 12-29 23:13:59.719  7415  7415 I Zygote:
+      // Process 11660 exited due to signal (9)
+      {0x2f, 0x00, 0x1c, 0x00, 0xf7, 0x1c, 0x00, 0x00, 0xf7, 0x1c, 0x00,
+       0x00, 0xb7, 0xff, 0x27, 0x5c, 0x7c, 0x11, 0xe2, 0x2a, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x5a, 0x79, 0x67, 0x6f,
+       0x74, 0x65, 0x00, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x20,
+       0x31, 0x31, 0x36, 0x36, 0x30, 0x20, 0x65, 0x78, 0x69, 0x74, 0x65,
+       0x64, 0x20, 0x64, 0x75, 0x65, 0x20, 0x74, 0x6f, 0x20, 0x73, 0x69,
+       0x67, 0x6e, 0x61, 0x6c, 0x20, 0x28, 0x39, 0x29, 0x00},
+  };
+
+  const std::vector<std::vector<uint8_t>> kValidBinaryEvents{
+      // 12-30 10:22:08.914 29981 30962 I am_kill :
+      // [0,31730,android.process.acore,985,empty #17]
+      {0x3d, 0x00, 0x1c, 0x00, 0x1d, 0x75, 0x00, 0x00, 0xf2, 0x78, 0x00, 0x00,
+       0x50, 0x9c, 0x28, 0x5c, 0xdb, 0x77, 0x7e, 0x36, 0x02, 0x00, 0x00, 0x00,
+       0xe8, 0x03, 0x00, 0x00, 0x47, 0x75, 0x00, 0x00, 0x03, 0x05, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0xf2, 0x7b, 0x00, 0x00, 0x02, 0x15, 0x00, 0x00,
+       0x00, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x70, 0x72, 0x6f,
+       0x63, 0x65, 0x73, 0x73, 0x2e, 0x61, 0x63, 0x6f, 0x72, 0x65, 0x00, 0xd9,
+       0x03, 0x00, 0x00, 0x02, 0x09, 0x00, 0x00, 0x00, 0x65, 0x6d, 0x70, 0x74,
+       0x79, 0x20, 0x23, 0x31, 0x37},
+
+      // 12-30 10:22:08.946 29981 30962 I am_uid_stopped: 10018
+      {0x09, 0x00, 0x1c, 0x00, 0x1d, 0x75, 0x00, 0x00, 0xf2, 0x78,
+       0x00, 0x00, 0x50, 0x9c, 0x28, 0x5c, 0x24, 0x5a, 0x66, 0x38,
+       0x02, 0x00, 0x00, 0x00, 0xe8, 0x03, 0x00, 0x00, 0x65, 0x75,
+       0x00, 0x00, 0x00, 0x22, 0x27, 0x00, 0x00},
+
+      // 12-30 10:22:08.960 29981 29998 I am_pss  :
+      // [1417,10098,com.google.android.connectivitymonitor,4831232,3723264,0,56053760,0,9,39]
+      {0x72, 0x00, 0x1c, 0x00, 0x1d, 0x75, 0x00, 0x00, 0x2e, 0x75, 0x00, 0x00,
+       0x50, 0x9c, 0x28, 0x5c, 0xf4, 0xd7, 0x44, 0x39, 0x02, 0x00, 0x00, 0x00,
+       0xe8, 0x03, 0x00, 0x00, 0x5f, 0x75, 0x00, 0x00, 0x03, 0x0a, 0x00, 0x89,
+       0x05, 0x00, 0x00, 0x00, 0x72, 0x27, 0x00, 0x00, 0x02, 0x26, 0x00, 0x00,
+       0x00, 0x63, 0x6f, 0x6d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
+       0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x63, 0x6f, 0x6e, 0x6e,
+       0x65, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x6d, 0x6f, 0x6e, 0x69,
+       0x74, 0x6f, 0x72, 0x01, 0x00, 0xb8, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x01, 0x00, 0xd0, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x50, 0x57, 0x03, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00,
+       0x00, 0x01, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
+};
+
+TEST_F(AndroidLogDataSourceTest, ParseEventLogDefinitions) {
+  CreateInstance(DataSourceConfig());
+  static const char kContents[] = R"(
+42 answer (to life the universe etc|3)
+314 pi
+1003 auditd (avc|3)
+1004 chatty (dropped|3)
+1005 tag_def (tag|1),(name|3),(format|3)
+2718 e
+2732 storaged_disk_stats (type|3),(start_time|2|3),(end_time|2|3),(read_ios|2|1),(read_merges|2|1),(read_sectors|2|1),(read_ticks|2|3),(write_ios|2|1),(write_merges|2|1),(write_sectors|2|1),(write_ticks|2|3),(o_in_flight|2|1),(io_ticks|2|3),(io_in_queue|2|1)
+invalid_line (
+9999 invalid_line2 (
+1937006964 stats_log (atom_id|1|5),(data|4)
+)";
+  EXPECT_CALL(*data_source_, ReadEventLogDefinitions())
+      .WillOnce(Return(kContents));
+  data_source_->ParseEventLogDefinitions();
+
+  auto* fmt = data_source_->GetEventFormat(42);
+  ASSERT_NE(fmt, nullptr);
+  ASSERT_EQ(fmt->name, "answer");
+  ASSERT_EQ(fmt->fields.size(), 1);
+  ASSERT_EQ(fmt->fields[0], "to life the universe etc");
+
+  fmt = data_source_->GetEventFormat(314);
+  ASSERT_NE(fmt, nullptr);
+  ASSERT_EQ(fmt->name, "pi");
+  ASSERT_EQ(fmt->fields.size(), 0);
+
+  fmt = data_source_->GetEventFormat(1005);
+  ASSERT_NE(fmt, nullptr);
+  ASSERT_EQ(fmt->name, "tag_def");
+  ASSERT_EQ(fmt->fields.size(), 3);
+  ASSERT_EQ(fmt->fields[0], "tag");
+  ASSERT_EQ(fmt->fields[1], "name");
+  ASSERT_EQ(fmt->fields[2], "format");
+
+  fmt = data_source_->GetEventFormat(1937006964);
+  ASSERT_NE(fmt, nullptr);
+  ASSERT_EQ(fmt->name, "stats_log");
+  ASSERT_EQ(fmt->fields.size(), 2);
+  ASSERT_EQ(fmt->fields[0], "atom_id");
+  ASSERT_EQ(fmt->fields[1], "data");
+}
+
+TEST_F(AndroidLogDataSourceTest, TextEvents) {
+  DataSourceConfig cfg;
+  CreateInstance(cfg);
+  EXPECT_CALL(*data_source_, ReadEventLogDefinitions()).WillOnce(Return(""));
+  StartAndSimulateLogd(kValidTextEvents);
+
+  // Read back the data that would have been written into the trace.
+
+  auto packet = writer_raw_->ParseProto();
+  ASSERT_TRUE(packet);
+  ASSERT_TRUE(packet->has_android_log());
+  EXPECT_EQ(packet->android_log().events_size(), 3);
+
+  const auto& decoded = packet->android_log().events();
+
+  EXPECT_EQ(decoded.Get(0).log_id(), protos::AndroidLogId::LID_SYSTEM);
+  EXPECT_EQ(decoded.Get(0).pid(), 7546);
+  EXPECT_EQ(decoded.Get(0).tid(), 8991);
+  EXPECT_EQ(decoded.Get(0).uid(), 1000);
+  EXPECT_EQ(decoded.Get(0).prio(), protos::AndroidLogPriority::PRIO_INFO);
+  EXPECT_EQ(decoded.Get(0).timestamp(), 1546125239679172326LL);
+  EXPECT_EQ(decoded.Get(0).tag(), "ActivityManager");
+  EXPECT_EQ(
+      decoded.Get(0).message(),
+      "Killing 11660:com.google.android.videos/u0a168 (adj 985): empty #17");
+
+  EXPECT_EQ(decoded.Get(1).log_id(), protos::AndroidLogId::LID_DEFAULT);
+  EXPECT_EQ(decoded.Get(1).pid(), 7546);
+  EXPECT_EQ(decoded.Get(1).tid(), 7570);
+  EXPECT_EQ(decoded.Get(1).uid(), 1000);
+  EXPECT_EQ(decoded.Get(1).prio(), protos::AndroidLogPriority::PRIO_WARN);
+  EXPECT_EQ(decoded.Get(1).timestamp(), 1546125239683537170LL);
+  EXPECT_EQ(decoded.Get(1).tag(), "libprocessgroup");
+  EXPECT_EQ(decoded.Get(1).message(),
+            "kill(-11660, 9) failed: No such process");
+
+  EXPECT_EQ(decoded.Get(2).log_id(), protos::AndroidLogId::LID_DEFAULT);
+  EXPECT_EQ(decoded.Get(2).pid(), 7415);
+  EXPECT_EQ(decoded.Get(2).tid(), 7415);
+  EXPECT_EQ(decoded.Get(2).uid(), 0);
+  EXPECT_EQ(decoded.Get(2).prio(), protos::AndroidLogPriority::PRIO_INFO);
+  EXPECT_EQ(decoded.Get(2).timestamp(), 1546125239719458684LL);
+  EXPECT_EQ(decoded.Get(2).tag(), "Zygote");
+  EXPECT_EQ(decoded.Get(2).message(), "Process 11660 exited due to signal (9)");
+}
+
+TEST_F(AndroidLogDataSourceTest, TextEventsWithTagFiltering) {
+  DataSourceConfig cfg;
+  *cfg.mutable_android_log_config()->add_filter_tags() = "Zygote";
+  *cfg.mutable_android_log_config()->add_filter_tags() = "ActivityManager";
+  *cfg.mutable_android_log_config()->add_filter_tags() = "Unmatched";
+  *cfg.mutable_android_log_config()->add_filter_tags() = "libprocessgroupZZ";
+
+  CreateInstance(cfg);
+  EXPECT_CALL(*data_source_, ReadEventLogDefinitions()).WillOnce(Return(""));
+  StartAndSimulateLogd(kValidTextEvents);
+
+  auto packet = writer_raw_->ParseProto();
+  ASSERT_TRUE(packet);
+  ASSERT_TRUE(packet->has_android_log());
+  EXPECT_EQ(packet->android_log().events_size(), 2);
+
+  const auto& decoded = packet->android_log().events();
+  EXPECT_EQ(decoded.Get(0).tag(), "ActivityManager");
+  EXPECT_EQ(decoded.Get(1).tag(), "Zygote");
+}
+
+TEST_F(AndroidLogDataSourceTest, TextEventsWithPrioFiltering) {
+  DataSourceConfig cfg;
+  cfg.mutable_android_log_config()->set_min_prio(
+      AndroidLogConfig::AndroidLogPriority::PRIO_WARN);
+
+  CreateInstance(cfg);
+  EXPECT_CALL(*data_source_, ReadEventLogDefinitions()).WillOnce(Return(""));
+  StartAndSimulateLogd(kValidTextEvents);
+
+  auto packet = writer_raw_->ParseProto();
+  ASSERT_TRUE(packet);
+  ASSERT_TRUE(packet->has_android_log());
+  EXPECT_EQ(packet->android_log().events_size(), 1);
+
+  const auto& decoded = packet->android_log().events();
+  EXPECT_EQ(decoded.Get(0).tag(), "libprocessgroup");
+}
+
+TEST_F(AndroidLogDataSourceTest, BinaryEvents) {
+  DataSourceConfig cfg;
+  CreateInstance(cfg);
+  static const char kDefs[] = R"(
+30023 am_kill (User|1|5),(PID|1|5),(Process Name|3),(OomAdj|1|5),(Reason|3)
+30053 am_uid_stopped (UID|1|5)
+30047 am_pss (Pid|1|5),(UID|1|5),(Process Name|3),(Pss|2|2),(Uss|2|2),(SwapPss|2|2),(Rss|2|2),(StatType|1|5),(ProcState|1|5),(TimeToCollect|2|2)
+)";
+  EXPECT_CALL(*data_source_, ReadEventLogDefinitions()).WillOnce(Return(kDefs));
+  StartAndSimulateLogd(kValidBinaryEvents);
+
+  // Read back the data that would have been written into the trace.
+
+  auto packet = writer_raw_->ParseProto();
+  ASSERT_TRUE(packet);
+  ASSERT_TRUE(packet->has_android_log());
+  EXPECT_EQ(packet->android_log().events_size(), 3);
+
+  const auto& decoded = packet->android_log().events();
+
+  EXPECT_EQ(decoded.Get(0).log_id(), protos::AndroidLogId::LID_EVENTS);
+  EXPECT_EQ(decoded.Get(0).pid(), 29981);
+  EXPECT_EQ(decoded.Get(0).tid(), 30962);
+  EXPECT_EQ(decoded.Get(0).uid(), 1000);
+  EXPECT_EQ(decoded.Get(0).timestamp(), 1546165328914257883LL);
+  EXPECT_EQ(decoded.Get(0).tag(), "am_kill");
+  ASSERT_EQ(decoded.Get(0).args_size(), 5);
+  EXPECT_EQ(decoded.Get(0).args(0).name(), "User");
+  EXPECT_EQ(decoded.Get(0).args(0).int_value(), 0);
+  EXPECT_EQ(decoded.Get(0).args(1).name(), "PID");
+  EXPECT_EQ(decoded.Get(0).args(1).int_value(), 31730);
+  EXPECT_EQ(decoded.Get(0).args(2).name(), "Process Name");
+  EXPECT_EQ(decoded.Get(0).args(2).string_value(), "android.process.acore");
+  EXPECT_EQ(decoded.Get(0).args(3).name(), "OomAdj");
+  EXPECT_EQ(decoded.Get(0).args(3).int_value(), 985);
+  EXPECT_EQ(decoded.Get(0).args(4).name(), "Reason");
+  EXPECT_EQ(decoded.Get(0).args(4).string_value(), "empty #17");
+
+  EXPECT_EQ(decoded.Get(1).log_id(), protos::AndroidLogId::LID_EVENTS);
+  EXPECT_EQ(decoded.Get(1).pid(), 29981);
+  EXPECT_EQ(decoded.Get(1).tid(), 30962);
+  EXPECT_EQ(decoded.Get(1).uid(), 1000);
+  EXPECT_EQ(decoded.Get(1).timestamp(), 1546165328946231844LL);
+  EXPECT_EQ(decoded.Get(1).tag(), "am_uid_stopped");
+  ASSERT_EQ(decoded.Get(1).args_size(), 1);
+  EXPECT_EQ(decoded.Get(1).args(0).name(), "UID");
+  EXPECT_EQ(decoded.Get(1).args(0).int_value(), 10018);
+
+  EXPECT_EQ(decoded.Get(2).log_id(), protos::AndroidLogId::LID_EVENTS);
+  EXPECT_EQ(decoded.Get(2).pid(), 29981);
+  EXPECT_EQ(decoded.Get(2).tid(), 29998);
+  EXPECT_EQ(decoded.Get(2).uid(), 1000);
+  EXPECT_EQ(decoded.Get(2).timestamp(), 1546165328960813044LL);
+  EXPECT_EQ(decoded.Get(2).tag(), "am_pss");
+  ASSERT_EQ(decoded.Get(2).args_size(), 10);
+  EXPECT_EQ(decoded.Get(2).args(0).name(), "Pid");
+  EXPECT_EQ(decoded.Get(2).args(0).int_value(), 1417);
+  EXPECT_EQ(decoded.Get(2).args(1).name(), "UID");
+  EXPECT_EQ(decoded.Get(2).args(1).int_value(), 10098);
+  EXPECT_EQ(decoded.Get(2).args(2).name(), "Process Name");
+  EXPECT_EQ(decoded.Get(2).args(2).string_value(),
+            "com.google.android.connectivitymonitor");
+  EXPECT_EQ(decoded.Get(2).args(3).name(), "Pss");
+  EXPECT_EQ(decoded.Get(2).args(3).int_value(), 4831232);
+  EXPECT_EQ(decoded.Get(2).args(4).name(), "Uss");
+  EXPECT_EQ(decoded.Get(2).args(4).int_value(), 3723264);
+  EXPECT_EQ(decoded.Get(2).args(5).name(), "SwapPss");
+  EXPECT_EQ(decoded.Get(2).args(5).int_value(), 0);
+  EXPECT_EQ(decoded.Get(2).args(6).name(), "Rss");
+  EXPECT_EQ(decoded.Get(2).args(6).int_value(), 56053760);
+  EXPECT_EQ(decoded.Get(2).args(7).name(), "StatType");
+  EXPECT_EQ(decoded.Get(2).args(7).int_value(), 0);
+  EXPECT_EQ(decoded.Get(2).args(8).name(), "ProcState");
+  EXPECT_EQ(decoded.Get(2).args(8).int_value(), 9);
+  EXPECT_EQ(decoded.Get(2).args(9).name(), "TimeToCollect");
+  EXPECT_EQ(decoded.Get(2).args(9).int_value(), 39);
+}
+
+TEST_F(AndroidLogDataSourceTest, BinaryEventsWithTagFiltering) {
+  DataSourceConfig cfg;
+  *cfg.mutable_android_log_config()->add_filter_tags() = "not mached";
+  *cfg.mutable_android_log_config()->add_filter_tags() = "am_uid_stopped";
+  CreateInstance(cfg);
+  static const char kDefs[] = R"(
+30023 am_kill (User|1|5),(PID|1|5),(Process Name|3),(OomAdj|1|5),(Reason|3)
+30053 am_uid_stopped (UID|1|5)
+30047 am_pss (Pid|1|5),(UID|1|5),(Process Name|3),(Pss|2|2),(Uss|2|2),(SwapPss|2|2),(Rss|2|2),(StatType|1|5),(ProcState|1|5),(TimeToCollect|2|2)
+)";
+  EXPECT_CALL(*data_source_, ReadEventLogDefinitions()).WillOnce(Return(kDefs));
+  StartAndSimulateLogd(kValidBinaryEvents);
+
+  // Read back the data that would have been written into the trace.
+
+  auto packet = writer_raw_->ParseProto();
+  ASSERT_TRUE(packet);
+  ASSERT_TRUE(packet->has_android_log());
+  EXPECT_EQ(packet->android_log().events_size(), 1);
+
+  const auto& decoded = packet->android_log().events();
+  EXPECT_EQ(decoded.Get(0).timestamp(), 1546165328946231844LL);
+  EXPECT_EQ(decoded.Get(0).tag(), "am_uid_stopped");
+}
+
+}  // namespace
+}  // namespace perfetto
diff --git a/src/traced/probes/probes_producer.cc b/src/traced/probes/probes_producer.cc
index 07c86d3..1832fd5 100644
--- a/src/traced/probes/probes_producer.cc
+++ b/src/traced/probes/probes_producer.cc
@@ -33,10 +33,13 @@
 #include "perfetto/tracing/core/trace_config.h"
 #include "perfetto/tracing/core/trace_packet.h"
 #include "perfetto/tracing/ipc/producer_ipc_client.h"
+#include "src/traced/probes/android_log/android_log_data_source.h"
 #include "src/traced/probes/filesystem/inode_file_data_source.h"
 #include "src/traced/probes/ftrace/ftrace_data_source.h"
 #include "src/traced/probes/power/android_power_data_source.h"
 #include "src/traced/probes/probes_data_source.h"
+#include "src/traced/probes/ps/process_stats_data_source.h"
+#include "src/traced/probes/sys_stats/sys_stats_data_source.h"
 
 #include "perfetto/trace/filesystem/inode_file_map.pbzero.h"
 #include "perfetto/trace/ftrace/ftrace_event_bundle.pbzero.h"
@@ -57,6 +60,7 @@
 constexpr char kInodeMapSourceName[] = "linux.inode_file_map";
 constexpr char kSysStatsSourceName[] = "linux.sys_stats";
 constexpr char kAndroidPowerSourceName[] = "android.power";
+constexpr char kAndroidLogSourceName[] = "android.log";
 
 }  // namespace.
 
@@ -110,6 +114,12 @@
     desc.set_name(kAndroidPowerSourceName);
     endpoint_->RegisterDataSource(desc);
   }
+
+  {
+    DataSourceDescriptor desc;
+    desc.set_name(kAndroidLogSourceName);
+    endpoint_->RegisterDataSource(desc);
+  }
 }
 
 void ProbesProducer::OnDisconnect() {
@@ -151,15 +161,17 @@
 
   std::unique_ptr<ProbesDataSource> data_source;
   if (config.name() == kFtraceSourceName) {
-    data_source = CreateFtraceDataSource(session_id, instance_id, config);
+    data_source = CreateFtraceDataSource(session_id, config);
   } else if (config.name() == kInodeMapSourceName) {
-    data_source = CreateInodeFileDataSource(session_id, instance_id, config);
+    data_source = CreateInodeFileDataSource(session_id, config);
   } else if (config.name() == kProcessStatsSourceName) {
-    data_source = CreateProcessStatsDataSource(session_id, instance_id, config);
+    data_source = CreateProcessStatsDataSource(session_id, config);
   } else if (config.name() == kSysStatsSourceName) {
-    data_source = CreateSysStatsDataSource(session_id, instance_id, config);
+    data_source = CreateSysStatsDataSource(session_id, config);
   } else if (config.name() == kAndroidPowerSourceName) {
-    data_source = CreateAndroidPowerDataSource(session_id, instance_id, config);
+    data_source = CreateAndroidPowerDataSource(session_id, config);
+  } else if (config.name() == kAndroidLogSourceName) {
+    data_source = CreateAndroidLogDataSource(session_id, config);
   }
 
   if (!data_source) {
@@ -195,7 +207,6 @@
 
 std::unique_ptr<ProbesDataSource> ProbesProducer::CreateFtraceDataSource(
     TracingSessionID session_id,
-    DataSourceInstanceID id,
     const DataSourceConfig& config) {
   // Don't retry if FtraceController::Create() failed once.
   // This can legitimately happen on user builds where we cannot access the
@@ -217,8 +228,7 @@
     ftrace_->ClearTrace();
   }
 
-  PERFETTO_LOG("Ftrace setup (id=%" PRIu64 ", target_buf=%" PRIu32 ")", id,
-               config.target_buffer());
+  PERFETTO_LOG("Ftrace setup (target_buf=%" PRIu32 ")", config.target_buffer());
   const BufferID buffer_id = static_cast<BufferID>(config.target_buffer());
   std::unique_ptr<FtraceDataSource> data_source(new FtraceDataSource(
       ftrace_->GetWeakPtr(), session_id, config.ftrace_config(),
@@ -234,10 +244,9 @@
 
 std::unique_ptr<ProbesDataSource> ProbesProducer::CreateInodeFileDataSource(
     TracingSessionID session_id,
-    DataSourceInstanceID id,
     DataSourceConfig source_config) {
-  PERFETTO_LOG("Inode file map setup (id=%" PRIu64 ", target_buf=%" PRIu32 ")",
-               id, source_config.target_buffer());
+  PERFETTO_LOG("Inode file map setup (target_buf=%" PRIu32 ")",
+               source_config.target_buffer());
   auto buffer_id = static_cast<BufferID>(source_config.target_buffer());
   if (system_inodes_.empty())
     CreateStaticDeviceToInodeMap("/system", &system_inodes_);
@@ -248,31 +257,34 @@
 
 std::unique_ptr<ProbesDataSource> ProbesProducer::CreateProcessStatsDataSource(
     TracingSessionID session_id,
-    DataSourceInstanceID id,
     const DataSourceConfig& config) {
-  base::ignore_result(id);
   auto buffer_id = static_cast<BufferID>(config.target_buffer());
   return std::unique_ptr<ProcessStatsDataSource>(new ProcessStatsDataSource(
       task_runner_, session_id, endpoint_->CreateTraceWriter(buffer_id),
       config));
 }
 
-std::unique_ptr<AndroidPowerDataSource>
-ProbesProducer::CreateAndroidPowerDataSource(TracingSessionID session_id,
-                                             DataSourceInstanceID id,
-                                             const DataSourceConfig& config) {
-  base::ignore_result(id);
+std::unique_ptr<ProbesDataSource> ProbesProducer::CreateAndroidPowerDataSource(
+    TracingSessionID session_id,
+    const DataSourceConfig& config) {
   auto buffer_id = static_cast<BufferID>(config.target_buffer());
-  return std::unique_ptr<AndroidPowerDataSource>(
+  return std::unique_ptr<ProbesDataSource>(
       new AndroidPowerDataSource(config, task_runner_, session_id,
                                  endpoint_->CreateTraceWriter(buffer_id)));
 }
 
-std::unique_ptr<SysStatsDataSource> ProbesProducer::CreateSysStatsDataSource(
+std::unique_ptr<ProbesDataSource> ProbesProducer::CreateAndroidLogDataSource(
     TracingSessionID session_id,
-    DataSourceInstanceID id,
     const DataSourceConfig& config) {
-  base::ignore_result(id);
+  auto buffer_id = static_cast<BufferID>(config.target_buffer());
+  return std::unique_ptr<ProbesDataSource>(
+      new AndroidLogDataSource(config, task_runner_, session_id,
+                               endpoint_->CreateTraceWriter(buffer_id)));
+}
+
+std::unique_ptr<ProbesDataSource> ProbesProducer::CreateSysStatsDataSource(
+    TracingSessionID session_id,
+    const DataSourceConfig& config) {
   auto buffer_id = static_cast<BufferID>(config.target_buffer());
   return std::unique_ptr<SysStatsDataSource>(
       new SysStatsDataSource(task_runner_, session_id,
@@ -419,6 +431,7 @@
         break;
       }
       case SysStatsDataSource::kTypeId:
+      case AndroidLogDataSource::kTypeId:
         break;
       default:
         PERFETTO_DFATAL("Invalid data source.");
diff --git a/src/traced/probes/probes_producer.h b/src/traced/probes/probes_producer.h
index f40447f..bbe6b41 100644
--- a/src/traced/probes/probes_producer.h
+++ b/src/traced/probes/probes_producer.h
@@ -30,9 +30,6 @@
 #include "src/traced/probes/filesystem/inode_file_data_source.h"
 #include "src/traced/probes/ftrace/ftrace_controller.h"
 #include "src/traced/probes/ftrace/ftrace_metadata.h"
-#include "src/traced/probes/power/android_power_data_source.h"
-#include "src/traced/probes/ps/process_stats_data_source.h"
-#include "src/traced/probes/sys_stats/sys_stats_data_source.h"
 
 #include "perfetto/trace/filesystem/inode_file_map.pbzero.h"
 
@@ -66,23 +63,21 @@
                           base::TaskRunner* task_runner);
   std::unique_ptr<ProbesDataSource> CreateFtraceDataSource(
       TracingSessionID session_id,
-      DataSourceInstanceID id,
       const DataSourceConfig& config);
   std::unique_ptr<ProbesDataSource> CreateProcessStatsDataSource(
       TracingSessionID session_id,
-      DataSourceInstanceID id,
       const DataSourceConfig& config);
   std::unique_ptr<ProbesDataSource> CreateInodeFileDataSource(
       TracingSessionID session_id,
-      DataSourceInstanceID id,
       DataSourceConfig config);
-  std::unique_ptr<SysStatsDataSource> CreateSysStatsDataSource(
+  std::unique_ptr<ProbesDataSource> CreateSysStatsDataSource(
       TracingSessionID session_id,
-      DataSourceInstanceID id,
       const DataSourceConfig& config);
-  std::unique_ptr<AndroidPowerDataSource> CreateAndroidPowerDataSource(
+  std::unique_ptr<ProbesDataSource> CreateAndroidPowerDataSource(
       TracingSessionID session_id,
-      DataSourceInstanceID id,
+      const DataSourceConfig& config);
+  std::unique_ptr<ProbesDataSource> CreateAndroidLogDataSource(
+      TracingSessionID session_id,
       const DataSourceConfig& config);
 
  private: