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