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 if (data_size > PW_TRACE_BUFFER_MAX_DATA_SIZE_BYTES) { 69 return pw::Status::InvalidArgument(); 70 } 71 event_queue_[head_].trace_token = trace_token; 72 event_queue_[head_].event_type = event_type; 73 event_queue_[head_].module = module; 74 event_queue_[head_].trace_id = trace_id; 75 event_queue_[head_].flags = flags; 76 event_queue_[head_].data_size = data_size; 77 for (size_t i = 0; i < data_size; i++) { 78 event_queue_[head_].data_buffer[i] = 79 reinterpret_cast<const std::byte*>(data_buffer)[i]; 80 } 81 head_ = (head_ + 1) % kSize; 82 is_empty_ = false; 83 return pw::OkStatus(); 84 } 85 PeekFront()86 const volatile QueueEventBlock* PeekFront() const { 87 if (IsEmpty()) { 88 return nullptr; 89 } 90 return &event_queue_[tail_]; 91 } 92 PopFront()93 void PopFront() { 94 if (!IsEmpty()) { 95 tail_ = (tail_ + 1) % kSize; 96 is_empty_ = (tail_ == head_); 97 } 98 } 99 Clear()100 void Clear() { 101 head_ = 0; 102 tail_ = 0; 103 is_empty_ = true; 104 } 105 IsEmpty()106 bool IsEmpty() const { return is_empty_; } IsFull()107 bool IsFull() const { return !is_empty_ && (head_ == tail_); } 108 109 private: 110 std::array<volatile QueueEventBlock, kSize> event_queue_; 111 volatile size_t head_ = 0; // Next write 112 volatile size_t tail_ = 0; // Next read 113 volatile bool is_empty_ = 114 true; // Used to distinquish if head==tail is empty or full 115 }; 116 117 } // namespace internal 118 119 class TokenizedTraceImpl { 120 public: Enable(bool enable)121 void Enable(bool enable) { 122 if (enable != enabled_ && enable) { 123 event_queue_.Clear(); 124 } 125 enabled_ = enable; 126 } IsEnabled()127 bool IsEnabled() const { return enabled_; } 128 129 void HandleTraceEvent(uint32_t trace_token, 130 EventType event_type, 131 const char* module, 132 uint32_t trace_id, 133 uint8_t flags, 134 const void* data_buffer, 135 size_t data_size); 136 137 private: 138 using TraceQueue = internal::TraceQueue<PW_TRACE_QUEUE_SIZE_EVENTS>; 139 PW_TRACE_TIME_TYPE last_trace_time_ = 0; 140 bool enabled_ = false; 141 TraceQueue event_queue_; 142 143 void HandleNextItemInQueue( 144 const volatile TraceQueue::QueueEventBlock* event_block); 145 }; 146 147 // A singleton object of the TokenizedTraceImpl class which can be used to 148 // interface with trace using the C++ API. 149 // Example: pw::trace::TokenizedTrace::Instance().Enable(true); 150 class TokenizedTrace { 151 public: Instance()152 static TokenizedTraceImpl& Instance() { return instance_; }; 153 154 private: 155 static TokenizedTraceImpl instance_; 156 }; 157 158 } // namespace trace 159 } // namespace pw 160 #endif // __cplusplus 161 162 // PW_TRACE_SET_ENABLED is used to enable or disable tracing. 163 #define PW_TRACE_SET_ENABLED(enabled) pw_trace_Enable(enabled) 164 165 // PW_TRACE_REF provides the uint32_t token value for a specific trace event. 166 // this can be used in the callback to perform specific actions for that trace. 167 // All the fields must match exactly to generate the correct trace reference. 168 // If the trace does not have a group, use PW_TRACE_GROUP_LABEL_DEFAULT. 169 // 170 // For example this can be used to skip a specific trace: 171 // pw_trace_TraceEventReturnFlags TraceEventCallback( 172 // uint32_t trace_ref, 173 // pw_trace_EventType event_type, 174 // const char* module, 175 // uint32_t trace_id, 176 // uint8_t flags) { 177 // auto skip_trace_ref = PW_TRACE_REF(PW_TRACE_TYPE_INSTANT, 178 // "test_module", // Module 179 // "test_label", // Label 180 // PW_TRACE_FLAGS_DEFAULT, 181 // PW_TRACE_GROUP_LABEL_DEFAULT); 182 // if (trace_ref == skip_trace_ref) { 183 // return PW_TRACE_EVENT_RETURN_FLAGS_SKIP_EVENT; 184 // } 185 // return 0; 186 // } 187 // 188 // The above trace ref would provide the tokenize value for the string: 189 // "1|0|test_module||test_label" 190 // 191 // Another example: 192 // #define PW_TRACE_MODULE test_module 193 // PW_TRACE_INSTANT_DATA_FLAG(2, "label", "group", id, "%d", 5, 1); 194 // Would internally generate a token value for the string: 195 // "1|2|test_module|group|label|%d" 196 // The trace_id, and data value are runtime values and not included in the 197 // token string. 198 #define PW_TRACE_REF(event_type, module, label, flags, group) \ 199 PW_TOKENIZE_STRING_DOMAIN("trace", \ 200 PW_STRINGIFY(event_type) "|" PW_STRINGIFY( \ 201 flags) "|" module "|" group "|" label) 202 203 #define PW_TRACE_REF_DATA(event_type, module, label, flags, group, type) \ 204 PW_TOKENIZE_STRING_DOMAIN( \ 205 "trace", \ 206 PW_STRINGIFY(event_type) "|" PW_STRINGIFY(flags) "|" module "|" group \ 207 "|" label "|" type) 208