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 "session.h"
6 
7 #include <inttypes.h>
8 #include <stdio.h>
9 
10 #include <zircon/assert.h>
11 #include <zircon/status.h>
12 #include <zircon/syscalls.h>
13 
14 #include <trace-provider/provider.h>
15 #include <lib/zx/vmar.h>
16 
17 #include <utility>
18 
19 #include "utils.h"
20 
21 namespace trace {
22 namespace internal {
23 
Session(void * buffer,size_t buffer_num_bytes,zx::fifo fifo,fbl::Vector<fbl::String> enabled_categories)24 Session::Session(void* buffer, size_t buffer_num_bytes,
25                  zx::fifo fifo,
26                  fbl::Vector<fbl::String> enabled_categories)
27     : buffer_(buffer),
28       buffer_num_bytes_(buffer_num_bytes),
29       fifo_(std::move(fifo)),
30       fifo_wait_(this, fifo_.get(),
31                  ZX_FIFO_READABLE | ZX_FIFO_PEER_CLOSED),
32       enabled_categories_(std::move(enabled_categories)) {
33     // Build a quick lookup table for IsCategoryEnabled().
34     for (const auto& cat : enabled_categories_) {
35         auto entry = fbl::make_unique<StringSetEntry>(cat.c_str());
36         enabled_category_set_.insert_or_find(std::move(entry));
37     }
38 }
39 
~Session()40 Session::~Session() {
41     zx_status_t status = zx::vmar::root_self()->unmap(
42         reinterpret_cast<uintptr_t>(buffer_), buffer_num_bytes_);
43     ZX_DEBUG_ASSERT(status == ZX_OK);
44     status = fifo_wait_.Cancel();
45     ZX_DEBUG_ASSERT(status == ZX_OK || status == ZX_ERR_NOT_FOUND);
46 }
47 
StartEngine(async_dispatcher_t * dispatcher,trace_buffering_mode_t buffering_mode,zx::vmo buffer,zx::fifo fifo,fbl::Vector<fbl::String> enabled_categories)48 void Session::StartEngine(async_dispatcher_t* dispatcher,
49                           trace_buffering_mode_t buffering_mode,
50                           zx::vmo buffer, zx::fifo fifo,
51                           fbl::Vector<fbl::String> enabled_categories) {
52     ZX_DEBUG_ASSERT(buffer);
53     ZX_DEBUG_ASSERT(fifo);
54 
55     // If the engine isn't stopped flag an error. No one else should be
56     // starting/stopping the engine so testing this here is ok.
57     switch (trace_state()) {
58     case TRACE_STOPPED:
59         break;
60     case TRACE_STOPPING:
61         fprintf(stderr, "Session for process %" PRIu64
62                 ": cannot start engine, still stopping from previous trace\n",
63                 GetPid());
64         return;
65     case TRACE_STARTED:
66         // We can get here if the app errantly tried to create two providers.
67         // This is a bug in the app, provide extra assistance for diagnosis.
68         // Including the pid here has been extraordinarily helpful.
69         fprintf(stderr, "Session for process %" PRIu64
70                 ": engine is already started. Is there perchance two"
71                 " providers in this app?\n",
72                 GetPid());
73         return;
74     default:
75         __UNREACHABLE;
76     }
77 
78     uint64_t buffer_num_bytes;
79     zx_status_t status = buffer.get_size(&buffer_num_bytes);
80     if (status != ZX_OK) {
81         fprintf(stderr, "Session: error getting buffer size, status=%d(%s)\n",
82                 status, zx_status_get_string(status));
83         return;
84     }
85 
86     uintptr_t buffer_ptr;
87     status = zx::vmar::root_self()->map(
88         0u, buffer, 0u, buffer_num_bytes,
89         ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, &buffer_ptr);
90     if (status != ZX_OK) {
91         fprintf(stderr, "Session: error mapping buffer, status=%d(%s)\n",
92                 status, zx_status_get_string(status));
93         return;
94     }
95 
96     auto session = new Session(reinterpret_cast<void*>(buffer_ptr),
97                                buffer_num_bytes, std::move(fifo),
98                                std::move(enabled_categories));
99 
100     status = session->fifo_wait_.Begin(dispatcher);
101     if (status != ZX_OK) {
102         fprintf(stderr, "Session: error starting fifo wait, status=%d(%s)\n",
103                 status, zx_status_get_string(status));
104         delete session;
105         return;
106     }
107 
108     status = trace_start_engine(dispatcher, session, buffering_mode,
109                                 session->buffer_, session->buffer_num_bytes_);
110     if (status != ZX_OK) {
111         fprintf(stderr, "Session: error starting engine, status=%d(%s)\n",
112                 status, zx_status_get_string(status));
113         delete session;
114         return;
115     }
116 
117     // The session will be destroyed in |TraceStopped()|.
118 }
119 
StopEngine()120 void Session::StopEngine() {
121     auto status = trace_stop_engine(ZX_OK);
122     if (status != ZX_OK) {
123         // During shutdown this can happen twice: once for the Stop() request
124         // and once when the channel is closed. Don't print anything for this
125         // case, it suggests an error that isn't there. We could keep track of
126         // this ourselves, but that has its own problems: Best just have one
127         // place that records engine state: in the engine.
128         if (status == ZX_ERR_BAD_STATE && trace_state() == TRACE_STOPPED) {
129             // this is ok
130         } else {
131             fprintf(stderr, "Session: Failed to stop engine, status=%d(%s)\n",
132                     status, zx_status_get_string(status));
133         }
134     }
135 }
136 
HandleFifo(async_dispatcher_t * dispatcher,async::WaitBase * wait,zx_status_t status,const zx_packet_signal_t * signal)137 void Session::HandleFifo(async_dispatcher_t* dispatcher,
138                          async::WaitBase* wait,
139                          zx_status_t status,
140                          const zx_packet_signal_t* signal) {
141     if (status == ZX_ERR_CANCELED) {
142         // The wait could be canceled if we're shutting down, e.g., the
143         // program is exiting.
144         return;
145     }
146     if (status != ZX_OK) {
147         fprintf(stderr, "Session: FIFO wait failed: status=%d(%s)\n",
148                 status, zx_status_get_string(status));
149     } else if (signal->observed & ZX_FIFO_READABLE) {
150         if (ReadFifoMessage()) {
151             if (wait->Begin(dispatcher) == ZX_OK) {
152                 return;
153             }
154             fprintf(stderr, "Session: Error re-registering FIFO wait\n");
155         }
156     } else {
157         ZX_DEBUG_ASSERT(signal->observed & ZX_FIFO_PEER_CLOSED);
158     }
159 
160     // TraceManager is gone or other error with the fifo.
161     StopEngine();
162 }
163 
ReadFifoMessage()164 bool Session::ReadFifoMessage() {
165     trace_provider_packet_t packet;
166     auto status = fifo_.read(sizeof(packet), &packet, 1u, nullptr);
167     ZX_DEBUG_ASSERT(status == ZX_OK);
168     if (packet.reserved != 0) {
169         fprintf(stderr, "Session: Reserved field non-zero from TraceManager: %u\n",
170                packet.reserved);
171         return false;
172     }
173     switch (packet.request) {
174     case TRACE_PROVIDER_BUFFER_SAVED: {
175         auto wrapped_count = packet.data32;
176         auto durable_data_end = packet.data64;
177 #if 0 // TODO(DX-367): Don't delete this, save for conversion to syslog.
178         fprintf(stderr, "Session: Received buffer_saved message"
179                 ", wrapped_count=%u, durable_data_end=0x%" PRIx64 "\n",
180                 wrapped_count, durable_data_end);
181 #endif
182         status = MarkBufferSaved(wrapped_count, durable_data_end);
183         if (status == ZX_ERR_BAD_STATE) {
184             // This happens when tracing has stopped. Ignore it.
185         } else if (status != ZX_OK) {
186             fprintf(stderr, "Session: MarkBufferSaved failed: status=%d\n",
187                     status);
188             return false;
189         }
190         break;
191     }
192     default:
193         fprintf(stderr, "Session: Bad request from TraceManager: %u\n",
194                 packet.request);
195         return false;
196     }
197 
198     return true;
199 }
200 
MarkBufferSaved(uint32_t wrapped_count,uint64_t durable_data_end)201 zx_status_t Session::MarkBufferSaved(uint32_t wrapped_count,
202                                      uint64_t durable_data_end) {
203     return trace_engine_mark_buffer_saved(wrapped_count,
204                                           durable_data_end);
205 }
206 
IsCategoryEnabled(const char * category)207 bool Session::IsCategoryEnabled(const char* category) {
208     if (enabled_categories_.size() == 0) {
209       // If none are specified, enable all categories.
210       return true;
211     }
212     return enabled_category_set_.find(category) != enabled_category_set_.end();
213 }
214 
TraceStarted()215 void Session::TraceStarted() {
216     trace_provider_packet_t packet{};
217     packet.request = TRACE_PROVIDER_STARTED;
218     packet.data32 = TRACE_PROVIDER_FIFO_PROTOCOL_VERSION;
219     auto status = fifo_.write(sizeof(packet), &packet, 1, nullptr);
220     ZX_DEBUG_ASSERT(status == ZX_OK ||
221                     status == ZX_ERR_PEER_CLOSED);
222 }
223 
TraceStopped(async_dispatcher_t * dispatcher,zx_status_t disposition,size_t buffer_bytes_written)224 void Session::TraceStopped(async_dispatcher_t* dispatcher, zx_status_t disposition,
225                            size_t buffer_bytes_written) {
226     // There's no need to notify the trace manager that records were dropped
227     // here. That can be determined from the buffer header.
228     delete this;
229 }
230 
NotifyBufferFull(uint32_t wrapped_count,uint64_t durable_data_end)231 void Session::NotifyBufferFull(uint32_t wrapped_count,
232                                uint64_t durable_data_end) {
233     trace_provider_packet_t packet{};
234     packet.request = TRACE_PROVIDER_SAVE_BUFFER;
235     packet.data32 = wrapped_count;
236     packet.data64 = durable_data_end;
237     auto status = fifo_.write(sizeof(packet), &packet, 1, nullptr);
238     // There's something wrong in our protocol or implementation if we fill
239     // the fifo buffer.
240     ZX_DEBUG_ASSERT(status == ZX_OK ||
241                     status == ZX_ERR_PEER_CLOSED);
242 }
243 
244 } // namespace internal
245 } // namespace trace
246