* Copyright (c) 2022 Huawei Technologies Co.,Ltd.
*
* CBB is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
* -------------------------------------------------------------------------
*
* cm_profile_stat.c
*
*
* IDENTIFICATION
* src/cm_utils/cm_profile_stat.c
*
* -------------------------------------------------------------------------
*/
#include "cm_profile_stat.h"
#include "cm_text.h"
#include "cm_system.h"
#ifdef __cplusplus
extern "C" {
#endif
#define DEFAULT_STAT_INTERVAL 3
#define MAX_LINES_PRINT_HEAD 50
#define STAT_THREAD_SLEEP_TIME 100
static thread_t g_profile_stat_thread;
static bool32 g_profile_stat_init = CM_FALSE;
static stat_item_t *g_stat_table[STAT_TABLE_SIZE][MAX_STAT_ITEM_SIZE][MAX_ITEM_COUNT] = {{{0}}};
static uint32 g_stat_count[STAT_TABLE_SIZE][MAX_STAT_ITEM_SIZE] = { {0} };
spinlock_t g_lock;
static thread_local_var stat_item_t *stat_item_local[STAT_TABLE_SIZE][MAX_STAT_ITEM_SIZE] = {{0}};
atomic_t g_stat_table_id;
static stat_result_t g_stat_result;
static const char *g_stat_unit_str[STAT_UNIT_CEIL] = {"", "us", "ms", "s", "byte", "KB", "MB", "GB"};
static stat_item_attr_t g_stat_item_attrs[MAX_STAT_ITEM_SIZE];
static uint32 g_stat_item_count;
status_t cm_register_stat_item(uint32 stat_item_id, const char *name, stat_unit_t unit, uint32 indicator,
cb_get_value_func_t value_func)
{
if (CM_IS_EMPTY_STR(name)) {
return CM_ERROR;
}
if (stat_item_id >= MAX_STAT_ITEM_SIZE) {
return CM_ERROR;
}
MEMS_RETURN_IFERR(strcpy_s(g_stat_item_attrs[stat_item_id].name, STAT_ITEM_NAME_MAX_LEN + 1, name));
g_stat_item_attrs[stat_item_id].unit = unit;
g_stat_item_attrs[stat_item_id].indicator = indicator;
g_stat_item_attrs[stat_item_id].func = value_func;
for (uint32 i = 0; i < stat_item_id; i++) {
if (CM_IS_EMPTY_STR(g_stat_item_attrs[stat_item_id].name)) {
return CM_ERROR;
}
}
g_stat_item_count++;
return CM_SUCCESS;
}
void cm_stat_record(uint32 stat_item_id, uint64 value)
{
if (!LOG_PROFILE_ON || !g_profile_stat_init) {
return;
}
int64 table_id = cm_atomic_get(&g_stat_table_id);
stat_item_t *item_local = stat_item_local[table_id][stat_item_id];
if (item_local == NULL) {
cm_spin_lock(&g_lock, NULL);
uint32 cnt = g_stat_count[table_id][stat_item_id];
if (cnt >= MAX_ITEM_COUNT) {
cm_spin_unlock(&g_lock);
return;
}
item_local = (stat_item_t *)cm_malloc_prot(sizeof(stat_item_t));
if (item_local == NULL) {
cm_spin_unlock(&g_lock);
return;
}
stat_item_local[table_id][stat_item_id] = item_local;
g_stat_count[table_id][stat_item_id]++;
cm_spin_unlock(&g_lock);
item_local->count = 0;
item_local->value = 0;
item_local->avg_value = 0;
item_local->max = 0;
item_local->min = CM_MAX_UINT64;
item_local->id = stat_item_id;
g_stat_table[table_id][stat_item_id][cnt] = item_local;
}
item_local->value += value;
item_local->count++;
if (g_stat_item_attrs[stat_item_id].indicator & STAT_INDICATOR_MAX) {
item_local->max = MAX(value, item_local->max);
}
if (g_stat_item_attrs[stat_item_id].indicator & STAT_INDICATOR_MIN) {
item_local->min = MIN(value, item_local->min);
}
}
static inline int get_cal_table_id(void)
{
return (int)((uint64)cm_atomic_get(&g_stat_table_id) ^ 1);
}
static inline void cal_item_result_by_ratio(const stat_item_t *stat_item, stat_item_result_t *result, double ratio)
{
result->id = stat_item->id;
if (g_stat_item_attrs[stat_item->id].func != NULL) {
result->value = (double)stat_item->value * ratio;
return;
}
result->value = (double)stat_item->value * ratio / DEFAULT_STAT_INTERVAL;
result->avg_value = stat_item->avg_value * ratio;
result->max = (double)stat_item->max * ratio;
result->min = (double)stat_item->min * ratio;
}
static void transform_unit(stat_item_t *stat_item, stat_item_result_t *result)
{
uint32 unit = g_stat_item_attrs[stat_item->id].unit;
result->is_valid = stat_item->count != 0;
switch (unit) {
case STAT_UNIT_DEFAULT:
case STAT_UNIT_BYTES:
case STAT_UNIT_US:
cal_item_result_by_ratio(stat_item, result, 1.0);
break;
case STAT_UNIT_MS:
cal_item_result_by_ratio(stat_item, result, 1.0 / MICROSECS_PER_MILLISEC);
break;
case STAT_UNIT_S:
cal_item_result_by_ratio(stat_item, result, 1.0 / MICROSECS_PER_SECOND);
break;
case STAT_UNIT_MB:
cal_item_result_by_ratio(stat_item, result, 1.0 / SIZE_M(1));
break;
case STAT_UNIT_KB:
cal_item_result_by_ratio(stat_item, result, 1.0 / SIZE_K(1));
break;
case STAT_UNIT_GB:
cal_item_result_by_ratio(stat_item, result, 1.0 / SIZE_G(1));
break;
default:
break;
}
}
static void stat_agg_items(stat_item_t *stat_item)
{
int cal_table_id = get_cal_table_id();
if (g_stat_item_attrs[stat_item->id].func != NULL) {
stat_item->count = 1;
stat_item->value = (uint64)g_stat_item_attrs[stat_item->id].func(stat_item->id);
stat_item->avg_value = (double)stat_item->value;
stat_item->max = stat_item->value;
stat_item->min = stat_item->value;
return;
}
uint32 item_count_total = g_stat_count[cal_table_id][stat_item->id];
for (uint32 item_count = 0; item_count < item_count_total; item_count++) {
stat_item_t *tmp = g_stat_table[cal_table_id][stat_item->id][item_count];
if (tmp == NULL) {
continue;
}
stat_item->value += tmp->value;
stat_item->count += tmp->count;
stat_item->max = ((g_stat_item_attrs[stat_item->id].indicator & STAT_INDICATOR_MAX) &&
(tmp->max > stat_item->max)) ? tmp->max : stat_item->max;
stat_item->min = ((g_stat_item_attrs[stat_item->id].indicator & STAT_INDICATOR_MIN)
&& (tmp->min < stat_item->min)) ? tmp->min : stat_item->min;
tmp->count = 0;
tmp->value = 0;
tmp->max = 0;
tmp->min = CM_MAX_UINT64;
}
if ((g_stat_item_attrs[stat_item->id].indicator & STAT_INDICATOR_AVG) && stat_item->count != 0) {
stat_item->avg_value = (double)stat_item->value / (1.0 * (double)stat_item->count);
}
}
static void stat_calculate(void)
{
if (!cm_atomic_cas(&g_stat_table_id, 0, 1)) {
(void)cm_atomic_cas(&g_stat_table_id, 1, 0);
}
cm_latch_x(&g_stat_result.latch, 0, NULL);
for (uint32 i = 0; i < g_stat_item_count; i++) {
stat_item_t stat_item = { i, 0, 0, 0, 0, CM_MAX_UINT64 };
stat_agg_items(&stat_item);
transform_unit(&stat_item, &g_stat_result.result_cache[i]);
}
cm_unlatch(&g_stat_result.latch, NULL);
}
static inline status_t build_item_head(const char *item_name, const char *suffix, const char *item_unit, char *item_buf)
{
item_buf[0] = '\0';
MEMS_RETURN_IFERR(strncat_s(item_buf, STAT_ITEM_WIDTH + 1, item_name, strlen(item_name)));
MEMS_RETURN_IFERR(strncat_s(item_buf, STAT_ITEM_WIDTH + 1, suffix, strlen(suffix)));
if (item_unit != NULL && strlen(item_unit) != 0) {
MEMS_RETURN_IFERR(strncat_s(item_buf, STAT_ITEM_WIDTH + 1, "(", 1));
MEMS_RETURN_IFERR(strncat_s(item_buf, STAT_ITEM_WIDTH + 1, item_unit, strlen(item_unit)));
MEMS_RETURN_IFERR(strncat_s(item_buf, STAT_ITEM_WIDTH + 1, ")", 1));
}
for (uint32 i = (uint32)strlen(item_buf); i < STAT_ITEM_WIDTH; i++) {
MEMS_RETURN_IFERR(strncat_s(item_buf, STAT_ITEM_WIDTH + 1, " ", 1));
}
return CM_SUCCESS;
}
static status_t stat_build_head(char *buf, uint32 begin, uint32 end)
{
char tmp_buf[STAT_ITEM_WIDTH + 1] = { 0 };
for (uint32 i = (uint32)begin; i < (uint32)end; i++) {
stat_unit_t unit = g_stat_item_attrs[i].unit;
if (g_stat_item_attrs[i].indicator & STAT_INDICATOR_ACC) {
CM_RETURN_IFERR(build_item_head(g_stat_item_attrs[i].name, "", g_stat_unit_str[unit], tmp_buf));
MEMS_RETURN_IFERR(strncat_s(buf, CM_MAX_LOG_CONTENT_LENGTH, tmp_buf, STAT_ITEM_WIDTH));
}
if (g_stat_item_attrs[i].indicator & STAT_INDICATOR_AVG) {
CM_RETURN_IFERR(build_item_head(g_stat_item_attrs[i].name, "Avg", g_stat_unit_str[unit], tmp_buf));
MEMS_RETURN_IFERR(strncat_s(buf, CM_MAX_LOG_CONTENT_LENGTH, tmp_buf, STAT_ITEM_WIDTH));
}
if (g_stat_item_attrs[i].indicator & STAT_INDICATOR_MAX) {
CM_RETURN_IFERR(build_item_head(g_stat_item_attrs[i].name, "Max", g_stat_unit_str[unit], tmp_buf));
MEMS_RETURN_IFERR(strncat_s(buf, CM_MAX_LOG_CONTENT_LENGTH, tmp_buf, STAT_ITEM_WIDTH));
}
if (g_stat_item_attrs[i].indicator & STAT_INDICATOR_MIN) {
CM_RETURN_IFERR(build_item_head(g_stat_item_attrs[i].name, "Min", g_stat_unit_str[unit], tmp_buf));
MEMS_RETURN_IFERR(strncat_s(buf, CM_MAX_LOG_CONTENT_LENGTH, tmp_buf, STAT_ITEM_WIDTH));
}
}
return CM_SUCCESS;
}
static status_t stat_concat_content_format(char *buf, double value, bool32 need_converted)
{
char tmp_buf[STAT_ITEM_WIDTH + 1] = { 0 };
if (need_converted) {
PRTS_RETURN_IFERR(
snprintf_s(tmp_buf, STAT_ITEM_WIDTH + 1, STAT_ITEM_WIDTH, "%-*llu", STAT_ITEM_WIDTH, (uint64)(value)));
} else {
PRTS_RETURN_IFERR(snprintf_s(tmp_buf, STAT_ITEM_WIDTH + 1, STAT_ITEM_WIDTH, "%-*.3f", STAT_ITEM_WIDTH, value));
}
MEMS_RETURN_IFERR(strncat_s(buf, CM_MAX_LOG_CONTENT_LENGTH, tmp_buf, STAT_ITEM_WIDTH));
return CM_SUCCESS;
}
static status_t stat_build_content(char *buf, uint32 begin, uint32 end)
{
for (uint32 i = (uint32)begin; i < (uint32)end; i++) {
bool32 need_converted = g_stat_item_attrs[i].unit == STAT_UNIT_DEFAULT ||
g_stat_item_attrs[i].unit == STAT_UNIT_US || g_stat_item_attrs[i].unit == STAT_UNIT_BYTES;
if (g_stat_item_attrs[i].indicator & STAT_INDICATOR_ACC) {
CM_RETURN_IFERR(stat_concat_content_format(buf, g_stat_result.result_cache[i].value, need_converted));
}
if (g_stat_item_attrs[i].indicator & STAT_INDICATOR_AVG) {
CM_RETURN_IFERR(stat_concat_content_format(buf, g_stat_result.result_cache[i].avg_value, 0));
}
if (g_stat_item_attrs[i].indicator & STAT_INDICATOR_MAX) {
CM_RETURN_IFERR(stat_concat_content_format(buf, g_stat_result.result_cache[i].max, need_converted));
}
if (g_stat_item_attrs[i].indicator & STAT_INDICATOR_MIN) {
CM_RETURN_IFERR(stat_concat_content_format(buf, g_stat_result.result_cache[i].min, need_converted));
}
}
return CM_SUCCESS;
}
static void stat_print_range(bool8 head_off, uint32 begin, uint32 end)
{
if (begin > end) {
return;
}
if (end > g_stat_item_count) {
end = g_stat_item_count;
}
char buf[CM_MAX_LOG_CONTENT_LENGTH] = { 0 };
status_t ret;
if (!head_off) {
ret = stat_build_head(buf, begin, end);
if (ret != CM_SUCCESS) {
LOG_RUN_ERR("[STAT] profile stat build head failed , retcode=%d, error code=%d, error info=%s", ret,
cm_get_error_code(), cm_get_errormsg(cm_get_error_code()));
return;
} else {
LOG_PROFILE("[STAT] %s", buf);
buf[0] = '\0';
}
}
cm_latch_s(&g_stat_result.latch, 0, CM_FALSE, NULL);
ret = stat_build_content(buf, begin, end);
if (ret != CM_SUCCESS) {
LOG_RUN_ERR("[STAT] profile stat build content failed , retcode=%d, error code=%d, error info=%s", ret,
cm_get_error_code(), cm_get_errormsg(cm_get_error_code()));
}
cm_unlatch(&g_stat_result.latch, NULL);
LOG_PROFILE("[STAT] %s", buf);
}
static void stat_print(void)
{
uint32 max_num_aline = DEFAULT_ITEM_NUM_ALINE;
uint32 i;
for (i = 0; i < g_stat_item_count; i += max_num_aline) {
stat_print_range(CM_FALSE, i, i + max_num_aline);
}
if (i < g_stat_item_count) {
stat_print_range(CM_FALSE, i, g_stat_item_count);
}
}
static void stat_free(void)
{
for (uint32 table_id = 0; table_id < STAT_TABLE_SIZE; table_id++) {
for (int item_id = 0; item_id < MAX_STAT_ITEM_SIZE; item_id++) {
uint32 item_count_total = g_stat_count[table_id][item_id];
for (uint32 item_count = 0; item_count < item_count_total; item_count++) {
if (g_stat_table[table_id][item_id][item_count] != NULL) {
CM_FREE_PROT_PTR(g_stat_table[table_id][item_id][item_count]);
g_stat_table[table_id][item_id][item_count] = NULL;
}
}
}
}
}
static void cm_profile_stat_entry(thread_t *thread)
{
cm_set_thread_name("cm_profile_stat");
cm_block_sighup_signal();
date_t last_check_time = g_timer()->monotonic_now;
while (!thread->closed) {
cm_sleep(STAT_THREAD_SLEEP_TIME);
if (!LOG_PROFILE_ON) {
continue;
}
date_t now = g_timer()->monotonic_now;
if (now - last_check_time >= DEFAULT_STAT_INTERVAL * MICROSECS_PER_SECOND) {
last_check_time = now;
stat_calculate();
stat_print();
}
}
}
status_t cm_profile_stat_init(void)
{
if (g_profile_stat_init) {
return CM_SUCCESS;
}
(void)cm_atomic_set(&g_stat_table_id, 0);
cm_latch_init(&g_stat_result.latch);
g_stat_item_count = 0;
status_t ret = cm_create_thread(cm_profile_stat_entry, 0, NULL, &g_profile_stat_thread);
if (ret != CM_SUCCESS) {
return ret;
}
g_profile_stat_init = CM_TRUE;
return CM_SUCCESS;
}
void cm_profile_stat_uninit(void)
{
if (g_profile_stat_init) {
cm_close_thread(&g_profile_stat_thread);
stat_free();
}
g_profile_stat_init = CM_FALSE;
}
void cm_set_stat_item_null(void)
{
for (uint32 i = 0; i < STAT_TABLE_SIZE; i++) {
for (uint32 j = 0; j < MAX_STAT_ITEM_SIZE; j++) {
stat_item_local[i][j] = NULL;
}
}
}
#ifdef __cplusplus
}
#endif