// 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 #include #include #include #include #include #include #include #include #include #include "handler_impl.h" #include "trace_provider.fidl.h" #include "utils.h" namespace trace { namespace internal { TraceProviderImpl::TraceProviderImpl(async_dispatcher_t* dispatcher, zx::channel channel) : dispatcher_(dispatcher), connection_(this, fbl::move(channel)) { } TraceProviderImpl::~TraceProviderImpl() = default; void TraceProviderImpl::Start(trace_buffering_mode_t buffering_mode, zx::vmo buffer, zx::fifo fifo, fbl::Vector enabled_categories) { TraceHandlerImpl::StartEngine( dispatcher_, buffering_mode, fbl::move(buffer), fbl::move(fifo), fbl::move(enabled_categories)); } void TraceProviderImpl::Stop() { TraceHandlerImpl::StopEngine(); } void TraceProviderImpl::OnClose() { Stop(); } TraceProviderImpl::Connection::Connection(TraceProviderImpl* impl, zx::channel channel) : impl_(impl), channel_(fbl::move(channel)), wait_(this, channel_.get(), ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED) { zx_status_t status = wait_.Begin(impl_->dispatcher_); if (status != ZX_OK) { fprintf(stderr, "TraceProvider: begin wait failed: status=%d(%s)\n", status, zx_status_get_string(status)); Close(); } } TraceProviderImpl::Connection::~Connection() { Close(); } void TraceProviderImpl::Connection::Handle( async_dispatcher_t* dispatcher, async::WaitBase* wait, zx_status_t status, const zx_packet_signal_t* signal) { if (status != ZX_OK) { fprintf(stderr, "TraceProvider: wait failed: status=%d(%s)\n", status, zx_status_get_string(status)); } else if (signal->observed & ZX_CHANNEL_READABLE) { if (ReadMessage()) { if (wait_.Begin(dispatcher) == ZX_OK) { return; } } else { fprintf(stderr, "TraceProvider: received invalid FIDL message or failed to send reply\n"); } } else { ZX_DEBUG_ASSERT(signal->observed & ZX_CHANNEL_PEER_CLOSED); } Close(); } bool TraceProviderImpl::Connection::ReadMessage() { FIDL_ALIGNDECL uint8_t buffer[16 * 1024]; uint32_t num_bytes = 0u; zx_handle_t handles[2]; uint32_t num_handles = 0u; zx_status_t status = channel_.read( 0u, buffer, sizeof(buffer), &num_bytes, handles, static_cast(fbl::count_of(handles)), &num_handles); if (status != ZX_OK) { fprintf(stderr, "TraceProvider: channel read failed: status=%d(%s)\n", status, zx_status_get_string(status)); return false; } if (!DecodeAndDispatch(buffer, num_bytes, handles, num_handles)) { fprintf(stderr, "TraceProvider: DecodeAndDispatch failed\n"); zx_handle_close_many(handles, num_handles); return false; } return true; } bool TraceProviderImpl::Connection::DecodeAndDispatch( uint8_t* buffer, uint32_t num_bytes, zx_handle_t* handles, uint32_t num_handles) { if (num_bytes < sizeof(fidl_message_header_t)) { zx_handle_close_many(handles, num_handles); return false; } auto hdr = reinterpret_cast(buffer); switch (hdr->ordinal) { case fuchsia_tracelink_ProviderStartOrdinal: { zx_status_t status = fidl_decode(&fuchsia_tracelink_ProviderStartRequestTable, buffer, num_bytes, handles, num_handles, nullptr); if (status != ZX_OK) break; auto request = reinterpret_cast(buffer); auto buffering_mode = request->buffering_mode; auto buffer = zx::vmo(request->buffer); auto fifo = zx::fifo(request->fifo); fbl::Vector categories; auto strings = reinterpret_cast(request->categories.data); for (size_t i = 0; i < request->categories.count; i++) { categories.push_back(fbl::String(strings[i].data, strings[i].size)); } trace_buffering_mode_t trace_buffering_mode; switch (buffering_mode) { case fuchsia_tracelink_BufferingMode_ONESHOT: trace_buffering_mode = TRACE_BUFFERING_MODE_ONESHOT; break; case fuchsia_tracelink_BufferingMode_CIRCULAR: trace_buffering_mode = TRACE_BUFFERING_MODE_CIRCULAR; break; case fuchsia_tracelink_BufferingMode_STREAMING: trace_buffering_mode = TRACE_BUFFERING_MODE_STREAMING; break; default: return false; } impl_->Start(trace_buffering_mode, fbl::move(buffer), fbl::move(fifo), fbl::move(categories)); return true; } case fuchsia_tracelink_ProviderStopOrdinal: { zx_status_t status = fidl_decode(&fuchsia_tracelink_ProviderStopRequestTable, buffer, num_bytes, handles, num_handles, nullptr); if (status != ZX_OK) break; impl_->Stop(); return true; } } return false; } void TraceProviderImpl::Connection::Close() { if (channel_) { wait_.Cancel(); channel_.reset(); impl_->OnClose(); } } } // namespace internal } // namespace trace static zx_status_t ConnectToServiceRegistry(zx::channel* out_registry_client) { // Connect to the trace registry. zx::channel registry_client; zx::channel registry_service; zx_status_t status = zx::channel::create(0u, ®istry_client, ®istry_service); if (status != ZX_OK) return status; status = fdio_service_connect("/svc/fuchsia.tracelink.Registry", registry_service.release()); // takes ownership if (status != ZX_OK) return status; *out_registry_client = fbl::move(registry_client); return ZX_OK; } trace_provider_t* trace_provider_create_with_name(async_dispatcher_t* dispatcher, const char* name) { ZX_DEBUG_ASSERT(dispatcher); zx::channel registry_client; auto status = ConnectToServiceRegistry(®istry_client); if (status != ZX_OK) { fprintf(stderr, "TraceProvider: connection failed: status=%d(%s)\n", status, zx_status_get_string(status)); return nullptr; } // Create the channel to which we will bind the trace provider. zx::channel provider_client; zx::channel provider_service; status = zx::channel::create(0u, &provider_client, &provider_service); if (status != ZX_OK) { fprintf(stderr, "TraceProvider: channel create failed: status=%d(%s)\n", status, zx_status_get_string(status)); return nullptr; } // Register the trace provider. status = fuchsia_tracelink_RegistryRegisterTraceProvider( registry_client.get(), provider_client.release(), trace::internal::GetPid(), name, strlen(name)); if (status != ZX_OK) { fprintf(stderr, "TraceProvider: registry failed: status=%d(%s)\n", status, zx_status_get_string(status)); return nullptr; } return new trace::internal::TraceProviderImpl(dispatcher, fbl::move(provider_service)); } trace_provider_t* trace_provider_create(async_dispatcher_t* dispatcher) { return trace_provider_create_with_name(dispatcher, ""); } trace_provider_t* trace_provider_create_synchronously(async_dispatcher_t* dispatcher, const char* name, bool* out_manager_is_tracing_already) { ZX_DEBUG_ASSERT(dispatcher); zx::channel registry_client; auto status = ConnectToServiceRegistry(®istry_client); if (status != ZX_OK) { fprintf(stderr, "TraceProvider: connection failed: status=%d(%s)\n", status, zx_status_get_string(status)); return nullptr; } // Create the channel to which we will bind the trace provider. zx::channel provider_client; zx::channel provider_service; status = zx::channel::create(0u, &provider_client, &provider_service); if (status != ZX_OK) { fprintf(stderr, "TraceProvider: channel create failed: status=%d(%s)\n", status, zx_status_get_string(status)); return nullptr; } // Register the trace provider. zx_status_t registry_status; bool manager_is_tracing_already; status = fuchsia_tracelink_RegistryRegisterTraceProviderSynchronously( registry_client.get(), provider_client.release(), trace::internal::GetPid(), name, strlen(name), ®istry_status, &manager_is_tracing_already); if (status != ZX_OK) { fprintf(stderr, "TraceProvider: RegisterTraceProviderSynchronously failed: status=%d(%s)\n", status, zx_status_get_string(status)); return nullptr; } if (registry_status != ZX_OK) { fprintf(stderr, "TraceProvider: registry failed: status=%d(%s)\n", status, zx_status_get_string(status)); return nullptr; } if (out_manager_is_tracing_already) *out_manager_is_tracing_already = manager_is_tracing_already; return new trace::internal::TraceProviderImpl(dispatcher, fbl::move(provider_service)); } void trace_provider_destroy(trace_provider_t* provider) { ZX_DEBUG_ASSERT(provider); delete static_cast(provider); }