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.size() > 0) {
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 }
234 return PW_STATUS_OK;
235 }
236
GetSink(SinkHandle handle)237 CallbacksImpl::SinkCallbacks* CallbacksImpl::GetSink(SinkHandle handle) {
238 if (handle >= PW_TRACE_CONFIG_MAX_EVENT_CALLBACKS) {
239 return nullptr;
240 }
241 return &sink_callbacks_[handle];
242 }
243
RegisterEventCallback(EventCallback callback,CallOnEveryEvent called_on_every_event,void * user_data,EventCallbackHandle * handle)244 pw::Status CallbacksImpl::RegisterEventCallback(
245 EventCallback callback,
246 CallOnEveryEvent called_on_every_event,
247 void* user_data,
248 EventCallbackHandle* handle) {
249 pw_Status status = PW_STATUS_RESOURCE_EXHAUSTED;
250 PW_TRACE_LOCK();
251 for (size_t i = 0; i < PW_TRACE_CONFIG_MAX_EVENT_CALLBACKS; i++) {
252 if (event_callbacks_[i].callback == nullptr) {
253 event_callbacks_[i].callback = callback;
254 event_callbacks_[i].user_data = user_data;
255 event_callbacks_[i].called_on_every_event = called_on_every_event;
256 called_on_every_event_count_ += called_on_every_event ? 1 : 0;
257 if (handle) {
258 *handle = i;
259 }
260 status = PW_STATUS_OK;
261 break;
262 }
263 }
264 PW_TRACE_UNLOCK();
265 return status;
266 }
267
UnregisterEventCallback(EventCallbackHandle handle)268 pw::Status CallbacksImpl::UnregisterEventCallback(EventCallbackHandle handle) {
269 PW_TRACE_LOCK();
270 if (handle >= PW_TRACE_CONFIG_MAX_EVENT_CALLBACKS) {
271 return PW_STATUS_INVALID_ARGUMENT;
272 }
273 event_callbacks_[handle].callback = nullptr;
274 event_callbacks_[handle].user_data = nullptr;
275 called_on_every_event_count_ +=
276 event_callbacks_[handle].called_on_every_event ? 1 : 0;
277 event_callbacks_[handle].called_on_every_event = kCallOnlyWhenEnabled;
278 PW_TRACE_UNLOCK();
279 return PW_STATUS_OK;
280 }
281
UnregisterAllEventCallbacks()282 pw::Status CallbacksImpl::UnregisterAllEventCallbacks() {
283 for (size_t i = 0; i < PW_TRACE_CONFIG_MAX_EVENT_CALLBACKS; i++) {
284 UnregisterEventCallback(i);
285 }
286 return PW_STATUS_OK;
287 }
288
GetEventCallback(EventCallbackHandle handle)289 CallbacksImpl::EventCallbacks* CallbacksImpl::GetEventCallback(
290 EventCallbackHandle handle) {
291 if (handle >= PW_TRACE_CONFIG_MAX_EVENT_CALLBACKS) {
292 return nullptr;
293 }
294 return &event_callbacks_[handle];
295 }
296
297 // C functions
298
299 PW_EXTERN_C_START
300
pw_trace_Enable(bool enable)301 void pw_trace_Enable(bool enable) { TokenizedTrace::Instance().Enable(enable); }
302
pw_trace_IsEnabled()303 bool pw_trace_IsEnabled() { return TokenizedTrace::Instance().IsEnabled(); }
304
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)305 void pw_trace_TraceEvent(uint32_t trace_token,
306 pw_trace_EventType event_type,
307 const char* module,
308 uint32_t trace_id,
309 uint8_t flags,
310 const void* data_buffer,
311 size_t data_size) {
312 TokenizedTrace::Instance().HandleTraceEvent(
313 trace_token, event_type, module, trace_id, flags, data_buffer, data_size);
314 }
315
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)316 pw_Status pw_trace_RegisterSink(pw_trace_SinkStartBlock start_func,
317 pw_trace_SinkAddBytes add_bytes_func,
318 pw_trace_SinkEndBlock end_block_func,
319 void* user_data,
320 pw_trace_SinkHandle* handle) {
321 return Callbacks::Instance()
322 .RegisterSink(
323 start_func, add_bytes_func, end_block_func, user_data, handle)
324 .code();
325 }
326
pw_trace_UnregisterSink(pw_trace_EventCallbackHandle handle)327 pw_Status pw_trace_UnregisterSink(pw_trace_EventCallbackHandle handle) {
328 return Callbacks::Instance().UnregisterSink(handle).code();
329 }
330
pw_trace_RegisterEventCallback(pw_trace_EventCallback callback,pw_trace_ShouldCallOnEveryEvent called_on_every_event,void * user_data,pw_trace_EventCallbackHandle * handle)331 pw_Status pw_trace_RegisterEventCallback(
332 pw_trace_EventCallback callback,
333 pw_trace_ShouldCallOnEveryEvent called_on_every_event,
334 void* user_data,
335 pw_trace_EventCallbackHandle* handle) {
336 return Callbacks::Instance()
337 .RegisterEventCallback(
338 callback,
339 static_cast<CallbacksImpl::CallOnEveryEvent>(called_on_every_event),
340 user_data,
341 handle)
342 .code();
343 }
344
pw_trace_UnregisterEventCallback(pw_trace_EventCallbackHandle handle)345 pw_Status pw_trace_UnregisterEventCallback(
346 pw_trace_EventCallbackHandle handle) {
347 return Callbacks::Instance().UnregisterEventCallback(handle).code();
348 }
349
350 PW_EXTERN_C_END
351
352 } // namespace trace
353 } // namespace pw
354