• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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