blob: 0af7eb59c4c9c39bda2a52b1bdd0509ac9df1e8a [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;
30 while (*c != '\0')
31 arch_putchar(*c++);
32 return c - str;
33}
34
35/*
36 * 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. */
50 while (str != suffix)
51 arch_putchar(*str++);
52
53 if (flags & FLAG_MINUS) {
54 /* Left-aligned. Print suffix, then print padding if needed. */
55 len += print_raw_string(suffix);
56 while (len < width) {
57 arch_putchar(' ');
58 len++;
59 }
60 return;
61 }
62
63 /* Fill until we reach the desired length. */
64 len += strlen(suffix);
65 while (len < width) {
66 arch_putchar(fill);
67 len++;
68 }
69
70 /* Now print the rest of the string. */
71 print_raw_string(suffix);
72}
73
74/*
75 * Prints a number to the debug log. The caller specifies the base, its minimum
76 * width and printf-style flags.
77 */
78static void print_num(size_t v, size_t base, size_t width, int flags)
79{
80 static const char *digits_lower = "0123456789abcdefx";
81 static const char *digits_upper = "0123456789ABCDEFX";
82 const char *d = (flags & FLAG_UPPER) ? digits_upper : digits_lower;
83 char buf[51];
84 char *ptr = buf + sizeof(buf) - 1;
85 char *num;
86 *ptr = '\0';
87 do {
88 --ptr;
89 *ptr = d[v % base];
90 v /= base;
91 } while (v);
92
93 /* Num stores where the actual number begins. */
94 num = ptr;
95
96 /* Add prefix if requested. */
97 if (flags & FLAG_ALT) {
98 switch (base) {
99 case 16:
100 ptr -= 2;
101 ptr[0] = '0';
102 ptr[1] = d[16];
103 break;
104
105 case 8:
106 ptr--;
107 *ptr = '0';
108 break;
109 }
110 }
111
112 /* Add sign if requested. */
113 if (flags & FLAG_NEG)
114 *--ptr = '-';
115 else if (flags & FLAG_PLUS)
116 *--ptr = '+';
117 else if (flags & FLAG_SPACE)
118 *--ptr = ' ';
119
120 if (flags & FLAG_ZERO)
121 print_string(ptr, num, width, flags, '0');
122 else
123 print_string(ptr, ptr, width, flags, ' ');
124}
125
126/*
127 * Parses the optional flags field of a printf-style format. It returns the spot
128 * on the string where a non-flag character was found.
129 */
130static const char *parse_flags(const char *p, int *flags)
131{
132 for (;;) {
133 switch (*p) {
134 case ' ':
135 *flags |= FLAG_SPACE;
136 break;
137
138 case '0':
139 *flags |= FLAG_ZERO;
140 break;
141
142 case '-':
143 *flags |= FLAG_MINUS;
144 break;
145
146 case '+':
147 *flags |= FLAG_PLUS;
148
149 case '#':
150 *flags |= FLAG_ALT;
151 break;
152
153 default:
154 return p;
155 }
156 p++;
157 }
158}
159
160/*
161 * Prints the given format string to the debug log.
162 */
163void dlog(const char *str, ...)
164{
165 static struct spinlock sl = SPINLOCK_INIT;
166 const char *p;
167 va_list args;
168 size_t w;
169 int flags;
170 char buf[2];
171
172 va_start(args, str);
173
174 sl_lock(&sl);
175
176 for (p = str; *p; p++) {
177 switch (*p) {
178 default:
179 arch_putchar(*p);
180 break;
181
182 case '%':
183 /* Read optional flags. */
184 flags = 0;
185 p = parse_flags(p + 1, &flags) - 1;
186
187 /* Read the minimum width, if one is specified. */
188 w = 0;
189 while (p[1] >= '0' && p[1] <= '9') {
190 w = (w * 10) + (p[1] - '0');
191 p++;
192 }
193
194 /* Read minimum width from arguments. */
195 if (w == 0 && p[1] == '*') {
196 int v = va_arg(args, int);
197 if (v >= 0) {
198 w = v;
199 } else {
200 w = -v;
201 flags |= FLAG_MINUS;
202 }
203 p++;
204 }
205
206 /* Handle the format specifier. */
207 switch (p[1]) {
Andrew Scull4f170f52018-07-19 12:58:20 +0100208 case 's': {
209 char *str = va_arg(args, char *);
210 print_string(str, str, w, flags, ' ');
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100211 p++;
Andrew Scull4f170f52018-07-19 12:58:20 +0100212 } break;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100213
214 case 'd':
Andrew Scull4f170f52018-07-19 12:58:20 +0100215 case 'i': {
216 int v = va_arg(args, int);
217 if (v < 0) {
218 flags |= FLAG_NEG;
219 v = -v;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100220 }
Andrew Scull4f170f52018-07-19 12:58:20 +0100221
222 print_num((size_t)v, 10, w, flags);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100223 p++;
Andrew Scull4f170f52018-07-19 12:58:20 +0100224 } break;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100225
226 case 'X':
227 flags |= FLAG_UPPER;
228 print_num(va_arg(args, size_t), 16, w, flags);
229 break;
230
231 case 'p':
232 print_num(va_arg(args, size_t), 16,
233 sizeof(size_t) * 2, FLAG_ZERO);
234 p++;
235 break;
236
237 case 'x':
238 print_num(va_arg(args, size_t), 16, w, flags);
239 p++;
240 break;
241
242 case 'u':
243 print_num(va_arg(args, size_t), 10, w, flags);
244 p++;
245 break;
246
247 case 'o':
248 print_num(va_arg(args, size_t), 8, w, flags);
249 p++;
250 break;
251
252 case 'c':
253 buf[1] = 0;
254 buf[0] = va_arg(args, int);
255 print_string(buf, buf, w, flags, ' ');
256 p++;
257 break;
258
259 case '%':
260 break;
261
262 default:
263 arch_putchar('%');
264 }
265
266 break;
267 }
268 }
269
270 sl_unlock(&sl);
271
272 va_end(args);
273}