1// Copyright 2017 The Fuchsia Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "provider_impl.h" 6 7#include <stdio.h> 8 9#include <fbl/algorithm.h> 10#include <fbl/type_support.h> 11#include <lib/async/default.h> 12#include <lib/fdio/util.h> 13#include <lib/fidl/coding.h> 14#include <zircon/assert.h> 15#include <zircon/process.h> 16#include <zircon/status.h> 17#include <zircon/syscalls.h> 18 19#include "handler_impl.h" 20#include "trace_provider.fidl.h" 21#include "utils.h" 22 23namespace trace { 24namespace internal { 25 26TraceProviderImpl::TraceProviderImpl(async_dispatcher_t* dispatcher, 27 zx::channel channel) 28 : dispatcher_(dispatcher), 29 connection_(this, fbl::move(channel)) { 30} 31 32TraceProviderImpl::~TraceProviderImpl() = default; 33 34void TraceProviderImpl::Start(trace_buffering_mode_t buffering_mode, 35 zx::vmo buffer, zx::fifo fifo, 36 fbl::Vector<fbl::String> enabled_categories) { 37 TraceHandlerImpl::StartEngine( 38 dispatcher_, buffering_mode, fbl::move(buffer), fbl::move(fifo), 39 fbl::move(enabled_categories)); 40} 41 42void TraceProviderImpl::Stop() { 43 TraceHandlerImpl::StopEngine(); 44} 45 46void TraceProviderImpl::OnClose() { 47 Stop(); 48} 49 50TraceProviderImpl::Connection::Connection(TraceProviderImpl* impl, 51 zx::channel channel) 52 : impl_(impl), channel_(fbl::move(channel)), 53 wait_(this, channel_.get(), 54 ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED) { 55 zx_status_t status = wait_.Begin(impl_->dispatcher_); 56 if (status != ZX_OK) { 57 fprintf(stderr, "TraceProvider: begin wait failed: status=%d(%s)\n", 58 status, zx_status_get_string(status)); 59 Close(); 60 } 61} 62 63TraceProviderImpl::Connection::~Connection() { 64 Close(); 65} 66 67void TraceProviderImpl::Connection::Handle( 68 async_dispatcher_t* dispatcher, async::WaitBase* wait, zx_status_t status, 69 const zx_packet_signal_t* signal) { 70 if (status != ZX_OK) { 71 fprintf(stderr, "TraceProvider: wait failed: status=%d(%s)\n", 72 status, zx_status_get_string(status)); 73 } else if (signal->observed & ZX_CHANNEL_READABLE) { 74 if (ReadMessage()) { 75 if (wait_.Begin(dispatcher) == ZX_OK) { 76 return; 77 } 78 } else { 79 fprintf(stderr, 80 "TraceProvider: received invalid FIDL message or failed to send reply\n"); 81 } 82 } else { 83 ZX_DEBUG_ASSERT(signal->observed & ZX_CHANNEL_PEER_CLOSED); 84 } 85 86 Close(); 87} 88 89bool TraceProviderImpl::Connection::ReadMessage() { 90 FIDL_ALIGNDECL uint8_t buffer[16 * 1024]; 91 uint32_t num_bytes = 0u; 92 zx_handle_t handles[2]; 93 uint32_t num_handles = 0u; 94 zx_status_t status = channel_.read( 95 0u, buffer, sizeof(buffer), &num_bytes, 96 handles, static_cast<uint32_t>(fbl::count_of(handles)), &num_handles); 97 if (status != ZX_OK) { 98 fprintf(stderr, "TraceProvider: channel read failed: status=%d(%s)\n", 99 status, zx_status_get_string(status)); 100 return false; 101 } 102 103 if (!DecodeAndDispatch(buffer, num_bytes, handles, num_handles)) { 104 fprintf(stderr, "TraceProvider: DecodeAndDispatch failed\n"); 105 zx_handle_close_many(handles, num_handles); 106 return false; 107 } 108 109 return true; 110} 111 112bool TraceProviderImpl::Connection::DecodeAndDispatch( 113 uint8_t* buffer, uint32_t num_bytes, 114 zx_handle_t* handles, uint32_t num_handles) { 115 if (num_bytes < sizeof(fidl_message_header_t)) { 116 zx_handle_close_many(handles, num_handles); 117 return false; 118 } 119 120 auto hdr = reinterpret_cast<fidl_message_header_t*>(buffer); 121 switch (hdr->ordinal) { 122 case fuchsia_tracelink_ProviderStartOrdinal: { 123 zx_status_t status = fidl_decode(&fuchsia_tracelink_ProviderStartRequestTable, 124 buffer, num_bytes, handles, num_handles, 125 nullptr); 126 if (status != ZX_OK) 127 break; 128 129 auto request = reinterpret_cast<fuchsia_tracelink_ProviderStartRequest*>(buffer); 130 auto buffering_mode = request->buffering_mode; 131 auto buffer = zx::vmo(request->buffer); 132 auto fifo = zx::fifo(request->fifo); 133 fbl::Vector<fbl::String> categories; 134 auto strings = reinterpret_cast<fidl_string_t*>(request->categories.data); 135 for (size_t i = 0; i < request->categories.count; i++) { 136 categories.push_back(fbl::String(strings[i].data, strings[i].size)); 137 } 138 trace_buffering_mode_t trace_buffering_mode; 139 switch (buffering_mode) { 140 case fuchsia_tracelink_BufferingMode_ONESHOT: 141 trace_buffering_mode = TRACE_BUFFERING_MODE_ONESHOT; 142 break; 143 case fuchsia_tracelink_BufferingMode_CIRCULAR: 144 trace_buffering_mode = TRACE_BUFFERING_MODE_CIRCULAR; 145 break; 146 case fuchsia_tracelink_BufferingMode_STREAMING: 147 trace_buffering_mode = TRACE_BUFFERING_MODE_STREAMING; 148 break; 149 default: 150 return false; 151 } 152 impl_->Start(trace_buffering_mode, fbl::move(buffer), fbl::move(fifo), 153 fbl::move(categories)); 154 return true; 155 } 156 case fuchsia_tracelink_ProviderStopOrdinal: { 157 zx_status_t status = fidl_decode(&fuchsia_tracelink_ProviderStopRequestTable, 158 buffer, num_bytes, handles, num_handles, 159 nullptr); 160 if (status != ZX_OK) 161 break; 162 163 impl_->Stop(); 164 return true; 165 } 166 } 167 return false; 168} 169 170void TraceProviderImpl::Connection::Close() { 171 if (channel_) { 172 wait_.Cancel(); 173 channel_.reset(); 174 impl_->OnClose(); 175 } 176} 177 178} // namespace internal 179} // namespace trace 180 181static zx_status_t ConnectToServiceRegistry(zx::channel* out_registry_client) { 182 // Connect to the trace registry. 183 zx::channel registry_client; 184 zx::channel registry_service; 185 zx_status_t status = zx::channel::create(0u, ®istry_client, ®istry_service); 186 if (status != ZX_OK) 187 return status; 188 189 status = fdio_service_connect("/svc/fuchsia.tracelink.Registry", 190 registry_service.release()); // takes ownership 191 if (status != ZX_OK) 192 return status; 193 194 *out_registry_client = fbl::move(registry_client); 195 return ZX_OK; 196} 197 198trace_provider_t* trace_provider_create_with_name(async_dispatcher_t* dispatcher, 199 const char* name) { 200 ZX_DEBUG_ASSERT(dispatcher); 201 202 zx::channel registry_client; 203 auto status = ConnectToServiceRegistry(®istry_client); 204 if (status != ZX_OK) { 205 fprintf(stderr, "TraceProvider: connection failed: status=%d(%s)\n", 206 status, zx_status_get_string(status)); 207 return nullptr; 208 } 209 210 // Create the channel to which we will bind the trace provider. 211 zx::channel provider_client; 212 zx::channel provider_service; 213 status = zx::channel::create(0u, &provider_client, &provider_service); 214 if (status != ZX_OK) { 215 fprintf(stderr, "TraceProvider: channel create failed: status=%d(%s)\n", 216 status, zx_status_get_string(status)); 217 return nullptr; 218 } 219 220 // Register the trace provider. 221 status = fuchsia_tracelink_RegistryRegisterTraceProvider( 222 registry_client.get(), provider_client.release(), 223 trace::internal::GetPid(), name, strlen(name)); 224 if (status != ZX_OK) { 225 fprintf(stderr, "TraceProvider: registry failed: status=%d(%s)\n", 226 status, zx_status_get_string(status)); 227 return nullptr; 228 } 229 230 return new trace::internal::TraceProviderImpl(dispatcher, 231 fbl::move(provider_service)); 232} 233 234trace_provider_t* trace_provider_create(async_dispatcher_t* dispatcher) { 235 return trace_provider_create_with_name(dispatcher, ""); 236} 237 238trace_provider_t* trace_provider_create_synchronously(async_dispatcher_t* dispatcher, 239 const char* name, 240 bool* out_manager_is_tracing_already) { 241 ZX_DEBUG_ASSERT(dispatcher); 242 243 zx::channel registry_client; 244 auto status = ConnectToServiceRegistry(®istry_client); 245 if (status != ZX_OK) { 246 fprintf(stderr, "TraceProvider: connection failed: status=%d(%s)\n", 247 status, zx_status_get_string(status)); 248 return nullptr; 249 } 250 251 // Create the channel to which we will bind the trace provider. 252 zx::channel provider_client; 253 zx::channel provider_service; 254 status = zx::channel::create(0u, &provider_client, &provider_service); 255 if (status != ZX_OK) { 256 fprintf(stderr, "TraceProvider: channel create failed: status=%d(%s)\n", 257 status, zx_status_get_string(status)); 258 return nullptr; 259 } 260 261 // Register the trace provider. 262 zx_status_t registry_status; 263 bool manager_is_tracing_already; 264 status = fuchsia_tracelink_RegistryRegisterTraceProviderSynchronously( 265 registry_client.get(), provider_client.release(), 266 trace::internal::GetPid(), name, strlen(name), 267 ®istry_status, &manager_is_tracing_already); 268 if (status != ZX_OK) { 269 fprintf(stderr, "TraceProvider: RegisterTraceProviderSynchronously failed: status=%d(%s)\n", 270 status, zx_status_get_string(status)); 271 return nullptr; 272 } 273 if (registry_status != ZX_OK) { 274 fprintf(stderr, "TraceProvider: registry failed: status=%d(%s)\n", 275 status, zx_status_get_string(status)); 276 return nullptr; 277 } 278 279 if (out_manager_is_tracing_already) 280 *out_manager_is_tracing_already = manager_is_tracing_already; 281 return new trace::internal::TraceProviderImpl(dispatcher, 282 fbl::move(provider_service)); 283} 284 285void trace_provider_destroy(trace_provider_t* provider) { 286 ZX_DEBUG_ASSERT(provider); 287 delete static_cast<trace::internal::TraceProviderImpl*>(provider); 288} 289