sdk: Fail gracefully when attempting to trace on an invalid backend

Adds a TracingBackendFake instance to TracingMuxerImpl, which
is used as a fallback backend for new tracing sessions in case they
specify a backend type that isn't available, e.g., because it wasn't
registered in Tracing::Initialize().

This backend instance simply disconnects consumers when they attempt to
connect. It still allows the SDK to register a producer with the
backend (in order to avoid a lot of connection retries or special
casing of the backend in the muxer), but ignores any data source
registrations.

Also fixes a few races during session callback registration.

Bug: 179761322
Change-Id: I1580461aad3711fb21f99db8e735d84ffe8cebda
diff --git a/Android.bp b/Android.bp
index 3352769..82338ee 100644
--- a/Android.bp
+++ b/Android.bp
@@ -8260,6 +8260,7 @@
     "src/tracing/interceptor.cc",
     "src/tracing/internal/checked_scope.cc",
     "src/tracing/internal/interceptor_trace_writer.cc",
+    "src/tracing/internal/tracing_backend_fake.cc",
     "src/tracing/internal/tracing_muxer_fake.cc",
     "src/tracing/internal/tracing_muxer_impl.cc",
     "src/tracing/internal/track_event_internal.cc",
diff --git a/BUILD b/BUILD
index 525a525..f151b0d 100644
--- a/BUILD
+++ b/BUILD
@@ -520,6 +520,7 @@
         "include/perfetto/tracing/internal/in_process_tracing_backend.h",
         "include/perfetto/tracing/internal/interceptor_trace_writer.h",
         "include/perfetto/tracing/internal/system_tracing_backend.h",
+        "include/perfetto/tracing/internal/tracing_backend_fake.h",
         "include/perfetto/tracing/internal/tracing_muxer.h",
         "include/perfetto/tracing/internal/tracing_tls.h",
         "include/perfetto/tracing/internal/track_event_data_source.h",
@@ -1594,6 +1595,7 @@
         "src/tracing/interceptor.cc",
         "src/tracing/internal/checked_scope.cc",
         "src/tracing/internal/interceptor_trace_writer.cc",
+        "src/tracing/internal/tracing_backend_fake.cc",
         "src/tracing/internal/tracing_muxer_fake.cc",
         "src/tracing/internal/tracing_muxer_fake.h",
         "src/tracing/internal/tracing_muxer_impl.cc",
diff --git a/include/perfetto/tracing/BUILD.gn b/include/perfetto/tracing/BUILD.gn
index 414e40f..39d1250 100644
--- a/include/perfetto/tracing/BUILD.gn
+++ b/include/perfetto/tracing/BUILD.gn
@@ -39,6 +39,7 @@
     "internal/in_process_tracing_backend.h",
     "internal/interceptor_trace_writer.h",
     "internal/system_tracing_backend.h",
+    "internal/tracing_backend_fake.h",
     "internal/tracing_muxer.h",
     "internal/tracing_tls.h",
     "internal/track_event_data_source.h",
diff --git a/include/perfetto/tracing/internal/tracing_backend_fake.h b/include/perfetto/tracing/internal/tracing_backend_fake.h
new file mode 100644
index 0000000..92c8ea1
--- /dev/null
+++ b/include/perfetto/tracing/internal/tracing_backend_fake.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2021 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 INCLUDE_PERFETTO_TRACING_INTERNAL_TRACING_BACKEND_FAKE_H_
+#define INCLUDE_PERFETTO_TRACING_INTERNAL_TRACING_BACKEND_FAKE_H_
+
+#include "perfetto/base/export.h"
+#include "perfetto/tracing/tracing_backend.h"
+
+namespace perfetto {
+namespace internal {
+
+// A built-in implementation of TracingBackend that fails any attempt to create
+// a tracing session.
+class PERFETTO_EXPORT TracingBackendFake : public TracingBackend {
+ public:
+  static TracingBackend* GetInstance();
+
+  // TracingBackend implementation.
+  std::unique_ptr<ProducerEndpoint> ConnectProducer(
+      const ConnectProducerArgs&) override;
+  std::unique_ptr<ConsumerEndpoint> ConnectConsumer(
+      const ConnectConsumerArgs&) override;
+
+ private:
+  TracingBackendFake();
+};
+
+}  // namespace internal
+}  // namespace perfetto
+
+#endif  // INCLUDE_PERFETTO_TRACING_INTERNAL_TRACING_BACKEND_FAKE_H_
diff --git a/src/tracing/BUILD.gn b/src/tracing/BUILD.gn
index f4b0b79..43ce774 100644
--- a/src/tracing/BUILD.gn
+++ b/src/tracing/BUILD.gn
@@ -82,7 +82,8 @@
   sources = [ "trace_writer_base.cc" ]
 }
 
-# Base target for the client API. On its own doesn't provide any backend.
+# Base target for the client API. On its own doesn't provide any backend other
+# than the unsupported one.
 source_set("client_api_without_backends") {
   deps = [
     "../../include/perfetto/tracing/core",
@@ -105,6 +106,7 @@
     "interceptor.cc",
     "internal/checked_scope.cc",
     "internal/interceptor_trace_writer.cc",
+    "internal/tracing_backend_fake.cc",
     "internal/tracing_muxer_fake.cc",
     "internal/tracing_muxer_fake.h",
     "internal/tracing_muxer_impl.cc",
diff --git a/src/tracing/internal/tracing_backend_fake.cc b/src/tracing/internal/tracing_backend_fake.cc
new file mode 100644
index 0000000..3c64fed
--- /dev/null
+++ b/src/tracing/internal/tracing_backend_fake.cc
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2021 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 "perfetto/tracing/internal/tracing_backend_fake.h"
+
+#include "perfetto/base/task_runner.h"
+#include "perfetto/ext/base/weak_ptr.h"
+#include "perfetto/ext/tracing/core/consumer.h"
+#include "perfetto/ext/tracing/core/producer.h"
+#include "perfetto/ext/tracing/core/trace_writer.h"
+#include "perfetto/ext/tracing/core/tracing_service.h"
+
+namespace perfetto {
+namespace internal {
+
+namespace {
+
+class UnsupportedProducerEndpoint : public ProducerEndpoint {
+ public:
+  UnsupportedProducerEndpoint(Producer* producer, base::TaskRunner* task_runner)
+      : producer_(producer), task_runner_(task_runner) {
+    // The SDK will attempt to reconnect the producer, so instead we allow it
+    // to connect successfully, but never start any sessions.
+    auto weak_ptr = weak_ptr_factory_.GetWeakPtr();
+    task_runner_->PostTask([weak_ptr] {
+      if (weak_ptr)
+        weak_ptr->producer_->OnConnect();
+    });
+  }
+  ~UnsupportedProducerEndpoint() override { producer_->OnDisconnect(); }
+
+  void RegisterDataSource(const DataSourceDescriptor&) override {}
+  void UnregisterDataSource(const std::string& /*name*/) override {}
+
+  void RegisterTraceWriter(uint32_t /*writer_id*/,
+                           uint32_t /*target_buffer*/) override {}
+  void UnregisterTraceWriter(uint32_t /*writer_id*/) override {}
+
+  void CommitData(const CommitDataRequest&,
+                  CommitDataCallback callback = {}) override {
+    callback();
+  }
+
+  SharedMemory* shared_memory() const override { return nullptr; }
+  size_t shared_buffer_page_size_kb() const override { return 0; }
+
+  std::unique_ptr<TraceWriter> CreateTraceWriter(
+      BufferID /*target_buffer*/,
+      BufferExhaustedPolicy = BufferExhaustedPolicy::kDefault) override {
+    return nullptr;
+  }
+
+  SharedMemoryArbiter* MaybeSharedMemoryArbiter() override { return nullptr; }
+  bool IsShmemProvidedByProducer() const override { return false; }
+
+  void NotifyFlushComplete(FlushRequestID) override {}
+  void NotifyDataSourceStarted(DataSourceInstanceID) override {}
+  void NotifyDataSourceStopped(DataSourceInstanceID) override {}
+  void ActivateTriggers(const std::vector<std::string>&) override {}
+
+  void Sync(std::function<void()> callback) override { callback(); }
+
+ private:
+  Producer* const producer_;
+  base::TaskRunner* const task_runner_;
+  base::WeakPtrFactory<UnsupportedProducerEndpoint> weak_ptr_factory_{
+      this};  // Keep last.
+};
+
+class UnsupportedConsumerEndpoint : public ConsumerEndpoint {
+ public:
+  UnsupportedConsumerEndpoint(Consumer* consumer, base::TaskRunner* task_runner)
+      : consumer_(consumer), task_runner_(task_runner) {
+    // The SDK will not to reconnect the consumer, so we just disconnect it
+    // immediately, which will cancel the tracing session.
+    auto weak_this = weak_ptr_factory_.GetWeakPtr();
+    task_runner_->PostTask([weak_this] {
+      if (weak_this)
+        weak_this->consumer_->OnDisconnect();
+    });
+  }
+  ~UnsupportedConsumerEndpoint() override = default;
+
+  void EnableTracing(const TraceConfig&,
+                     base::ScopedFile = base::ScopedFile()) override {}
+  void ChangeTraceConfig(const TraceConfig&) override {}
+
+  void StartTracing() override {}
+  void DisableTracing() override {}
+
+  void Flush(uint32_t /*timeout_ms*/, FlushCallback callback) override {
+    callback(/*success=*/false);
+  }
+
+  void ReadBuffers() override {}
+  void FreeBuffers() override {}
+
+  void Detach(const std::string& /*key*/) override {}
+  void Attach(const std::string& /*key*/) override {}
+
+  void GetTraceStats() override {}
+  void ObserveEvents(uint32_t /*events_mask*/) override {}
+  void QueryServiceState(QueryServiceStateCallback) override {}
+  void QueryCapabilities(QueryCapabilitiesCallback) override {}
+
+  void SaveTraceForBugreport(SaveTraceForBugreportCallback) override {}
+
+ private:
+  Consumer* const consumer_;
+  base::TaskRunner* const task_runner_;
+  base::WeakPtrFactory<UnsupportedConsumerEndpoint> weak_ptr_factory_{
+      this};  // Keep last.
+};
+
+}  // namespace
+
+// static
+TracingBackend* TracingBackendFake::GetInstance() {
+  static auto* instance = new TracingBackendFake();
+  return instance;
+}
+
+TracingBackendFake::TracingBackendFake() = default;
+
+std::unique_ptr<ProducerEndpoint> TracingBackendFake::ConnectProducer(
+    const ConnectProducerArgs& args) {
+  return std::unique_ptr<ProducerEndpoint>(
+      new UnsupportedProducerEndpoint(args.producer, args.task_runner));
+}
+
+std::unique_ptr<ConsumerEndpoint> TracingBackendFake::ConnectConsumer(
+    const ConnectConsumerArgs& args) {
+  return std::unique_ptr<ConsumerEndpoint>(
+      new UnsupportedConsumerEndpoint(args.consumer, args.task_runner));
+}
+
+}  // namespace internal
+}  // namespace perfetto
diff --git a/src/tracing/internal/tracing_muxer_impl.cc b/src/tracing/internal/tracing_muxer_impl.cc
index 54a355c..eed7477 100644
--- a/src/tracing/internal/tracing_muxer_impl.cc
+++ b/src/tracing/internal/tracing_muxer_impl.cc
@@ -38,6 +38,7 @@
 #include "perfetto/tracing/data_source.h"
 #include "perfetto/tracing/internal/data_source_internal.h"
 #include "perfetto/tracing/internal/interceptor_trace_writer.h"
+#include "perfetto/tracing/internal/tracing_backend_fake.h"
 #include "perfetto/tracing/trace_writer_base.h"
 #include "perfetto/tracing/tracing.h"
 #include "perfetto/tracing/tracing_backend.h"
@@ -572,6 +573,8 @@
   auto session_id = session_id_;
   muxer->task_runner_->PostTask([muxer, session_id, cb] {
     auto* consumer = muxer->FindConsumer(session_id);
+    if (!consumer)
+      return;
     consumer->start_complete_callback_ = cb;
   });
 }
@@ -583,8 +586,12 @@
   auto session_id = session_id_;
   muxer->task_runner_->PostTask([muxer, session_id, cb] {
     auto* consumer = muxer->FindConsumer(session_id);
-    if (!consumer)
+    if (!consumer) {
+      // Notify the client about concurrent disconnection of the session.
+      if (cb)
+        cb(TracingError{TracingError::kDisconnected, "Peer disconnected"});
       return;
+    }
     consumer->error_callback_ = cb;
   });
 }
@@ -596,6 +603,8 @@
   auto session_id = session_id_;
   muxer->task_runner_->PostTask([muxer, session_id, cb] {
     auto* consumer = muxer->FindConsumer(session_id);
+    if (!consumer)
+      return;
     consumer->stop_complete_callback_ = cb;
   });
 }
@@ -686,6 +695,12 @@
   if (args.backends & ~(kSystemBackend | kInProcessBackend | kCustomBackend)) {
     PERFETTO_FATAL("Unsupported tracing backend type");
   }
+
+  // Fallback backend for consumer creation for an unsupported backend type.
+  // This backend simply fails any attempt to start a tracing session.
+  // NOTE: This backend instance has to be added last.
+  add_backend(internal::TracingBackendFake::GetInstance(),
+              BackendType::kUnspecifiedBackend);
 }
 
 // Can be called from any thread (but not concurrently).
@@ -1435,17 +1450,27 @@
 // This is called via the public API Tracing::NewTrace().
 // Can be called from any thread.
 std::unique_ptr<TracingSession> TracingMuxerImpl::CreateTracingSession(
-    BackendType backend_type) {
+    BackendType requested_backend_type) {
   TracingSessionGlobalID session_id = ++next_tracing_session_id_;
 
   // |backend_type| can only specify one backend, not an OR-ed mask.
-  PERFETTO_CHECK((backend_type & (backend_type - 1)) == 0);
+  PERFETTO_CHECK((requested_backend_type & (requested_backend_type - 1)) == 0);
 
   // Capturing |this| is fine because the TracingMuxer is a leaky singleton.
-  task_runner_->PostTask([this, backend_type, session_id] {
+  task_runner_->PostTask([this, requested_backend_type, session_id] {
     for (RegisteredBackend& backend : backends_) {
-      if (backend_type && backend.type != backend_type)
+      if (requested_backend_type && backend.type &&
+          backend.type != requested_backend_type) {
         continue;
+      }
+
+      // The last registered backend in |backends_| is the unsupported backend
+      // without a valid type.
+      if (!backend.type) {
+        PERFETTO_ELOG(
+            "No tracing backend ready for type=%d, consumer will disconnect",
+            requested_backend_type);
+      }
 
       backend.consumers.emplace_back(
           new ConsumerImpl(this, backend.type, backend.id, session_id));
@@ -1456,13 +1481,11 @@
       consumer->Initialize(backend.backend->ConnectConsumer(conn_args));
       return;
     }
-    PERFETTO_ELOG(
-        "Cannot create tracing session, no tracing backend ready for type=%d",
-        backend_type);
+    PERFETTO_DFATAL("Not reached");
   });
 
   return std::unique_ptr<TracingSession>(
-      new TracingSessionImpl(this, session_id, backend_type));
+      new TracingSessionImpl(this, session_id, requested_backend_type));
 }
 
 void TracingMuxerImpl::InitializeInstance(const TracingInitArgs& args) {
diff --git a/src/tracing/test/api_integrationtest.cc b/src/tracing/test/api_integrationtest.cc
index acb86a0..69f4401 100644
--- a/src/tracing/test/api_integrationtest.cc
+++ b/src/tracing/test/api_integrationtest.cc
@@ -407,9 +407,15 @@
 
   TestTracingSessionHandle* NewTrace(const perfetto::TraceConfig& cfg,
                                      int fd = -1) {
+    return NewTrace(cfg, /*backend_type=*/GetParam(), fd);
+  }
+
+  TestTracingSessionHandle* NewTrace(const perfetto::TraceConfig& cfg,
+                                     perfetto::BackendType backend_type,
+                                     int fd = -1) {
     sessions_.emplace_back();
     TestTracingSessionHandle* handle = &sessions_.back();
-    handle->session = perfetto::Tracing::NewTrace(/*BackendType=*/GetParam());
+    handle->session = perfetto::Tracing::NewTrace(backend_type);
     handle->session->SetOnStopCallback([handle] { handle->on_stop.Notify(); });
     handle->session->Setup(cfg, fd);
     return handle;
@@ -2729,6 +2735,28 @@
   tracing_session->get()->StopBlocking();
 }
 
+TEST_P(PerfettoApiTest, UnsupportedBackend) {
+  // Create a new trace session with an invalid backend type specified.
+  // Specifically, the custom backend isn't initialized for these tests.
+  perfetto::TraceConfig cfg;
+  cfg.add_buffers()->set_size_kb(1024);
+  auto* tracing_session = NewTrace(cfg, perfetto::BackendType::kCustomBackend);
+
+  // Creating the consumer should cause an asynchronous disconnect error.
+  WaitableTestEvent got_error;
+  tracing_session->get()->SetOnErrorCallback([&](perfetto::TracingError error) {
+    EXPECT_EQ(perfetto::TracingError::kDisconnected, error.code);
+    EXPECT_FALSE(error.message.empty());
+    got_error.Notify();
+  });
+  got_error.Wait();
+
+  // Clear the callback for test tear down.
+  tracing_session->get()->SetOnErrorCallback(nullptr);
+  // Synchronize the consumer channel to ensure the callback has propagated.
+  tracing_session->get()->StopBlocking();
+}
+
 TEST_P(PerfettoApiTest, GetTraceStats) {
   perfetto::TraceConfig cfg;
   cfg.set_duration_ms(500);