Coverage Report

Created: 2026-05-16 06:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/work/svt-av1/Source/Lib/Codec/svt_log.c
Line
Count
Source
1
/*
2
* Copyright(c) 2019 Intel Corporation
3
*
4
* This source code is subject to the terms of the BSD 2 Clause License and
5
* the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
6
* was not distributed with this source code in the LICENSE file, you can
7
* obtain it at https://www.aomedia.org/license/software-license. If the Alliance for Open
8
* Media Patent License 1.0 was not distributed with this source code in the
9
* PATENTS file, you can obtain it at https://www.aomedia.org/license/patent-license.
10
*/
11
//for getenv on windows
12
#if defined(_WIN32) && !defined(_CRT_SECURE_NO_WARNINGS)
13
#define _CRT_SECURE_NO_WARNINGS
14
#endif
15
#include "svt_log.h"
16
#include "svt_threads.h"
17
#include <stdio.h>
18
#include <stdlib.h>
19
#include <stdarg.h>
20
21
#if !CONFIG_LOG_QUIET
22
23
11.8k
static const char* log_level_str(SvtAv1LogLevel level) {
24
11.8k
    switch (level) {
25
0
    case SVT_AV1_LOG_FATAL:
26
0
        return "fatal";
27
0
    case SVT_AV1_LOG_ERROR:
28
0
        return "error";
29
1.89k
    case SVT_AV1_LOG_WARN:
30
1.89k
        return "warn";
31
9.95k
    case SVT_AV1_LOG_INFO:
32
9.95k
        return "info";
33
0
    case SVT_AV1_LOG_DEBUG:
34
0
        return "debug";
35
0
    default:
36
0
        return "unknown";
37
11.8k
    }
38
11.8k
}
39
40
struct DefaultCtx {
41
    SvtAv1LogLevel level;
42
    FILE*          file;
43
};
44
45
/**
46
 * @brief Global logger structure
47
 *
48
 * Handles both default cases and custom loggers.
49
 */
50
static struct Logger {
51
    SvtAv1LogCallback fn;
52
    // If fn == default_logger, ctx is DefaultCtx*
53
    void* ctx;
54
}* g_logger;
55
56
11.8k
static void default_logger(void* context, SvtAv1LogLevel level, const char* tag, const char* fmt, va_list args) {
57
11.8k
    struct DefaultCtx* logger = context;
58
11.8k
    if (level > logger->level) {
59
0
        return;
60
0
    }
61
11.8k
    if (tag) {
62
11.8k
        fprintf(logger->file, "%s[%s]: ", tag, log_level_str(level));
63
11.8k
    }
64
11.8k
    vfprintf(logger->file, fmt, args);
65
11.8k
    fflush(logger->file);
66
11.8k
}
67
68
1
static void logger_cleanup(void) {
69
1
    if (g_logger->fn == default_logger) {
70
1
        struct DefaultCtx* ctx = g_logger->ctx;
71
1
        if (ctx->file && ctx->file != stderr) {
72
0
            fclose(ctx->file);
73
0
        }
74
1
        free(ctx);
75
1
    }
76
1
    free(g_logger);
77
1
    g_logger = NULL;
78
1
}
79
80
// file scoped variable just to handle the init for a custom logger.
81
// It's only ever read once.
82
// Any writes after being read are ignored.
83
static struct Logger custom_logger_input;
84
85
1
static ONCE_ROUTINE(logger_create) {
86
1
    g_logger = calloc(1, sizeof(*g_logger));
87
1
    if (!g_logger) {
88
        // Not sure what else to do here...
89
0
        ONCE_ROUTINE_EPILOG;
90
0
    }
91
1
    struct Logger custom = custom_logger_input;
92
93
1
    if (custom.fn && custom.fn != default_logger) {
94
        // A custom logger was requested before initialization
95
0
        *g_logger = custom;
96
0
        atexit(logger_cleanup);
97
0
        ONCE_ROUTINE_EPILOG;
98
0
    }
99
100
    // Default logger
101
1
    struct DefaultCtx* ctx = calloc(1, sizeof(*ctx));
102
1
    if (!ctx) {
103
0
        free(g_logger);
104
0
        g_logger = NULL;
105
0
        ONCE_ROUTINE_EPILOG;
106
0
    }
107
108
1
    const char* log = getenv("SVT_LOG");
109
1
    ctx->level      = SVT_AV1_LOG_INFO;
110
1
    if (log) {
111
0
        const int new_level = atoi(log);
112
0
        if (new_level >= SVT_AV1_LOG_ALL && new_level <= SVT_AV1_LOG_DEBUG) {
113
0
            ctx->level = (SvtAv1LogLevel)new_level;
114
0
        }
115
0
    }
116
117
1
    const char* file = getenv("SVT_LOG_FILE");
118
1
    if (file) {
119
0
        FOPEN(ctx->file, file, "w+");
120
0
    }
121
    // If the file couldn't be opened, fall back to stderr
122
1
    if (!ctx->file) {
123
1
        ctx->file = stderr;
124
1
    }
125
126
1
    g_logger->fn  = default_logger;
127
1
    g_logger->ctx = ctx;
128
1
    atexit(logger_cleanup);
129
1
    ONCE_ROUTINE_EPILOG;
130
1
}
131
132
DEFINE_ONCE(g_logger_once);
133
134
11.8k
static struct Logger* get_logger() {
135
11.8k
    svt_run_once(&g_logger_once, logger_create);
136
11.8k
    return g_logger;
137
11.8k
}
138
139
0
void svt_aom_log_set_callback(SvtAv1LogCallback callback, void* context) {
140
    // We only want to allow using a custom context if a custom callback is provided.
141
    // Otherwise the context for the default logger will be incorrect.
142
    // This will still allow passing NULL for context in case the callback doesn't use it.
143
    // Due to how the rest of the code is structured, this only takes effect once,
144
    // and only if done before any logging happens.
145
0
    if (callback) {
146
0
        custom_logger_input.fn  = callback;
147
0
        custom_logger_input.ctx = context;
148
0
    }
149
0
}
150
151
11.8k
void svt_aom_log(SvtAv1LogLevel level, const char* tag, const char* format, ...) {
152
11.8k
    struct Logger* logger = get_logger();
153
11.8k
    if (!logger) {
154
0
        return;
155
0
    }
156
11.8k
    va_list args;
157
11.8k
    va_start(args, format);
158
11.8k
    logger->fn(logger->ctx, level, tag, format, args);
159
    va_end(args);
160
11.8k
}
161
162
#endif //CONFIG_LOG_QUIET