#include #include #include "chip.h" #include "board.h" #include "ulog.h" #define ULOG_ALIGN(size, align) (((size) + (align) - 1) & ~((align) - 1)) #define ULOG_ALIGN_SIZE 4 #ifdef ULOG_USING_SYSLOG #include #endif #ifdef ULOG_OUTPUT_FLOAT #include #endif #ifdef ULOG_USING_ASYNC_OUTPUT #include "ringblk_buf.h" #endif #ifdef USE_ULOG /* the number which is max stored line logs */ #ifndef ULOG_ASYNC_OUTPUT_STORE_LINES #define ULOG_ASYNC_OUTPUT_STORE_LINES (ULOG_ASYNC_OUTPUT_BUF_SIZE * 3 / 2 / ULOG_LINE_BUF_SIZE) #endif #ifdef ULOG_USING_COLOR /** * CSI(Control Sequence Introducer/Initiator) sign * more information on https://en.wikipedia.org/wiki/ANSI_escape_code */ #define CSI_START "\033[" #define CSI_END "\033[0m" /* output log front color */ #define F_BLACK "30m" #define F_RED "31m" #define F_GREEN "32m" #define F_YELLOW "33m" #define F_BLUE "34m" #define F_MAGENTA "35m" #define F_CYAN "36m" #define F_WHITE "37m" /* output log default color definition */ #ifndef ULOG_COLOR_DEBUG #define ULOG_COLOR_DEBUG NULL #endif #ifndef ULOG_COLOR_INFO #define ULOG_COLOR_INFO (F_GREEN) #endif #ifndef ULOG_COLOR_WARN #define ULOG_COLOR_WARN (F_YELLOW) #endif #ifndef ULOG_COLOR_ERROR #define ULOG_COLOR_ERROR (F_RED) #endif #ifndef ULOG_COLOR_ASSERT #define ULOG_COLOR_ASSERT (F_MAGENTA) #endif #endif /* ULOG_USING_COLOR */ #if ULOG_LINE_BUF_SIZE < 80 #error "the log line buffer size must more than 80" #endif struct ulog { int init_ok; SemaphoreHandle_t output_locker; /* all backends */ List_t backend_list; /* the thread log's line buffer */ char log_buf_th[ULOG_LINE_BUF_SIZE + 1]; #ifdef ULOG_USING_ISR_LOG /* the ISR log's line buffer */ UBaseType_t output_locker_isr_lvl; char log_buf_isr[ULOG_LINE_BUF_SIZE + 1]; #endif /* ULOG_USING_ISR_LOG */ #ifdef ULOG_USING_ASYNC_OUTPUT rbb_t async_rbb; TaskHandle_t async_th; QueueHandle_t async_notice; #endif #ifdef ULOG_USING_FILTER struct { /* all tag's level filter */ List_t tag_lvl_list; /* global filter level, tag and keyword */ uint32_t level; char tag[ULOG_FILTER_TAG_MAX_LEN + 1]; char keyword[ULOG_FILTER_KW_MAX_LEN + 1]; } filter; #endif /* ULOG_USING_FILTER */ }; #ifdef ULOG_OUTPUT_LEVEL /* level output info */ static const char * const level_output_info[] = { "A/", NULL, NULL, "E/", "W/", NULL, "I/", "D/", }; #endif #ifdef ULOG_USING_COLOR /* color output info */ static const char * const color_output_info[] = { ULOG_COLOR_ASSERT, NULL, NULL, ULOG_COLOR_ERROR, ULOG_COLOR_WARN, NULL, ULOG_COLOR_INFO, ULOG_COLOR_DEBUG, }; #endif /* ULOG_USING_COLOR */ /* ulog local object */ static struct ulog ulog = { 0 }; size_t ulog_strcpy(size_t cur_len, char *dst, const char *src) { const char *src_old = src; configASSERT(dst); configASSERT(src); while (*src != 0) { /* make sure destination has enough space */ if (cur_len++ < ULOG_LINE_BUF_SIZE) { *dst++ = *src++; } else { break; } } return src - src_old; } size_t ulog_ultoa(char *s, unsigned long int n) { size_t i = 0, j = 0, len = 0; char swap; do { s[len++] = n % 10 + '0'; } while (n /= 10); s[len] = '\0'; /* reverse string */ for (i = 0, j = len - 1; i < j; ++i, --j) { swap = s[i]; s[i] = s[j]; s[j] = swap; } return len; } static void output_unlock(void) { /* is in thread context */ if (interrupt_get_nest() == 0) { xSemaphoreGive(ulog.output_locker); } else { #ifdef ULOG_USING_ISR_LOG portCLEAR_INTERRUPT_MASK_FROM_ISR(ulog.output_locker_isr_lvl); #endif } } static void output_lock(void) { /* is in thread context */ if (interrupt_get_nest() == 0) { xSemaphoreTake(ulog.output_locker, portMAX_DELAY); } else { #ifdef ULOG_USING_ISR_LOG ulog.output_locker_isr_lvl = portSET_INTERRUPT_MASK_FROM_ISR(); #endif } } static char *get_log_buf(void) { /* is in thread context */ if (interrupt_get_nest() == 0) { return ulog.log_buf_th; } else { #ifdef ULOG_USING_ISR_LOG return ulog.log_buf_isr; #else printf("Error: Current mode not supported run in ISR. Please enable ULOG_USING_ISR_LOG.\n"); return NULL; #endif } } WEAK size_t ulog_formater(char *log_buf, uint32_t level, const char *tag, int newline, const char *format, va_list args) { /* the caller has locker, so it can use static variable for reduce stack usage */ static size_t log_len, newline_len; static int fmt_result; configASSERT(log_buf); configASSERT(level <= LOG_LVL_DBG); configASSERT(tag); configASSERT(format); log_len = 0; newline_len = strlen(ULOG_NEWLINE_SIGN); #ifdef ULOG_USING_COLOR /* add CSI start sign and color info */ if (color_output_info[level]) { log_len += ulog_strcpy(log_len, log_buf + log_len, CSI_START); log_len += ulog_strcpy(log_len, log_buf + log_len, color_output_info[level]); } #endif /* ULOG_USING_COLOR */ #ifdef ULOG_OUTPUT_TIME /* add time info */ { #ifdef ULOG_TIME_USING_TIMESTAMP SystemTime_t tm; iGetLocalTime(&tm); snprintf(log_buf + log_len, ULOG_LINE_BUF_SIZE - log_len, "%02d-%02d %02d:%02d:%02d", tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); #else static size_t tick_len = 0; log_buf[log_len] = '['; tick_len = ulog_ultoa(log_buf + log_len + 1, xTaskGetTickCount()); log_buf[log_len + 1 + tick_len] = ']'; log_buf[log_len + 1 + tick_len + 1] = '\0'; #endif /* ULOG_TIME_USING_TIMESTAMP */ log_len += strlen(log_buf + log_len); } #endif /* ULOG_OUTPUT_TIME */ #ifdef ULOG_OUTPUT_LEVEL #ifdef ULOG_OUTPUT_TIME log_len += ulog_strcpy(log_len, log_buf + log_len, " "); #endif /* add level info */ log_len += ulog_strcpy(log_len, log_buf + log_len, level_output_info[level]); #endif /* ULOG_OUTPUT_LEVEL */ #ifdef ULOG_OUTPUT_TAG #if !defined(ULOG_OUTPUT_LEVEL) && defined(ULOG_OUTPUT_TIME) log_len += ulog_strcpy(log_len, log_buf + log_len, " "); #endif /* add tag info */ log_len += ulog_strcpy(log_len, log_buf + log_len, tag); #endif /* ULOG_OUTPUT_TAG */ #ifdef ULOG_OUTPUT_THREAD_NAME /* add thread info */ { #if defined(ULOG_OUTPUT_TIME) || defined(ULOG_OUTPUT_LEVEL) || defined(ULOG_OUTPUT_TAG) log_len += ulog_strcpy(log_len, log_buf + log_len, " "); #endif /* is not in interrupt context */ if (interrupt_get_nest() == 0) { size_t name_len = strnlen(xTaskGetCurrentTaskHandle()->pcTaskName, ULOG_NAME_MAX); strncpy(log_buf + log_len, xTaskGetCurrentTaskHandle()->pcTaskName, name_len); log_len += name_len; } else { log_len += ulog_strcpy(log_len, log_buf + log_len, "ISR"); } } #endif /* ULOG_OUTPUT_THREAD_NAME */ log_len += ulog_strcpy(log_len, log_buf + log_len, ": "); #ifdef ULOG_OUTPUT_FLOAT fmt_result = vsnprintf(log_buf + log_len, ULOG_LINE_BUF_SIZE - log_len, format, args); #else fmt_result = vsnprintf(log_buf + log_len, ULOG_LINE_BUF_SIZE - log_len, format, args); #endif /* ULOG_OUTPUT_FLOAT */ /* calculate log length */ if ((log_len + fmt_result <= ULOG_LINE_BUF_SIZE) && (fmt_result > -1)) { log_len += fmt_result; } else { /* using max length */ log_len = ULOG_LINE_BUF_SIZE; } /* overflow check and reserve some space for CSI end sign and newline sign */ #ifdef ULOG_USING_COLOR if (log_len + (sizeof(CSI_END) - 1) + newline_len > ULOG_LINE_BUF_SIZE) { /* using max length */ log_len = ULOG_LINE_BUF_SIZE; /* reserve some space for CSI end sign */ log_len -= (sizeof(CSI_END) - 1); #else if (log_len + newline_len > ULOG_LINE_BUF_SIZE) { /* using max length */ log_len = ULOG_LINE_BUF_SIZE; #endif /* ULOG_USING_COLOR */ /* reserve some space for newline sign */ log_len -= newline_len; } /* package newline sign */ if (newline) { log_len += ulog_strcpy(log_len, log_buf + log_len, ULOG_NEWLINE_SIGN); } #ifdef ULOG_USING_COLOR /* add CSI end sign */ if (color_output_info[level]) { log_len += ulog_strcpy(log_len, log_buf + log_len, CSI_END); } #endif /* ULOG_USING_COLOR */ return log_len; } void ulog_output_to_all_backend(uint32_t level, const char *tag, int is_raw, const char *log, size_t size) { ListItem_t *node; ulog_backend_t backend; if (!ulog.init_ok) return; /* output for all backends */ for (node = listGET_HEAD_ENTRY(&ulog.backend_list); node != listGET_END_MARKER(&ulog.backend_list); node = listGET_NEXT(node)) { backend = listGET_LIST_ITEM_OWNER(node); #if !defined(ULOG_USING_COLOR) || defined(ULOG_USING_SYSLOG) backend->output(backend, level, tag, is_raw, log, size); #else if (backend->support_color || is_raw) { backend->output(backend, level, tag, is_raw, log, size); } else { /* recalculate the log start address and log size when backend not supported color */ size_t color_info_len = strlen(color_output_info[level]), output_size = size; if (color_info_len) { size_t color_hdr_len = strlen(CSI_START) + color_info_len; log += color_hdr_len; output_size -= (color_hdr_len + (sizeof(CSI_END) - 1)); } backend->output(backend, level, tag, is_raw, log, output_size); } #endif /* !defined(ULOG_USING_COLOR) || defined(ULOG_USING_SYSLOG) */ } } static void do_output(uint32_t level, const char *tag, int is_raw, const char *log_buf, size_t log_len) { #ifdef ULOG_USING_ASYNC_OUTPUT rbb_blk_t log_blk; ulog_frame_t log_frame; /* allocate log frame */ log_blk = rbb_blk_alloc(ulog.async_rbb, ULOG_ALIGN(sizeof(struct ulog_frame) + log_len, ULOG_ALIGN_SIZE)); if (log_blk) { /* package the log frame */ log_frame = (ulog_frame_t) log_blk->buf; log_frame->magic = ULOG_FRAME_MAGIC; log_frame->is_raw = is_raw; log_frame->level = level; log_frame->log_len = log_len; log_frame->tag = tag; log_frame->log = (const char *)log_blk->buf + sizeof(struct ulog_frame); /* copy log data */ memcpy(log_blk->buf + sizeof(struct ulog_frame), log_buf, log_len); /* put the block */ rbb_blk_put(log_blk); /* send a notice */ if (interrupt_get_nest() == 0) xQueueSend(ulog.async_notice, NULL, 0); else xQueueSendFromISR(ulog.async_notice, NULL, 0); } else { static int already_output = pdFALSE; if (already_output == pdFALSE) { printf("Warning: There is no enough buffer for saving async log," " please increase the ULOG_ASYNC_OUTPUT_BUF_SIZE option.\n"); already_output = pdTRUE; } } #else /* is in thread context */ if (interrupt_get_nest() == 0) { /* output to all backends */ ulog_output_to_all_backend(level, tag, is_raw, log_buf, log_len); } else { #ifdef ULOG_BACKEND_USING_CONSOLE /* We can't ensure that all backends support ISR context output. * So only using printf when context is ISR */ extern void ulog_console_backend_output(struct ulog_backend *backend, uint32_t level, const char *tag, int is_raw, const char *log, size_t len); ulog_console_backend_output(NULL, level, tag, is_raw, log_buf, log_len); #endif /* ULOG_BACKEND_USING_CONSOLE */ } #endif /* ULOG_USING_ASYNC_OUTPUT */ } /** * output the log by variable argument list * * @param level level * @param tag tag * @param newline has_newline * @param format output format * @param args variable argument list */ void ulog_voutput(uint32_t level, const char *tag, int newline, const char *format, va_list args) { char *log_buf = NULL; size_t log_len = 0; #ifndef ULOG_USING_SYSLOG configASSERT(level <= LOG_LVL_DBG); #else configASSERT(LOG_PRI(level) <= LOG_DEBUG); #endif /* ULOG_USING_SYSLOG */ configASSERT(tag); configASSERT(format); if (!ulog.init_ok) { return; } #ifdef ULOG_USING_FILTER /* level filter */ #ifndef ULOG_USING_SYSLOG if (level > ulog.filter.level || level > ulog_tag_lvl_filter_get(tag)) { return; } #else if (((LOG_MASK(LOG_PRI(level)) & ulog.filter.level) == 0) || ((LOG_MASK(LOG_PRI(level)) & ulog_tag_lvl_filter_get(tag)) == 0)) { return; } #endif /* ULOG_USING_SYSLOG */ else if (!strstr(tag, ulog.filter.tag)) { /* tag filter */ return; } #endif /* ULOG_USING_FILTER */ /* get log buffer */ log_buf = get_log_buf(); /* lock output */ output_lock(); #ifndef ULOG_USING_SYSLOG log_len = ulog_formater(log_buf, level, tag, newline, format, args); #else extern size_t syslog_formater(char *log_buf, uint8_t level, const char *tag, int newline, const char *format, va_list args); log_len = syslog_formater(log_buf, level, tag, newline, format, args); #endif /* ULOG_USING_SYSLOG */ #ifdef ULOG_USING_FILTER /* keyword filter */ if (ulog.filter.keyword[0] != '\0') { /* add string end sign */ log_buf[log_len] = '\0'; /* find the keyword */ if (!strstr(log_buf, ulog.filter.keyword)) { /* unlock output */ output_unlock(); return; } } #endif /* ULOG_USING_FILTER */ /* do log output */ do_output(level, tag, pdFALSE, log_buf, log_len); /* unlock output */ output_unlock(); } /** * output the log * * @param level level * @param tag tag * @param newline has newline * @param format output format * @param ... args */ void ulog_output(uint32_t level, const char *tag, int newline, const char *format, ...) { va_list args; /* args point to the first variable parameter */ va_start(args, format); ulog_voutput(level, tag, newline, format, args); va_end(args); } /** * output RAW string format log * * @param format output format * @param ... args */ void ulog_raw(const char *format, ...) { size_t log_len = 0; char *log_buf = NULL; va_list args; int fmt_result; configASSERT(ulog.init_ok); /* get log buffer */ log_buf = get_log_buf(); /* lock output */ output_lock(); /* args point to the first variable parameter */ va_start(args, format); #ifdef ULOG_OUTPUT_FLOAT fmt_result = vsnprintf(log_buf, ULOG_LINE_BUF_SIZE, format, args); #else fmt_result = vsnprintf(log_buf, ULOG_LINE_BUF_SIZE, format, args); #endif /* ULOG_OUTPUT_FLOAT */ va_end(args); /* calculate log length */ if ((fmt_result > -1) && (fmt_result <= ULOG_LINE_BUF_SIZE)) { log_len = fmt_result; } else { log_len = ULOG_LINE_BUF_SIZE; } /* do log output */ do_output(LOG_LVL_DBG, NULL, pdTRUE, log_buf, log_len); /* unlock output */ output_unlock(); } /** * dump the hex format data to log * * @param tag name for hex object, it will show on log header * @param width hex number for every line, such as: 16, 32 * @param buf hex buffer * @param size buffer size */ void ulog_hexdump(const char *tag, size_t width, uint8_t *buf, size_t size) { #define __is_print(ch) ((unsigned int)((ch) - ' ') < 127u - ' ') size_t i, j; size_t log_len = 0, name_len = strlen(tag); char *log_buf = NULL, dump_string[8]; int fmt_result; configASSERT(ulog.init_ok); #ifdef ULOG_USING_FILTER /* level filter */ #ifndef ULOG_USING_SYSLOG if (LOG_LVL_DBG > ulog.filter.level || LOG_LVL_DBG > ulog_tag_lvl_filter_get(tag)) { return; } #else if ((LOG_MASK(LOG_DEBUG) & ulog.filter.level) == 0) { return; } #endif /* ULOG_USING_SYSLOG */ else if (!strstr(tag, ulog.filter.tag)) { /* tag filter */ return; } #endif /* ULOG_USING_FILTER */ /* get log buffer */ log_buf = get_log_buf(); /* lock output */ output_lock(); for (i = 0, log_len = 0; i < size; i += width) { /* package header */ if (i == 0) { log_len += ulog_strcpy(log_len, log_buf + log_len, "D/HEX "); log_len += ulog_strcpy(log_len, log_buf + log_len, tag); log_len += ulog_strcpy(log_len, log_buf + log_len, ": "); } else { log_len = 6 + name_len + 2; memset(log_buf, ' ', log_len); } fmt_result = snprintf(log_buf + log_len, ULOG_LINE_BUF_SIZE, "%04X-%04X: ", i, i + width - 1); /* calculate log length */ if ((fmt_result > -1) && (fmt_result <= ULOG_LINE_BUF_SIZE)) { log_len += fmt_result; } else { log_len = ULOG_LINE_BUF_SIZE; } /* dump hex */ for (j = 0; j < width; j++) { if (i + j < size) { snprintf(dump_string, sizeof(dump_string), "%02X ", buf[i + j]); } else { strncpy(dump_string, " ", sizeof(dump_string)); } log_len += ulog_strcpy(log_len, log_buf + log_len, dump_string); if ((j + 1) % 8 == 0) { log_len += ulog_strcpy(log_len, log_buf + log_len, " "); } } log_len += ulog_strcpy(log_len, log_buf + log_len, " "); /* dump char for hex */ for (j = 0; j < width; j++) { if (i + j < size) { snprintf(dump_string, sizeof(dump_string), "%c", __is_print(buf[i + j]) ? buf[i + j] : '.'); log_len += ulog_strcpy(log_len, log_buf + log_len, dump_string); } } /* overflow check and reserve some space for newline sign */ if (log_len + strlen(ULOG_NEWLINE_SIGN) > ULOG_LINE_BUF_SIZE) { log_len = ULOG_LINE_BUF_SIZE - strlen(ULOG_NEWLINE_SIGN); } /* package newline sign */ log_len += ulog_strcpy(log_len, log_buf + log_len, ULOG_NEWLINE_SIGN); /*add string end sign*/ log_buf[log_len] = '\0'; /* do log output */ do_output(LOG_LVL_DBG, NULL, pdTRUE, log_buf, log_len); } /* unlock output */ output_unlock(); } #ifdef ULOG_USING_FILTER /** * Set the filter's level by different tag. * The log on this tag which level is less than it will stop output. * * example: * // the example tag log enter silent mode * ulog_set_filter_lvl("example", LOG_FILTER_LVL_SILENT); * // the example tag log which level is less than INFO level will stop output * ulog_set_filter_lvl("example", LOG_LVL_INFO); * // remove example tag's level filter, all level log will resume output * ulog_set_filter_lvl("example", LOG_FILTER_LVL_ALL); * * @param tag log tag * @param level The filter level. When the level is LOG_FILTER_LVL_SILENT, the log enter silent mode. * When the level is LOG_FILTER_LVL_ALL, it will remove this tag's level filer. * Then all level log will resume output. * * @return 0 : success * -5 : no memory * -10: level is out of range */ int ulog_tag_lvl_filter_set(const char *tag, uint32_t level) { ListItem_t *node; ulog_tag_lvl_filter_t tag_lvl = NULL; int result = 0; if (level > LOG_FILTER_LVL_ALL) return -EINVAL; if (!ulog.init_ok) return result; /* lock output */ output_lock(); /* find the tag in list */ for (node = listGET_HEAD_ENTRY(ulog_tag_lvl_list_get()); node != listGET_END_MARKER(ulog_tag_lvl_list_get()); node = listGET_NEXT(node)) { tag_lvl = listGET_LIST_ITEM_OWNER(node); if (!strncmp(tag_lvl->tag, tag, ULOG_FILTER_TAG_MAX_LEN)) { break; } else { tag_lvl = NULL; } } /* find OK */ if (tag_lvl) { if (level == LOG_FILTER_LVL_ALL) { /* remove current tag's level filter when input level is the lowest level */ uxListRemove(&tag_lvl->list); vPortFree(tag_lvl); } else { /* update level */ tag_lvl->level = level; } } else { /* only add the new tag's level filer when level is not LOG_FILTER_LVL_ALL */ if (level != LOG_FILTER_LVL_ALL) { /* new a tag's level filter */ tag_lvl = (ulog_tag_lvl_filter_t)pvPortMalloc(sizeof(struct ulog_tag_lvl_filter)); if (tag_lvl) { memset(tag_lvl->tag, 0 , sizeof(tag_lvl->tag)); strncpy(tag_lvl->tag, tag, ULOG_FILTER_TAG_MAX_LEN); tag_lvl->level = level; listSET_LIST_ITEM_OWNER(&tag_lvl->list, tag_lvl); vListInsertEnd(ulog_tag_lvl_list_get(), &tag_lvl->list); } else { result = -ENOMEM; } } } /* unlock output */ output_unlock(); return result; } /** * get the level on tag's level filer * * @param tag log tag * * @return It will return the lowest level when tag was not found. * Other level will return when tag was found. */ uint32_t ulog_tag_lvl_filter_get(const char *tag) { ListItem_t *node; ulog_tag_lvl_filter_t tag_lvl = NULL; uint32_t level = LOG_FILTER_LVL_ALL; if (!ulog.init_ok) return level; /* lock output */ output_lock(); /* find the tag in list */ for (node = listGET_HEAD_ENTRY(ulog_tag_lvl_list_get()); node != listGET_END_MARKER(ulog_tag_lvl_list_get()); node = listGET_NEXT(node)) { tag_lvl = listGET_LIST_ITEM_OWNER(node); if (!strncmp(tag_lvl->tag, tag, ULOG_FILTER_TAG_MAX_LEN)) { level = tag_lvl->level; break; } } /* unlock output */ output_unlock(); return level; } /** * get the tag's level list on filter * * @return tag's level list */ List_t *ulog_tag_lvl_list_get(void) { return &ulog.filter.tag_lvl_list; } /** * set log global filter level * * @param level log level: LOG_LVL_ASSERT, LOG_LVL_ERROR, LOG_LVL_WARNING, LOG_LVL_INFO, LOG_LVL_DBG * LOG_FILTER_LVL_SILENT: disable all log output, except assert level * LOG_FILTER_LVL_ALL: enable all log output */ void ulog_global_filter_lvl_set(uint32_t level) { configASSERT(level <= LOG_FILTER_LVL_ALL); ulog.filter.level = level; } /** * get log global filter level * * @return log level: LOG_LVL_ASSERT, LOG_LVL_ERROR, LOG_LVL_WARNING, LOG_LVL_INFO, LOG_LVL_DBG * LOG_FILTER_LVL_SILENT: disable all log output, except assert level * LOG_FILTER_LVL_ALL: enable all log output */ uint32_t ulog_global_filter_lvl_get(void) { return ulog.filter.level; } /** * set log global filter tag * * @param tag tag */ void ulog_global_filter_tag_set(const char *tag) { configASSERT(tag); strncpy(ulog.filter.tag, tag, ULOG_FILTER_TAG_MAX_LEN); } /** * get log global filter tag * * @return tag */ const char *ulog_global_filter_tag_get(void) { return ulog.filter.tag; } /** * set log global filter keyword * * @param keyword keyword */ void ulog_global_filter_kw_set(const char *keyword) { configASSERT(keyword); strncpy(ulog.filter.keyword, keyword, ULOG_FILTER_KW_MAX_LEN); } /** * get log global filter keyword * * @return keyword */ const char *ulog_global_filter_kw_get(void) { return ulog.filter.keyword; } #endif /* ULOG_USING_FILTER */ int ulog_backend_register(ulog_backend_t backend, const char *name, int support_color) { UBaseType_t uxSavedInterruptStatus; configASSERT(backend); configASSERT(name); configASSERT(ulog.init_ok); configASSERT(backend->output); if (backend->init) { backend->init(backend); } backend->support_color = support_color; memcpy(backend->name, name, ULOG_NAME_MAX); uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); listSET_LIST_ITEM_OWNER(&backend->list, backend); vListInsertEnd(&ulog.backend_list, &backend->list); portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptStatus); return 0; } int ulog_backend_unregister(ulog_backend_t backend) { UBaseType_t uxSavedInterruptStatus; configASSERT(backend); configASSERT(ulog.init_ok); if (backend->deinit) { backend->deinit(backend); } uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); uxListRemove(&backend->list); portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptStatus); return 0; } #ifdef ULOG_USING_ASYNC_OUTPUT /** * asynchronous output logs to all backends * * @note you must call this function when ULOG_ASYNC_OUTPUT_BY_THREAD is disable */ void ulog_async_output(void) { rbb_blk_t log_blk; ulog_frame_t log_frame; while ((log_blk = rbb_blk_get(ulog.async_rbb)) != NULL) { log_frame = (ulog_frame_t) log_blk->buf; if (log_frame->magic == ULOG_FRAME_MAGIC) { /* output to all backends */ ulog_output_to_all_backend(log_frame->level, log_frame->tag, log_frame->is_raw, log_frame->log, log_frame->log_len); } rbb_blk_free(ulog.async_rbb, log_blk); } } /** * waiting for get asynchronous output log * * @param time the waiting time */ void ulog_async_waiting_log(uint32_t time) { xQueueReset(ulog.async_notice); xQueueReceive(ulog.async_notice, NULL, time); } static void async_output_thread_entry(void *param) { ulog_async_output(); while (1) { ulog_async_waiting_log(portMAX_DELAY); ulog_async_output(); } } #endif /* ULOG_USING_ASYNC_OUTPUT */ /** * flush all backends's log */ void ulog_flush(void) { ListItem_t *node; ulog_backend_t backend; if (!ulog.init_ok) return; #ifdef ULOG_USING_ASYNC_OUTPUT ulog_async_output(); #endif /* flush all backends */ for (node = listGET_HEAD_ENTRY(&ulog.backend_list); node != listGET_END_MARKER(&ulog.backend_list); node = listGET_NEXT(node)) { backend = listGET_LIST_ITEM_OWNER(node); if (backend->flush) { backend->flush(backend); } } } int ulog_init(void) { if (ulog.init_ok) return 0; ulog.output_locker = xSemaphoreCreateMutex(); vListInitialise(&ulog.backend_list); #ifdef ULOG_USING_FILTER vListInitialise(ulog_tag_lvl_list_get()); #endif #ifdef ULOG_USING_ASYNC_OUTPUT configASSERT(ULOG_ASYNC_OUTPUT_STORE_LINES >= 2); /* async output ring block buffer */ ulog.async_rbb = rbb_create(ULOG_ALIGN(ULOG_ASYNC_OUTPUT_BUF_SIZE, ULOG_ALIGN_SIZE), ULOG_ASYNC_OUTPUT_STORE_LINES); if (ulog.async_rbb == NULL) { printf("Error: ulog init failed! No memory for async rbb.\n"); vSemaphoreDelete(ulog.output_locker); return -ENOMEM; } ulog.async_notice = xQueueCreate(1, 0); /* async output thread startup */ /* async output thread */ xTaskCreate(async_output_thread_entry, "ulog_async", ULOG_ASYNC_OUTPUT_THREAD_STACK, &ulog, ULOG_ASYNC_OUTPUT_THREAD_PRIORITY, &ulog.async_th); if (ulog.async_th == NULL) { printf("Error: ulog init failed! No memory for async output thread.\n"); vSemaphoreDelete(ulog.output_locker); rbb_destroy(ulog.async_rbb); return -ENOMEM; } #endif /* ULOG_USING_ASYNC_OUTPUT */ #ifdef ULOG_USING_FILTER ulog_global_filter_lvl_set(LOG_FILTER_LVL_ALL); #endif ulog.init_ok = pdTRUE; return 0; } void ulog_deinit(void) { ListItem_t *node; ulog_backend_t backend; if (!ulog.init_ok) return; /* deinit all backends */ for (node = listGET_HEAD_ENTRY(&ulog.backend_list); node != listGET_END_MARKER(&ulog.backend_list); node = listGET_NEXT(node)) { backend = listGET_LIST_ITEM_OWNER(node); if (backend->deinit) { backend->deinit(backend); } } #ifdef ULOG_USING_FILTER /* deinit tag's level filter */ { ulog_tag_lvl_filter_t tag_lvl; for (node = listGET_HEAD_ENTRY(ulog_tag_lvl_list_get()); node != listGET_END_MARKER(ulog_tag_lvl_list_get()); node = listGET_NEXT(node)) { tag_lvl = listGET_LIST_ITEM_OWNER(node); vPortFree(tag_lvl); } } #endif /* ULOG_USING_FILTER */ vSemaphoreDelete(ulog.output_locker); #ifdef ULOG_USING_ASYNC_OUTPUT rbb_destroy(ulog.async_rbb); vTaskDelete(ulog.async_th); #endif ulog.init_ok = pdFALSE; } #endif /* USE_ULOG */