/* * Copyright (c) CompanyNameMagicTag 2021-2023. All rights reserved. * Description: offline log file saved to the storage */ #include "log_file.h" #include #include #include "uapi_crc.h" #include "errcode.h" #include "securec.h" #include "stdbool.h" #include "soc_osal.h" #include "common_def.h" #include "dfx_adapt_layer.h" #include "debug_print.h" #include "log_file_common.h" #include "log_file_file.h" #include "log_file_flash.h" #if (CONFIG_DFX_SUPPORT_OFFLINE_LOG_FILE == YES) STATIC store_file_manage_t g_logfile_manage = {0}; STATIC osal_mutex g_instance_mutex = {NULL}; /* 实例互斥锁 */ store_file_manage_t* logfile_get_manage(void) { return &g_logfile_manage; } bool logfile_check_record_head_valid(store_record_info_t *record_header) { if (record_header->magic == RECORD_HEAD_MAGIC) { uint16_t crc = uapi_crc16(0, (uint8_t *)record_header, sizeof(store_record_info_t) - sizeof(uint16_t)); if (crc == record_header->crc) { return true; } } return false; } void logfile_init_file_head(store_file_info_t *file_info) { #if CONFIG_DFX_SUPPORT_FILE_SYSTEM == DFX_NO file_info->file_head.version = 2; // flash存储version值为2 file_info->file_head.cur_pos = (uint32_t)sizeof(file_info->file_head); file_info->file_head.file_size = file_info->file_cfg.file_size; file_info->file_head.first_record_pos = (uint32_t)sizeof(file_info->file_head); #else file_info->file_head.version = 1; // 文件存储version值为1 file_info->file_head.cur_pos = (uint32_t)sizeof(file_info->file_head); file_info->file_head.file_size = file_info->file_cfg.file_size; file_info->file_head.first_record_pos = (uint32_t)sizeof(file_info->file_head); #endif file_info->file_head.offset = (uint8_t)sizeof(file_info->file_head); file_info->file_head.start_flag = FILE_HEAD_START_FLAG; file_info->file_head.records = 0; file_info->file_head.service_type = file_info->type; file_info->file_head.crc = uapi_crc16(0, (uint8_t *)&(file_info->file_head), sizeof(store_file_head_t) - sizeof(uint16_t)); } STATIC errcode_t logfile_write_to_cache(store_file_info_t *file_info, uint8_t *data, uint32_t data_len) { store_cache_t *cache = file_info->cache; int32_t tmp_pos = 0; tmp_pos = (int32_t)cache->cache_read_pos; /* 判断数据是否需要翻转至cache头存储 */ if ((cache->cache_size - cache->cache_write_pos) >= data_len) { if (memcpy_s(&(cache->data[cache->cache_write_pos]), cache->cache_size - cache->cache_write_pos, data, data_len) != EOK) { return ERRCODE_FAIL; } cache->cache_write_pos += data_len; } else { uint32_t record_len_written = cache->cache_size - cache->cache_write_pos; uint32_t record_len_remained = data_len - record_len_written; if (record_len_written != 0 && memcpy_s(&(cache->data[cache->cache_write_pos]), cache->cache_size - cache->cache_write_pos, data, record_len_written) != EOK) { return ERRCODE_FAIL; } cache->cache_write_pos = 0; if (record_len_remained != 0 && memcpy_s(&(cache->data[cache->cache_write_pos]), (uint32_t)tmp_pos, data + record_len_written, record_len_remained) != EOK) { return ERRCODE_FAIL; } cache->cache_write_pos += record_len_remained; } return ERRCODE_SUCC; } STATIC int logfile_save_process(void *arg) { int32_t ret = 0; store_file_manage_t *manage = (store_file_manage_t *)arg; while (true) { for (int i = 0; i < (int)(sizeof(manage->file_info) / sizeof(manage->file_info[0])); i++) { manage->file_info[i].finish_flag = true; } ret = osal_event_read(&(manage->event), LOGFILE_SAVE_EVENT_MASK, OSAL_WAIT_FOREVER, OSAL_WAITMODE_OR | OSAL_WAITMODE_CLR); for (int i = 0; i < (int)(sizeof(manage->file_info) / sizeof(manage->file_info[0])); i++) { if ((((uint32_t)ret & (1 << (uint32_t)i)) == (uint32_t)(1 << (uint32_t)i)) && (manage->file_info[i].run_flag == true)) { manage->file_info[i].finish_flag = false; #if CONFIG_DFX_SUPPORT_FILE_SYSTEM == DFX_NO logfile_write_cache_to_flash(&manage->file_info[i]); #else logfile_write_cache_to_file(&manage->file_info[i]); #endif } } } return 0; } STATIC void logfile_timer_handler(unsigned long data) { unused(data); osal_timer *timer = &(g_logfile_manage.timer_handle); for (int i = 0; i < (int)(sizeof(g_logfile_manage.file_info) / sizeof(g_logfile_manage.file_info[0]) - 1); i++) { if (g_logfile_manage.file_info[i].cache != NULL) { osal_event_write(&(g_logfile_manage.event), 1 << (uint32_t)i); } } osal_timer_start(timer); } STATIC void logfile_timer_stop(void) { osal_timer *timer = &(g_logfile_manage.timer_handle); osal_timer_stop(timer); } STATIC errcode_t logfile_timer_init(void) { osal_timer *timer = &(g_logfile_manage.timer_handle); timer->handler = logfile_timer_handler; timer->data = 0; timer->interval = LOGFILE_TIMER_PERIOD; if (osal_timer_init(timer) < 0) { return ERRCODE_FAIL; } osal_timer_start(timer); return ERRCODE_SUCC; } STATIC errcode_t logfile_create_thread(void) { errcode_t ret = ERRCODE_DFX_LOGFILE_EVENT_FAILURE; if (g_logfile_manage.task_handle != NULL) { return ERRCODE_SUCC; } if ((osal_event_init(&g_logfile_manage.event)) != OSAL_SUCCESS) { goto err1; } ret = ERRCODE_DFX_LOGFILE_MUTEX_FAILURE; if (osal_mutex_init(&g_logfile_manage.file_write_mutex) != OSAL_SUCCESS) { goto err2; } osal_kthread_lock(); g_logfile_manage.task_handle = osal_kthread_create(logfile_save_process, &g_logfile_manage, "log_save", LOGFILE_SAVE_TASK_SIZE); if (g_logfile_manage.task_handle == NULL) { ret = ERRCODE_DFX_LOGFILE_THREAD_FAILURE; osal_kthread_unlock(); goto err3; } osal_kthread_set_priority(g_logfile_manage.task_handle, THREAD_PRIORITY_NUM); osal_kthread_unlock(); if (logfile_timer_init() != OSAL_SUCCESS) { ret = ERRCODE_DFX_LOGFILE_TIMER_FAILURE; goto err4; } return ERRCODE_SUCC; err4: if (g_logfile_manage.task_handle != NULL) { osal_kthread_destroy(g_logfile_manage.task_handle, 0); } err3: osal_mutex_destroy(&g_logfile_manage.file_write_mutex); err2: (void)osal_event_destroy(&g_logfile_manage.event); err1: (void)memset_s(&g_logfile_manage, (sizeof(g_logfile_manage) - sizeof(store_file_info_t) * STORE_MAX), 0, (sizeof(g_logfile_manage) - sizeof(store_file_info_t) * STORE_MAX)); dfx_log_err("logfile_create_thread failed. ret = 0x%x\r\n", ret); return ret; } STATIC errcode_t logfile_create_cache(store_file_info_t *file_info, store_file_cfg_t *cfg) { if (file_info->cache == NULL) { file_info->cache = (store_cache_t *)osal_kmalloc_align(sizeof(store_cache_t) + cfg->cache_size, OSAL_GFP_ZERO, 4); /* align 4 bytes */ if (file_info->cache == NULL) { dfx_log_err("cache space create failed!\r\n"); return ERRCODE_MALLOC; } (void)memset_s(file_info->cache, sizeof(store_cache_t) + cfg->cache_size, 0, sizeof(store_cache_t) + cfg->cache_size); file_info->cache->cache_write_pos = 0; file_info->cache->cache_read_pos = 0; file_info->cache->cache_size = cfg->cache_size; file_info->cache->threshold_size = cfg->cache_size * 1 / 4; /* threshold size is 1/4 of cache size */ } return ERRCODE_SUCC; } STATIC errcode_t logfile_file_write_with_cache(store_file_info_t *file_info, uint8_t *data, uint32_t data_len) { store_cache_t *cache = file_info->cache; int32_t cache_space_left; if (cache != NULL) { store_record_info_t record_info; record_info.magic = RECORD_HEAD_MAGIC; record_info.type = file_info->file_head.service_type; record_info.len = (uint16_t)sizeof(store_record_info_t) + (uint16_t)data_len; record_info.rev = 1; #if CONFIG_DFX_SUPPORT_FILE_SYSTEM == DFX_NO if (file_info->file_cfg.mult_files != 1 || file_info->file_cfg.cache_size <= 0) { return ERRCODE_INVALID_PARAM; } record_info.index = (uint16_t)(file_info->index); file_info->index = (file_info->index == MAX_INDEX_NUM) ? 1 : (file_info->index + 1); #endif record_info.crc = uapi_crc16(0, (uint8_t *)&record_info, sizeof(store_record_info_t) - sizeof(uint16_t)); if (cache->cache_read_pos > cache->cache_write_pos) { cache_space_left = (int32_t)(cache->cache_read_pos - cache->cache_write_pos); } else { cache_space_left = (int32_t)(cache->cache_size - cache->cache_write_pos + cache->cache_read_pos); } /* 如果cache空间不足,先把当前cache中数据全部写入flash或file */ if (cache_space_left <= (int32_t)record_info.len) { #if CONFIG_DFX_SUPPORT_FILE_SYSTEM == DFX_NO logfile_write_cache_to_flash(file_info); #else logfile_write_cache_to_file(file_info); #endif } logfile_write_to_cache(file_info, (uint8_t *)&record_info, (uint32_t)sizeof(store_record_info_t)); logfile_write_to_cache(file_info, data, data_len); if (cache_space_left < (int32_t)cache->threshold_size) { /* cache剩余空间小于门限时,触发保存 */ osal_event_write(&(g_logfile_manage.event), 1 << (uint32_t)file_info->type); } } return ERRCODE_SUCC; } STATIC void logfile_free_file_info(store_file_info_t *file_info) { #if CONFIG_DFX_SUPPORT_FILE_SYSTEM == DFX_YES if (file_info->idx_fd != 0) { close(file_info->idx_fd); file_info->idx_fd = 0; } if (file_info->fd != 0) { close(file_info->fd); file_info->fd = 0; } #else file_info->fd = 0; #endif if (file_info->cache != NULL) { osal_kfree(file_info->cache); file_info->cache = NULL; } (void)memset_s(file_info, sizeof(store_file_info_t), 0, sizeof(store_file_info_t)); } STATIC void logfile_free_os_resouce(void) { if (g_logfile_manage.task_handle != NULL) { logfile_timer_stop(); (void)osal_timer_destroy(&g_logfile_manage.timer_handle); osal_kthread_destroy(g_logfile_manage.task_handle, 0); (void)osal_event_destroy(&g_logfile_manage.event); osal_mutex_destroy(&g_logfile_manage.file_write_mutex); (void)memset_s(&g_logfile_manage, (sizeof(g_logfile_manage) - sizeof(store_file_info_t) * STORE_MAX), 0, (sizeof(g_logfile_manage) - sizeof(store_file_info_t) * STORE_MAX)); } } STATIC errcode_t logfile_write(store_file_info_t *file_info, uint8_t *data, uint32_t data_len) { errcode_t ret = ERRCODE_SUCC; if (file_info->file_cfg.mult_files < 1) { return ERRCODE_INVALID_PARAM; } if (file_info->run_flag == false) { return ERRCODE_DFX_LOGFILE_SUSPENDED; } if (file_info->file_cfg.enable_cache == 0) { #if CONFIG_DFX_SUPPORT_FILE_SYSTEM == DFX_YES if (file_info->file_cfg.mult_files <= 1) { ret = logfile_single_file_write(file_info, data, data_len); } else { ret = logfile_multi_file_write(file_info, data, data_len); } #endif } else { ret = logfile_file_write_with_cache(file_info, data, data_len); } return ret; } errcode_t uapi_logfile_write(store_service_t service_type, uint8_t sub_type, uint8_t *data, uint32_t data_len) { errcode_t ret; unused(sub_type); if (service_type >= STORE_MAX || data == NULL || data_len == 0) { return ERRCODE_INVALID_PARAM; } store_file_info_t *file_info = &(g_logfile_manage.file_info[service_type]); osal_mutex_lock(&g_instance_mutex); ret = logfile_write(file_info, data, data_len); osal_mutex_unlock(&g_instance_mutex); return ret; } errcode_t uapi_logfile_init(void) { if (g_instance_mutex.mutex != NULL) { return ERRCODE_SUCC; } (void)memset_s(&g_logfile_manage, sizeof(g_logfile_manage), 0, sizeof(g_logfile_manage)); if (osal_mutex_init(&g_instance_mutex) != OSAL_SUCCESS) { return ERRCODE_DFX_LOGFILE_MUTEX_FAILURE; } return ERRCODE_SUCC; } STATIC errcode_t logfile_open(store_file_info_t *file_info) { errcode_t ret = ERRCODE_SUCC; /* 判断文件是否打开 */ if (file_info->fd > 0) { return ERRCODE_DFX_LOGFILE_ALREADY_OPEN; } #if CONFIG_DFX_SUPPORT_FILE_SYSTEM == DFX_NO if (file_info->file_cfg.enable_cache != 1) { return ERRCODE_INVALID_PARAM; } if (logfile_flash_prepare(file_info) != ERRCODE_SUCC) { ret = ERRCODE_DFX_LOGFILE_FLASH_PREPARE_FAIL; goto err; } file_info->fd = (int32_t)file_info->type + 1; /* 在无文件系统场景下fd作为open成功的标记 */ #else if (file_info->file_cfg.path == NULL || file_info->file_cfg.name == NULL) { return ERRCODE_INVALID_PARAM; } if (logfile_create_path(&(file_info->file_cfg)) != ERRCODE_SUCC) { ret = ERRCODE_DFX_LOGFILE_MKDIR_FATAL; goto err; } ret = logfile_prepare_file_fd(file_info, &(file_info->file_cfg)); if (ret != ERRCODE_SUCC) { goto err; } #endif if (file_info->file_cfg.enable_cache != 0) { ret = logfile_create_cache(file_info, &(file_info->file_cfg)); if (ret != ERRCODE_SUCC) { goto err; } ret = logfile_create_thread(); if (ret != ERRCODE_SUCC) { goto err; } } file_info->run_flag = true; return ERRCODE_SUCC; err: logfile_free_file_info(file_info); return ret; } errcode_t uapi_logfile_open(store_service_t service_type, store_file_cfg_t *cfg) { errcode_t ret; if (service_type >= STORE_MAX || cfg == NULL) { return ERRCODE_INVALID_PARAM; } store_file_info_t *file_info = &(g_logfile_manage.file_info[service_type]); file_info->type = service_type; file_info->file_cfg = *cfg; osal_mutex_lock(&g_instance_mutex); ret = logfile_open(file_info); osal_mutex_unlock(&g_instance_mutex); return ret; } STATIC errcode_t logfile_close(store_file_info_t *file_info) { /* 如果句柄为NULL,说明已经close */ if (file_info->fd == 0) { return ERRCODE_SUCC; } file_info->run_flag = false; if (file_info->file_cfg.enable_cache == 1) { /* 等待线程执行完成 */ while (file_info->finish_flag == false) { sleep(CLOSE_SLEEP_PERIOD); } } logfile_free_file_info(file_info); uint32_t i; for (i = 0; i < STORE_MAX; i++) { if (g_logfile_manage.file_info[i].fd != 0) { break; } } /* 如果全部文件关闭,删除线程和Timer */ if (i >= STORE_MAX) { logfile_free_os_resouce(); } return ERRCODE_SUCC; } errcode_t uapi_logfile_close(store_service_t service_type) { errcode_t ret; if (service_type >= STORE_MAX) { return ERRCODE_INVALID_PARAM; } store_file_info_t *file_info = &(g_logfile_manage.file_info[service_type]); osal_mutex_lock(&g_instance_mutex); ret = logfile_close(file_info); osal_mutex_unlock(&g_instance_mutex); return ret; } errcode_t uapi_logfile_fsync(store_service_t service_type) { if (service_type >= STORE_MAX) { return ERRCODE_INVALID_PARAM; } store_file_info_t *file_info = &(g_logfile_manage.file_info[service_type]); if (file_info->fd == 0) { return ERRCODE_SUCC; } #if CONFIG_DFX_SUPPORT_FILE_SYSTEM == DFX_YES osal_mutex_lock(&g_instance_mutex); fsync(file_info->fd); osal_mutex_unlock(&g_instance_mutex); #endif return ERRCODE_SUCC; } errcode_t uapi_logfile_suspend(store_service_t service_type) { if (service_type >= STORE_MAX) { return ERRCODE_INVALID_PARAM; } store_file_info_t *file_info = &(g_logfile_manage.file_info[service_type]); osal_mutex_lock(&g_instance_mutex); file_info->run_flag = false; osal_mutex_unlock(&g_instance_mutex); return ERRCODE_SUCC; } errcode_t uapi_logfile_resume(store_service_t service_type) { if (service_type >= STORE_MAX) { return ERRCODE_INVALID_PARAM; } store_file_info_t *file_info = &(g_logfile_manage.file_info[service_type]); osal_mutex_lock(&g_instance_mutex); file_info->run_flag = true; osal_mutex_unlock(&g_instance_mutex); return ERRCODE_SUCC; } errcode_t uapi_logfile_reset(store_service_t service_type, store_file_cfg_t *cfg) { errcode_t ret; if (service_type >= STORE_MAX || cfg == NULL) { return ERRCODE_INVALID_PARAM; } store_file_info_t *file_info = &(g_logfile_manage.file_info[service_type]); osal_mutex_lock(&g_instance_mutex); /* 判断文件是否打开 */ if (file_info->fd > 0) { osal_mutex_unlock(&g_instance_mutex); return ERRCODE_DFX_LOGFILE_ALREADY_OPEN; } #if CONFIG_DFX_SUPPORT_FILE_SYSTEM == DFX_NO ret = logfile_flash_erase(service_type, cfg); #else ret = logfile_remove_files(service_type, cfg); #endif osal_mutex_unlock(&g_instance_mutex); return ret; } #endif /* CONFIG_DFX_SUPPORT_OFFLINE_LOG_FILE */