|
| 1 | +#include <thermal/simd/logging.h> |
| 2 | +#include <thermal/simd/metrics.h> |
| 3 | +#include <thermal/simd/simd_width.h> |
| 4 | + |
| 5 | +#include <errno.h> |
| 6 | +#include <stdint.h> |
| 7 | +#include <stdio.h> |
| 8 | +#include <stdlib.h> |
| 9 | +#include <string.h> |
| 10 | +#include <unistd.h> |
| 11 | + |
| 12 | +static void check_true(int condition, const char *message) { |
| 13 | + if (!condition) { |
| 14 | + fprintf(stderr, "test failure: %s\n", message); |
| 15 | + exit(1); |
| 16 | + } |
| 17 | +} |
| 18 | + |
| 19 | +static void check_nonneg(int value, const char *message) { |
| 20 | + if (value < 0) { |
| 21 | + fprintf(stderr, "test failure: %s (errno=%d)\n", message, errno); |
| 22 | + exit(1); |
| 23 | + } |
| 24 | +} |
| 25 | + |
| 26 | +static void check_zero(int rc, const char *message) { |
| 27 | + if (rc != 0) { |
| 28 | + fprintf(stderr, "test failure: %s (errno=%d)\n", message, errno); |
| 29 | + exit(1); |
| 30 | + } |
| 31 | +} |
| 32 | + |
| 33 | +static ssize_t read_into_buffer(int fd, char *buffer, size_t buffer_len) { |
| 34 | + ssize_t total = 0; |
| 35 | + while ((size_t)total < buffer_len - 1) { |
| 36 | + ssize_t n = read(fd, buffer + total, buffer_len - 1 - (size_t)total); |
| 37 | + if (n <= 0) { |
| 38 | + break; |
| 39 | + } |
| 40 | + total += n; |
| 41 | + } |
| 42 | + buffer[total >= 0 ? (size_t)total : 0] = '\0'; |
| 43 | + return total; |
| 44 | +} |
| 45 | + |
| 46 | +typedef void (*capture_fn)(void *arg); |
| 47 | + |
| 48 | +static void capture_stream(FILE *stream, int fd, capture_fn fn, void *arg, char *buffer, size_t buffer_len) { |
| 49 | + int pipefd[2]; |
| 50 | + check_zero(pipe(pipefd), "pipe"); |
| 51 | + fflush(stream); |
| 52 | + int saved_fd = dup(fd); |
| 53 | + check_nonneg(saved_fd, "dup"); |
| 54 | + check_nonneg(dup2(pipefd[1], fd), "dup2 redirect"); |
| 55 | + close(pipefd[1]); |
| 56 | + fn(arg); |
| 57 | + fflush(stream); |
| 58 | + check_zero(close(fd), "close stream fd"); |
| 59 | + read_into_buffer(pipefd[0], buffer, buffer_len); |
| 60 | + close(pipefd[0]); |
| 61 | + check_nonneg(dup2(saved_fd, fd), "restore dup2"); |
| 62 | + check_zero(close(saved_fd), "close saved fd"); |
| 63 | +} |
| 64 | + |
| 65 | +static void log_message(void *arg) { |
| 66 | + const char *message = (const char *)arg; |
| 67 | + tsd_log_info("test_component", "%s", message); |
| 68 | +} |
| 69 | + |
| 70 | +static void log_warning(void *arg) { |
| 71 | + const char *message = (const char *)arg; |
| 72 | + tsd_log_warn("test_component", "%s", message); |
| 73 | +} |
| 74 | + |
| 75 | +static void verify_logging_outputs(void) { |
| 76 | + tsd_log_set_level(TSD_LOG_LEVEL_DEBUG); |
| 77 | + char buffer[1024]; |
| 78 | + |
| 79 | + const char *info_message = "informational payload"; |
| 80 | + capture_stream(stdout, STDOUT_FILENO, log_message, (void *)info_message, buffer, sizeof(buffer)); |
| 81 | + check_true(strstr(buffer, "test_component") != NULL, "info log missing component"); |
| 82 | + check_true(strstr(buffer, info_message) != NULL, "info log missing payload"); |
| 83 | + |
| 84 | + const char *warn_message = "warning payload"; |
| 85 | + capture_stream(stderr, STDERR_FILENO, log_warning, (void *)warn_message, buffer, sizeof(buffer)); |
| 86 | + check_true(strstr(buffer, "test_component") != NULL, "warn log missing component"); |
| 87 | + check_true(strstr(buffer, warn_message) != NULL, "warn log missing payload"); |
| 88 | + |
| 89 | + char long_message[600]; |
| 90 | + memset(long_message, 'A', sizeof(long_message) - 1); |
| 91 | + long_message[sizeof(long_message) - 1] = '\0'; |
| 92 | + capture_stream(stdout, STDOUT_FILENO, log_message, long_message, buffer, sizeof(buffer)); |
| 93 | + check_true(strstr(buffer, long_message) != NULL, "long message not emitted"); |
| 94 | +} |
| 95 | + |
| 96 | +static void verify_log_level_controls(void) { |
| 97 | + tsd_log_set_level(TSD_LOG_LEVEL_INFO); |
| 98 | + check_true(tsd_log_get_level() == TSD_LOG_LEVEL_INFO, "info level clamp"); |
| 99 | + check_true(tsd_log_should_log(TSD_LOG_LEVEL_ERROR), "error should log"); |
| 100 | + check_true(tsd_log_should_log(TSD_LOG_LEVEL_WARN), "warn should log"); |
| 101 | + check_true(tsd_log_should_log(TSD_LOG_LEVEL_INFO), "info should log"); |
| 102 | + check_true(!tsd_log_should_log(TSD_LOG_LEVEL_DEBUG), "debug should not log"); |
| 103 | + |
| 104 | + tsd_log_set_level(TSD_LOG_LEVEL_ERROR); |
| 105 | + check_true(tsd_log_get_level() == TSD_LOG_LEVEL_ERROR, "error clamp lower bound"); |
| 106 | + check_true(!tsd_log_should_log(TSD_LOG_LEVEL_INFO), "info suppressed at error"); |
| 107 | + |
| 108 | + tsd_log_set_level((tsd_log_level_t)100); |
| 109 | + check_true(tsd_log_get_level() == TSD_LOG_LEVEL_DEBUG, "high level clamps to debug"); |
| 110 | +} |
| 111 | + |
| 112 | +static void verify_log_level_conversion(void) { |
| 113 | + tsd_log_level_t level = TSD_LOG_LEVEL_ERROR; |
| 114 | + check_true(tsd_log_level_from_string("warn", &level) == 0, "parse warn"); |
| 115 | + check_true(level == TSD_LOG_LEVEL_WARN, "warn level value"); |
| 116 | + check_true(tsd_log_level_from_string("INFO", &level) == 0, "parse info"); |
| 117 | + check_true(level == TSD_LOG_LEVEL_INFO, "info level value"); |
| 118 | + check_true(tsd_log_level_from_string("debug", &level) == 0, "parse debug"); |
| 119 | + check_true(level == TSD_LOG_LEVEL_DEBUG, "debug level value"); |
| 120 | + check_true(tsd_log_level_from_string("error", &level) == 0, "parse error"); |
| 121 | + check_true(level == TSD_LOG_LEVEL_ERROR, "error level value"); |
| 122 | + check_true(tsd_log_level_from_string("warning", &level) == 0, "parse warning alias"); |
| 123 | + check_true(level == TSD_LOG_LEVEL_WARN, "warning alias level value"); |
| 124 | + check_true(tsd_log_level_from_string(NULL, &level) != 0, "null string rejected"); |
| 125 | + check_true(tsd_log_level_from_string("invalid", &level) != 0, "invalid string rejected"); |
| 126 | + check_true(tsd_log_level_from_string("info", NULL) != 0, "null output rejected"); |
| 127 | + |
| 128 | + check_true(strcmp(tsd_log_level_to_string(TSD_LOG_LEVEL_ERROR), "ERROR") == 0, "level to string error"); |
| 129 | + check_true(strcmp(tsd_log_level_to_string(TSD_LOG_LEVEL_WARN), "WARN") == 0, "level to string warn"); |
| 130 | + check_true(strcmp(tsd_log_level_to_string(TSD_LOG_LEVEL_INFO), "INFO") == 0, "level to string info"); |
| 131 | + check_true(strcmp(tsd_log_level_to_string(TSD_LOG_LEVEL_DEBUG), "DEBUG") == 0, "level to string debug"); |
| 132 | +} |
| 133 | + |
| 134 | +static void verify_strerror_wrapper(void) { |
| 135 | + char buffer[64]; |
| 136 | + const char *msg = tsd_log_strerror(ENOENT, buffer, sizeof(buffer)); |
| 137 | + check_true(msg != NULL, "strerror returned null"); |
| 138 | + check_true(strlen(msg) > 0, "strerror empty message"); |
| 139 | + |
| 140 | + const char *empty = tsd_log_strerror(ENOENT, buffer, 0); |
| 141 | + check_true(empty != NULL, "strerror zero buffer null"); |
| 142 | + check_true(strcmp(empty, "") == 0, "strerror zero buffer output"); |
| 143 | +} |
| 144 | + |
| 145 | +static void verify_metrics_counters(void) { |
| 146 | + tsd_metrics_snapshot_t before; |
| 147 | + tsd_metrics_snapshot(&before); |
| 148 | + |
| 149 | + tsd_metrics_increment(TSD_METRIC_PERF_FALLBACKS); |
| 150 | + tsd_metrics_add(TSD_METRIC_PERF_FALLBACKS, 5); |
| 151 | + tsd_metrics_increment(TSD_METRIC_COUNT); |
| 152 | + tsd_metrics_add(TSD_METRIC_COUNT, 10); |
| 153 | + |
| 154 | + tsd_metrics_snapshot_t after; |
| 155 | + tsd_metrics_snapshot(&after); |
| 156 | + |
| 157 | + uint64_t perf_fallbacks_delta = after.counters[TSD_METRIC_PERF_FALLBACKS] - |
| 158 | + before.counters[TSD_METRIC_PERF_FALLBACKS]; |
| 159 | + check_true(perf_fallbacks_delta == 6, "perf fallback counter delta"); |
| 160 | + |
| 161 | + for (int i = 0; i < TSD_METRIC_COUNT; ++i) { |
| 162 | + const char *name = tsd_metrics_counter_name((tsd_metric_counter_t)i); |
| 163 | + check_true(name != NULL, "metric name null"); |
| 164 | + check_true(strlen(name) > 0, "metric name empty"); |
| 165 | + } |
| 166 | + check_true(strcmp(tsd_metrics_counter_name((tsd_metric_counter_t)100), "invalid") == 0, |
| 167 | + "invalid metric name fallback"); |
| 168 | + |
| 169 | + tsd_metrics_snapshot_t mid; |
| 170 | + tsd_metrics_snapshot(&mid); |
| 171 | + tsd_metrics_record_width_transition(SIMD_SSE41, SIMD_AVX2, 0); |
| 172 | + tsd_metrics_record_width_transition(SIMD_AVX2, SIMD_AVX512, -1); |
| 173 | + tsd_metrics_snapshot_t end; |
| 174 | + tsd_metrics_snapshot(&end); |
| 175 | + |
| 176 | + uint64_t transitions_delta = end.counters[TSD_METRIC_PATCH_TRANSITIONS] - |
| 177 | + mid.counters[TSD_METRIC_PATCH_TRANSITIONS]; |
| 178 | + uint64_t failures_delta = end.counters[TSD_METRIC_PATCH_FAILURES] - |
| 179 | + mid.counters[TSD_METRIC_PATCH_FAILURES]; |
| 180 | + check_true(transitions_delta == 1, "patch transitions delta"); |
| 181 | + check_true(failures_delta == 1, "patch failures delta"); |
| 182 | +} |
| 183 | + |
| 184 | +int main(void) { |
| 185 | + verify_logging_outputs(); |
| 186 | + verify_log_level_controls(); |
| 187 | + verify_log_level_conversion(); |
| 188 | + verify_strerror_wrapper(); |
| 189 | + verify_metrics_counters(); |
| 190 | + return 0; |
| 191 | +} |
0 commit comments