blob: 9737805d85c411a66644646103fd4401b7571619 [file] [log] [blame]
Andrew Scull18834872018-10-12 11:48:09 +01001/*
2 * Copyright 2018 Google LLC
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * https://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Andrew Scull18c78fc2018-08-20 12:57:41 +010017#include "hf/dlog.h"
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010018
19#include <stdbool.h>
20#include <stddef.h>
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010021
Andrew Scull18c78fc2018-08-20 12:57:41 +010022#include "hf/arch.h"
23#include "hf/spinlock.h"
24#include "hf/std.h"
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010025
Andrew Scull4f170f52018-07-19 12:58:20 +010026/* Keep macro alignment */
27/* clang-format off */
28
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010029#define FLAG_SPACE 0x01
30#define FLAG_ZERO 0x02
31#define FLAG_MINUS 0x04
32#define FLAG_PLUS 0x08
33#define FLAG_ALT 0x10
34#define FLAG_UPPER 0x20
35#define FLAG_NEG 0x40
36
Andrew Scull4f170f52018-07-19 12:58:20 +010037/* clang-format on */
38
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010039/**
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010040 * Prints a raw string to the debug log and returns its length.
41 */
42static size_t print_raw_string(const char *str)
43{
44 const char *c = str;
Andrew Scull7364a8e2018-07-19 15:39:29 +010045 while (*c != '\0') {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010046 arch_putchar(*c++);
Andrew Scull7364a8e2018-07-19 15:39:29 +010047 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010048 return c - str;
49}
50
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010051/**
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010052 * Prints a formatted string to the debug log. The format includes a minimum
53 * width, the fill character, and flags (whether to align to left or right).
54 *
55 * str is the full string, while suffix is a pointer within str that indicates
56 * where the suffix begins. This is used when printing right-aligned numbers
57 * with a zero fill; for example, -10 with width 4 should be padded to -010,
58 * so suffix would point to index one of the "-10" string .
59 */
60static void print_string(const char *str, const char *suffix, size_t width,
61 int flags, char fill)
62{
63 size_t len = suffix - str;
64
65 /* Print the string up to the beginning of the suffix. */
Andrew Scull7364a8e2018-07-19 15:39:29 +010066 while (str != suffix) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010067 arch_putchar(*str++);
Andrew Scull7364a8e2018-07-19 15:39:29 +010068 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010069
70 if (flags & FLAG_MINUS) {
71 /* Left-aligned. Print suffix, then print padding if needed. */
72 len += print_raw_string(suffix);
73 while (len < width) {
74 arch_putchar(' ');
75 len++;
76 }
77 return;
78 }
79
80 /* Fill until we reach the desired length. */
81 len += strlen(suffix);
82 while (len < width) {
83 arch_putchar(fill);
84 len++;
85 }
86
87 /* Now print the rest of the string. */
88 print_raw_string(suffix);
89}
90
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010091/**
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010092 * Prints a number to the debug log. The caller specifies the base, its minimum
93 * width and printf-style flags.
94 */
95static void print_num(size_t v, size_t base, size_t width, int flags)
96{
97 static const char *digits_lower = "0123456789abcdefx";
98 static const char *digits_upper = "0123456789ABCDEFX";
99 const char *d = (flags & FLAG_UPPER) ? digits_upper : digits_lower;
100 char buf[51];
Andrew Scullf3d45592018-09-20 14:30:22 +0100101 char *ptr = &buf[sizeof(buf) - 1];
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100102 char *num;
103 *ptr = '\0';
104 do {
105 --ptr;
106 *ptr = d[v % base];
107 v /= base;
108 } while (v);
109
110 /* Num stores where the actual number begins. */
111 num = ptr;
112
113 /* Add prefix if requested. */
114 if (flags & FLAG_ALT) {
115 switch (base) {
116 case 16:
117 ptr -= 2;
118 ptr[0] = '0';
119 ptr[1] = d[16];
120 break;
121
122 case 8:
123 ptr--;
124 *ptr = '0';
125 break;
126 }
127 }
128
129 /* Add sign if requested. */
Andrew Scull7364a8e2018-07-19 15:39:29 +0100130 if (flags & FLAG_NEG) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100131 *--ptr = '-';
Andrew Scull7364a8e2018-07-19 15:39:29 +0100132 } else if (flags & FLAG_PLUS) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100133 *--ptr = '+';
Andrew Scull7364a8e2018-07-19 15:39:29 +0100134 } else if (flags & FLAG_SPACE) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100135 *--ptr = ' ';
Andrew Scull7364a8e2018-07-19 15:39:29 +0100136 }
137 if (flags & FLAG_ZERO) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100138 print_string(ptr, num, width, flags, '0');
Andrew Scull7364a8e2018-07-19 15:39:29 +0100139 } else {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100140 print_string(ptr, ptr, width, flags, ' ');
Andrew Scull7364a8e2018-07-19 15:39:29 +0100141 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100142}
143
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100144/**
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100145 * Parses the optional flags field of a printf-style format. It returns the spot
146 * on the string where a non-flag character was found.
147 */
148static const char *parse_flags(const char *p, int *flags)
149{
150 for (;;) {
151 switch (*p) {
152 case ' ':
153 *flags |= FLAG_SPACE;
154 break;
155
156 case '0':
157 *flags |= FLAG_ZERO;
158 break;
159
160 case '-':
161 *flags |= FLAG_MINUS;
162 break;
163
164 case '+':
165 *flags |= FLAG_PLUS;
166
167 case '#':
168 *flags |= FLAG_ALT;
169 break;
170
171 default:
172 return p;
173 }
174 p++;
175 }
176}
177
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100178/**
Andrew Scullcb0a7412018-11-06 17:28:14 +0000179 * Same as "dlog_nosync", except that arguments are passed as a va_list. This
180 * must only be used before the MMU has been initialized so that the atomic
181 * operations needed for locking are operational.
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100182 */
Andrew Scullcb0a7412018-11-06 17:28:14 +0000183void vdlog_nosync(const char *fmt, va_list args)
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100184{
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100185 const char *p;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100186 size_t w;
187 int flags;
188 char buf[2];
189
Andrew Scull020ae692018-07-19 16:20:14 +0100190 for (p = fmt; *p; p++) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100191 switch (*p) {
192 default:
193 arch_putchar(*p);
194 break;
195
196 case '%':
197 /* Read optional flags. */
198 flags = 0;
199 p = parse_flags(p + 1, &flags) - 1;
200
201 /* Read the minimum width, if one is specified. */
202 w = 0;
203 while (p[1] >= '0' && p[1] <= '9') {
204 w = (w * 10) + (p[1] - '0');
205 p++;
206 }
207
208 /* Read minimum width from arguments. */
209 if (w == 0 && p[1] == '*') {
210 int v = va_arg(args, int);
211 if (v >= 0) {
212 w = v;
213 } else {
214 w = -v;
215 flags |= FLAG_MINUS;
216 }
217 p++;
218 }
219
220 /* Handle the format specifier. */
221 switch (p[1]) {
Andrew Scull4f170f52018-07-19 12:58:20 +0100222 case 's': {
223 char *str = va_arg(args, char *);
224 print_string(str, str, w, flags, ' ');
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100225 p++;
Andrew Scull4f170f52018-07-19 12:58:20 +0100226 } break;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100227
228 case 'd':
Andrew Scull4f170f52018-07-19 12:58:20 +0100229 case 'i': {
230 int v = va_arg(args, int);
231 if (v < 0) {
232 flags |= FLAG_NEG;
233 v = -v;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100234 }
Andrew Scull4f170f52018-07-19 12:58:20 +0100235
236 print_num((size_t)v, 10, w, flags);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100237 p++;
Andrew Scull4f170f52018-07-19 12:58:20 +0100238 } break;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100239
240 case 'X':
241 flags |= FLAG_UPPER;
242 print_num(va_arg(args, size_t), 16, w, flags);
243 break;
244
245 case 'p':
246 print_num(va_arg(args, size_t), 16,
247 sizeof(size_t) * 2, FLAG_ZERO);
248 p++;
249 break;
250
251 case 'x':
252 print_num(va_arg(args, size_t), 16, w, flags);
253 p++;
254 break;
255
256 case 'u':
257 print_num(va_arg(args, size_t), 10, w, flags);
258 p++;
259 break;
260
261 case 'o':
262 print_num(va_arg(args, size_t), 8, w, flags);
263 p++;
264 break;
265
266 case 'c':
267 buf[1] = 0;
268 buf[0] = va_arg(args, int);
269 print_string(buf, buf, w, flags, ' ');
270 p++;
271 break;
272
273 case '%':
274 break;
275
276 default:
277 arch_putchar('%');
278 }
279
280 break;
281 }
282 }
Andrew Scullcb0a7412018-11-06 17:28:14 +0000283}
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100284
Andrew Scullcb0a7412018-11-06 17:28:14 +0000285/**
286 * Prints the given format string to the debug log without locking. This must
287 * only be used before the MMU has been initialized so that the atomic
288 * operations needed for locking are operational.
289 */
290void dlog_nosync(const char *fmt, ...)
291{
292 va_list args;
293
294 va_start(args, fmt);
295 vdlog_nosync(fmt, args);
296 va_end(args);
297}
298
299/**
300 * Same as "dlog", except that arguments are passed as a va_list.
301 */
302void vdlog(const char *fmt, va_list args)
303{
304 static struct spinlock sl = SPINLOCK_INIT;
305
306 sl_lock(&sl);
307 vdlog_nosync(fmt, args);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100308 sl_unlock(&sl);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100309}
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100310
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100311/**
312 * Prints the given format string to the debug log.
313 */
314void dlog(const char *fmt, ...)
315{
316 va_list args;
317
318 va_start(args, fmt);
319 vdlog(fmt, args);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100320 va_end(args);
321}