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