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