ESPHome 2026.3.0-dev
Loading...
Searching...
No Matches
task_log_buffer_libretiny.cpp
Go to the documentation of this file.
1#ifdef USE_LIBRETINY
2
5#include "esphome/core/log.h"
6
7#ifdef USE_ESPHOME_TASK_LOG_BUFFER
8
9namespace esphome::logger {
10
11TaskLogBuffer::TaskLogBuffer(size_t total_buffer_size) {
12 this->size_ = total_buffer_size;
13 // Allocate memory for the circular buffer using ESPHome's RAM allocator
14 RAMAllocator<uint8_t> allocator;
15 this->storage_ = allocator.allocate(this->size_);
16 // Create mutex for thread-safe access
17 this->mutex_ = xSemaphoreCreateMutex();
18}
19
21 if (this->mutex_ != nullptr) {
22 vSemaphoreDelete(this->mutex_);
23 this->mutex_ = nullptr;
24 }
25 if (this->storage_ != nullptr) {
26 RAMAllocator<uint8_t> allocator;
27 allocator.deallocate(this->storage_, this->size_);
28 this->storage_ = nullptr;
29 }
30}
31
32size_t TaskLogBuffer::available_contiguous_space() const {
33 if (this->head_ >= this->tail_) {
34 // head is ahead of or equal to tail
35 // Available space is from head to end, plus from start to tail
36 // But for contiguous, just from head to end (minus 1 to avoid head==tail ambiguity)
37 size_t space_to_end = this->size_ - this->head_;
38 if (this->tail_ == 0) {
39 // Can't use the last byte or head would equal tail
40 return space_to_end > 0 ? space_to_end - 1 : 0;
41 }
42 return space_to_end;
43 } else {
44 // tail is ahead of head
45 // Available contiguous space is from head to tail - 1
46 return this->tail_ - this->head_ - 1;
47 }
48}
49
50bool TaskLogBuffer::borrow_message_main_loop(LogMessage *&message, uint16_t &text_length) {
51 // Check if buffer was initialized successfully
52 if (this->mutex_ == nullptr || this->storage_ == nullptr) {
53 return false;
54 }
55
56 // Try to take mutex without blocking - if busy, we'll get messages next loop iteration
57 if (xSemaphoreTake(this->mutex_, 0) != pdTRUE) {
58 return false;
59 }
60
61 if (this->head_ == this->tail_) {
62 xSemaphoreGive(this->mutex_);
63 return false;
64 }
65
66 // Read message header from tail
67 LogMessage *msg = reinterpret_cast<LogMessage *>(this->storage_ + this->tail_);
68
69 // Check for padding marker (indicates wrap-around)
70 // We check the level field since valid levels are 0-7, and 0xFF indicates padding
71 if (msg->level == PADDING_MARKER_LEVEL) {
72 // Skip to start of buffer and re-read
73 this->tail_ = 0;
74 msg = reinterpret_cast<LogMessage *>(this->storage_);
75 }
76 message = msg;
77 text_length = msg->text_length;
78 this->current_message_size_ = message_total_size(msg->text_length);
79
80 // Keep mutex held until release_message_main_loop()
81 return true;
82}
83
85 // Advance tail past the current message
86 this->tail_ += this->current_message_size_;
87
88 // Handle wrap-around if we've reached the end
89 if (this->tail_ >= this->size_) {
90 this->tail_ = 0;
91 }
92
93 this->message_count_--;
94 this->current_message_size_ = 0;
95
96 xSemaphoreGive(this->mutex_);
97}
98
99bool TaskLogBuffer::send_message_thread_safe(uint8_t level, const char *tag, uint16_t line, const char *thread_name,
100 const char *format, va_list args) {
101 // First, calculate the exact length needed using a null buffer (no actual writing)
102 va_list args_copy;
103 va_copy(args_copy, args);
104 int ret = vsnprintf(nullptr, 0, format, args_copy);
105 va_end(args_copy);
106
107 if (ret <= 0) {
108 return false; // Formatting error or empty message
109 }
110
111 // Calculate actual text length (capped to maximum size)
112 static constexpr size_t MAX_TEXT_SIZE = 255;
113 size_t text_length = (static_cast<size_t>(ret) > MAX_TEXT_SIZE) ? MAX_TEXT_SIZE : ret;
114
115 // Calculate total size needed (header + text length + null terminator)
116 size_t total_size = message_total_size(text_length);
117
118 // Check if buffer was initialized successfully
119 if (this->mutex_ == nullptr || this->storage_ == nullptr) {
120 return false; // Buffer not initialized, fall back to direct output
121 }
122
123 // Try to acquire mutex without blocking - don't block logging tasks
124 if (xSemaphoreTake(this->mutex_, 0) != pdTRUE) {
125 return false; // Mutex busy, fall back to direct output
126 }
127
128 // Check if we have enough contiguous space
129 size_t contiguous = this->available_contiguous_space();
130
131 if (contiguous < total_size) {
132 // Not enough contiguous space at end
133 // Check if we can wrap around
134 size_t space_at_start = (this->head_ >= this->tail_) ? this->tail_ : 0;
135 if (space_at_start > 0) {
136 space_at_start--; // Leave 1 byte gap to distinguish full from empty
137 }
138
139 // Need at least enough space to safely write padding marker (level field is at end of struct)
140 constexpr size_t PADDING_MARKER_MIN_SPACE = offsetof(LogMessage, level) + 1;
141
142 if (space_at_start >= total_size && this->head_ > 0 && contiguous >= PADDING_MARKER_MIN_SPACE) {
143 // Add padding marker (set level field to indicate this is padding, not a real message)
144 LogMessage *padding = reinterpret_cast<LogMessage *>(this->storage_ + this->head_);
145 padding->level = PADDING_MARKER_LEVEL;
146 this->head_ = 0;
147 } else {
148 // Not enough space anywhere, or can't safely write padding marker
149 xSemaphoreGive(this->mutex_);
150 return false;
151 }
152 }
153
154 // Write message header
155 LogMessage *msg = reinterpret_cast<LogMessage *>(this->storage_ + this->head_);
156 msg->level = level;
157 msg->tag = tag;
158 msg->line = line;
159
160 // Store the thread name now to avoid crashes if task is deleted before processing
161 if (thread_name != nullptr) {
162 strncpy(msg->thread_name, thread_name, sizeof(msg->thread_name) - 1);
163 msg->thread_name[sizeof(msg->thread_name) - 1] = '\0';
164 } else {
165 msg->thread_name[0] = '\0';
166 }
167
168 // Format the message text directly into the buffer
169 char *text_area = msg->text_data();
170 ret = vsnprintf(text_area, text_length + 1, format, args);
171
172 if (ret <= 0) {
173 xSemaphoreGive(this->mutex_);
174 return false;
175 }
176
177 // Remove trailing newlines
178 while (text_length > 0 && text_area[text_length - 1] == '\n') {
179 text_length--;
180 }
181
182 msg->text_length = text_length;
183
184 // Advance head
185 this->head_ += total_size;
186
187 // Handle wrap-around (shouldn't happen due to contiguous space check, but be safe)
188 if (this->head_ >= this->size_) {
189 this->head_ = 0;
190 }
191
192 this->message_count_++;
193
194 xSemaphoreGive(this->mutex_);
195 return true;
196}
197
198} // namespace esphome::logger
199
200#endif // USE_ESPHOME_TASK_LOG_BUFFER
201#endif // USE_LIBRETINY
TaskLogBuffer(size_t total_buffer_size)
bool borrow_message_main_loop(LogMessage *&message, uint16_t &text_length)
bool send_message_thread_safe(uint8_t level, const char *tag, uint16_t line, const char *thread_name, const char *format, va_list args)
static constexpr uint8_t PADDING_MARKER_LEVEL
const char * message
Definition component.cpp:38
va_end(args)