• 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 // This file provides the interface for working with the tokenized trace
17 // backend.
18 
19 #pragma once
20 
21 #include <stdbool.h>
22 #include <stdint.h>
23 #include <string.h>
24 
25 #ifndef PW_TRACE_GET_TIME_DELTA
26 #ifdef __cplusplus
27 #include <type_traits>
28 #endif  // __cplusplus
29 #endif  // PW_TRACE_GET_TIME_DELTA
30 
31 #include "pw_status/status.h"
32 #include "pw_tokenizer/tokenize.h"
33 #include "pw_trace_tokenized/config.h"
34 #include "pw_trace_tokenized/internal/trace_tokenized_internal.h"
35 
36 #ifdef __cplusplus
37 namespace pw {
38 namespace trace {
39 
40 using EventType = pw_trace_EventType;
41 
42 namespace internal {
43 
44 // Simple ring buffer which is suitable for use in a critical section.
45 template <size_t kSize>
46 class TraceQueue {
47  public:
48   struct QueueEventBlock {
49     uint32_t trace_token;
50     EventType event_type;
51     const char* module;
52     uint32_t trace_id;
53     uint8_t flags;
54     size_t data_size;
55     std::byte data_buffer[PW_TRACE_BUFFER_MAX_DATA_SIZE_BYTES];
56   };
57 
TryPushBack(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)58   pw::Status TryPushBack(uint32_t trace_token,
59                          EventType event_type,
60                          const char* module,
61                          uint32_t trace_id,
62                          uint8_t flags,
63                          const void* data_buffer,
64                          size_t data_size) {
65     if (IsFull()) {
66       return pw::Status::ResourceExhausted();
67     }
68     event_queue_[head_].trace_token = trace_token;
69     event_queue_[head_].event_type = event_type;
70     event_queue_[head_].module = module;
71     event_queue_[head_].trace_id = trace_id;
72     event_queue_[head_].flags = flags;
73     event_queue_[head_].data_size = data_size;
74     for (size_t i = 0; i < data_size; i++) {
75       event_queue_[head_].data_buffer[i] =
76           reinterpret_cast<const std::byte*>(data_buffer)[i];
77     }
78     head_ = (head_ + 1) % kSize;
79     is_empty_ = false;
80     return pw::OkStatus();
81   }
82 
PeekFront()83   const volatile QueueEventBlock* PeekFront() const {
84     if (IsEmpty()) {
85       return nullptr;
86     }
87     return &event_queue_[tail_];
88   }
89 
PopFront()90   void PopFront() {
91     if (!IsEmpty()) {
92       tail_ = (tail_ + 1) % kSize;
93       is_empty_ = (tail_ == head_);
94     }
95   }
96 
Clear()97   void Clear() {
98     head_ = 0;
99     tail_ = 0;
100     is_empty_ = true;
101   }
102 
IsEmpty()103   bool IsEmpty() const { return is_empty_; }
IsFull()104   bool IsFull() const { return !is_empty_ && (head_ == tail_); }
105 
106  private:
107   std::array<volatile QueueEventBlock, kSize> event_queue_;
108   volatile size_t head_ = 0;  // Next write
109   volatile size_t tail_ = 0;  // Next read
110   volatile bool is_empty_ =
111       true;  // Used to distinquish if head==tail is empty or full
112 };
113 
114 }  // namespace internal
115 
116 class TokenizedTraceImpl {
117  public:
Enable(bool enable)118   void Enable(bool enable) {
119     if (enable != enabled_ && enable) {
120       event_queue_.Clear();
121     }
122     enabled_ = enable;
123   }
IsEnabled()124   bool IsEnabled() const { return enabled_; }
125 
126   void HandleTraceEvent(uint32_t trace_token,
127                         EventType event_type,
128                         const char* module,
129                         uint32_t trace_id,
130                         uint8_t flags,
131                         const void* data_buffer,
132                         size_t data_size);
133 
134  private:
135   using TraceQueue = internal::TraceQueue<PW_TRACE_QUEUE_SIZE_EVENTS>;
136   PW_TRACE_TIME_TYPE last_trace_time_ = 0;
137   bool enabled_ = false;
138   TraceQueue event_queue_;
139 
140   void HandleNextItemInQueue(
141       const volatile TraceQueue::QueueEventBlock* event_block);
142 };
143 
144 // A singleton object of the TokenizedTraceImpl class which can be used to
145 // interface with trace using the C++ API.
146 // Example: pw::trace::TokenizedTrace::Instance().Enable(true);
147 class TokenizedTrace {
148  public:
Instance()149   static TokenizedTraceImpl& Instance() { return instance_; };
150 
151  private:
152   static TokenizedTraceImpl instance_;
153 };
154 
155 }  // namespace trace
156 }  // namespace pw
157 #endif  // __cplusplus
158 
159 // PW_TRACE_SET_ENABLED is used to enable or disable tracing.
160 #define PW_TRACE_SET_ENABLED(enabled) pw_trace_Enable(enabled)
161 
162 // PW_TRACE_REF provides the uint32_t token value for a specific trace event.
163 // this can be used in the callback to perform specific actions for that trace.
164 // All the fields must match exactly to generate the correct trace reference.
165 // If the trace does not have a group, use PW_TRACE_GROUP_LABEL_DEFAULT.
166 //
167 // For example this can be used to skip a specific trace:
168 //   pw_trace_TraceEventReturnFlags TraceEventCallback(
169 //       uint32_t trace_ref,
170 //       pw_trace_EventType event_type,
171 //       const char* module,
172 //       uint32_t trace_id,
173 //       uint8_t flags) {
174 //     auto skip_trace_ref = PW_TRACE_REF(PW_TRACE_TYPE_INSTANT,
175 //                                        "test_module",    // Module
176 //                                        "test_label",     // Label
177 //                                        PW_TRACE_FLAGS_DEFAULT,
178 //                                        PW_TRACE_GROUP_LABEL_DEFAULT);
179 //     if (trace_ref == skip_trace_ref) {
180 //       return PW_TRACE_EVENT_RETURN_FLAGS_SKIP_EVENT;
181 //     }
182 //     return 0;
183 //   }
184 //
185 // The above trace ref would provide the tokenize value for the string:
186 //     "1|0|test_module||test_label"
187 //
188 // Another example:
189 //    #define PW_TRACE_MODULE test_module
190 //    PW_TRACE_INSTANT_DATA_FLAG(2, "label", "group", id, "%d", 5, 1);
191 // Would internally generate a token value for the string:
192 //    "1|2|test_module|group|label|%d"
193 // The trace_id, and data value are runtime values and not included in the
194 // token string.
195 #define PW_TRACE_REF(event_type, module, label, flags, group)          \
196   PW_TOKENIZE_STRING_DOMAIN("trace",                                   \
197                             PW_STRINGIFY(event_type) "|" PW_STRINGIFY( \
198                                 flags) "|" module "|" group "|" label)
199 
200 #define PW_TRACE_REF_DATA(event_type, module, label, flags, group, type)    \
201   PW_TOKENIZE_STRING_DOMAIN(                                                \
202       "trace",                                                              \
203       PW_STRINGIFY(event_type) "|" PW_STRINGIFY(flags) "|" module "|" group \
204                                                        "|" label "|" type)
205