Add lazy startup producer class to traced.

This is sufficiently different to the heapprofd system properties to warrant
a parallel implementation.
heapprofd requires a global property that signals whether to profile at all or
whether to profile all processes.

Change-Id: I5ff5e317b9cd99310b145b5501bff3f4afccb56d
Bug: 126724929
diff --git a/Android.bp b/Android.bp
index d450d6f..aa87928 100644
--- a/Android.bp
+++ b/Android.bp
@@ -276,6 +276,7 @@
     "src/traced/probes/probes_producer.cc",
     "src/traced/probes/ps/process_stats_data_source.cc",
     "src/traced/probes/sys_stats/sys_stats_data_source.cc",
+    "src/traced/service/lazy_producer.cc",
     "src/traced/service/service.cc",
     "src/tracing/api_impl/consumer_api.cc",
     "src/tracing/core/android_log_config.cc",
@@ -2940,6 +2941,9 @@
     "src/traced/probes/ps/process_stats_data_source_unittest.cc",
     "src/traced/probes/sys_stats/sys_stats_data_source.cc",
     "src/traced/probes/sys_stats/sys_stats_data_source_unittest.cc",
+    "src/traced/service/lazy_producer.cc",
+    "src/traced/service/lazy_producer_unittest.cc",
+    "src/traced/service/service.cc",
     "src/tracing/core/android_log_config.cc",
     "src/tracing/core/android_power_config.cc",
     "src/tracing/core/chrome_config.cc",
diff --git a/BUILD.gn b/BUILD.gn
index 971d25d..d941328 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -113,6 +113,7 @@
     "gn:gtest_main",
     "src/base:unittests",
     "src/protozero:unittests",
+    "src/traced/service:unittests",
     "src/tracing:unittests",
   ]
 
diff --git a/heapprofd.rc b/heapprofd.rc
index f1869bf..8868751 100644
--- a/heapprofd.rc
+++ b/heapprofd.rc
@@ -24,5 +24,8 @@
 on property:persist.heapprofd.enable=1
     start heapprofd
 
-on property:persist.heapprofd.enable=0
+on property:traced.lazy.heapprofd=1
+    start heapprofd
+
+on property:persist.heapprofd.enable=0 && property:traced.lazy.heapprofd=0
     stop heapprofd
diff --git a/include/perfetto/tracing/ipc/service_ipc_host.h b/include/perfetto/tracing/ipc/service_ipc_host.h
index ccdc89b..eeec568 100644
--- a/include/perfetto/tracing/ipc/service_ipc_host.h
+++ b/include/perfetto/tracing/ipc/service_ipc_host.h
@@ -50,6 +50,8 @@
   virtual bool Start(base::ScopedFile producer_socket_fd,
                      base::ScopedFile consumer_socket_fd) = 0;
 
+  virtual TracingService* service() const = 0;
+
  protected:
   ServiceIPCHost();
 
diff --git a/src/traced/service/BUILD.gn b/src/traced/service/BUILD.gn
index 6b027c6..f351f0e 100644
--- a/src/traced/service/BUILD.gn
+++ b/src/traced/service/BUILD.gn
@@ -23,6 +23,23 @@
     "../../tracing:ipc",
   ]
   sources = [
+    "lazy_producer.cc",
+    "lazy_producer.h",
     "service.cc",
   ]
 }
+
+source_set("unittests") {
+  testonly = true
+  deps = [
+    ":service",
+    "../../../gn:default_deps",
+    "../../../gn:gtest_deps",
+    "../../base",
+    "../../base:test_support",
+    "../../tracing",
+  ]
+  sources = [
+    "lazy_producer_unittest.cc",
+  ]
+}
diff --git a/src/traced/service/lazy_producer.cc b/src/traced/service/lazy_producer.cc
new file mode 100644
index 0000000..0787f26
--- /dev/null
+++ b/src/traced/service/lazy_producer.cc
@@ -0,0 +1,91 @@
+/*
+ * 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/traced/service/lazy_producer.h"
+
+#include "perfetto/base/build_config.h"
+
+#include "perfetto/tracing/core/data_source_config.h"
+#include "perfetto/tracing/core/data_source_descriptor.h"
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+#include <sys/system_properties.h>
+#endif
+
+namespace perfetto {
+
+LazyProducer::LazyProducer(base::TaskRunner* task_runner,
+                           uint32_t delay_ms,
+                           std::string data_source_name,
+                           std::string property_name)
+    : task_runner_(task_runner),
+      delay_ms_(delay_ms),
+      data_source_name_(data_source_name),
+      property_name_(property_name),
+      weak_factory_(this) {}
+
+void LazyProducer::ConnectInProcess(TracingService* svc) {
+  endpoint_ = svc->ConnectProducer(this, geteuid(), "lazy_producer",
+                                   /*shm_hint_kb*/ 16);
+}
+
+void LazyProducer::OnConnect() {
+  DataSourceDescriptor dsd;
+  dsd.set_name(data_source_name_);
+  endpoint_->RegisterDataSource(dsd);
+}
+
+void LazyProducer::SetupDataSource(DataSourceInstanceID,
+                                   const DataSourceConfig&) {
+  SetAndroidProperty(property_name_, "1");
+  active_sessions_++;
+  generation_++;
+}
+
+void LazyProducer::StopDataSource(DataSourceInstanceID) {
+  if (--active_sessions_)
+    return;
+
+  uint64_t cur_generation = generation_;
+  auto weak_this = weak_factory_.GetWeakPtr();
+  task_runner_->PostDelayedTask(
+      [weak_this, cur_generation] {
+        if (!weak_this)
+          return;
+        if (weak_this->generation_ == cur_generation)
+          weak_this->SetAndroidProperty(weak_this->property_name_, "0");
+      },
+      delay_ms_);
+}
+
+bool LazyProducer::SetAndroidProperty(const std::string& name,
+                                      const std::string& value) {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+  return __system_property_set(name.c_str(), value.c_str()) == 0;
+#else
+  // Allow this to be mocked out for tests on other platforms.
+  base::ignore_result(name);
+  base::ignore_result(value);
+  return true;
+#endif
+}
+
+LazyProducer::~LazyProducer() {
+  if (active_sessions_)
+    SetAndroidProperty(property_name_, "0");
+}
+
+}  // namespace perfetto
diff --git a/src/traced/service/lazy_producer.h b/src/traced/service/lazy_producer.h
new file mode 100644
index 0000000..dc6f3aa
--- /dev/null
+++ b/src/traced/service/lazy_producer.h
@@ -0,0 +1,71 @@
+/*
+ * 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_TRACED_SERVICE_LAZY_PRODUCER_H_
+#define SRC_TRACED_SERVICE_LAZY_PRODUCER_H_
+
+#include <set>
+#include <string>
+
+#include "perfetto/base/task_runner.h"
+#include "perfetto/base/weak_ptr.h"
+
+#include "perfetto/tracing/core/basic_types.h"
+#include "perfetto/tracing/core/producer.h"
+#include "perfetto/tracing/core/tracing_service.h"
+
+namespace perfetto {
+
+class LazyProducer : public Producer {
+ public:
+  LazyProducer(base::TaskRunner* task_runner,
+               uint32_t delay_ms,
+               std::string data_source_name,
+               std::string property_name);
+
+  ~LazyProducer() override;
+  // No-ops to satisfy the Producer implementation.
+  void OnDisconnect() override {}
+  void OnTracingSetup() override {}
+  void StartDataSource(DataSourceInstanceID, const DataSourceConfig&) override {
+  }
+  void Flush(FlushRequestID, const DataSourceInstanceID*, size_t) override {}
+
+  void OnConnect() override;
+  void SetupDataSource(DataSourceInstanceID, const DataSourceConfig&) override;
+  void StopDataSource(DataSourceInstanceID) override;
+
+  void ConnectInProcess(TracingService* svc);
+  virtual bool SetAndroidProperty(const std::string& name,
+                                  const std::string& value);
+
+ private:
+  base::TaskRunner* task_runner_;
+  uint32_t delay_ms_;
+
+  std::string data_source_name_;
+  std::string property_name_;
+
+  std::unique_ptr<TracingService::ProducerEndpoint> endpoint_;
+  uint64_t active_sessions_ = 0;
+  uint64_t generation_ = 0;
+
+  base::WeakPtrFactory<LazyProducer> weak_factory_;  // Keep last.
+};
+
+}  // namespace perfetto
+
+#endif  // SRC_TRACED_SERVICE_LAZY_PRODUCER_H_
diff --git a/src/traced/service/lazy_producer_unittest.cc b/src/traced/service/lazy_producer_unittest.cc
new file mode 100644
index 0000000..5565481
--- /dev/null
+++ b/src/traced/service/lazy_producer_unittest.cc
@@ -0,0 +1,93 @@
+/*
+ * 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/service/lazy_producer.h"
+
+#include "src/base/test/test_task_runner.h"
+
+#include "perfetto/tracing/core/data_source_config.h"
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace perfetto {
+namespace {
+
+constexpr const char* kDataSourceName = "android.heapprofd";
+constexpr const char* kPropertyName = "persist.heapprofd.enable";
+
+using ::testing::_;
+using ::testing::InSequence;
+using ::testing::Return;
+
+class MockLazyProducer : public LazyProducer {
+ public:
+  MockLazyProducer(base::TaskRunner* task_runner)
+      : LazyProducer(task_runner, 0, kDataSourceName, kPropertyName) {}
+
+  MOCK_METHOD2(SetAndroidProperty,
+               bool(const std::string&, const std::string&));
+};
+
+TEST(LazyProducersTest, Simple) {
+  DataSourceConfig cfg;
+  cfg.set_name(kDataSourceName);
+  base::TestTaskRunner task_runner;
+  MockLazyProducer p(&task_runner);
+  InSequence s;
+  EXPECT_CALL(p, SetAndroidProperty(kPropertyName, "1")).WillOnce(Return(true));
+  EXPECT_CALL(p, SetAndroidProperty(kPropertyName, "0")).WillOnce(Return(true));
+  p.SetupDataSource(1, cfg);
+  p.StopDataSource(1);
+  task_runner.RunUntilIdle();
+}
+
+TEST(LazyProducersTest, RefCount) {
+  DataSourceConfig cfg;
+  cfg.set_name(kDataSourceName);
+  base::TestTaskRunner task_runner;
+  MockLazyProducer p(&task_runner);
+  InSequence s;
+  EXPECT_CALL(p, SetAndroidProperty(kPropertyName, "1"))
+      .WillRepeatedly(Return(true));
+  p.SetupDataSource(1, cfg);
+  p.SetupDataSource(2, cfg);
+  p.StopDataSource(2);
+  task_runner.RunUntilIdle();
+  EXPECT_CALL(p, SetAndroidProperty(kPropertyName, "0")).WillOnce(Return(true));
+  p.StopDataSource(1);
+  task_runner.RunUntilIdle();
+}
+
+TEST(LazyProducersTest, NoFlap) {
+  DataSourceConfig cfg;
+  cfg.set_name(kDataSourceName);
+  base::TestTaskRunner task_runner;
+  MockLazyProducer p(&task_runner);
+  InSequence s;
+  EXPECT_CALL(p, SetAndroidProperty(kPropertyName, "1"))
+      .WillRepeatedly(Return(true));
+  p.SetupDataSource(1, cfg);
+  p.StopDataSource(1);
+  p.SetupDataSource(2, cfg);
+  task_runner.RunUntilIdle();
+  p.StopDataSource(2);
+  EXPECT_CALL(p, SetAndroidProperty(kPropertyName, "0")).WillOnce(Return(true));
+  task_runner.RunUntilIdle();
+}
+
+}  // namespace
+}  // namespace perfetto
diff --git a/src/traced/service/service.cc b/src/traced/service/service.cc
index ae8d82d..c52b246 100644
--- a/src/traced/service/service.cc
+++ b/src/traced/service/service.cc
@@ -18,6 +18,7 @@
 #include "perfetto/base/watchdog.h"
 #include "perfetto/traced/traced.h"
 #include "perfetto/tracing/ipc/service_ipc_host.h"
+#include "src/traced/service/lazy_producer.h"
 #include "src/tracing/ipc/default_socket.h"
 
 namespace perfetto {
@@ -49,6 +50,10 @@
     return 1;
   }
 
+  LazyProducer lazy_heapprofd(&task_runner, /*delay_ms=*/30000,
+                              "android.heapprofd", "traced.lazy.heapprofd");
+  lazy_heapprofd.ConnectInProcess(svc->service());
+
   // Set the CPU limit and start the watchdog running. The memory limit will
   // be set inside the service code as it relies on the size of buffers.
   // The CPU limit is 75% over a 30 second interval.
diff --git a/src/tracing/ipc/service/service_ipc_host_impl.cc b/src/tracing/ipc/service/service_ipc_host_impl.cc
index 4338a3b..9c185ac 100644
--- a/src/tracing/ipc/service/service_ipc_host_impl.cc
+++ b/src/tracing/ipc/service/service_ipc_host_impl.cc
@@ -88,7 +88,7 @@
   return true;
 }
 
-TracingService* ServiceIPCHostImpl::service_for_testing() const {
+TracingService* ServiceIPCHostImpl::service() const {
   return svc_.get();
 }
 
diff --git a/src/tracing/ipc/service/service_ipc_host_impl.h b/src/tracing/ipc/service/service_ipc_host_impl.h
index 8e10322..e7f5cdd 100644
--- a/src/tracing/ipc/service/service_ipc_host_impl.h
+++ b/src/tracing/ipc/service/service_ipc_host_impl.h
@@ -42,7 +42,7 @@
   bool Start(base::ScopedFile producer_socket_fd,
              base::ScopedFile consumer_socket_fd) override;
 
-  TracingService* service_for_testing() const;
+  TracingService* service() const override;
 
  private:
   bool DoStart();