blob: d8093d7f1bb182efb6ee89819a3788802f78ce18 [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "provider_impl.h"
#include <fidl/fuchsia.tracing.provider/cpp/wire.h>
#include <fidl/fuchsia.tracing/cpp/wire.h>
#include <lib/async/cpp/task.h>
#include <lib/zx/process.h>
#include <stdio.h>
#include <zircon/assert.h>
#include <zircon/status.h>
#include <utility>
#include "export.h"
#include "lib/trace-engine/handler.h"
#include "lib/trace-engine/types.h"
#include "lib/trace-provider/provider.h"
#include "session.h"
#include "utils.h"
namespace {
trace_buffering_mode_t FidlBufferingModeToTraceEngineBufferingMode(
fuchsia_tracing::wire::BufferingMode buffering_mode) {
switch (buffering_mode) {
case fuchsia_tracing::wire::BufferingMode::kOneshot:
return TRACE_BUFFERING_MODE_ONESHOT;
case fuchsia_tracing::wire::BufferingMode::kCircular:
return TRACE_BUFFERING_MODE_CIRCULAR;
case fuchsia_tracing::wire::BufferingMode::kStreaming:
return TRACE_BUFFERING_MODE_STREAMING;
}
}
trace_start_mode_t FidlBufferingDispositionToTraceEngineStartMode(
fuchsia_tracing::BufferDisposition buffer_disposition) {
switch (buffer_disposition) {
case fuchsia_tracing::wire::BufferDisposition::kClearEntire:
return TRACE_START_CLEAR_ENTIRE_BUFFER;
case fuchsia_tracing::wire::BufferDisposition::kClearNondurable:
return TRACE_START_CLEAR_NONDURABLE_BUFFER;
case fuchsia_tracing::wire::BufferDisposition::kRetain:
return TRACE_START_RETAIN_BUFFER;
}
}
std::vector<std::string> CloneCategories(
const fuchsia_tracing_provider::wire::ProviderConfig& config) {
std::vector<std::string> categories;
categories.reserve(config.categories.count());
for (const auto& category : config.categories) {
categories.emplace_back(category.data(), category.size());
}
return categories;
}
} // namespace
namespace trace {
namespace internal {
constexpr bool kVerboseTraceErrors = false;
TraceProviderImpl::TraceProviderImpl(std::string name, async_dispatcher_t* dispatcher,
fidl::ServerEnd<fuchsia_tracing_provider::Provider> server_end)
: name_(std::move(name)), dispatcher_(dispatcher), executor_(dispatcher) {
fidl::BindServer(
dispatcher_, std::move(server_end), this,
[](TraceProviderImpl* impl, fidl::UnbindInfo info,
fidl::ServerEnd<fuchsia_tracing_provider::Provider> server_end) { OnClose(); });
}
void TraceProviderImpl::Initialize(
fuchsia_tracing_provider::wire::ProviderInitializeRequest* request,
InitializeCompleter::Sync& completer) {
fuchsia_tracing_provider::wire::ProviderConfig& config = request->config;
Session::InitializeEngine(
dispatcher_, FidlBufferingModeToTraceEngineBufferingMode(config.buffering_mode),
std::move(config.buffer), std::move(config.fifo), CloneCategories(config));
provider_config_ = {
.buffering_mode = FidlBufferingModeToTraceEngineBufferingMode(config.buffering_mode),
.categories = CloneCategories(config),
};
}
void TraceProviderImpl::Start(fuchsia_tracing_provider::wire::ProviderStartRequest* request,
StartCompleter::Sync& completer) {
const fuchsia_tracing_provider::wire::StartOptions& options = request->options;
// TODO(https://fxbug.dev/42097006): Add support for additional categories.
Session::StartEngine(FidlBufferingDispositionToTraceEngineStartMode(options.buffer_disposition));
}
void TraceProviderImpl::Stop(StopCompleter::Sync& completer) { Session::StopEngine(); }
void TraceProviderImpl::Terminate(TerminateCompleter::Sync& completer) { OnClose(); }
void TraceProviderImpl::GetKnownCategories(GetKnownCategoriesCompleter::Sync& completer) {
if (get_known_categories_callback_ != nullptr) {
auto promise = get_known_categories_callback_().then(
[completer = completer.ToAsync(),
name = name_](fpromise::result<std::vector<trace::KnownCategory>>& result) mutable {
if (result.is_error()) {
fprintf(stderr, "TraceProvider: error getting known categories for %s\n", name.c_str());
completer.Reply({});
return;
}
auto known_categories = result.take_value();
std::vector<fuchsia_tracing::wire::KnownCategory> known_categories_fidl;
known_categories_fidl.reserve(known_categories.size());
for (const auto& known_category : known_categories) {
known_categories_fidl.push_back(fuchsia_tracing::wire::KnownCategory{
fidl::StringView::FromExternal(known_category.name),
fidl::StringView::FromExternal(known_category.description)});
}
completer.Reply(fidl::VectorView<fuchsia_tracing::wire::KnownCategory>::FromExternal(
known_categories_fidl));
});
executor_.schedule_task(std::move(promise));
return;
}
// TODO(https://fxbug.dev/42068744): Return the trace categories that were registered with the category string
// literal.
completer.Reply({});
}
void TraceProviderImpl::SetGetKnownCategoriesCallback(GetKnownCategoriesCallback callback) {
get_known_categories_callback_ = std::move(callback);
}
void TraceProviderImpl::OnClose() { Session::TerminateEngine(); }
const ProviderConfig& TraceProviderImpl::GetProviderConfig() const { return provider_config_; }
} // namespace internal
ProviderConfig TraceProvider::GetProviderConfig() const {
ZX_DEBUG_ASSERT(provider_);
const auto* provider_impl = reinterpret_cast<internal::TraceProviderImpl*>(provider_);
return provider_impl->GetProviderConfig();
}
void TraceProvider::SetGetKnownCategoriesCallback(GetKnownCategoriesCallback callback) {
ZX_DEBUG_ASSERT(provider_);
auto* provider_impl = reinterpret_cast<internal::TraceProviderImpl*>(provider_);
provider_impl->SetGetKnownCategoriesCallback(std::move(callback));
}
} // namespace trace
EXPORT trace_provider_t* trace_provider_create_with_name(zx_handle_t to_service_h,
async_dispatcher_t* dispatcher,
const char* name) {
const fidl::ClientEnd<fuchsia_tracing_provider::Registry> to_service{zx::channel{to_service_h}};
ZX_DEBUG_ASSERT(to_service.is_valid());
ZX_DEBUG_ASSERT(dispatcher);
// Create the channel to which we will bind the trace provider.
zx::result endpoints = fidl::CreateEndpoints<fuchsia_tracing_provider::Provider>();
if (endpoints.is_error()) {
fprintf(stderr, "TraceProvider: channel create failed: status=%d(%s)\n",
endpoints.status_value(), endpoints.status_string());
return nullptr;
}
// Register the trace provider.
const fidl::Status result =
fidl::WireCall(to_service)
->RegisterProvider(std::move(endpoints->client), trace::internal::GetPid(),
fidl::StringView::FromExternal(name));
if (!result.ok()) {
if (trace::internal::kVerboseTraceErrors) {
fprintf(stderr, "TraceProvider: registry failed: result=%s\n",
result.FormatDescription().c_str());
}
return nullptr;
}
// Note: |to_service| can be closed now. Let it close as a consequence
// of going out of scope.
return new trace::internal::TraceProviderImpl(name, dispatcher, std::move(endpoints->server));
}
EXPORT trace_provider_t* trace_provider_create(zx_handle_t to_service,
async_dispatcher_t* dispatcher) {
auto self = zx::process::self();
char name[ZX_MAX_NAME_LEN];
auto status = self->get_property(ZX_PROP_NAME, name, sizeof(name));
if (status != ZX_OK) {
fprintf(stderr, "TraceProvider: error getting process name: status=%d(%s)\n", status,
zx_status_get_string(status));
name[0] = '\0';
}
return trace_provider_create_with_name(to_service, dispatcher, name);
}
EXPORT trace_provider_t* trace_provider_create_synchronously(zx_handle_t to_service_h,
async_dispatcher_t* dispatcher,
const char* name,
bool* out_already_started) {
const fidl::ClientEnd<fuchsia_tracing_provider::Registry> to_service{zx::channel{to_service_h}};
ZX_DEBUG_ASSERT(to_service.is_valid());
ZX_DEBUG_ASSERT(dispatcher);
// Create the channel to which we will bind the trace provider.
zx::result endpoints = fidl::CreateEndpoints<fuchsia_tracing_provider::Provider>();
if (endpoints.is_error()) {
fprintf(stderr, "TraceProvider: channel create failed: status=%d(%s)\n",
endpoints.status_value(), endpoints.status_string());
return nullptr;
}
// Register the trace provider.
const fidl::WireResult result =
fidl::WireCall(to_service)
->RegisterProviderSynchronously(std::move(endpoints->client), trace::internal::GetPid(),
fidl::StringView::FromExternal(name));
if (!result.ok()) {
fprintf(stderr, "TraceProvider: RegisterProviderSynchronously failed: result=%s\n",
result.FormatDescription().c_str());
return nullptr;
}
const fidl::WireResponse response = result.value();
if (const zx_status_t status = response.s; status != ZX_OK) {
fprintf(stderr, "TraceProvider: registry failed: status=%d(%s)\n", status,
zx_status_get_string(status));
return nullptr;
}
// Note: |to_service| can be closed now. Let it close as a consequence
// of going out of scope.
if (out_already_started) {
*out_already_started = response.started;
}
return new trace::internal::TraceProviderImpl(name, dispatcher, std::move(endpoints->server));
}
EXPORT void trace_provider_destroy(trace_provider_t* provider) {
ZX_DEBUG_ASSERT(provider);
// The provider's dispatcher may be running on a different thread. This happens when, e.g., the
// dispatcher is running in a background thread and we are called in the foreground thread.
// async::WaitBase, which we use, requires all calls be made on the dispatcher thread. Thus we
// can't delete |provider| here. Instead we schedule it to be deleted on the dispatcher's thread.
//
// There are two cases to be handled:
// 1) The dispatcher's thread is our thread.
// 2) The dispatcher's thread is a different thread.
// In both cases there's an additional wrinkle:
// a) The task we post is run.
// b) The task we post is not run.
// In cases (1a,2a) we're ok: The provider is deleted. The provider isn't destroyed immediately
// but that's ok, it will be shortly.
// In cases (1b,2b) we're also ok. The only time this happens is if the loop is shutdown before
// our task is run. This is ok because when this happens our WaitBase method cannot be running.
//
// While one might want to check whether we're running in a different thread from the dispatcher
// with dispatcher == async_get_default_dispatcher(), we don't do this as we don't assume the
// default dispatcher has been set.
auto raw_provider_impl = static_cast<trace::internal::TraceProviderImpl*>(provider);
std::unique_ptr<trace::internal::TraceProviderImpl> provider_impl(raw_provider_impl);
async::PostTask(raw_provider_impl->dispatcher(), [provider_impl = std::move(provider_impl)]() {
// The provider will be deleted when the closure is deleted.
});
}