dte test coverage


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 50.0% high: ≥ 85.0%
Coverage Exec / Excl / Total
Lines: 98.7% 75 / 0 / 76
Functions: 100.0% 10 / 0 / 10
Branches: 68.8% 33 / 28 / 76

src/util/log.c
Line Branch Exec Source
1 #include <errno.h>
2 #include <stdio.h>
3 #include <unistd.h>
4 #include "log.h"
5 #include "array.h"
6 #include "fd.h"
7 #include "xreadwrite.h"
8 #include "xstring.h"
9
10 typedef struct {
11 LogLevel level; // Maximum level at which to log
12 int fd; // File descriptor to write log messages to, or -1 when disabled
13 bool use_color;
14 } Logger;
15
16 // This is initialized during early startup and then never changed,
17 // so it's deemed an acceptable use of non-const globals.
18 // NOLINTNEXTLINE(*-avoid-non-const-global-variables)
19 static Logger logger = {
20 .level = LOG_LEVEL_NONE,
21 .fd = -1,
22 .use_color = false,
23 };
24
25 static const struct LogLevelMap {
26 char name[8];
27 char prefix[5];
28 char color[8];
29 } log_level_map[] = {
30 [LOG_LEVEL_NONE] = {"none", "_", ""},
31 [LOG_LEVEL_CRITICAL] = {"crit", "crit", "\033[1;31m"},
32 [LOG_LEVEL_ERROR] = {"error", " err", "\033[31m"},
33 [LOG_LEVEL_WARNING] = {"warning", "warn", "\033[33m"},
34 [LOG_LEVEL_NOTICE] = {"notice", "note", "\033[34m"},
35 [LOG_LEVEL_INFO] = {"info", "info", ""},
36 [LOG_LEVEL_DEBUG] = {"debug", "dbug", ""},
37 [LOG_LEVEL_TRACE] = {"trace", "trce", ""},
38 };
39
40 24 UNITTEST {
41 24 CHECK_STRUCT_ARRAY(log_level_map, name);
42
2/2
✓ Branch 9 → 4 taken 192 times.
✓ Branch 9 → 10 taken 24 times.
240 for (size_t i = 0; i < ARRAYLEN(log_level_map); i++) {
43 192 const struct LogLevelMap *m = &log_level_map[i];
44 192 BUG_ON(m->prefix[sizeof(m->prefix) - 1] != '\0');
45 192 BUG_ON(m->color[sizeof(m->color) - 1] != '\0');
46 }
47 24 }
48
49 14 LogLevel log_level_from_str(const char *str)
50 {
51
4/4
✓ Branch 2 → 3 taken 13 times.
✓ Branch 2 → 4 taken 1 time.
✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 7 taken 12 times.
14 if (!str || str[0] == '\0') {
52 2 return log_level_default();
53 }
54
2/2
✓ Branch 7 → 5 taken 68 times.
✓ Branch 7 → 8 taken 4 times.
72 for (LogLevel i = 0; i < ARRAYLEN(log_level_map); i++) {
55
2/2
✓ Branch 5 → 6 taken 60 times.
✓ Branch 5 → 8 taken 8 times.
68 if (streq(str, log_level_map[i].name)) {
56 return i;
57 }
58 }
59 return LOG_LEVEL_INVALID;
60 }
61
62 8 const char *log_level_to_str(LogLevel level)
63 {
64 8 BUG_ON(level < LOG_LEVEL_NONE);
65 8 BUG_ON(level > LOG_LEVEL_TRACE);
66 8 return log_level_map[level].name;
67 }
68
69 4 LogLevel log_open(const char *filename, LogLevel level, LogOpenFlags logflags)
70 {
71 4 BUG_ON(!filename);
72 4 BUG_ON(level < LOG_LEVEL_NONE);
73 4 BUG_ON(level > LOG_LEVEL_TRACE);
74 4 BUG_ON(logger.fd >= 0);
75 4 BUG_ON(logger.level != LOG_LEVEL_NONE);
76
77
2/2
✓ Branch 12 → 13 taken 3 times.
✓ Branch 12 → 31 taken 1 time.
4 if (level == LOG_LEVEL_NONE) {
78 return LOG_LEVEL_NONE;
79 }
80
81 3 int oflags = O_WRONLY | O_CREAT | O_APPEND | O_TRUNC | O_CLOEXEC;
82 3 int fd = xopen(filename, oflags, 0666);
83
2/2
✓ Branch 14 → 15 taken 2 times.
✓ Branch 14 → 31 taken 1 time.
3 if (unlikely(fd < 0)) {
84 return LOG_LEVEL_NONE;
85 }
86
87
2/4
✓ Branch 15 → 16 taken 2 times.
✗ Branch 15 → 18 not taken.
✓ Branch 17 → 18 taken 2 times.
✗ Branch 17 → 20 not taken.
2 bool ctty_err = !(logflags & LOGOPEN_ALLOW_CTTY) && is_controlling_tty(fd);
88
2/2
✓ Branch 19 → 20 taken 1 time.
✓ Branch 19 → 24 taken 1 time.
2 if (unlikely(ctty_err || xwrite_all(fd, "\n", 1) != 1)) {
89
1/2
✓ Branch 20 → 21 taken 1 time.
✗ Branch 20 → 22 not taken.
1 errno = ctty_err ? EINVAL : errno;
90 1 xclose(fd);
91 1 return LOG_LEVEL_NONE;
92 }
93
94 1 logger.fd = fd;
95
1/4
✗ Branch 24 → 25 not taken.
✓ Branch 24 → 28 taken 1 time.
✗ Branch 26 → 27 not taken.
✗ Branch 26 → 28 not taken.
1 logger.use_color = (logflags & LOGOPEN_USE_COLOR) && isatty(fd);
96
1/2
✓ Branch 28 → 29 taken 1 time.
✗ Branch 28 → 30 not taken.
1 logger.level = MIN(level, log_level_max());
97 1 return logger.level;
98 }
99
100 9 bool log_close(void)
101 {
102
3/4
✓ Branch 2 → 3 taken 1 time.
✓ Branch 2 → 6 taken 8 times.
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 1 time.
9 return logger.level == LOG_LEVEL_NONE || xclose(logger.fd) == 0;
103 }
104
105 563 bool log_level_enabled(LogLevel level)
106 {
107 563 BUG_ON(level <= LOG_LEVEL_NONE || level > LOG_LEVEL_TRACE);
108 563 BUG_ON(logger.level < LOG_LEVEL_NONE || logger.level > log_level_max());
109 563 return level <= logger.level;
110 }
111
112 // This function should be kept async signal safe
113 2 void log_write(LogLevel level, const char *str, size_t len)
114 {
115 // logger.level is a non-const global, but as noted above, it's never
116 // modified after early startup and so poses no signal safety issues
117 // (see https://austingroupbugs.net/view.php?id=728#c6430)
118
1/2
✓ Branch 3 → 4 taken 2 times.
✗ Branch 3 → 8 not taken.
2 if (!log_level_enabled(level)) {
119 return;
120 }
121
122 // xwrite_all() only calls write(3), which is async signal safe
123 // (see signal-safety(7))
124 2 BUG_ON(logger.fd < 0);
125 2 (void)!xwrite_all(logger.fd, str, len);
126 2 (void)!xwrite_all(logger.fd, "\n", 1);
127 }
128
129 532 void log_msgv(LogLevel level, const char *file, int line, const char *fmt, va_list ap)
130 {
131
2/2
✓ Branch 3 → 4 taken 117 times.
✓ Branch 3 → 5 taken 415 times.
532 if (!log_level_enabled(level)) {
132 117 return;
133 }
134
135 415 BUG_ON(logger.fd < 0);
136 415 char buf[2048];
137 415 const int saved_errno = errno;
138 415 const size_t buf_size = sizeof(buf) - 1;
139 415 const char *prefix = log_level_map[level].prefix;
140
1/2
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 9 taken 415 times.
415 const char *color = logger.use_color ? log_level_map[level].color : "";
141
1/2
✓ Branch 9 → 10 taken 415 times.
✗ Branch 9 → 11 not taken.
415 const char *dim = logger.use_color ? "\033[2m" : "";
142
1/2
✓ Branch 11 → 12 taken 415 times.
✗ Branch 11 → 13 not taken.
415 const char *sgr0 = logger.use_color ? "\033[0m" : "";
143
144
1/2
✓ Branch 13 → 14 taken 415 times.
✗ Branch 13 → 15 not taken.
415 int len1 = snprintf (
145 buf, buf_size,
146 "%s%s%s: %s%s:%d:%s ",
147 color, prefix, color[0] ? sgr0 : "",
148 dim, file, line, sgr0
149 );
150
151
1/2
✗ Branch 15 → 16 not taken.
✓ Branch 15 → 17 taken 415 times.
415 if (unlikely(len1 < 0 || len1 >= buf_size)) {
152 len1 = 0;
153 }
154
155 415 int len2 = vsnprintf(buf + len1, buf_size - len1, fmt, ap);
156 415 size_t n = MIN(len1 + MAX(len2, 0), buf_size);
157 415 buf[n++] = '\n';
158 415 (void)!xwrite_all(logger.fd, buf, n);
159 415 errno = saved_errno;
160 }
161
162 398 void log_msg(LogLevel level, const char *file, int line, const char *fmt, ...)
163 {
164 398 va_list ap;
165 398 va_start(ap, fmt);
166 398 log_msgv(level, file, line, fmt, ap);
167 398 va_end(ap);
168 398 }
169
170 1 SystemErrno log_errno(const char *file, int line, const char *prefix)
171 {
172 1 SystemErrno err = errno;
173
1/2
✓ Branch 3 → 4 taken 1 time.
✗ Branch 3 → 6 not taken.
1 if (log_level_enabled(LOG_LEVEL_ERROR)) {
174 1 log_msg(LOG_LEVEL_ERROR, file, line, "%s: %s", prefix, strerror(err));
175 }
176 1 return err;
177 }
178