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