#include #include #include #include #include #include #include "log.h" #include "stellar_stat.h" #include "fieldstat/fieldstat_easy.h" #include "fieldstat/fieldstat_exporter.h" #define STAT_LOG_ERROR(format, ...) LOG_ERROR("stat", format, ##__VA_ARGS__) #define IS_FREE 0 #define IS_BUSY 0xf enum METRIC_TYPE { // device packet METRIC_TYPE_PKTS_RX, METRIC_TYPE_BYTES_RX, METRIC_TYPE_PKTS_TX, METRIC_TYPE_BYTES_TX, // keep-alive packet METRIC_TYPE_KEEP_ALIVE_PKTS, METRIC_TYPE_KEEP_ALIVE_BYTES, // raw packet METRIC_TYPE_RAW_PKTS_RX, METRIC_TYPE_RAW_BYTES_RX, METRIC_TYPE_RAW_PKTS_TX, METRIC_TYPE_RAW_BYTES_TX, // drop packet METRIC_TYPE_PKTS_DROPPED, METRIC_TYPE_BYTES_DROPPED, // inject packet METRIC_TYPE_PKTS_INJECTED, METRIC_TYPE_BYTES_INJECTED, // ctrl packet METRIC_TYPE_CTRL_PKTS_RX, METRIC_TYPE_CTRL_BYTES_RX, METRIC_TYPE_CTRL_PKTS_TX, METRIC_TYPE_CTRL_BYTES_TX, // ipv4 reassembly METRIC_TYPE_IP4_FLOW_FIND, METRIC_TYPE_IP4_FLOW_ADD, METRIC_TYPE_IP4_FLOW_DEL, METRIC_TYPE_IP4_FLOW_TIMEOUT, METRIC_TYPE_IP4_FLOW_FAIL_NO_SPACE, METRIC_TYPE_IP4_FLOW_FAIL_OVERLAP, METRIC_TYPE_IP4_FLOW_FAIL_MANY_FRAG, METRIC_TYPE_IP4_FLOW_FAIL_INVALID_LENGTH, METRIC_TYPE_IP4_FLOW_BYPASS_DUP_FIST_FRAG, METRIC_TYPE_IP4_FLOW_BYPASS_DUP_LAST_FRAG, // ipv6 reassembly METRIC_TYPE_IP6_FLOW_FIND, METRIC_TYPE_IP6_FLOW_ADD, METRIC_TYPE_IP6_FLOW_DEL, METRIC_TYPE_IP6_FLOW_TIMEOUT, METRIC_TYPE_IP6_FLOW_FAIL_NO_SPACE, METRIC_TYPE_IP6_FLOW_FAIL_OVERLAP, METRIC_TYPE_IP6_FLOW_FAIL_MANY_FRAG, METRIC_TYPE_IP6_FLOW_FAIL_INVALID_LENGTH, METRIC_TYPE_IP6_FLOW_BYPASS_DUP_FIST_FRAG, METRIC_TYPE_IP6_FLOW_BYPASS_DUP_LAST_FRAG, // TCP session METRIC_TYPE_HISTORY_TCP_SESSIONS, METRIC_TYPE_TCP_SESS_USED, METRIC_TYPE_TCP_SESS_OPENING, METRIC_TYPE_TCP_SESS_ACTIVE, METRIC_TYPE_TCP_SESS_CLOSING, METRIC_TYPE_TCP_SESS_DISCARD, METRIC_TYPE_TCP_SESS_CLOSED, // UDP session METRIC_TYPE_HISTORY_UDP_SESSIONS, METRIC_TYPE_UDP_SESS_USED, METRIC_TYPE_UDP_SESS_OPENING, METRIC_TYPE_UDP_SESS_ACTIVE, METRIC_TYPE_UDP_SESS_CLOSING, METRIC_TYPE_UDP_SESS_DISCARD, METRIC_TYPE_UDP_SESS_CLOSED, // Evicted session METRIC_TYPE_TCP_SESS_EVICTED, METRIC_TYPE_UDP_SESS_EVICTED, // Packet METRIC_TYPE_UDP_PKTS_BYPASS_TABLE_FULL, METRIC_TYPE_TCP_PKTS_BYPASS_TABLE_FULL, METRIC_TYPE_TCP_PKTS_BYPASS_SESSION_NOT_FOUND, METRIC_TYPE_TCP_PKTS_BYPASS_DUPLICATED, METRIC_TYPE_UDP_PKTS_BYPASS_DUPLICATED, METRIC_TYPE_UDP_PKTS_BYPASS_SESSION_EVICTED, // TCP segments METRIC_TYPE_TCP_SEGS_INPUT, METRIC_TYPE_TCP_SEGS_TIMEOUT, METRIC_TYPE_TCP_SEGS_RETRANSMITED, METRIC_TYPE_TCP_SEGS_OVERLAPPED, METRIC_TYPE_TCP_SEGS_OMITTED_TOO_MANY, METRIC_TYPE_TCP_SEGS_INORDER, METRIC_TYPE_TCP_SEGS_REORDERED, METRIC_TYPE_TCP_SEGS_BUFFERED, METRIC_TYPE_TCP_SEGS_FREED, // end METRIC_TYPE_MAX, }; const char *name[] = { // device packet "pkts_rx", "bytes_rx", "pkts_tx", "bytes_tx", // keep-alive packet "keep_alive_pkts", "keep_alive_bytes", // raw packet "raw_pkts_rx", "raw_bytes_rx", "raw_pkts_tx", "raw_bytes_tx", // drop packet "pkts_dropped", "bytes_dropped", // inject packet "pkts_injected", "bytes_injected", // ctrl packet "ctrl_pkts_rx", "ctrl_bytes_rx", "ctrl_pkts_tx", "ctrl_bytes_tx", // ipv4 reassembly // TODO rename "ip4_flow_find", "ip4_flow_add", "ip4_flow_del", "ip4_flow_timeout", "ip4_flow_fail_no_space", "ip4_flow_fail_overlap", "ip4_flow_fail_many_frag", "ip4_flow_fail_invalid_length", "ip4_flow_bypass_dup_fist_frag", "ip4_flow_bypass_dup_last_frag", // ipv6 reassembly // TODO rename "ip6_flow_find", "ip6_flow_add", "ip6_flow_del", "ip6_flow_timeout", "ip6_flow_fail_no_space", "ip6_flow_fail_overlap", "ip6_flow_fail_many_frag", "ip6_flow_fail_invalid_length", "ip6_flow_bypass_dup_fist_frag", "ip6_flow_bypass_dup_last_frag", // TCP session "history_tcp_sessions", "tcp_sess_used", "tcp_sess_opening", "tcp_sess_active", "tcp_sess_closing", "tcp_sess_discard", "tcp_sess_closed", // UDP session "history_udp_sessions", "udp_sess_used", "udp_sess_opening", "udp_sess_active", "udp_sess_closing", "udp_sess_discard", "udp_sess_closed", // Evicted session "tcp_sess_evicted", "udp_sess_evicted", // Packet "udp_pkts_bypass_table_full", "tcp_pkts_bypass_table_full", "tcp_pkts_bypass_session_not_found", "tcp_pkts_bypass_duplicated", "udp_pkts_bypass_duplicated", "udp_pkts_bypass_session_evicted", // TCP segments "tcp_segs_input", "tcp_segs_timeout", "tcp_segs_retransmited", "tcp_segs_overlapped", "tcp_segs_omitted_too_many", "tcp_segs_inorder", "tcp_segs_reordered", "tcp_segs_buffered", "tcp_segs_freed", }; /* * This naming convention maintains consistency by using a clear, structured pattern: * • tcp_sessions_ or ipv6_frags_ as the prefix to indicate the type of data. * • Action or status (e.g., bypassed, active, dropped) as the middle part. * • Cause or condition (e.g., full_table, buffer_limit) as the suffix for additional clarity. */ struct stellar_stat { uint16_t nr_thread; char output_file[2048]; struct fieldstat_easy *fs; int flag[MAX_THREAD_NUM]; // IS_FREE or IS_BUSY struct thread_stat thr_stat[MAX_THREAD_NUM]; uint64_t metric_key[METRIC_TYPE_MAX]; uint64_t metric_val[METRIC_TYPE_MAX]; }; // python3 -m pip install prettytable // python3 -m pip install jinja2 // /opt/MESA/bin/fieldstat_exporter.py local -j log/stellar_fs4.json -e -l --clear-screen struct stellar_stat *stellar_stat_new(uint16_t nr_thread) { char cwd[1024] = {0}; struct stellar_stat *stat = (struct stellar_stat *)calloc(1, sizeof(struct stellar_stat)); if (stat == NULL) { return NULL; } if (getcwd(cwd, sizeof(cwd)) == NULL) { STAT_LOG_ERROR("failed to get current working directory: %s", strerror(errno)); goto error_out; } snprintf(stat->output_file, sizeof(stat->output_file), "%s/log/stellar_fs4.json", cwd); stat->fs = fieldstat_easy_new(1, "stellar", NULL, 0); if (stat->fs == NULL) { STAT_LOG_ERROR("failed to create fieldstat_easy"); goto error_out; } stat->nr_thread = nr_thread; for (int i = 0; i < MAX_THREAD_NUM; i++) { stat->flag[i] = IS_FREE; } for (size_t i = 0; i < METRIC_TYPE_MAX; i++) { stat->metric_key[i] = fieldstat_easy_register_counter(stat->fs, name[i]); } return stat; error_out: stellar_stat_free(stat); return NULL; } void stellar_stat_free(struct stellar_stat *stat) { if (stat) { if (stat->fs) { fieldstat_easy_free(stat->fs); stat->fs = NULL; } free(stat); stat = NULL; } } void stellar_stat_output(struct stellar_stat *stat) { for (uint16_t i = 0; i < stat->nr_thread; i++) { if (ATOMIC_READ(&(stat->flag[i])) == IS_BUSY) { struct thread_stat *thr_stat = &stat->thr_stat[i]; for (size_t j = 0; j < METRIC_TYPE_MAX; j++) { switch (j) { // device packet case METRIC_TYPE_PKTS_RX: stat->metric_val[j] += thr_stat->packet_io->pkts_rx; break; case METRIC_TYPE_BYTES_RX: stat->metric_val[j] += thr_stat->packet_io->bytes_rx; break; case METRIC_TYPE_PKTS_TX: stat->metric_val[j] += thr_stat->packet_io->pkts_tx; break; case METRIC_TYPE_BYTES_TX: stat->metric_val[j] += thr_stat->packet_io->bytes_tx; break; // keep-alive packet case METRIC_TYPE_KEEP_ALIVE_PKTS: stat->metric_val[j] += thr_stat->packet_io->keep_alive_pkts; break; case METRIC_TYPE_KEEP_ALIVE_BYTES: stat->metric_val[j] += thr_stat->packet_io->keep_alive_bytes; break; // raw packet case METRIC_TYPE_RAW_PKTS_RX: stat->metric_val[j] += thr_stat->packet_io->raw_pkts_rx; break; case METRIC_TYPE_RAW_BYTES_RX: stat->metric_val[j] += thr_stat->packet_io->raw_bytes_rx; break; case METRIC_TYPE_RAW_PKTS_TX: stat->metric_val[j] += thr_stat->packet_io->raw_pkts_tx; break; case METRIC_TYPE_RAW_BYTES_TX: stat->metric_val[j] += thr_stat->packet_io->raw_bytes_tx; break; // drop packet case METRIC_TYPE_PKTS_DROPPED: stat->metric_val[j] += thr_stat->packet_io->pkts_dropped; break; case METRIC_TYPE_BYTES_DROPPED: stat->metric_val[j] += thr_stat->packet_io->bytes_dropped; break; // inject packet case METRIC_TYPE_PKTS_INJECTED: stat->metric_val[j] += thr_stat->packet_io->pkts_injected; break; case METRIC_TYPE_BYTES_INJECTED: stat->metric_val[j] += thr_stat->packet_io->bytes_injected; break; // ctrl packet case METRIC_TYPE_CTRL_PKTS_RX: stat->metric_val[j] += thr_stat->packet_io->ctrl_pkts_rx; break; case METRIC_TYPE_CTRL_BYTES_RX: stat->metric_val[j] += thr_stat->packet_io->ctrl_bytes_rx; break; case METRIC_TYPE_CTRL_PKTS_TX: stat->metric_val[j] += thr_stat->packet_io->ctrl_pkts_tx; break; case METRIC_TYPE_CTRL_BYTES_TX: stat->metric_val[j] += thr_stat->packet_io->ctrl_bytes_tx; break; // ipv4 reassembly case METRIC_TYPE_IP4_FLOW_FIND: stat->metric_val[j] += thr_stat->ip_reassembly->ip4_flow_find; break; case METRIC_TYPE_IP4_FLOW_ADD: stat->metric_val[j] += thr_stat->ip_reassembly->ip4_flow_add; break; case METRIC_TYPE_IP4_FLOW_DEL: stat->metric_val[j] += thr_stat->ip_reassembly->ip4_flow_del; break; case METRIC_TYPE_IP4_FLOW_TIMEOUT: stat->metric_val[j] += thr_stat->ip_reassembly->ip4_flow_timeout; break; case METRIC_TYPE_IP4_FLOW_FAIL_NO_SPACE: stat->metric_val[j] += thr_stat->ip_reassembly->ip4_flow_fail_no_space; break; case METRIC_TYPE_IP4_FLOW_FAIL_OVERLAP: stat->metric_val[j] += thr_stat->ip_reassembly->ip4_flow_fail_overlap; break; case METRIC_TYPE_IP4_FLOW_FAIL_MANY_FRAG: stat->metric_val[j] += thr_stat->ip_reassembly->ip4_flow_fail_many_frag; break; case METRIC_TYPE_IP4_FLOW_FAIL_INVALID_LENGTH: stat->metric_val[j] += thr_stat->ip_reassembly->ip4_flow_fail_invalid_length; break; case METRIC_TYPE_IP4_FLOW_BYPASS_DUP_FIST_FRAG: stat->metric_val[j] += thr_stat->ip_reassembly->ip4_flow_bypass_dup_fist_frag; break; case METRIC_TYPE_IP4_FLOW_BYPASS_DUP_LAST_FRAG: stat->metric_val[j] += thr_stat->ip_reassembly->ip4_flow_bypass_dup_last_frag; break; // ipv6 reassembly case METRIC_TYPE_IP6_FLOW_FIND: stat->metric_val[j] += thr_stat->ip_reassembly->ip6_flow_find; break; case METRIC_TYPE_IP6_FLOW_ADD: stat->metric_val[j] += thr_stat->ip_reassembly->ip6_flow_add; break; case METRIC_TYPE_IP6_FLOW_DEL: stat->metric_val[j] += thr_stat->ip_reassembly->ip6_flow_del; break; case METRIC_TYPE_IP6_FLOW_TIMEOUT: stat->metric_val[j] += thr_stat->ip_reassembly->ip6_flow_timeout; break; case METRIC_TYPE_IP6_FLOW_FAIL_NO_SPACE: stat->metric_val[j] += thr_stat->ip_reassembly->ip6_flow_fail_no_space; break; case METRIC_TYPE_IP6_FLOW_FAIL_OVERLAP: stat->metric_val[j] += thr_stat->ip_reassembly->ip6_flow_fail_overlap; break; case METRIC_TYPE_IP6_FLOW_FAIL_MANY_FRAG: stat->metric_val[j] += thr_stat->ip_reassembly->ip6_flow_fail_many_frag; break; case METRIC_TYPE_IP6_FLOW_FAIL_INVALID_LENGTH: stat->metric_val[j] += thr_stat->ip_reassembly->ip6_flow_fail_invalid_length; break; case METRIC_TYPE_IP6_FLOW_BYPASS_DUP_FIST_FRAG: stat->metric_val[j] += thr_stat->ip_reassembly->ip6_flow_bypass_dup_fist_frag; break; case METRIC_TYPE_IP6_FLOW_BYPASS_DUP_LAST_FRAG: stat->metric_val[j] += thr_stat->ip_reassembly->ip6_flow_bypass_dup_last_frag; break; // TCP session case METRIC_TYPE_HISTORY_TCP_SESSIONS: stat->metric_val[j] += thr_stat->session_mgr->history_tcp_sessions; break; case METRIC_TYPE_TCP_SESS_USED: stat->metric_val[j] += thr_stat->session_mgr->tcp_sess_used; break; case METRIC_TYPE_TCP_SESS_OPENING: stat->metric_val[j] += thr_stat->session_mgr->tcp_sess_opening; break; case METRIC_TYPE_TCP_SESS_ACTIVE: stat->metric_val[j] += thr_stat->session_mgr->tcp_sess_active; break; case METRIC_TYPE_TCP_SESS_CLOSING: stat->metric_val[j] += thr_stat->session_mgr->tcp_sess_closing; break; case METRIC_TYPE_TCP_SESS_DISCARD: stat->metric_val[j] += thr_stat->session_mgr->tcp_sess_discard; break; case METRIC_TYPE_TCP_SESS_CLOSED: stat->metric_val[j] += thr_stat->session_mgr->tcp_sess_closed; break; // UDP session case METRIC_TYPE_HISTORY_UDP_SESSIONS: stat->metric_val[j] += thr_stat->session_mgr->history_udp_sessions; break; case METRIC_TYPE_UDP_SESS_USED: stat->metric_val[j] += thr_stat->session_mgr->udp_sess_used; break; case METRIC_TYPE_UDP_SESS_OPENING: stat->metric_val[j] += thr_stat->session_mgr->udp_sess_opening; break; case METRIC_TYPE_UDP_SESS_ACTIVE: stat->metric_val[j] += thr_stat->session_mgr->udp_sess_active; break; case METRIC_TYPE_UDP_SESS_CLOSING: stat->metric_val[j] += thr_stat->session_mgr->udp_sess_closing; break; case METRIC_TYPE_UDP_SESS_DISCARD: stat->metric_val[j] += thr_stat->session_mgr->udp_sess_discard; break; case METRIC_TYPE_UDP_SESS_CLOSED: stat->metric_val[j] += thr_stat->session_mgr->udp_sess_closed; break; // Evicted session case METRIC_TYPE_TCP_SESS_EVICTED: stat->metric_val[j] += thr_stat->session_mgr->tcp_sess_evicted; break; case METRIC_TYPE_UDP_SESS_EVICTED: stat->metric_val[j] += thr_stat->session_mgr->udp_sess_evicted; break; // Packet case METRIC_TYPE_UDP_PKTS_BYPASS_TABLE_FULL: stat->metric_val[j] += thr_stat->session_mgr->udp_pkts_bypass_table_full; break; case METRIC_TYPE_TCP_PKTS_BYPASS_TABLE_FULL: stat->metric_val[j] += thr_stat->session_mgr->tcp_pkts_bypass_table_full; break; case METRIC_TYPE_TCP_PKTS_BYPASS_SESSION_NOT_FOUND: stat->metric_val[j] += thr_stat->session_mgr->tcp_pkts_bypass_session_not_found; break; case METRIC_TYPE_TCP_PKTS_BYPASS_DUPLICATED: stat->metric_val[j] += thr_stat->session_mgr->tcp_pkts_bypass_duplicated; break; case METRIC_TYPE_UDP_PKTS_BYPASS_DUPLICATED: stat->metric_val[j] += thr_stat->session_mgr->udp_pkts_bypass_duplicated; break; case METRIC_TYPE_UDP_PKTS_BYPASS_SESSION_EVICTED: stat->metric_val[j] += thr_stat->session_mgr->udp_pkts_bypass_session_evicted; break; // TCP segments case METRIC_TYPE_TCP_SEGS_INPUT: stat->metric_val[j] += thr_stat->session_mgr->tcp_segs_input; break; case METRIC_TYPE_TCP_SEGS_TIMEOUT: stat->metric_val[j] += thr_stat->session_mgr->tcp_segs_timeout; break; case METRIC_TYPE_TCP_SEGS_RETRANSMITED: stat->metric_val[j] += thr_stat->session_mgr->tcp_segs_retransmited; break; case METRIC_TYPE_TCP_SEGS_OVERLAPPED: stat->metric_val[j] += thr_stat->session_mgr->tcp_segs_overlapped; break; case METRIC_TYPE_TCP_SEGS_OMITTED_TOO_MANY: stat->metric_val[j] += thr_stat->session_mgr->tcp_segs_omitted_too_many; break; case METRIC_TYPE_TCP_SEGS_INORDER: stat->metric_val[j] += thr_stat->session_mgr->tcp_segs_inorder; break; case METRIC_TYPE_TCP_SEGS_REORDERED: stat->metric_val[j] += thr_stat->session_mgr->tcp_segs_reordered; break; case METRIC_TYPE_TCP_SEGS_BUFFERED: stat->metric_val[j] += thr_stat->session_mgr->tcp_segs_buffered; break; case METRIC_TYPE_TCP_SEGS_FREED: stat->metric_val[j] += thr_stat->session_mgr->tcp_segs_freed; break; default: assert(0); break; } } memset(thr_stat, 0, sizeof(struct thread_stat)); ATOMIC_SET(&(stat->flag[i]), IS_FREE); } } for (size_t j = 0; j < METRIC_TYPE_MAX; j++) { fieldstat_easy_counter_set(stat->fs, 0, stat->metric_key[j], NULL, 0, stat->metric_val[j]); } char *buff; size_t len; fieldstat_easy_output(stat->fs, &buff, &len); if (buff) { FILE *fp = fopen(stat->output_file, "w+"); if (fp == NULL) { STAT_LOG_ERROR("failed to open file: %s, %s", stat->output_file, strerror(errno)); } else { fwrite(buff, len, 1, fp); fflush(fp); fclose(fp); } free(buff); } for (size_t j = 0; j < METRIC_TYPE_MAX; j++) { stat->metric_val[j] = 0; } } void stellar_stat_merge(struct stellar_stat *stat, const struct thread_stat *thr_stat, uint16_t thr_idx) { if (ATOMIC_READ(&(stat->flag[thr_idx])) == IS_FREE) { memcpy(&stat->thr_stat[thr_idx], thr_stat, sizeof(struct thread_stat)); ATOMIC_SET(&(stat->flag[thr_idx]), IS_BUSY); } }