1 /*
2  * SPDX-FileCopyrightText: 2017-2021 Espressif Systems (Shanghai) CO LTD
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <string.h>
8 #include "esp_log.h"
9 #include "esp_app_trace.h"
10 #include "esp_app_trace_port.h"
11 
12 #define ESP_APPTRACE_MAX_VPRINTF_ARGS           256
13 #define ESP_APPTRACE_HOST_BUF_SIZE              256
14 
15 #define ESP_APPTRACE_PRINT_LOCK                 0
16 
17 const static char *TAG = "esp_apptrace";
18 
19 /** tracing module internal data */
20 typedef struct {
21     esp_apptrace_hw_t * hw;
22     void *              hw_data;
23 } esp_apptrace_channel_t;
24 
25 static esp_apptrace_channel_t   s_trace_channels[ESP_APPTRACE_DEST_NUM];
26 static bool s_inited;
27 
esp_apptrace_init(void)28 esp_err_t esp_apptrace_init(void)
29 {
30     int res;
31     esp_apptrace_hw_t *hw = NULL;
32     void *hw_data = NULL;
33 
34     // 'esp_apptrace_init()' is called on every core, so ensure to do main initialization only once
35     if (cpu_hal_get_core_id() == 0) {
36         memset(&s_trace_channels, 0, sizeof(s_trace_channels));
37         hw = esp_apptrace_jtag_hw_get(&hw_data);
38         ESP_APPTRACE_LOGD("HW interface %p", hw);
39         if (hw != NULL) {
40             s_trace_channels[ESP_APPTRACE_DEST_JTAG].hw = hw;
41             s_trace_channels[ESP_APPTRACE_DEST_JTAG].hw_data = hw_data;
42         }
43         hw = esp_apptrace_uart_hw_get(0, &hw_data);
44         if (hw != NULL) {
45             s_trace_channels[ESP_APPTRACE_DEST_UART0].hw = hw;
46             s_trace_channels[ESP_APPTRACE_DEST_UART0].hw_data = hw_data;
47         }
48         s_inited = true;
49     }
50 
51     // esp_apptrace_init() is called on every core, so initialize trace channel on every core
52     for (int i = 0; i < sizeof(s_trace_channels)/sizeof(s_trace_channels[0]); i++) {
53         esp_apptrace_channel_t *ch = &s_trace_channels[i];
54         if (ch->hw) {
55             res = ch->hw->init(ch->hw_data);
56             if (res != ESP_OK) {
57                 ESP_APPTRACE_LOGE("Failed to init trace channel HW interface (%d)!", res);
58                 return res;
59             }
60         }
61     }
62 
63     return ESP_OK;
64 }
65 
esp_apptrace_down_buffer_config(uint8_t * buf,uint32_t size)66 void esp_apptrace_down_buffer_config(uint8_t *buf, uint32_t size)
67 {
68     esp_apptrace_channel_t *ch;
69 
70     if (!s_inited) {
71         return;
72     }
73     // currently down buffer is supported for JTAG interface only
74     // TODO: one more argument should be added to this function to specify HW inteface: JTAG, UART0 etc
75     ch = &s_trace_channels[ESP_APPTRACE_DEST_JTAG];
76     if (ch->hw == NULL) {
77         ESP_APPTRACE_LOGE("Trace destination not supported!");
78         return;
79     }
80     if (ch->hw->down_buffer_config == NULL) {
81         return;
82     }
83 
84     ch->hw->down_buffer_config(ch->hw_data, buf, size);
85 }
86 
esp_apptrace_down_buffer_get(esp_apptrace_dest_t dest,uint32_t * size,uint32_t user_tmo)87 uint8_t *esp_apptrace_down_buffer_get(esp_apptrace_dest_t dest, uint32_t *size, uint32_t user_tmo)
88 {
89     esp_apptrace_tmo_t tmo;
90     esp_apptrace_channel_t *ch;
91 
92     ESP_APPTRACE_LOGV("%s(): enter", __func__);
93     if (dest >= ESP_APPTRACE_DEST_MAX) {
94         return NULL;
95     }
96     if (size == NULL || *size == 0) {
97         return NULL;
98     }
99     if (!s_inited) {
100         return NULL;
101     }
102     ch = &s_trace_channels[dest];
103     if (ch->hw == NULL) {
104         ESP_APPTRACE_LOGE("Trace destination %d not supported!", dest);
105         return NULL;
106     }
107     if (ch->hw->get_down_buffer == NULL) {
108         return NULL;
109     }
110 
111     esp_apptrace_tmo_init(&tmo, user_tmo);
112     return ch->hw->get_down_buffer(ch->hw_data, size, &tmo);
113 }
114 
esp_apptrace_down_buffer_put(esp_apptrace_dest_t dest,uint8_t * ptr,uint32_t user_tmo)115 esp_err_t esp_apptrace_down_buffer_put(esp_apptrace_dest_t dest, uint8_t *ptr, uint32_t user_tmo)
116 {
117     esp_apptrace_tmo_t tmo;
118     esp_apptrace_channel_t *ch;
119 
120     ESP_APPTRACE_LOGV("%s(): enter", __func__);
121     if (dest >= ESP_APPTRACE_DEST_MAX) {
122         return ESP_ERR_INVALID_ARG;
123     }
124     if (ptr == NULL) {
125         return ESP_ERR_INVALID_ARG;
126     }
127     if (!s_inited) {
128         return ESP_ERR_INVALID_STATE;
129     }
130     ch = &s_trace_channels[dest];
131     if (ch->hw == NULL) {
132         ESP_APPTRACE_LOGE("Trace destination %d not supported!", dest);
133         return ESP_ERR_NOT_SUPPORTED;
134     }
135     if (ch->hw->get_down_buffer == NULL) {
136         return ESP_ERR_NOT_SUPPORTED;
137     }
138 
139     esp_apptrace_tmo_init(&tmo, user_tmo);
140     return ch->hw->put_down_buffer(ch->hw_data, ptr, &tmo);
141 }
142 
esp_apptrace_read(esp_apptrace_dest_t dest,void * buf,uint32_t * size,uint32_t user_tmo)143 esp_err_t esp_apptrace_read(esp_apptrace_dest_t dest, void *buf, uint32_t *size, uint32_t user_tmo)
144 {
145     int res = ESP_OK;
146     esp_apptrace_tmo_t tmo;
147     esp_apptrace_channel_t *ch;
148 
149     ESP_APPTRACE_LOGV("%s(): enter", __func__);
150     if (dest >= ESP_APPTRACE_DEST_MAX) {
151         return ESP_ERR_INVALID_ARG;
152     }
153     if (buf == NULL || size == NULL || *size == 0) {
154         return ESP_ERR_INVALID_ARG;
155     }
156     if (!s_inited) {
157         return ESP_ERR_INVALID_STATE;
158     }
159     ch = &s_trace_channels[dest];
160     if (ch->hw == NULL) {
161         ESP_APPTRACE_LOGE("Trace destination %d not supported!", dest);
162         return ESP_ERR_NOT_SUPPORTED;
163     }
164     if (ch->hw->get_down_buffer == NULL || ch->hw->put_down_buffer == NULL) {
165         return ESP_ERR_NOT_SUPPORTED;
166     }
167 
168     //TODO: callback system
169     esp_apptrace_tmo_init(&tmo, user_tmo);
170     uint32_t act_sz = *size;
171     *size = 0;
172     uint8_t * ptr = ch->hw->get_down_buffer(ch->hw_data, &act_sz, &tmo);
173     if (ptr && act_sz > 0) {
174         ESP_APPTRACE_LOGD("Read %d bytes from host", act_sz);
175         memcpy(buf, ptr, act_sz);
176         res = ch->hw->put_down_buffer(ch->hw_data, ptr, &tmo);
177         *size = act_sz;
178     } else {
179         res = ESP_ERR_TIMEOUT;
180     }
181 
182     return res;
183 }
184 
esp_apptrace_buffer_get(esp_apptrace_dest_t dest,uint32_t size,uint32_t user_tmo)185 uint8_t *esp_apptrace_buffer_get(esp_apptrace_dest_t dest, uint32_t size, uint32_t user_tmo)
186 {
187     esp_apptrace_tmo_t tmo;
188     esp_apptrace_channel_t *ch;
189 
190     ESP_APPTRACE_LOGV("%s(): enter", __func__);
191     if (dest >= ESP_APPTRACE_DEST_MAX) {
192         return NULL;
193     }
194     if (size == 0) {
195         return NULL;
196     }
197     if (!s_inited) {
198         return NULL;
199     }
200     ch = &s_trace_channels[dest];
201     if (ch->hw == NULL) {
202         ESP_APPTRACE_LOGE("Trace destination %d not supported!", dest);
203         return NULL;
204     }
205     if (ch->hw->get_up_buffer == NULL) {
206         return NULL;
207     }
208 
209     esp_apptrace_tmo_init(&tmo, user_tmo);
210     return ch->hw->get_up_buffer(ch->hw_data, size, &tmo);
211 }
212 
esp_apptrace_buffer_put(esp_apptrace_dest_t dest,uint8_t * ptr,uint32_t user_tmo)213 esp_err_t esp_apptrace_buffer_put(esp_apptrace_dest_t dest, uint8_t *ptr, uint32_t user_tmo)
214 {
215     esp_apptrace_tmo_t tmo;
216     esp_apptrace_channel_t *ch;
217 
218     ESP_APPTRACE_LOGV("%s(): enter", __func__);
219     if (dest >= ESP_APPTRACE_DEST_MAX) {
220         return ESP_ERR_INVALID_ARG;
221     }
222     if (ptr == NULL) {
223         return ESP_ERR_INVALID_ARG;
224     }
225     if (!s_inited) {
226         return ESP_ERR_INVALID_STATE;
227     }
228     ch = &s_trace_channels[dest];
229     if (ch->hw == NULL) {
230         ESP_APPTRACE_LOGE("Trace destination %d not supported!", dest);
231         return ESP_ERR_NOT_SUPPORTED;
232     }
233     if (ch->hw->put_up_buffer == NULL) {
234         return ESP_ERR_NOT_SUPPORTED;
235     }
236 
237     esp_apptrace_tmo_init(&tmo, user_tmo);
238     return ch->hw->put_up_buffer(ch->hw_data, ptr, &tmo);
239 }
240 
esp_apptrace_write(esp_apptrace_dest_t dest,const void * data,uint32_t size,uint32_t user_tmo)241 esp_err_t esp_apptrace_write(esp_apptrace_dest_t dest, const void *data, uint32_t size, uint32_t user_tmo)
242 {
243     uint8_t *ptr = NULL;
244     esp_apptrace_tmo_t tmo;
245     esp_apptrace_channel_t *ch;
246 
247     ESP_APPTRACE_LOGV("%s(): enter", __func__);
248     if (dest >= ESP_APPTRACE_DEST_MAX) {
249         return ESP_ERR_INVALID_ARG;
250     }
251     if (data == NULL || size == 0) {
252         return ESP_ERR_INVALID_ARG;
253     }
254     if (!s_inited) {
255         return ESP_ERR_INVALID_STATE;
256     }
257     ch = &s_trace_channels[dest];
258     if (ch->hw == NULL) {
259         ESP_APPTRACE_LOGE("Trace destination %d not supported!", dest);
260         return ESP_ERR_NOT_SUPPORTED;
261     }
262     if (ch->hw->get_up_buffer == NULL || ch->hw->put_up_buffer == NULL) {
263         return ESP_ERR_NOT_SUPPORTED;
264     }
265 
266     esp_apptrace_tmo_init(&tmo, user_tmo);
267     ptr = ch->hw->get_up_buffer(ch->hw_data, size, &tmo);
268     if (ptr == NULL) {
269         return ESP_ERR_NO_MEM;
270     }
271 
272     // actually can be suspended here by higher prio tasks/ISRs
273     //TODO: use own memcpy with dead trace calls kick-off algo and tmo expiration check
274     memcpy(ptr, data, size);
275 
276     // now indicate that this buffer is ready to be sent off to host
277     return ch->hw->put_up_buffer(ch->hw_data, ptr, &tmo);
278 }
279 
esp_apptrace_vprintf_to(esp_apptrace_dest_t dest,uint32_t user_tmo,const char * fmt,va_list ap)280 int esp_apptrace_vprintf_to(esp_apptrace_dest_t dest, uint32_t user_tmo, const char *fmt, va_list ap)
281 {
282     uint16_t nargs = 0;
283     uint8_t *pout, *p = (uint8_t *)fmt;
284     esp_apptrace_tmo_t tmo;
285     esp_apptrace_channel_t *ch;
286 
287     ESP_APPTRACE_LOGV("%s(): enter", __func__);
288     if (dest >= ESP_APPTRACE_DEST_MAX) {
289         return -1;
290     }
291     if (fmt == NULL) {
292         return -1;
293     }
294     if (!s_inited) {
295         return -1;
296     }
297     ch = &s_trace_channels[dest];
298     if (ch->hw == NULL) {
299         ESP_APPTRACE_LOGE("Trace destination %d not supported!", dest);
300         return -1;
301     }
302     if (ch->hw->get_up_buffer == NULL || ch->hw->put_up_buffer == NULL) {
303         return -1;
304     }
305 
306     esp_apptrace_tmo_init(&tmo, user_tmo);
307     ESP_APPTRACE_LOGD("fmt %x", fmt);
308     while ((p = (uint8_t *)strchr((char *)p, '%')) && nargs < ESP_APPTRACE_MAX_VPRINTF_ARGS) {
309         p++;
310         if (*p != '%' && *p != 0) {
311             nargs++;
312         }
313     }
314     ESP_APPTRACE_LOGD("nargs = %d", nargs);
315     if (p) {
316         ESP_APPTRACE_LOGE("Failed to store all printf args!");
317     }
318 
319     pout = ch->hw->get_up_buffer(ch->hw_data, 1 + sizeof(char *) + nargs * sizeof(uint32_t), &tmo);
320     if (pout == NULL) {
321         ESP_APPTRACE_LOGE("Failed to get buffer!");
322         return -1;
323     }
324     p = pout;
325     *pout = nargs;
326     pout++;
327     *(const char **)pout = fmt;
328     pout += sizeof(char *);
329     while (nargs-- > 0) {
330         uint32_t arg = va_arg(ap, uint32_t);
331         *(uint32_t *)pout = arg;
332         pout += sizeof(uint32_t);
333         ESP_APPTRACE_LOGD("arg %x", arg);
334     }
335 
336     int ret = ch->hw->put_up_buffer(ch->hw_data, p, &tmo);
337     if (ret != ESP_OK) {
338         ESP_APPTRACE_LOGE("Failed to put printf buf (%d)!", ret);
339         return -1;
340     }
341 
342     return (pout - p);
343 }
344 
esp_apptrace_vprintf(const char * fmt,va_list ap)345 int esp_apptrace_vprintf(const char *fmt, va_list ap)
346 {
347     return esp_apptrace_vprintf_to(ESP_APPTRACE_DEST_JTAG, 0, fmt, ap);
348 }
349 
esp_apptrace_flush_nolock(esp_apptrace_dest_t dest,uint32_t min_sz,uint32_t usr_tmo)350 esp_err_t esp_apptrace_flush_nolock(esp_apptrace_dest_t dest, uint32_t min_sz, uint32_t usr_tmo)
351 {
352     esp_apptrace_tmo_t tmo;
353     esp_apptrace_channel_t *ch;
354 
355     ESP_APPTRACE_LOGV("%s(): enter", __func__);
356     if (dest >= ESP_APPTRACE_DEST_MAX) {
357         return ESP_ERR_INVALID_ARG;
358     }
359     if (!s_inited) {
360         return ESP_ERR_INVALID_STATE;
361     }
362     ch = &s_trace_channels[dest];
363     if (ch->hw == NULL) {
364         ESP_APPTRACE_LOGE("Trace destination %d not supported!", dest);
365         return ESP_ERR_NOT_SUPPORTED;
366     }
367     if (ch->hw->flush_up_buffer_nolock == NULL) {
368         return ESP_ERR_NOT_SUPPORTED;
369     }
370 
371     esp_apptrace_tmo_init(&tmo, usr_tmo);
372     return ch->hw->flush_up_buffer_nolock(ch->hw_data, min_sz, &tmo);
373 }
374 
esp_apptrace_flush(esp_apptrace_dest_t dest,uint32_t usr_tmo)375 esp_err_t esp_apptrace_flush(esp_apptrace_dest_t dest, uint32_t usr_tmo)
376 {
377     esp_apptrace_tmo_t tmo;
378     esp_apptrace_channel_t *ch;
379 
380     ESP_APPTRACE_LOGV("%s(): enter", __func__);
381     if (dest >= ESP_APPTRACE_DEST_MAX) {
382         return ESP_ERR_INVALID_ARG;
383     }
384     if (!s_inited) {
385         return ESP_ERR_INVALID_STATE;
386     }
387     ch = &s_trace_channels[dest];
388     if (ch->hw == NULL) {
389         ESP_APPTRACE_LOGE("Trace destination %d not supported!", dest);
390         return ESP_ERR_NOT_SUPPORTED;
391     }
392     if (ch->hw->flush_up_buffer == NULL) {
393         return ESP_ERR_NOT_SUPPORTED;
394     }
395 
396     esp_apptrace_tmo_init(&tmo, usr_tmo);
397     return ch->hw->flush_up_buffer(ch->hw_data, &tmo);
398 }
399 
esp_apptrace_host_is_connected(esp_apptrace_dest_t dest)400 bool esp_apptrace_host_is_connected(esp_apptrace_dest_t dest)
401 {
402     esp_apptrace_channel_t *ch;
403 
404     ESP_APPTRACE_LOGV("%s(): enter", __func__);
405     if (dest >= ESP_APPTRACE_DEST_MAX) {
406         return false;
407     }
408     if (!s_inited) {
409         return false;
410     }
411     ch = &s_trace_channels[dest];
412     if (ch->hw == NULL) {
413         ESP_APPTRACE_LOGE("Trace destination %d not supported!", dest);
414         return false;
415     }
416     if (ch->hw->host_is_connected == NULL) {
417         return false;
418     }
419 
420     return ch->hw->host_is_connected(ch->hw_data);
421 }
422