1 /*
2 * This file is part of libplacebo.
3 *
4 * libplacebo is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * libplacebo is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with libplacebo. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #include <stdio.h>
19 #include <locale.h>
20
21 #include "common.h"
22 #include "log.h"
23 #include "pl_thread.h"
24
25 struct priv {
26 pl_mutex lock;
27 enum pl_log_level log_level_cap;
28 pl_str logbuffer;
29 };
30
pl_log_create(int api_ver,const struct pl_log_params * params)31 pl_log pl_log_create(int api_ver, const struct pl_log_params *params)
32 {
33 if (api_ver != PL_API_VER) {
34 fprintf(stderr,
35 "*************************************************************\n"
36 "libplacebo: ABI mismatch detected! (requested: %d, compiled: %d)\n"
37 "\n"
38 "This is usually indicative of a linking mismatch, and will\n"
39 "result in serious issues including stack corruption, random\n"
40 "crashes and arbitrary code execution. Aborting as a safety\n"
41 "precaution. Fix your system!\n",
42 api_ver, PL_API_VER);
43 abort();
44 }
45
46 struct pl_log *log = pl_zalloc_obj(NULL, log, struct priv);
47 struct priv *p = PL_PRIV(log);
48 log->params = *PL_DEF(params, &pl_log_default_params);
49 pl_mutex_init(&p->lock);
50 pl_info(log, "Initialized libplacebo %s (API v%d)", PL_VERSION, PL_API_VER);
51 return log;
52 }
53
54 const struct pl_log_params pl_log_default_params = {0};
55
pl_log_destroy(pl_log * plog)56 void pl_log_destroy(pl_log *plog)
57 {
58 pl_log log = *plog;
59 if (!log)
60 return;
61
62 struct priv *p = PL_PRIV(log);
63 pl_mutex_destroy(&p->lock);
64 pl_free((void *) log);
65 *plog = NULL;
66 }
67
pl_log_update(pl_log ptr,const struct pl_log_params * params)68 struct pl_log_params pl_log_update(pl_log ptr, const struct pl_log_params *params)
69 {
70 struct pl_log *log = (struct pl_log *) ptr;
71 if (!log)
72 return pl_log_default_params;
73
74 struct priv *p = PL_PRIV(log);
75 pl_mutex_lock(&p->lock);
76 struct pl_log_params prev_params = log->params;
77 log->params = *PL_DEF(params, &pl_log_default_params);
78 pl_mutex_unlock(&p->lock);
79
80 return prev_params;
81 }
82
pl_log_level_update(pl_log ptr,enum pl_log_level level)83 enum pl_log_level pl_log_level_update(pl_log ptr, enum pl_log_level level)
84 {
85 struct pl_log *log = (struct pl_log *) ptr;
86 if (!log)
87 return PL_LOG_NONE;
88
89 struct priv *p = PL_PRIV(log);
90 pl_mutex_lock(&p->lock);
91 enum pl_log_level prev_level = log->params.log_level;
92 log->params.log_level = level;
93 pl_mutex_unlock(&p->lock);
94
95 return prev_level;
96 }
97
pl_log_level_cap(pl_log log,enum pl_log_level cap)98 void pl_log_level_cap(pl_log log, enum pl_log_level cap)
99 {
100 if (!log)
101 return;
102
103 struct priv *p = PL_PRIV(log);
104 pl_mutex_lock(&p->lock);
105 p->log_level_cap = cap;
106 pl_mutex_unlock(&p->lock);
107 }
108
default_stream(void * stream,enum pl_log_level level)109 static FILE *default_stream(void *stream, enum pl_log_level level)
110 {
111 return PL_DEF(stream, level <= PL_LOG_WARN ? stderr : stdout);
112 }
113
pl_log_simple(void * stream,enum pl_log_level level,const char * msg)114 void pl_log_simple(void *stream, enum pl_log_level level, const char *msg)
115 {
116 static const char *prefix[] = {
117 [PL_LOG_FATAL] = "fatal",
118 [PL_LOG_ERR] = "error",
119 [PL_LOG_WARN] = "warn",
120 [PL_LOG_INFO] = "info",
121 [PL_LOG_DEBUG] = "debug",
122 [PL_LOG_TRACE] = "trace",
123 };
124
125 FILE *h = default_stream(stream, level);
126 fprintf(h, "%5s: %s\n", prefix[level], msg);
127 if (level <= PL_LOG_WARN)
128 fflush(h);
129 }
130
pl_log_color(void * stream,enum pl_log_level level,const char * msg)131 void pl_log_color(void *stream, enum pl_log_level level, const char *msg)
132 {
133 static const char *color[] = {
134 [PL_LOG_FATAL] = "31;1", // bright red
135 [PL_LOG_ERR] = "31", // red
136 [PL_LOG_WARN] = "33", // yellow/orange
137 [PL_LOG_INFO] = "32", // green
138 [PL_LOG_DEBUG] = "34", // blue
139 [PL_LOG_TRACE] = "30;1", // bright black
140 };
141
142 FILE *h = default_stream(stream, level);
143 fprintf(h, "\033[%sm%s\033[0m\n", color[level], msg);
144 if (level <= PL_LOG_WARN)
145 fflush(h);
146 }
147
pl_msg_va(pl_log log,enum pl_log_level lev,const char * fmt,va_list va)148 static void pl_msg_va(pl_log log, enum pl_log_level lev,
149 const char *fmt, va_list va)
150 {
151 // Test log message without taking the lock, to avoid thrashing the
152 // lock for thousands of trace messages unless those are actually
153 // enabled. This may be a false negative, in which case log messages may
154 // be lost as a result. But this shouldn't be a big deal, since any
155 // situation leading to lost log messages would itself be a race condition.
156 if (!pl_msg_test(log, lev))
157 return;
158
159 // Re-test the log message level with held lock to avoid false positives,
160 // which would be a considerably bigger deal than false negatives
161 struct priv *p = PL_PRIV(log);
162 pl_mutex_lock(&p->lock);
163
164 // Apply this cap before re-testing the log level, to avoid giving users
165 // messages that should have been dropped by the log level.
166 lev = PL_MAX(lev, p->log_level_cap);
167 if (!pl_msg_test(log, lev))
168 goto done;
169
170 p->logbuffer.len = 0;
171 pl_str_append_vasprintf((void *) log, &p->logbuffer, fmt, va);
172 log->params.log_cb(log->params.log_priv, lev, p->logbuffer.buf);
173
174 done:
175 pl_mutex_unlock(&p->lock);
176 }
177
pl_msg(pl_log log,enum pl_log_level lev,const char * fmt,...)178 void pl_msg(pl_log log, enum pl_log_level lev, const char *fmt, ...)
179 {
180 va_list va;
181 va_start(va, fmt);
182 pl_msg_va(log, lev, fmt, va);
183 va_end(va);
184 }
185
pl_msg_source(pl_log log,enum pl_log_level lev,const char * src)186 void pl_msg_source(pl_log log, enum pl_log_level lev, const char *src)
187 {
188 if (!pl_msg_test(log, lev) || !src)
189 return;
190
191 int line = 1;
192 while (*src) {
193 const char *end = strchr(src, '\n');
194 if (!end) {
195 pl_msg(log, lev, "[%3d] %s", line, src);
196 break;
197 }
198
199 pl_msg(log, lev, "[%3d] %.*s", line, (int)(end - src), src);
200 src = end + 1;
201 line++;
202 }
203 }
204