aboutsummaryrefslogtreecommitdiff
path: root/src/tracing/consumer_api_deprecated/consumer_api_deprecated.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/tracing/consumer_api_deprecated/consumer_api_deprecated.cc')
-rw-r--r--src/tracing/consumer_api_deprecated/consumer_api_deprecated.cc425
1 files changed, 425 insertions, 0 deletions
diff --git a/src/tracing/consumer_api_deprecated/consumer_api_deprecated.cc b/src/tracing/consumer_api_deprecated/consumer_api_deprecated.cc
new file mode 100644
index 000000000..8ef266059
--- /dev/null
+++ b/src/tracing/consumer_api_deprecated/consumer_api_deprecated.cc
@@ -0,0 +1,425 @@
+/*
+ * 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 "perfetto/public/consumer_api.h"
+
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <sys/select.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#include <unistd.h>
+
+#include <atomic>
+#include <condition_variable>
+#include <memory>
+#include <mutex>
+#include <thread>
+
+#include "perfetto/base/build_config.h"
+#include "perfetto/ext/base/scoped_file.h"
+#include "perfetto/ext/base/temp_file.h"
+#include "perfetto/ext/base/thread_checker.h"
+#include "perfetto/ext/base/unix_task_runner.h"
+#include "perfetto/ext/base/utils.h"
+#include "perfetto/ext/tracing/core/consumer.h"
+#include "perfetto/ext/tracing/core/trace_packet.h"
+#include "perfetto/ext/tracing/ipc/consumer_ipc_client.h"
+#include "perfetto/ext/tracing/ipc/default_socket.h"
+#include "perfetto/tracing/core/trace_config.h"
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+#include <linux/memfd.h>
+#include <sys/syscall.h>
+#endif
+
+#include "protos/perfetto/config/trace_config.gen.h"
+
+#define PERFETTO_EXPORTED_API __attribute__((visibility("default")))
+
+namespace perfetto {
+namespace consumer {
+
+namespace {
+
+class TracingSession : public Consumer {
+ public:
+ TracingSession(base::TaskRunner*,
+ Handle,
+ OnStateChangedCb,
+ void* callback_arg,
+ const TraceConfig&);
+ ~TracingSession() override;
+
+ // Note: if making this class moveable, the move-ctor/dtor must be updated
+ // to clear up mapped_buf_ on dtor.
+
+ // These methods are called on a thread != |task_runner_|.
+ State state() const { return state_; }
+ std::pair<char*, size_t> mapped_buf() const {
+ // The comparison operator will do an acquire-load on the atomic |state_|.
+ if (state_ == State::kTraceEnded)
+ return std::make_pair(mapped_buf_, mapped_buf_size_);
+ return std::make_pair(nullptr, 0);
+ }
+
+ // All the methods below are called only on the |task_runner_| thread.
+
+ bool Initialize();
+ void StartTracing();
+
+ // perfetto::Consumer implementation.
+ void OnConnect() override;
+ void OnDisconnect() override;
+ void OnTracingDisabled() override;
+ void OnTraceData(std::vector<TracePacket>, bool has_more) override;
+ void OnDetach(bool) override;
+ void OnAttach(bool, const TraceConfig&) override;
+ void OnTraceStats(bool, const TraceStats&) override;
+ void OnObservableEvents(const ObservableEvents&) override;
+
+ private:
+ TracingSession(const TracingSession&) = delete;
+ TracingSession& operator=(const TracingSession&) = delete;
+
+ void DestroyConnection();
+ void NotifyCallback();
+
+ base::TaskRunner* const task_runner_;
+ Handle const handle_;
+ OnStateChangedCb const callback_ = nullptr;
+ void* const callback_arg_ = nullptr;
+ TraceConfig trace_config_;
+ base::ScopedFile buf_fd_;
+ std::unique_ptr<TracingService::ConsumerEndpoint> consumer_endpoint_;
+
+ // |mapped_buf_| and |mapped_buf_size_| are seq-consistent with |state_|.
+ std::atomic<State> state_{State::kIdle};
+ char* mapped_buf_ = nullptr;
+ size_t mapped_buf_size_ = 0;
+
+ PERFETTO_THREAD_CHECKER(thread_checker_)
+};
+
+TracingSession::TracingSession(base::TaskRunner* task_runner,
+ Handle handle,
+ OnStateChangedCb callback,
+ void* callback_arg,
+ const TraceConfig& trace_config_proto)
+ : task_runner_(task_runner),
+ handle_(handle),
+ callback_(callback),
+ callback_arg_(callback_arg) {
+ PERFETTO_DETACH_FROM_THREAD(thread_checker_);
+ trace_config_ = trace_config_proto;
+ trace_config_.set_write_into_file(true);
+
+ // TODO(primiano): this really doesn't matter because the trace will be
+ // flushed into the file when stopping. We need a way to be able to say
+ // "disable periodic flushing and flush only when stopping".
+ trace_config_.set_file_write_period_ms(60000);
+}
+
+TracingSession::~TracingSession() {
+ PERFETTO_DCHECK_THREAD(thread_checker_);
+ if (mapped_buf_)
+ PERFETTO_CHECK(munmap(mapped_buf_, mapped_buf_size_) == 0);
+}
+
+bool TracingSession::Initialize() {
+ PERFETTO_DCHECK_THREAD(thread_checker_);
+
+ if (state_ != State::kIdle)
+ return false;
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+ char memfd_name[64];
+ snprintf(memfd_name, sizeof(memfd_name), "perfetto_trace_%" PRId64, handle_);
+ buf_fd_.reset(
+ static_cast<int>(syscall(__NR_memfd_create, memfd_name, MFD_CLOEXEC)));
+#else
+ // Fallback for testing on Linux/mac.
+ buf_fd_ = base::TempFile::CreateUnlinked().ReleaseFD();
+#endif
+
+ if (!buf_fd_) {
+ PERFETTO_PLOG("Failed to allocate temporary tracing buffer");
+ return false;
+ }
+
+ state_ = State::kConnecting;
+ consumer_endpoint_ =
+ ConsumerIPCClient::Connect(GetConsumerSocket(), this, task_runner_);
+
+ return true;
+}
+
+// Called after EnabledTracing, soon after the IPC connection is established.
+void TracingSession::OnConnect() {
+ PERFETTO_DCHECK_THREAD(thread_checker_);
+
+ PERFETTO_DLOG("OnConnect");
+ PERFETTO_DCHECK(state_ == State::kConnecting);
+ consumer_endpoint_->EnableTracing(trace_config_,
+ base::ScopedFile(dup(*buf_fd_)));
+ if (trace_config_.deferred_start())
+ state_ = State::kConfigured;
+ else
+ state_ = State::kTracing;
+ NotifyCallback();
+}
+
+void TracingSession::StartTracing() {
+ PERFETTO_DCHECK_THREAD(thread_checker_);
+
+ auto state = state_.load();
+ if (state != State::kConfigured) {
+ PERFETTO_ELOG("StartTracing(): invalid state (%d)",
+ static_cast<int>(state));
+ return;
+ }
+ state_ = State::kTracing;
+ consumer_endpoint_->StartTracing();
+}
+
+void TracingSession::OnTracingDisabled() {
+ PERFETTO_DCHECK_THREAD(thread_checker_);
+ PERFETTO_DLOG("OnTracingDisabled");
+
+ struct stat stat_buf {};
+ int res = fstat(buf_fd_.get(), &stat_buf);
+ mapped_buf_size_ = res == 0 ? static_cast<size_t>(stat_buf.st_size) : 0;
+ mapped_buf_ =
+ static_cast<char*>(mmap(nullptr, mapped_buf_size_, PROT_READ | PROT_WRITE,
+ MAP_SHARED, buf_fd_.get(), 0));
+ DestroyConnection();
+ if (mapped_buf_size_ == 0 || mapped_buf_ == MAP_FAILED) {
+ mapped_buf_ = nullptr;
+ mapped_buf_size_ = 0;
+ state_ = State::kTraceFailed;
+ PERFETTO_ELOG("Tracing session failed");
+ } else {
+ state_ = State::kTraceEnded;
+ }
+ NotifyCallback();
+}
+
+void TracingSession::OnDisconnect() {
+ PERFETTO_DCHECK_THREAD(thread_checker_);
+ PERFETTO_DLOG("OnDisconnect");
+ DestroyConnection();
+ state_ = State::kConnectionError;
+ NotifyCallback();
+}
+
+void TracingSession::OnDetach(bool) {
+ PERFETTO_DCHECK(false); // Should never be called, Detach() is not used here.
+}
+
+void TracingSession::OnAttach(bool, const TraceConfig&) {
+ PERFETTO_DCHECK(false); // Should never be called, Attach() is not used here.
+}
+
+void TracingSession::OnTraceStats(bool, const TraceStats&) {
+ // Should never be called, GetTraceStats() is not used here.
+ PERFETTO_DCHECK(false);
+}
+
+void TracingSession::OnObservableEvents(const ObservableEvents&) {
+ // Should never be called, ObserveEvents() is not used here.
+ PERFETTO_DCHECK(false);
+}
+
+void TracingSession::DestroyConnection() {
+ // Destroys the connection in a separate task. This is to avoid destroying
+ // the IPC connection directly from within the IPC callback.
+ TracingService::ConsumerEndpoint* endpoint = consumer_endpoint_.release();
+ task_runner_->PostTask([endpoint] { delete endpoint; });
+}
+
+void TracingSession::OnTraceData(std::vector<TracePacket>, bool) {
+ // This should be never called because we are using |write_into_file| and
+ // asking the traced service to directly write into the |buf_fd_|.
+ PERFETTO_DFATAL("Should be unreachable.");
+}
+
+void TracingSession::NotifyCallback() {
+ if (!callback_)
+ return;
+ auto state = state_.load();
+ auto callback = callback_;
+ auto handle = handle_;
+ auto callback_arg = callback_arg_;
+ task_runner_->PostTask([callback, callback_arg, handle, state] {
+ callback(handle, state, callback_arg);
+ });
+}
+
+class TracingController {
+ public:
+ static TracingController* GetInstance();
+ TracingController();
+
+ // These methods are called from a thread != |task_runner_|.
+ Handle Create(const void*, size_t, OnStateChangedCb, void* callback_arg);
+ void StartTracing(Handle);
+ State PollState(Handle);
+ TraceBuffer ReadTrace(Handle);
+ void Destroy(Handle);
+
+ private:
+ void ThreadMain(); // Called on |task_runner_| thread.
+
+ std::mutex mutex_;
+ std::thread thread_;
+ std::unique_ptr<base::UnixTaskRunner> task_runner_;
+ std::condition_variable task_runner_initialized_;
+ Handle last_handle_ = 0;
+ std::map<Handle, std::unique_ptr<TracingSession>> sessions_;
+};
+
+TracingController* TracingController::GetInstance() {
+ static TracingController* instance = new TracingController();
+ return instance;
+}
+
+TracingController::TracingController()
+ : thread_(&TracingController::ThreadMain, this) {
+ std::unique_lock<std::mutex> lock(mutex_);
+ task_runner_initialized_.wait(lock, [this] { return !!task_runner_; });
+}
+
+void TracingController::ThreadMain() {
+ {
+ std::unique_lock<std::mutex> lock(mutex_);
+ task_runner_.reset(new base::UnixTaskRunner());
+ }
+ task_runner_initialized_.notify_one();
+ task_runner_->Run();
+}
+
+Handle TracingController::Create(const void* config_proto_buf,
+ size_t config_len,
+ OnStateChangedCb callback,
+ void* callback_arg) {
+ TraceConfig config_proto;
+ bool parsed = config_proto.ParseFromArray(config_proto_buf, config_len);
+ if (!parsed) {
+ PERFETTO_ELOG("Failed to decode TraceConfig proto");
+ return kInvalidHandle;
+ }
+
+ if (!config_proto.duration_ms()) {
+ PERFETTO_ELOG("The trace config must specify a duration");
+ return kInvalidHandle;
+ }
+
+ std::unique_lock<std::mutex> lock(mutex_);
+ Handle handle = ++last_handle_;
+ auto* session = new TracingSession(task_runner_.get(), handle, callback,
+ callback_arg, config_proto);
+ sessions_.emplace(handle, std::unique_ptr<TracingSession>(session));
+
+ // Enable the TracingSession on its own thread.
+ task_runner_->PostTask([session] { session->Initialize(); });
+
+ return handle;
+}
+
+void TracingController::StartTracing(Handle handle) {
+ std::unique_lock<std::mutex> lock(mutex_);
+ auto it = sessions_.find(handle);
+ if (it == sessions_.end()) {
+ PERFETTO_ELOG("StartTracing(): Invalid tracing session handle");
+ return;
+ }
+ TracingSession* session = it->second.get();
+ task_runner_->PostTask([session] { session->StartTracing(); });
+}
+
+State TracingController::PollState(Handle handle) {
+ std::unique_lock<std::mutex> lock(mutex_);
+ auto it = sessions_.find(handle);
+ if (it == sessions_.end())
+ return State::kSessionNotFound;
+ return it->second->state();
+}
+
+TraceBuffer TracingController::ReadTrace(Handle handle) {
+ TraceBuffer buf{};
+
+ std::unique_lock<std::mutex> lock(mutex_);
+ auto it = sessions_.find(handle);
+ if (it == sessions_.end()) {
+ PERFETTO_DLOG("Handle invalid");
+ return buf;
+ }
+
+ TracingSession* session = it->second.get();
+ auto state = session->state();
+ if (state == State::kTraceEnded) {
+ std::tie(buf.begin, buf.size) = session->mapped_buf();
+ return buf;
+ }
+
+ PERFETTO_DLOG("ReadTrace(): called in an unexpected state (%d)",
+ static_cast<int>(state));
+ return buf;
+}
+
+void TracingController::Destroy(Handle handle) {
+ // Post an empty task on the task runner to delete the session on its own
+ // thread.
+ std::unique_lock<std::mutex> lock(mutex_);
+ auto it = sessions_.find(handle);
+ if (it == sessions_.end())
+ return;
+ TracingSession* session = it->second.release();
+ sessions_.erase(it);
+ task_runner_->PostTask([session] { delete session; });
+}
+
+} // namespace
+
+PERFETTO_EXPORTED_API Handle Create(const void* config_proto,
+ size_t config_len,
+ OnStateChangedCb callback,
+ void* callback_arg) {
+ return TracingController::GetInstance()->Create(config_proto, config_len,
+ callback, callback_arg);
+}
+
+PERFETTO_EXPORTED_API
+void StartTracing(Handle handle) {
+ return TracingController::GetInstance()->StartTracing(handle);
+}
+
+PERFETTO_EXPORTED_API State PollState(Handle handle) {
+ return TracingController::GetInstance()->PollState(handle);
+}
+
+PERFETTO_EXPORTED_API TraceBuffer ReadTrace(Handle handle) {
+ return TracingController::GetInstance()->ReadTrace(handle);
+}
+
+PERFETTO_EXPORTED_API void Destroy(Handle handle) {
+ TracingController::GetInstance()->Destroy(handle);
+}
+} // namespace consumer
+} // namespace perfetto