blob: 7f4e9352e8bd59c08b03a944706d89b9887fe984 [file] [log] [blame]
Andrew Scull18c78fc2018-08-20 12:57:41 +01001#include "hf/dlog.h"
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +01002
3#include <stdbool.h>
4#include <stddef.h>
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +01005
Andrew Scull18c78fc2018-08-20 12:57:41 +01006#include "hf/arch.h"
7#include "hf/spinlock.h"
8#include "hf/std.h"
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +01009
Andrew Scull4f170f52018-07-19 12:58:20 +010010/* Keep macro alignment */
11/* clang-format off */
12
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010013#define FLAG_SPACE 0x01
14#define FLAG_ZERO 0x02
15#define FLAG_MINUS 0x04
16#define FLAG_PLUS 0x08
17#define FLAG_ALT 0x10
18#define FLAG_UPPER 0x20
19#define FLAG_NEG 0x40
20
Andrew Scull4f170f52018-07-19 12:58:20 +010021/* clang-format on */
22
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010023/**
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010024 * Prints a raw string to the debug log and returns its length.
25 */
26static size_t print_raw_string(const char *str)
27{
28 const char *c = str;
Andrew Scull7364a8e2018-07-19 15:39:29 +010029 while (*c != '\0') {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010030 arch_putchar(*c++);
Andrew Scull7364a8e2018-07-19 15:39:29 +010031 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010032 return c - str;
33}
34
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010035/**
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010036 * Prints a formatted string to the debug log. The format includes a minimum
37 * width, the fill character, and flags (whether to align to left or right).
38 *
39 * str is the full string, while suffix is a pointer within str that indicates
40 * where the suffix begins. This is used when printing right-aligned numbers
41 * with a zero fill; for example, -10 with width 4 should be padded to -010,
42 * so suffix would point to index one of the "-10" string .
43 */
44static void print_string(const char *str, const char *suffix, size_t width,
45 int flags, char fill)
46{
47 size_t len = suffix - str;
48
49 /* Print the string up to the beginning of the suffix. */
Andrew Scull7364a8e2018-07-19 15:39:29 +010050 while (str != suffix) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010051 arch_putchar(*str++);
Andrew Scull7364a8e2018-07-19 15:39:29 +010052 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010053
54 if (flags & FLAG_MINUS) {
55 /* Left-aligned. Print suffix, then print padding if needed. */
56 len += print_raw_string(suffix);
57 while (len < width) {
58 arch_putchar(' ');
59 len++;
60 }
61 return;
62 }
63
64 /* Fill until we reach the desired length. */
65 len += strlen(suffix);
66 while (len < width) {
67 arch_putchar(fill);
68 len++;
69 }
70
71 /* Now print the rest of the string. */
72 print_raw_string(suffix);
73}
74
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010075/**
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010076 * Prints a number to the debug log. The caller specifies the base, its minimum
77 * width and printf-style flags.
78 */
79static void print_num(size_t v, size_t base, size_t width, int flags)
80{
81 static const char *digits_lower = "0123456789abcdefx";
82 static const char *digits_upper = "0123456789ABCDEFX";
83 const char *d = (flags & FLAG_UPPER) ? digits_upper : digits_lower;
84 char buf[51];
85 char *ptr = buf + sizeof(buf) - 1;
86 char *num;
87 *ptr = '\0';
88 do {
89 --ptr;
90 *ptr = d[v % base];
91 v /= base;
92 } while (v);
93
94 /* Num stores where the actual number begins. */
95 num = ptr;
96
97 /* Add prefix if requested. */
98 if (flags & FLAG_ALT) {
99 switch (base) {
100 case 16:
101 ptr -= 2;
102 ptr[0] = '0';
103 ptr[1] = d[16];
104 break;
105
106 case 8:
107 ptr--;
108 *ptr = '0';
109 break;
110 }
111 }
112
113 /* Add sign if requested. */
Andrew Scull7364a8e2018-07-19 15:39:29 +0100114 if (flags & FLAG_NEG) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100115 *--ptr = '-';
Andrew Scull7364a8e2018-07-19 15:39:29 +0100116 } else if (flags & FLAG_PLUS) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100117 *--ptr = '+';
Andrew Scull7364a8e2018-07-19 15:39:29 +0100118 } else if (flags & FLAG_SPACE) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100119 *--ptr = ' ';
Andrew Scull7364a8e2018-07-19 15:39:29 +0100120 }
121 if (flags & FLAG_ZERO) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100122 print_string(ptr, num, width, flags, '0');
Andrew Scull7364a8e2018-07-19 15:39:29 +0100123 } else {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100124 print_string(ptr, ptr, width, flags, ' ');
Andrew Scull7364a8e2018-07-19 15:39:29 +0100125 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100126}
127
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100128/**
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100129 * Parses the optional flags field of a printf-style format. It returns the spot
130 * on the string where a non-flag character was found.
131 */
132static const char *parse_flags(const char *p, int *flags)
133{
134 for (;;) {
135 switch (*p) {
136 case ' ':
137 *flags |= FLAG_SPACE;
138 break;
139
140 case '0':
141 *flags |= FLAG_ZERO;
142 break;
143
144 case '-':
145 *flags |= FLAG_MINUS;
146 break;
147
148 case '+':
149 *flags |= FLAG_PLUS;
150
151 case '#':
152 *flags |= FLAG_ALT;
153 break;
154
155 default:
156 return p;
157 }
158 p++;
159 }
160}
161
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100162/**
163 * Same as "dlog", except that arguments are passed as a va_list.
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100164 */
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100165void vdlog(const char *fmt, va_list args)
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100166{
167 static struct spinlock sl = SPINLOCK_INIT;
168 const char *p;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100169 size_t w;
170 int flags;
171 char buf[2];
172
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100173 sl_lock(&sl);
174
Andrew Scull020ae692018-07-19 16:20:14 +0100175 for (p = fmt; *p; p++) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100176 switch (*p) {
177 default:
178 arch_putchar(*p);
179 break;
180
181 case '%':
182 /* Read optional flags. */
183 flags = 0;
184 p = parse_flags(p + 1, &flags) - 1;
185
186 /* Read the minimum width, if one is specified. */
187 w = 0;
188 while (p[1] >= '0' && p[1] <= '9') {
189 w = (w * 10) + (p[1] - '0');
190 p++;
191 }
192
193 /* Read minimum width from arguments. */
194 if (w == 0 && p[1] == '*') {
195 int v = va_arg(args, int);
196 if (v >= 0) {
197 w = v;
198 } else {
199 w = -v;
200 flags |= FLAG_MINUS;
201 }
202 p++;
203 }
204
205 /* Handle the format specifier. */
206 switch (p[1]) {
Andrew Scull4f170f52018-07-19 12:58:20 +0100207 case 's': {
208 char *str = va_arg(args, char *);
209 print_string(str, str, w, flags, ' ');
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100210 p++;
Andrew Scull4f170f52018-07-19 12:58:20 +0100211 } break;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100212
213 case 'd':
Andrew Scull4f170f52018-07-19 12:58:20 +0100214 case 'i': {
215 int v = va_arg(args, int);
216 if (v < 0) {
217 flags |= FLAG_NEG;
218 v = -v;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100219 }
Andrew Scull4f170f52018-07-19 12:58:20 +0100220
221 print_num((size_t)v, 10, w, flags);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100222 p++;
Andrew Scull4f170f52018-07-19 12:58:20 +0100223 } break;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100224
225 case 'X':
226 flags |= FLAG_UPPER;
227 print_num(va_arg(args, size_t), 16, w, flags);
228 break;
229
230 case 'p':
231 print_num(va_arg(args, size_t), 16,
232 sizeof(size_t) * 2, FLAG_ZERO);
233 p++;
234 break;
235
236 case 'x':
237 print_num(va_arg(args, size_t), 16, w, flags);
238 p++;
239 break;
240
241 case 'u':
242 print_num(va_arg(args, size_t), 10, w, flags);
243 p++;
244 break;
245
246 case 'o':
247 print_num(va_arg(args, size_t), 8, w, flags);
248 p++;
249 break;
250
251 case 'c':
252 buf[1] = 0;
253 buf[0] = va_arg(args, int);
254 print_string(buf, buf, w, flags, ' ');
255 p++;
256 break;
257
258 case '%':
259 break;
260
261 default:
262 arch_putchar('%');
263 }
264
265 break;
266 }
267 }
268
269 sl_unlock(&sl);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100270}
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100271
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100272/**
273 * Prints the given format string to the debug log.
274 */
275void dlog(const char *fmt, ...)
276{
277 va_list args;
278
279 va_start(args, fmt);
280 vdlog(fmt, args);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100281 va_end(args);
282}