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