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, &registry_client, &registry_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(&registry_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(&registry_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        &registry_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