Merge "traced_perf: basic producer scaffolding with logging of samples on Flush."
diff --git a/BUILD.gn b/BUILD.gn
index 8f2df22..6e2753d 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -51,6 +51,10 @@
   }
 }
 
+if (enable_perfetto_traced_perf) {
+  all_targets += [ "src/profiling/perf:traced_perf" ]
+}
+
 if (perfetto_build_with_android) {
   all_targets += [ "src/android_internal/:libperfetto_android_internal" ]
 }
diff --git a/gn/perfetto.gni b/gn/perfetto.gni
index 4dbcd42..42f0cc5 100644
--- a/gn/perfetto.gni
+++ b/gn/perfetto.gni
@@ -147,6 +147,11 @@
       (perfetto_build_standalone && is_clang &&
        (is_linux || (is_android && android_api_level >= 26)))
 
+  # Build the perf event profiler (traced_perf).
+  # TODO(b/144281346): under development.
+  enable_perfetto_traced_perf =
+      perfetto_build_standalone && (is_linux || is_android)
+
   # The Trace Processor: offline analytical engine to process traces and compute
   # metrics using a SQL engine.
   enable_perfetto_trace_processor =
diff --git a/gn/perfetto_unittests.gni b/gn/perfetto_unittests.gni
index b8e033f..167bb94 100644
--- a/gn/perfetto_unittests.gni
+++ b/gn/perfetto_unittests.gni
@@ -56,6 +56,10 @@
   ]
 }
 
+if (enable_perfetto_traced_perf) {
+  perfetto_unittests_targets += [ "src/profiling/perf:producer_unittests" ]
+}
+
 if (enable_perfetto_trace_processor) {
   perfetto_unittests_targets += [ "src/trace_processor:unittests" ]
 
diff --git a/src/profiling/perf/BUILD.gn b/src/profiling/perf/BUILD.gn
new file mode 100644
index 0000000..e24d619
--- /dev/null
+++ b/src/profiling/perf/BUILD.gn
@@ -0,0 +1,79 @@
+# Copyright (C) 2019 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.
+
+import("../../../gn/fuzzer.gni")
+import("../../../gn/perfetto.gni")
+import("../../../gn/test.gni")
+
+assert(enable_perfetto_traced_perf)
+
+executable("traced_perf") {
+  deps = [
+    ":traced_perf_main",
+    "../../../gn:default_deps",
+  ]
+  sources = [
+    "main.cc",
+  ]
+}
+
+source_set("traced_perf_main") {
+  deps = [
+    ":producer",
+    "../../../gn:default_deps",
+    "../../../src/base",
+    "../../../src/tracing:ipc",
+  ]
+  sources = [
+    "traced_perf.cc",
+    "traced_perf.h",
+  ]
+}
+
+source_set("producer") {
+  deps = [
+    "../../../gn:default_deps",
+    "../../../protos/perfetto/config:cpp",
+    "../../../protos/perfetto/config/profiling:zero",
+    "../../../protos/perfetto/trace:zero",
+    "../../../src/base",
+    "../../../src/base:unix_socket",
+    "../../../src/tracing:ipc",
+  ]
+  sources = [
+    "event_config.h",
+    "event_reader.cc",
+    "event_reader.h",
+    "perf_producer.cc",
+    "perf_producer.h",
+  ]
+}
+
+source_set("producer_unittests") {
+  testonly = true
+  deps = [
+    ":producer",
+    "../../../gn:default_deps",
+    "../../../gn:gtest_and_gmock",
+    "../../../protos/perfetto/config:cpp",
+    "../../../protos/perfetto/config:zero",
+    "../../../protos/perfetto/config/profiling:zero",
+    "../../../protos/perfetto/trace:zero",
+    "../../../src/protozero",
+    "../../base",
+  ]
+  sources = [
+    "event_config_unittest.cc",
+  ]
+}
diff --git a/src/profiling/perf/event_config.h b/src/profiling/perf/event_config.h
new file mode 100644
index 0000000..3b17ef5
--- /dev/null
+++ b/src/profiling/perf/event_config.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2019 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_PROFILING_PERF_EVENT_CONFIG_H_
+#define SRC_PROFILING_PERF_EVENT_CONFIG_H_
+
+#include <linux/perf_event.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include "perfetto/ext/base/optional.h"
+#include "protos/perfetto/config/data_source_config.gen.h"
+#include "protos/perfetto/config/profiling/perf_event_config.pbzero.h"
+
+namespace perfetto {
+namespace profiling {
+
+// Describes a single profiling configuration. Bridges the gap between the data
+// source config proto, and the raw "perf_event_attr" structs to pass to the
+// perf_event_open syscall.
+// TODO(rsavitski): make sampling conditional? Or should we always go through
+// the sampling interface for simplicity? Reads can be done on-demand even if
+// sampling is on. So the question becomes whether we need *only* on-demand
+// reads.
+class EventConfig {
+ public:
+  static base::Optional<EventConfig> Create(const DataSourceConfig& ds_config) {
+    protos::pbzero::PerfEventConfig::Decoder pb_config(
+        ds_config.perf_event_config_raw());
+
+    if (!pb_config.has_tid())
+      return base::nullopt;
+
+    return EventConfig(pb_config);
+  }
+
+  int32_t target_tid() const { return target_tid_; }
+
+  perf_event_attr* perf_attr() const {
+    return const_cast<perf_event_attr*>(&perf_event_attr_);
+  }
+
+ private:
+  EventConfig(const protos::pbzero::PerfEventConfig::Decoder& pb_config)
+      : target_tid_(pb_config.tid()) {
+    auto& pe = perf_event_attr_;
+    memset(&pe, 0, sizeof(perf_event_attr));
+    pe.size = sizeof(perf_event_attr);
+
+    pe.exclude_kernel = true;
+    pe.disabled = false;
+
+    // Ask the kernel to tune sampling period to get ~100 Hz.
+    pe.type = PERF_TYPE_SOFTWARE;
+    pe.config = PERF_COUNT_SW_CPU_CLOCK;
+    pe.sample_freq = 100;
+    pe.freq = true;
+
+    pe.sample_type = PERF_SAMPLE_TID | PERF_SAMPLE_STACK_USER;
+    // Needs to be < ((u16)(~0u)), and have bottom 8 bits clear.
+    pe.sample_stack_user = (1u << 15);
+
+    // Note: can't use inherit with task-scoped event mmap
+    pe.inherit = false;
+  }
+
+  // TODO(rsavitski): this will have to represent entire event groups, thus this
+  // class will represent N events. So we'll need N cpus/tids, but likely still
+  // a single perf_event_attr.
+  int32_t target_tid_ = 0;
+  perf_event_attr perf_event_attr_;
+};
+
+}  // namespace profiling
+}  // namespace perfetto
+
+#endif  // SRC_PROFILING_PERF_EVENT_CONFIG_H_
diff --git a/src/profiling/perf/event_config_unittest.cc b/src/profiling/perf/event_config_unittest.cc
new file mode 100644
index 0000000..1e32374
--- /dev/null
+++ b/src/profiling/perf/event_config_unittest.cc
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2019 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/profiling/perf/event_config.h"
+
+#include <linux/perf_event.h>
+#include <stdint.h>
+
+#include "perfetto/base/logging.h"
+#include "perfetto/ext/base/optional.h"
+#include "perfetto/protozero/scattered_heap_buffer.h"
+#include "protos/perfetto/config/data_source_config.gen.h"
+#include "protos/perfetto/config/data_source_config.pbzero.h"
+#include "protos/perfetto/config/profiling/perf_event_config.pbzero.h"
+#include "test/gtest_and_gmock.h"
+
+namespace perfetto {
+namespace profiling {
+namespace {
+
+static DataSourceConfig ConfigForTid(int32_t tid) {
+  protozero::HeapBuffered<protos::pbzero::PerfEventConfig> pb_config;
+  pb_config->set_tid(tid);
+  protozero::HeapBuffered<protos::pbzero::DataSourceConfig> ds_config;
+  ds_config->set_perf_event_config_raw(pb_config.SerializeAsString());
+  DataSourceConfig cfg;
+  PERFETTO_CHECK(cfg.ParseRawProto(ds_config.SerializeAsString()));
+  return cfg;
+}
+
+TEST(EventConfigTest, TidRequired) {
+  // Doesn't pass validation without a TID
+  DataSourceConfig cfg;
+  ASSERT_TRUE(cfg.ParseRawProto(std::string{}));
+
+  base::Optional<EventConfig> event_config = EventConfig::Create(cfg);
+  ASSERT_FALSE(event_config.has_value());
+}
+
+TEST(EventConfigTest, AttrStructConstructed) {
+  auto cfg = ConfigForTid(42);
+  base::Optional<EventConfig> event_config = EventConfig::Create(cfg);
+
+  ASSERT_TRUE(event_config.has_value());
+  ASSERT_TRUE(event_config->perf_attr() != nullptr);
+}
+
+}  // namespace
+}  // namespace profiling
+}  // namespace perfetto
diff --git a/src/profiling/perf/event_reader.cc b/src/profiling/perf/event_reader.cc
new file mode 100644
index 0000000..7bc5ccf
--- /dev/null
+++ b/src/profiling/perf/event_reader.cc
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 2019 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/profiling/perf/event_reader.h"
+
+#include <linux/perf_event.h>
+#include <sys/mman.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "perfetto/ext/base/utils.h"
+
+namespace perfetto {
+namespace profiling {
+
+namespace {
+
+template <typename T>
+const char* ReadValue(T* value_out, const char* ptr) {
+  memcpy(value_out, reinterpret_cast<const void*>(ptr), sizeof(T));
+  return ptr + sizeof(T);
+}
+
+bool IsPowerOfTwo(size_t v) {
+  return (v != 0 && ((v & (v - 1)) == 0));
+}
+
+static int perf_event_open(perf_event_attr* attr,
+                           pid_t pid,
+                           int cpu,
+                           int group_fd,
+                           unsigned long flags) {
+  return static_cast<int>(
+      syscall(__NR_perf_event_open, attr, pid, cpu, group_fd, flags));
+}
+
+base::ScopedFile PerfEventOpen(const EventConfig& event_cfg) {
+  base::ScopedFile perf_fd{
+      perf_event_open(event_cfg.perf_attr(), event_cfg.target_tid(),
+                      /*cpu=*/-1, /*group_fd=*/-1, PERF_FLAG_FD_CLOEXEC)};
+  return perf_fd;
+}
+
+}  // namespace
+
+PerfRingBuffer::PerfRingBuffer(PerfRingBuffer&& other) noexcept
+    : metadata_page_(other.metadata_page_),
+      mmap_sz_(other.mmap_sz_),
+      data_buf_(other.data_buf_),
+      data_buf_sz_(other.data_buf_sz_) {
+  other.metadata_page_ = nullptr;
+  other.mmap_sz_ = 0;
+  other.data_buf_ = nullptr;
+  other.data_buf_sz_ = 0;
+}
+
+PerfRingBuffer& PerfRingBuffer::operator=(PerfRingBuffer&& other) noexcept {
+  if (this == &other)
+    return *this;
+
+  this->~PerfRingBuffer();
+  new (this) PerfRingBuffer(std::move(other));
+  return *this;
+}
+
+PerfRingBuffer::~PerfRingBuffer() {
+  if (!valid())
+    return;
+
+  if (munmap(reinterpret_cast<void*>(metadata_page_), mmap_sz_) != 0)
+    PERFETTO_PLOG("failed munmap");
+}
+
+base::Optional<PerfRingBuffer> PerfRingBuffer::Allocate(
+    int perf_fd,
+    size_t data_page_count) {
+  // perf_event_open requires the ring buffer to be a power of two in size.
+  PERFETTO_CHECK(IsPowerOfTwo(data_page_count));
+
+  PerfRingBuffer ret;
+
+  // mmap request is one page larger than the buffer size (for the metadata).
+  ret.data_buf_sz_ = data_page_count * base::kPageSize;
+  ret.mmap_sz_ = ret.data_buf_sz_ + base::kPageSize;
+
+  // If PROT_WRITE, kernel won't overwrite unread samples.
+  void* mmap_addr = mmap(nullptr, ret.mmap_sz_, PROT_READ | PROT_WRITE,
+                         MAP_SHARED, perf_fd, 0);
+  if (mmap_addr == MAP_FAILED) {
+    PERFETTO_PLOG("failed mmap (check perf_event_mlock_kb in procfs)");
+    return base::nullopt;
+  }
+
+  // Expected layout is [ metadata page ] [ data pages ... ]
+  ret.metadata_page_ = reinterpret_cast<perf_event_mmap_page*>(mmap_addr);
+  ret.data_buf_ = reinterpret_cast<char*>(mmap_addr) + base::kPageSize;
+  PERFETTO_CHECK(ret.metadata_page_->data_offset == base::kPageSize);
+  PERFETTO_CHECK(ret.metadata_page_->data_size = ret.data_buf_sz_);
+
+  return base::make_optional(std::move(ret));
+}
+
+// TODO(rsavitski): look into more specific barrier builtins. Copying simpleperf
+// for now. See |perf_output_put_handle| in the kernel for the barrier
+// requirements.
+#pragma GCC diagnostic push
+#if defined(__clang__)
+#pragma GCC diagnostic ignored "-Watomic-implicit-seq-cst"
+#endif
+std::vector<char> PerfRingBuffer::ReadAvailable() {
+  if (!valid())
+    return {};
+
+  uint64_t write_offset = metadata_page_->data_head;
+  uint64_t read_offset = metadata_page_->data_tail;
+  __sync_synchronize();  // needs to be rmb()
+
+  size_t read_pos = static_cast<size_t>(read_offset & (data_buf_sz_ - 1));
+  size_t data_sz = static_cast<size_t>(write_offset - read_offset);
+
+  if (data_sz == 0) {
+    return {};
+  }
+
+  // memcpy accounting for wrapping
+  std::vector<char> data(data_sz);
+  size_t copy_sz = std::min(data_sz, data_buf_sz_ - read_pos);
+  memcpy(data.data(), data_buf_ + read_pos, copy_sz);
+  if (copy_sz < data_sz) {
+    memcpy(data.data() + copy_sz, data_buf_, data_sz - copy_sz);
+  }
+
+  // consume the data
+  __sync_synchronize();  // needs to be mb()
+  metadata_page_->data_tail += data_sz;
+
+  PERFETTO_LOG("WIP: consumed [%zu] bytes from ring buffer", data_sz);
+  return data;
+}
+#pragma GCC diagnostic pop
+
+EventReader::EventReader(const EventConfig& event_cfg,
+                         base::ScopedFile perf_fd,
+                         PerfRingBuffer ring_buffer)
+    : event_cfg_(event_cfg),
+      perf_fd_(std::move(perf_fd)),
+      ring_buffer_(std::move(ring_buffer)) {}
+
+EventReader::EventReader(EventReader&& other) noexcept
+    : event_cfg_(other.event_cfg_),
+      perf_fd_(std::move(other.perf_fd_)),
+      ring_buffer_(std::move(other.ring_buffer_)) {}
+
+EventReader& EventReader::operator=(EventReader&& other) noexcept {
+  if (this == &other)
+    return *this;
+
+  this->~EventReader();
+  new (this) EventReader(std::move(other));
+  return *this;
+}
+
+base::Optional<EventReader> EventReader::ConfigureEvents(
+    const EventConfig& event_cfg) {
+  auto perf_fd = PerfEventOpen(event_cfg);
+  if (!perf_fd) {
+    PERFETTO_PLOG("failed perf_event_open");
+    return base::nullopt;
+  }
+
+  auto ring_buffer =
+      PerfRingBuffer::Allocate(perf_fd.get(), /*data_page_count=*/128);
+  if (!ring_buffer.has_value()) {
+    return base::nullopt;
+  }
+
+  return base::make_optional<EventReader>(event_cfg, std::move(perf_fd),
+                                          std::move(ring_buffer.value()));
+}
+
+void EventReader::ParseNextSampleBatch() {
+  std::vector<char> data = ring_buffer_.ReadAvailable();
+  if (data.size() == 0) {
+    PERFETTO_LOG("WIP: no samples");
+    return;
+  }
+
+  for (const char* ptr = data.data(); ptr < data.data() + data.size();) {
+    if (!ParseSampleAndAdvance(&ptr))
+      break;
+  }
+}
+
+bool EventReader::ParseSampleAndAdvance(const char** ptr) {
+  const char* sample_start = *ptr;
+  auto* event_hdr = reinterpret_cast<const perf_event_header*>(sample_start);
+
+  PERFETTO_LOG("WIP: event_header[%zu][%zu][%zu]",
+               static_cast<size_t>(event_hdr->type),
+               static_cast<size_t>(event_hdr->misc),
+               static_cast<size_t>(event_hdr->size));
+
+  if (event_hdr->type == PERF_RECORD_SAMPLE) {
+    ParsePerfRecordSample(sample_start, event_hdr->size);
+  } else {
+    PERFETTO_ELOG("WIP: unsupported event type");
+  }
+
+  *ptr = sample_start + event_hdr->size;
+  return true;
+}
+
+// TODO(rsavitski): actually handle the samples instead of logging.
+void EventReader::ParsePerfRecordSample(const char* sample_start,
+                                        size_t sample_size) {
+  const perf_event_attr* cfg = event_cfg_.perf_attr();
+
+  if (cfg->sample_type &
+      (~uint64_t(PERF_SAMPLE_TID | PERF_SAMPLE_STACK_USER))) {
+    PERFETTO_ELOG("WIP: unsupported sampling option.");
+    return;
+  }
+
+  // Parse the payload, which consists of concatenated data for each
+  // |attr.sample_type| flag.
+  const char* parse_pos = sample_start + sizeof(perf_event_header);
+
+  if (cfg->sample_type & PERF_SAMPLE_TID) {
+    uint32_t pid;
+    parse_pos = ReadValue(&pid, parse_pos);
+    PERFETTO_LOG("pid: %" PRIu32 "", pid);
+
+    uint32_t tid;
+    parse_pos = ReadValue(&tid, parse_pos);
+    PERFETTO_LOG("tid: %" PRIu32 "", tid);
+  }
+
+  if (cfg->sample_type & PERF_SAMPLE_STACK_USER) {
+    uint64_t max_stack_size;  // the requested size
+    parse_pos = ReadValue(&max_stack_size, parse_pos);
+    PERFETTO_LOG("max_stack_size: %" PRIu64 "", max_stack_size);
+
+    parse_pos += max_stack_size;  // skip raw data
+
+    // not written if requested stack sampling size is zero
+    if (max_stack_size > 0) {
+      uint64_t filled_stack_size;
+      parse_pos = ReadValue(&filled_stack_size, parse_pos);
+      PERFETTO_LOG("filled_stack_size: %" PRIu64 "", filled_stack_size);
+    }
+  }
+
+  PERFETTO_CHECK(parse_pos == sample_start + sample_size);
+}
+
+}  // namespace profiling
+}  // namespace perfetto
diff --git a/src/profiling/perf/event_reader.h b/src/profiling/perf/event_reader.h
new file mode 100644
index 0000000..537f489
--- /dev/null
+++ b/src/profiling/perf/event_reader.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2019 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_PROFILING_PERF_EVENT_READER_H_
+#define SRC_PROFILING_PERF_EVENT_READER_H_
+
+#include <linux/perf_event.h>
+#include <stdint.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+
+#include "perfetto/ext/base/optional.h"
+#include "perfetto/ext/base/scoped_file.h"
+#include "src/profiling/perf/event_config.h"
+
+namespace perfetto {
+namespace profiling {
+
+// TODO(rsavitski): currently written for the non-overwriting ring buffer mode
+// (PROT_WRITE). Decide on whether there are use-cases for supporting the other.
+// TODO(rsavitski): given perf_event_mlock_kb limit, can we afford a ring buffer
+// per data source, or will we be forced to multiplex everything onto a single
+// ring buffer in the worst case? Alternatively, obtain CAP_IPC_LOCK (and do own
+// limiting)? Or get an adjusted RLIMIT_MEMLOCK?
+// TODO(rsavitski): polling for now, look into supporting the notification
+// mechanisms (such as epoll) later.
+class PerfRingBuffer {
+ public:
+  static base::Optional<PerfRingBuffer> Allocate(int perf_fd,
+                                                 size_t data_page_count);
+
+  ~PerfRingBuffer();
+
+  // move-only
+  PerfRingBuffer(const PerfRingBuffer&) = delete;
+  PerfRingBuffer& operator=(const PerfRingBuffer&) = delete;
+  PerfRingBuffer(PerfRingBuffer&& other) noexcept;
+  PerfRingBuffer& operator=(PerfRingBuffer&& other) noexcept;
+
+  std::vector<char> ReadAvailable();
+
+ private:
+  PerfRingBuffer() = default;
+
+  bool valid() const { return metadata_page_ != nullptr; }
+
+  // TODO(rsavitski): volatile?
+  // Is exactly the start of the mmap'd region.
+  perf_event_mmap_page* metadata_page_ = nullptr;
+
+  // size of the mmap'd region (1 metadata page + data_buf_sz_)
+  size_t mmap_sz_ = 0;
+
+  // mmap'd ring buffer
+  char* data_buf_ = nullptr;
+  size_t data_buf_sz_ = 0;
+};
+
+class EventReader {
+ public:
+  // Allow base::Optional<EventReader> without making the constructor public.
+  template <typename EventReader, bool>
+  friend struct base::internal::OptionalStorageBase;
+
+  static base::Optional<EventReader> ConfigureEvents(
+      const EventConfig& event_cfg);
+
+  ~EventReader() = default;
+
+  // move-only
+  EventReader(const EventReader&) = delete;
+  EventReader& operator=(const EventReader) = delete;
+  EventReader(EventReader&&) noexcept;
+  EventReader& operator=(EventReader&&) noexcept;
+
+  // TODO(rsavitski): temporary.
+  void ParseNextSampleBatch();
+
+ private:
+  EventReader(const EventConfig& event_cfg,
+              base::ScopedFile perf_fd,
+              PerfRingBuffer ring_buffer);
+
+  bool ParseSampleAndAdvance(const char** ptr);
+  void ParsePerfRecordSample(const char* sample_payload, size_t sample_size);
+
+  const EventConfig event_cfg_;
+  base::ScopedFile perf_fd_;
+  PerfRingBuffer ring_buffer_;
+};
+
+}  // namespace profiling
+}  // namespace perfetto
+
+#endif  // SRC_PROFILING_PERF_EVENT_READER_H_
diff --git a/src/profiling/perf/main.cc b/src/profiling/perf/main.cc
new file mode 100644
index 0000000..7ca3729
--- /dev/null
+++ b/src/profiling/perf/main.cc
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2019 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/profiling/perf/traced_perf.h"
+
+int main(int argc, char** argv) {
+  return perfetto::TracedPerfMain(argc, argv);
+}
diff --git a/src/profiling/perf/perf_producer.cc b/src/profiling/perf/perf_producer.cc
new file mode 100644
index 0000000..dd5d642
--- /dev/null
+++ b/src/profiling/perf/perf_producer.cc
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2019 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/profiling/perf/perf_producer.h"
+
+#include <unistd.h>
+#include <utility>
+
+#include "perfetto/base/logging.h"
+#include "perfetto/base/task_runner.h"
+#include "perfetto/ext/base/weak_ptr.h"
+#include "perfetto/ext/tracing/core/basic_types.h"
+#include "perfetto/ext/tracing/core/producer.h"
+#include "perfetto/ext/tracing/core/tracing_service.h"
+#include "perfetto/ext/tracing/ipc/producer_ipc_client.h"
+#include "perfetto/tracing/core/data_source_config.h"
+#include "perfetto/tracing/core/data_source_descriptor.h"
+#include "protos/perfetto/config/profiling/perf_event_config.pbzero.h"
+#include "src/profiling/perf/event_reader.h"
+
+namespace perfetto {
+namespace profiling {
+namespace {
+
+constexpr uint32_t kInitialConnectionBackoffMs = 100;
+constexpr uint32_t kMaxConnectionBackoffMs = 30 * 1000;
+
+constexpr char kProducerName[] = "perfetto.traced_perf";
+constexpr char kDataSourceName[] = "linux.perf";
+
+}  // namespace
+
+PerfProducer::PerfProducer(base::TaskRunner* task_runner)
+    : task_runner_(task_runner), weak_factory_(this) {}
+
+// TODO(rsavitski): configure at setup + enable at start, or do everything on
+// start? Also, do we try to work around the old(?) cpu hotplug bugs as
+// simpleperf does?
+void PerfProducer::SetupDataSource(DataSourceInstanceID,
+                                   const DataSourceConfig&) {}
+
+void PerfProducer::StartDataSource(DataSourceInstanceID instance_id,
+                                   const DataSourceConfig& config) {
+  PERFETTO_LOG("StartDataSource(id=%" PRIu64 ", name=%s)", instance_id,
+               config.name().c_str());
+
+  if (config.name() != kDataSourceName)
+    return;
+
+  base::Optional<EventConfig> event_config = EventConfig::Create(config);
+  if (!event_config.has_value()) {
+    PERFETTO_LOG("PerfEventConfig rejected.");
+    return;
+  }
+
+  std::string maps_path = std::string("/proc/") +
+                          std::to_string(event_config->target_tid()) +
+                          std::string("/maps");
+  auto maps_fd = base::OpenFile(maps_path, O_RDONLY);
+  if (!maps_fd)
+    PERFETTO_PLOG("failed /proc/pid/maps open (proceeding)");
+
+  std::string mem_path = std::string("/proc/") +
+                         std::to_string(event_config->target_tid()) +
+                         std::string("/mem");
+  auto mem_fd = base::OpenFile(mem_path, O_RDONLY);
+  if (!mem_fd)
+    PERFETTO_PLOG("failed /proc/pid/mem open (proceeding)");
+
+  base::Optional<EventReader> event_reader =
+      EventReader::ConfigureEvents(event_config.value());
+  if (!event_reader.has_value()) {
+    PERFETTO_LOG("Failed to set up perf events.");
+    return;
+  }
+
+  // Build the DataSource instance.
+  auto it_inserted = data_sources_.emplace(
+      std::piecewise_construct, std::forward_as_tuple(instance_id),
+      std::forward_as_tuple(std::move(event_reader.value()), std::move(maps_fd),
+                            std::move(mem_fd)));
+
+  PERFETTO_DCHECK(it_inserted.second);
+}
+
+void PerfProducer::StopDataSource(DataSourceInstanceID instance_id) {
+  PERFETTO_LOG("StopDataSource(id=%" PRIu64 ")", instance_id);
+
+  data_sources_.erase(instance_id);
+}
+
+void PerfProducer::Flush(FlushRequestID,
+                         const DataSourceInstanceID* data_source_ids,
+                         size_t num_data_sources) {
+  for (size_t i = 0; i < num_data_sources; i++) {
+    PERFETTO_LOG("Flush(id=%" PRIu64 ")", data_source_ids[i]);
+
+    auto ds_it = data_sources_.find(data_source_ids[i]);
+    if (ds_it != data_sources_.end()) {
+      auto& ds = ds_it->second;
+
+      // For now, parse whatever's been accumulated in the ring buffer.
+      ds.event_reader.ParseNextSampleBatch();
+    }
+  }
+}
+
+void PerfProducer::ConnectWithRetries(const char* socket_name) {
+  PERFETTO_DCHECK(state_ == kNotStarted);
+  state_ = kNotConnected;
+
+  ResetConnectionBackoff();
+  producer_socket_name_ = socket_name;
+  ConnectService();
+}
+
+void PerfProducer::ConnectService() {
+  PERFETTO_DCHECK(state_ == kNotConnected);
+  state_ = kConnecting;
+  endpoint_ = ProducerIPCClient::Connect(producer_socket_name_, this,
+                                         kProducerName, task_runner_);
+}
+
+void PerfProducer::IncreaseConnectionBackoff() {
+  connection_backoff_ms_ *= 2;
+  if (connection_backoff_ms_ > kMaxConnectionBackoffMs)
+    connection_backoff_ms_ = kMaxConnectionBackoffMs;
+}
+
+void PerfProducer::ResetConnectionBackoff() {
+  connection_backoff_ms_ = kInitialConnectionBackoffMs;
+}
+
+void PerfProducer::OnConnect() {
+  PERFETTO_DCHECK(state_ == kConnecting);
+  state_ = kConnected;
+  ResetConnectionBackoff();
+  PERFETTO_LOG("Connected to the service");
+
+  DataSourceDescriptor desc;
+  desc.set_name(kDataSourceName);
+  endpoint_->RegisterDataSource(desc);
+}
+
+void PerfProducer::OnDisconnect() {
+  PERFETTO_DCHECK(state_ == kConnected || state_ == kConnecting);
+  PERFETTO_LOG("Disconnected from tracing service");
+
+  auto weak_producer = weak_factory_.GetWeakPtr();
+  if (state_ == kConnected)
+    return task_runner_->PostTask([weak_producer] {
+      if (weak_producer)
+        weak_producer->Restart();
+    });
+
+  state_ = kNotConnected;
+  IncreaseConnectionBackoff();
+  task_runner_->PostDelayedTask(
+      [weak_producer] {
+        if (weak_producer)
+          weak_producer->ConnectService();
+      },
+      connection_backoff_ms_);
+}
+
+void PerfProducer::Restart() {
+  // We lost the connection with the tracing service. At this point we need
+  // to reset all the data sources. Trying to handle that manually is going to
+  // be error prone. What we do here is simply destroy the instance and
+  // recreate it again.
+  base::TaskRunner* task_runner = task_runner_;
+  const char* socket_name = producer_socket_name_;
+
+  // Invoke destructor and then the constructor again.
+  this->~PerfProducer();
+  new (this) PerfProducer(task_runner);
+
+  ConnectWithRetries(socket_name);
+}
+
+}  // namespace profiling
+}  // namespace perfetto
diff --git a/src/profiling/perf/perf_producer.h b/src/profiling/perf/perf_producer.h
new file mode 100644
index 0000000..d028c75
--- /dev/null
+++ b/src/profiling/perf/perf_producer.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2019 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_PROFILING_PERF_PERF_PRODUCER_H_
+#define SRC_PROFILING_PERF_PERF_PRODUCER_H_
+
+#include <map>
+
+#include "perfetto/base/task_runner.h"
+#include "perfetto/ext/base/scoped_file.h"
+#include "perfetto/ext/base/weak_ptr.h"
+#include "perfetto/ext/tracing/core/basic_types.h"
+#include "perfetto/ext/tracing/core/producer.h"
+#include "perfetto/ext/tracing/core/tracing_service.h"
+#include "src/profiling/perf/event_config.h"
+#include "src/profiling/perf/event_reader.h"
+
+namespace perfetto {
+namespace profiling {
+
+// TODO(b/144281346): work in progress. Do not use.
+class PerfProducer : public Producer {
+ public:
+  PerfProducer(base::TaskRunner* task_runner);
+  ~PerfProducer() override = default;
+
+  PerfProducer(const PerfProducer&) = delete;
+  PerfProducer& operator=(const PerfProducer&) = delete;
+  PerfProducer(PerfProducer&&) = delete;
+  PerfProducer& operator=(PerfProducer&&) = delete;
+
+  void ConnectWithRetries(const char* socket_name);
+
+  // Producer Impl:
+  void OnConnect() override;
+  void OnDisconnect() override;
+  void OnTracingSetup() override {}
+  void SetupDataSource(DataSourceInstanceID, const DataSourceConfig&) override;
+  void StartDataSource(DataSourceInstanceID, const DataSourceConfig&) override;
+  void StopDataSource(DataSourceInstanceID) override;
+  void Flush(FlushRequestID,
+             const DataSourceInstanceID* data_source_ids,
+             size_t num_data_sources) override;
+  void ClearIncrementalState(const DataSourceInstanceID* /*data_source_ids*/,
+                             size_t /*num_data_sources*/) override {}
+
+ private:
+  // State of the connection to tracing service (traced).
+  enum State {
+    kNotStarted = 0,
+    kNotConnected,
+    kConnecting,
+    kConnected,
+  };
+
+  // TODO(rsavitski): proc-fds need to live elsewhere, as they can be shared
+  // across data sources. We might also have arbitrarily many tasks handled
+  // by one data source (if scoping events to a cpu).
+  struct DataSource {
+    DataSource(EventReader _event_reader,
+               base::ScopedFile _maps_fd,
+               base::ScopedFile _mem_fd)
+        : event_reader(std::move(_event_reader)),
+          maps_fd(std::move(_maps_fd)),
+          mem_fd(std::move(_mem_fd)) {}
+
+    EventReader event_reader;
+
+    // note: currently populated, but unused.
+    base::ScopedFile maps_fd;
+    base::ScopedFile mem_fd;
+  };
+
+  void ConnectService();
+  void Restart();
+  void ResetConnectionBackoff();
+  void IncreaseConnectionBackoff();
+
+  base::TaskRunner* const task_runner_;
+  State state_ = kNotStarted;
+  const char* producer_socket_name_ = nullptr;
+  uint32_t connection_backoff_ms_ = 0;
+
+  std::unique_ptr<TracingService::ProducerEndpoint> endpoint_;
+  std::map<DataSourceInstanceID, DataSource> data_sources_;
+
+  base::WeakPtrFactory<PerfProducer> weak_factory_;  // keep last
+};
+
+}  // namespace profiling
+}  // namespace perfetto
+
+#endif  // SRC_PROFILING_PERF_PERF_PRODUCER_H_
diff --git a/src/profiling/perf/traced_perf.cc b/src/profiling/perf/traced_perf.cc
new file mode 100644
index 0000000..c165b20
--- /dev/null
+++ b/src/profiling/perf/traced_perf.cc
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2019 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/profiling/perf/traced_perf.h"
+#include "perfetto/ext/base/unix_task_runner.h"
+#include "perfetto/ext/tracing/ipc/default_socket.h"
+#include "src/profiling/perf/perf_producer.h"
+
+namespace perfetto {
+
+// TODO(rsavitski): watchdog.
+int TracedPerfMain(int, char**) {
+  base::UnixTaskRunner task_runner;
+  profiling::PerfProducer producer(&task_runner);
+  producer.ConnectWithRetries(GetProducerSocket());
+  task_runner.Run();
+  return 0;
+}
+
+}  // namespace perfetto
diff --git a/src/profiling/perf/traced_perf.h b/src/profiling/perf/traced_perf.h
new file mode 100644
index 0000000..71c85e2
--- /dev/null
+++ b/src/profiling/perf/traced_perf.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2019 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_PROFILING_PERF_TRACED_PERF_H_
+#define SRC_PROFILING_PERF_TRACED_PERF_H_
+
+namespace perfetto {
+
+int TracedPerfMain(int argc, char** argv);
+
+}  // namespace perfetto
+
+#endif  // SRC_PROFILING_PERF_TRACED_PERF_H_