| // Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "global.h" |
| #include "log.h" |
| #include <time.h> |
| |
| #define FILE_LOG_TIME_INFO |
| //#define STDOUT_LOG_TIME_INFO |
| #define FORCE_ENABLE_STDOUT_API_LOG |
| |
| bool log_print_all; |
| |
| #if defined(FORCE_ENABLE_STDOUT_API_LOG) |
| #define FORCE_ENABLED_STDOUT_MASK \ |
| (SDK_FORCE|SDK_ERR|SDK_STD_ERR|SDK_NOTICE|SDK_API_LOG) |
| #else |
| #define FORCE_ENABLED_STDOUT_MASK \ |
| (SDK_FORCE|SDK_ERR|SDK_STD_ERR|SDK_NOTICE) |
| #endif |
| |
| #if (CONFIG_LOG_FILE_BUF_SIZE > 0) |
| #define LOG_FILE_BUF_SIZE (CONFIG_LOG_FILE_BUF_SIZE) |
| #endif |
| |
| #define MAX_LOG_BUF 4096 |
| static char log_file[256]; |
| static int log_mask_idx = 0; |
| #if !defined(LOG_FILE_BUF_SIZE) |
| static int log_fd = 0; |
| #endif |
| |
| static const unsigned log_mask[] = { |
| /*STDOUT*/ |
| SDK_LOG_LEVEL_0, |
| SDK_LOG_LEVEL_1, |
| SDK_LOG_LEVEL_2, |
| SDK_LOG_LEVEL_3, |
| SDK_LOG_LEVEL_4, |
| /*FILE*/ |
| SDK_LOG_LEVEL_5, |
| SDK_LOG_LEVEL_6, |
| SDK_LOG_LEVEL_7, |
| SDK_LOG_LEVEL_8, |
| SDK_LOG_LEVEL_9, |
| |
| /*Flush log buffer to file*/ |
| SDK_LOG_LEVEL_10, |
| }; |
| |
| #define cur_log_mask() (log_mask[log_mask_idx]) |
| #define get_log_mask(level) (log_mask[level]) |
| |
| #if defined(LOG_FILE_BUF_SIZE) |
| typedef struct log_buf_mng_s { |
| char *buffer; |
| unsigned put_idx, |
| count; |
| |
| pthread_mutex_t log_lock; |
| |
| } log_buf_mng_t; |
| |
| static log_buf_mng_t log_buf_mnger; |
| |
| static void log_buffer_init(void) |
| { |
| log_buf_mng_t *mnger = &log_buf_mnger; |
| |
| mnger->put_idx = mnger->count = 0; |
| pthread_mutex_init(&mnger->log_lock, NULL); |
| mnger->buffer = sdk_malloc(LOG_FILE_BUF_SIZE); |
| } |
| |
| static int log_buffer_write(const char *str) |
| { |
| log_buf_mng_t *mnger = &log_buf_mnger; |
| int len, put_idx, available; |
| |
| len = strlen(str); |
| |
| pthread_mutex_lock(&mnger->log_lock); |
| put_idx = mnger->put_idx % LOG_FILE_BUF_SIZE; |
| available = LOG_FILE_BUF_SIZE - put_idx; |
| |
| if (available >= len) |
| memcpy(&mnger->buffer[put_idx], str, len); |
| else { |
| memcpy(&mnger->buffer[put_idx], str, available); |
| memcpy(&mnger->buffer[0], &str[available], len-available); |
| } |
| |
| mnger->put_idx += len; |
| if (mnger->count <= LOG_FILE_BUF_SIZE) |
| mnger->count += len; |
| pthread_mutex_unlock(&mnger->log_lock); |
| return len; |
| } |
| #define log_fprintf log_buffer_write |
| |
| static void log_buffer_flush(void) |
| { |
| log_buf_mng_t *mnger = &log_buf_mnger; |
| int fd, ret, put_idx, to_write; |
| |
| if (!strlen(log_file)) { |
| fprintf(stderr, "Log file was not set up!\n"); |
| return; |
| } |
| |
| if ((fd = open(log_file, O_CREAT|O_WRONLY|O_TRUNC, 0644)) < 0) { |
| fprintf(stderr, "open(%s) file failed(%s)\n", log_file, strerror(errno)); |
| return; |
| } |
| |
| pthread_mutex_lock(&mnger->log_lock); |
| if (mnger->count < LOG_FILE_BUF_SIZE) { |
| to_write = mnger->count; |
| ret = write(fd, mnger->buffer, to_write); |
| } |
| else { |
| put_idx = mnger->put_idx % LOG_FILE_BUF_SIZE; |
| to_write = LOG_FILE_BUF_SIZE - put_idx; |
| ret = write(fd, &mnger->buffer[put_idx], to_write); |
| if (ret == to_write) { |
| to_write = LOG_FILE_BUF_SIZE - to_write; |
| ret = write(fd, &mnger->buffer[0], to_write); |
| } |
| } |
| pthread_mutex_unlock(&mnger->log_lock); |
| |
| if (ret != to_write) { |
| fprintf(stderr, "ERROR write: %d!=%d, %s(%d)\n", |
| to_write, ret, strerror(errno), errno); |
| } |
| else |
| printf("log buffer has been flushed to %s(%d)!\n", log_file, ret); |
| close(fd); |
| } |
| |
| static void log_buffer_deinit(void) |
| { |
| log_buf_mng_t *mnger = &log_buf_mnger; |
| |
| if (mnger->buffer) { |
| //log_buffer_flush(); |
| sdk_free(mnger->buffer); |
| mnger->buffer = NULL; |
| pthread_mutex_destroy(&mnger->log_lock); |
| } |
| } |
| #endif |
| |
| int log_set_level(int level) |
| { |
| int prev_level = log_mask_idx; |
| #if !defined(LOG_FILE_BUF_SIZE) |
| int fd; |
| #endif |
| |
| if (level >= numof_array(log_mask)) |
| return log_mask_idx; |
| |
| #if defined(LOG_FILE_BUF_SIZE) |
| if (cur_log_mask() & SDK_LOG_FILE) { |
| if (get_log_mask(level) & SDK_LOG_FILE_FLUSH) { |
| log_buffer_flush(); |
| return log_mask_idx; |
| } |
| if (!(get_log_mask(level) & SDK_LOG_FILE)) |
| log_buffer_deinit(); |
| } |
| else if ((get_log_mask(level) & SDK_LOG_FILE)) |
| log_buffer_init(); |
| #else |
| if ((get_log_mask(level) & SDK_LOG_FILE) && log_fd <= 0) { |
| if (!strlen(log_file)) { |
| fprintf(stderr, "Log file was not set up!\n"); |
| return -1; |
| } |
| if ((fd = open(log_file, O_RDWR|O_APPEND)) < 0 && errno == ENOENT) { |
| if ((fd = open(log_file, O_CREAT|O_RDWR|O_APPEND, 0644)) < 0) { |
| fprintf(stderr, "open(%s) file failed\n", log_file); |
| return -1; |
| } |
| } |
| log_fd = fd; |
| } |
| #endif |
| |
| log_mask_idx = level; |
| return prev_level; |
| } |
| |
| void log_init(const char *logfile, int log_level) |
| { |
| #if !defined(LOG_FILE_BUF_SIZE) |
| if (log_fd) { |
| fprintf(stderr, "log_fd(%d) is already opened\n", log_fd); |
| return; |
| } |
| #endif |
| |
| if (logfile) |
| strcpy(log_file, logfile); |
| |
| log_set_level(log_level); |
| |
| xprintf(SDK_NOTICE, "SDK Log Level:%d\n", log_mask_idx); |
| } |
| |
| void log_deinit(void) |
| { |
| #if defined(LOG_FILE_BUF_SIZE) |
| log_set_level(0); |
| #else |
| if (log_fd > 0) { |
| close(log_fd); |
| log_fd = 0; |
| } |
| #endif |
| } |
| |
| #if !defined(LOG_FILE_BUF_SIZE) |
| static int log_fprintf(const char *str) |
| { |
| int fd = log_fd; |
| int ret = 0; |
| |
| if (fd > 0) |
| ret = write(fd, str, strlen(str)); |
| return ret; |
| } |
| #endif |
| |
| static int log_add_time_info(char *buf, const char *title) |
| { |
| char *pos = buf; |
| time_t t; |
| struct tm *tm; |
| struct timeval tv; |
| |
| time(&t); |
| tm = localtime(&t); |
| gettimeofday(&tv, NULL); |
| |
| if (title) |
| pos += sprintf(pos, "[%02d:%02d:%02d:%03d %s] ", |
| tm->tm_hour, tm->tm_min, tm->tm_sec, (int)(tv.tv_usec/1000), title); |
| else |
| pos += sprintf(pos, "[%02d:%02d:%02d:%03d] ", |
| tm->tm_hour, tm->tm_min, tm->tm_sec, (int)(tv.tv_usec/1000)); |
| |
| return pos - buf; |
| } |
| |
| static inline int log_add_decoration(char *buf, bool file, const char *title) |
| { |
| char *pos = buf; |
| |
| if (file) { |
| #if defined(FILE_LOG_TIME_INFO) |
| pos += log_add_time_info(pos, title); |
| #else |
| if (title) |
| pos += sprintf(pos, "[%s] ", title); |
| #endif |
| } |
| else { |
| #if defined(STDOUT_LOG_TIME_INFO) |
| pos += log_add_time_info(pos, title); |
| #else |
| if (title) |
| pos += sprintf(pos, "[%s] ", title); |
| #endif |
| } |
| |
| return pos - buf; |
| } |
| |
| static int log_vfprintf(const char *title, const char *fmt, va_list args) |
| { |
| char buf[MAX_LOG_BUF+256]; |
| char *pos = buf; |
| int ret; |
| |
| pos += log_add_decoration(pos, TRUE, title); |
| |
| vsprintf(pos, fmt, args); |
| ret = log_fprintf(buf); |
| |
| return ret; |
| } |
| |
| static int log_vprintf(const char *title, const char *fmt, va_list args) |
| { |
| char buf[MAX_LOG_BUF]; |
| char *pos = buf; |
| int ret; |
| |
| pos += log_add_decoration(pos, FALSE, title); |
| |
| vsprintf(pos, fmt, args); |
| #if 1 |
| ret = printf("%s", buf); |
| fflush(stdout); |
| #else |
| ret = fprintf(stderr, buf); |
| #endif |
| |
| return ret; |
| } |
| |
| int log_printf(unsigned mask, const char *title, const char *fmt, ...) |
| { |
| va_list args; |
| int ret = 0; |
| |
| if ((cur_log_mask() & mask) || log_print_all) { |
| va_start(args, fmt); |
| if (cur_log_mask() & SDK_LOG_FILE) |
| ret = log_vfprintf(title, fmt, args); |
| if ((cur_log_mask() & SDK_LOG_STDOUT) || (mask & FORCE_ENABLED_STDOUT_MASK) |
| || log_print_all) |
| ret = log_vprintf(title, fmt, args); |
| va_end(args); |
| } |
| |
| return ret; |
| } |
| |
| int log_printf_string(unsigned mask, int flag, const char *title, const char *str) |
| { |
| char buf[MAX_LOG_BUF+256]; |
| char *pos = buf; |
| int ret = 0; |
| |
| if ((cur_log_mask() & mask) || log_print_all) { |
| if (cur_log_mask() & SDK_LOG_FILE) { |
| pos += log_add_decoration(pos, TRUE, title); |
| strcpy(pos, str); |
| ret = log_fprintf(buf); |
| if (flag & SDK_LOG_FLAG_NO_STDOUT_IN_LOGFILE) |
| goto out; |
| } |
| if ((cur_log_mask() & SDK_LOG_STDOUT) || (mask & FORCE_ENABLED_STDOUT_MASK) |
| || log_print_all) { |
| pos = buf; |
| #if 1 |
| pos += log_add_decoration(pos, FALSE, NULL); |
| #else |
| pos += log_add_decoration(pos, FALSE, title); |
| #endif |
| strcpy(pos, str); |
| ret = printf("%s", buf); |
| fflush(stdout); |
| } |
| } |
| out: |
| return ret; |
| } |
| |
| void log_printf_hex(unsigned mask, const char *title, void *buf, int len) |
| { |
| char *mbuf, *pos; |
| u8 *p = buf; |
| int i; |
| |
| if ((cur_log_mask() & mask) || log_print_all) { |
| if (len > MAX_LOG_BUF/4) |
| len = MAX_LOG_BUF/4; |
| |
| mbuf = pos = sdk_malloc(len * 7); |
| |
| for (i = 0; i < len; i++) { |
| if (i && (i % 16 == 0)) |
| pos += sprintf(pos, "\n"); |
| pos += sprintf(pos, "%02x ", *p++); |
| }; |
| |
| log_printf(mask, title, "\n%s\n", mbuf); |
| sdk_free(mbuf); |
| } |
| } |
| |