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