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