1 // Copyright 2020 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 //==============================================================================
15 //
16
17 #include "pw_trace/trace.h"
18
19 #include "pw_preprocessor/util.h"
20 #include "pw_trace_tokenized/trace_callback.h"
21 #include "pw_trace_tokenized/trace_tokenized.h"
22 #include "pw_varint/varint.h"
23
24 namespace pw {
25 namespace trace {
26
27 TokenizedTraceImpl TokenizedTrace::instance_;
28 CallbacksImpl Callbacks::instance_;
29
HandleTraceEvent(uint32_t trace_token,EventType event_type,const char * module,uint32_t trace_id,uint8_t flags,const void * data_buffer,size_t data_size)30 void TokenizedTraceImpl::HandleTraceEvent(uint32_t trace_token,
31 EventType event_type,
32 const char* module,
33 uint32_t trace_id,
34 uint8_t flags,
35 const void* data_buffer,
36 size_t data_size) {
37 // Early exit if disabled and no callbacks are register to receive events
38 // while disabled.
39 if (!enabled_ && Callbacks::Instance().GetCalledOnEveryEventCount() == 0) {
40 return;
41 }
42
43 // Create trace event
44 PW_TRACE_QUEUE_LOCK();
45 if (!event_queue_
46 .TryPushBack(trace_token,
47 event_type,
48 module,
49 trace_id,
50 flags,
51 data_buffer,
52 data_size)
53 .ok()) {
54 // Queue full dropping sample
55 // TODO(rgoliver): Allow other strategies, for example: drop oldest, try
56 // empty queue, or block.
57 }
58 PW_TRACE_QUEUE_UNLOCK();
59
60 // Sample is now in queue (if not dropped), try to empty the queue if not
61 // already being emptied.
62 if (PW_TRACE_TRY_LOCK()) {
63 while (!event_queue_.IsEmpty()) {
64 HandleNextItemInQueue(event_queue_.PeekFront());
65 event_queue_.PopFront();
66 }
67 PW_TRACE_UNLOCK();
68 }
69 }
70
HandleNextItemInQueue(const volatile TraceQueue::QueueEventBlock * event_block)71 void TokenizedTraceImpl::HandleNextItemInQueue(
72 const volatile TraceQueue::QueueEventBlock* event_block) {
73 // Get next item in queue
74 uint32_t trace_token = event_block->trace_token;
75 EventType event_type = event_block->event_type;
76 const char* module = event_block->module;
77 uint32_t trace_id = event_block->trace_id;
78 uint8_t flags = event_block->flags;
79 const std::byte* data_buffer =
80 const_cast<const std::byte*>(event_block->data_buffer);
81 size_t data_size = event_block->data_size;
82
83 // Call any event callback which is registered to receive every event.
84 pw_trace_TraceEventReturnFlags ret_flags = 0;
85 ret_flags |=
86 Callbacks::Instance().CallEventCallbacks(CallbacksImpl::kCallOnEveryEvent,
87 trace_token,
88 event_type,
89 module,
90 trace_id,
91 flags);
92 // Return if disabled.
93 if ((PW_TRACE_EVENT_RETURN_FLAGS_SKIP_EVENT & ret_flags) || !enabled_) {
94 return;
95 }
96
97 // Call any event callback not already called.
98 ret_flags |= Callbacks::Instance().CallEventCallbacks(
99 CallbacksImpl::kCallOnlyWhenEnabled,
100 trace_token,
101 event_type,
102 module,
103 trace_id,
104 flags);
105 // Return if disabled (from a callback) or if a callback has indicated the
106 // sample should be skipped.
107 if ((PW_TRACE_EVENT_RETURN_FLAGS_SKIP_EVENT & ret_flags) || !enabled_) {
108 return;
109 }
110
111 // Create header to store trace info
112 static constexpr size_t kMaxHeaderSize =
113 sizeof(trace_token) + pw::varint::kMaxVarint64SizeBytes + // time
114 pw::varint::kMaxVarint64SizeBytes; // trace_id
115 std::byte header[kMaxHeaderSize];
116 memcpy(header, &trace_token, sizeof(trace_token));
117 size_t header_size = sizeof(trace_token);
118
119 // Compute delta of time elapsed since last trace entry.
120 PW_TRACE_TIME_TYPE trace_time = pw_trace_GetTraceTime();
121 PW_TRACE_TIME_TYPE delta =
122 (last_trace_time_ == 0)
123 ? 0
124 : PW_TRACE_GET_TIME_DELTA(last_trace_time_, trace_time);
125 header_size += pw::varint::Encode(
126 delta,
127 std::span<std::byte>(&header[header_size], kMaxHeaderSize - header_size));
128 last_trace_time_ = trace_time;
129
130 // Calculate packet id if needed.
131 if (PW_TRACE_HAS_TRACE_ID(event_type)) {
132 header_size +=
133 pw::varint::Encode(trace_id,
134 std::span<std::byte>(&header[header_size],
135 kMaxHeaderSize - header_size));
136 }
137
138 // Send encoded output to any registered trace sinks.
139 Callbacks::Instance().CallSinks(
140 std::span<const std::byte>(header, header_size),
141 std::span<const std::byte>(
142 reinterpret_cast<const std::byte*>(data_buffer), data_size));
143 // Disable after processing if an event callback had set the flag.
144 if (PW_TRACE_EVENT_RETURN_FLAGS_DISABLE_AFTER_PROCESSING & ret_flags) {
145 enabled_ = false;
146 }
147 }
148
CallEventCallbacks(CallOnEveryEvent called_on_every_event,uint32_t trace_ref,EventType event_type,const char * module,uint32_t trace_id,uint8_t flags)149 pw_trace_TraceEventReturnFlags CallbacksImpl::CallEventCallbacks(
150 CallOnEveryEvent called_on_every_event,
151 uint32_t trace_ref,
152 EventType event_type,
153 const char* module,
154 uint32_t trace_id,
155 uint8_t flags) {
156 pw_trace_TraceEventReturnFlags ret_flags = 0;
157 for (size_t i = 0; i < PW_TRACE_CONFIG_MAX_EVENT_CALLBACKS; i++) {
158 if (event_callbacks_[i].callback &&
159 event_callbacks_[i].called_on_every_event == called_on_every_event) {
160 ret_flags |= Callbacks::Instance().GetEventCallback(i)->callback(
161 event_callbacks_[i].user_data,
162 trace_ref,
163 event_type,
164 module,
165 trace_id,
166 flags);
167 }
168 }
169 return ret_flags;
170 }
171
CallSinks(std::span<const std::byte> header,std::span<const std::byte> data)172 void CallbacksImpl::CallSinks(std::span<const std::byte> header,
173 std::span<const std::byte> data) {
174 for (size_t sink_idx = 0; sink_idx < PW_TRACE_CONFIG_MAX_SINKS; sink_idx++) {
175 void* user_data = sink_callbacks_[sink_idx].user_data;
176 if (sink_callbacks_[sink_idx].start_block) {
177 sink_callbacks_[sink_idx].start_block(user_data,
178 header.size() + data.size());
179 }
180 if (sink_callbacks_[sink_idx].add_bytes) {
181 sink_callbacks_[sink_idx].add_bytes(
182 user_data, header.data(), header.size());
183 if (!data.empty()) {
184 sink_callbacks_[sink_idx].add_bytes(
185 user_data, data.data(), data.size());
186 }
187 }
188 if (sink_callbacks_[sink_idx].end_block) {
189 sink_callbacks_[sink_idx].end_block(user_data);
190 }
191 }
192 }
193
RegisterSink(SinkStartBlock start_func,SinkAddBytes add_bytes_func,SinkEndBlock end_block_func,void * user_data,SinkHandle * handle)194 pw::Status CallbacksImpl::RegisterSink(SinkStartBlock start_func,
195 SinkAddBytes add_bytes_func,
196 SinkEndBlock end_block_func,
197 void* user_data,
198 SinkHandle* handle) {
199 pw_Status status = PW_STATUS_RESOURCE_EXHAUSTED;
200 PW_TRACE_LOCK();
201 for (size_t sink_idx = 0; sink_idx < PW_TRACE_CONFIG_MAX_SINKS; sink_idx++) {
202 if (IsSinkFree(sink_idx)) {
203 sink_callbacks_[sink_idx].start_block = start_func;
204 sink_callbacks_[sink_idx].add_bytes = add_bytes_func;
205 sink_callbacks_[sink_idx].end_block = end_block_func;
206 sink_callbacks_[sink_idx].user_data = user_data;
207 if (handle) {
208 *handle = sink_idx;
209 }
210 status = PW_STATUS_OK;
211 break;
212 }
213 }
214 PW_TRACE_UNLOCK();
215 return status;
216 }
217
UnregisterSink(SinkHandle handle)218 pw::Status CallbacksImpl::UnregisterSink(SinkHandle handle) {
219 PW_TRACE_LOCK();
220 if (handle >= PW_TRACE_CONFIG_MAX_SINKS) {
221 return PW_STATUS_INVALID_ARGUMENT;
222 }
223 sink_callbacks_[handle].start_block = nullptr;
224 sink_callbacks_[handle].add_bytes = nullptr;
225 sink_callbacks_[handle].end_block = nullptr;
226 PW_TRACE_UNLOCK();
227 return PW_STATUS_OK;
228 }
229
UnregisterAllSinks()230 pw::Status CallbacksImpl::UnregisterAllSinks() {
231 for (size_t sink_idx = 0; sink_idx < PW_TRACE_CONFIG_MAX_SINKS; sink_idx++) {
232 UnregisterSink(sink_idx)
233 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
234 }
235 return PW_STATUS_OK;
236 }
237
GetSink(SinkHandle handle)238 CallbacksImpl::SinkCallbacks* CallbacksImpl::GetSink(SinkHandle handle) {
239 if (handle >= PW_TRACE_CONFIG_MAX_EVENT_CALLBACKS) {
240 return nullptr;
241 }
242 return &sink_callbacks_[handle];
243 }
244
RegisterEventCallback(EventCallback callback,CallOnEveryEvent called_on_every_event,void * user_data,EventCallbackHandle * handle)245 pw::Status CallbacksImpl::RegisterEventCallback(
246 EventCallback callback,
247 CallOnEveryEvent called_on_every_event,
248 void* user_data,
249 EventCallbackHandle* handle) {
250 pw_Status status = PW_STATUS_RESOURCE_EXHAUSTED;
251 PW_TRACE_LOCK();
252 for (size_t i = 0; i < PW_TRACE_CONFIG_MAX_EVENT_CALLBACKS; i++) {
253 if (event_callbacks_[i].callback == nullptr) {
254 event_callbacks_[i].callback = callback;
255 event_callbacks_[i].user_data = user_data;
256 event_callbacks_[i].called_on_every_event = called_on_every_event;
257 called_on_every_event_count_ += called_on_every_event ? 1 : 0;
258 if (handle) {
259 *handle = i;
260 }
261 status = PW_STATUS_OK;
262 break;
263 }
264 }
265 PW_TRACE_UNLOCK();
266 return status;
267 }
268
UnregisterEventCallback(EventCallbackHandle handle)269 pw::Status CallbacksImpl::UnregisterEventCallback(EventCallbackHandle handle) {
270 PW_TRACE_LOCK();
271 if (handle >= PW_TRACE_CONFIG_MAX_EVENT_CALLBACKS) {
272 return PW_STATUS_INVALID_ARGUMENT;
273 }
274 event_callbacks_[handle].callback = nullptr;
275 event_callbacks_[handle].user_data = nullptr;
276 called_on_every_event_count_ +=
277 event_callbacks_[handle].called_on_every_event ? 1 : 0;
278 event_callbacks_[handle].called_on_every_event = kCallOnlyWhenEnabled;
279 PW_TRACE_UNLOCK();
280 return PW_STATUS_OK;
281 }
282
UnregisterAllEventCallbacks()283 pw::Status CallbacksImpl::UnregisterAllEventCallbacks() {
284 for (size_t i = 0; i < PW_TRACE_CONFIG_MAX_EVENT_CALLBACKS; i++) {
285 UnregisterEventCallback(i)
286 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
287 }
288 return PW_STATUS_OK;
289 }
290
GetEventCallback(EventCallbackHandle handle)291 CallbacksImpl::EventCallbacks* CallbacksImpl::GetEventCallback(
292 EventCallbackHandle handle) {
293 if (handle >= PW_TRACE_CONFIG_MAX_EVENT_CALLBACKS) {
294 return nullptr;
295 }
296 return &event_callbacks_[handle];
297 }
298
299 // C functions
300
301 PW_EXTERN_C_START
302
pw_trace_Enable(bool enable)303 void pw_trace_Enable(bool enable) { TokenizedTrace::Instance().Enable(enable); }
304
pw_trace_IsEnabled()305 bool pw_trace_IsEnabled() { return TokenizedTrace::Instance().IsEnabled(); }
306
pw_trace_TraceEvent(uint32_t trace_token,pw_trace_EventType event_type,const char * module,uint32_t trace_id,uint8_t flags,const void * data_buffer,size_t data_size)307 void pw_trace_TraceEvent(uint32_t trace_token,
308 pw_trace_EventType event_type,
309 const char* module,
310 uint32_t trace_id,
311 uint8_t flags,
312 const void* data_buffer,
313 size_t data_size) {
314 TokenizedTrace::Instance().HandleTraceEvent(
315 trace_token, event_type, module, trace_id, flags, data_buffer, data_size);
316 }
317
pw_trace_RegisterSink(pw_trace_SinkStartBlock start_func,pw_trace_SinkAddBytes add_bytes_func,pw_trace_SinkEndBlock end_block_func,void * user_data,pw_trace_SinkHandle * handle)318 pw_Status pw_trace_RegisterSink(pw_trace_SinkStartBlock start_func,
319 pw_trace_SinkAddBytes add_bytes_func,
320 pw_trace_SinkEndBlock end_block_func,
321 void* user_data,
322 pw_trace_SinkHandle* handle) {
323 return Callbacks::Instance()
324 .RegisterSink(
325 start_func, add_bytes_func, end_block_func, user_data, handle)
326 .code();
327 }
328
pw_trace_UnregisterSink(pw_trace_EventCallbackHandle handle)329 pw_Status pw_trace_UnregisterSink(pw_trace_EventCallbackHandle handle) {
330 return Callbacks::Instance().UnregisterSink(handle).code();
331 }
332
pw_trace_RegisterEventCallback(pw_trace_EventCallback callback,pw_trace_ShouldCallOnEveryEvent called_on_every_event,void * user_data,pw_trace_EventCallbackHandle * handle)333 pw_Status pw_trace_RegisterEventCallback(
334 pw_trace_EventCallback callback,
335 pw_trace_ShouldCallOnEveryEvent called_on_every_event,
336 void* user_data,
337 pw_trace_EventCallbackHandle* handle) {
338 return Callbacks::Instance()
339 .RegisterEventCallback(
340 callback,
341 static_cast<CallbacksImpl::CallOnEveryEvent>(called_on_every_event),
342 user_data,
343 handle)
344 .code();
345 }
346
pw_trace_UnregisterEventCallback(pw_trace_EventCallbackHandle handle)347 pw_Status pw_trace_UnregisterEventCallback(
348 pw_trace_EventCallbackHandle handle) {
349 return Callbacks::Instance().UnregisterEventCallback(handle).code();
350 }
351
352 PW_EXTERN_C_END
353
354 } // namespace trace
355 } // namespace pw
356