blob: e9643c519d3c81a1faed6826a7abfac3813f7cc7 [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/**
179 * Same as "dlog", except that arguments are passed as a va_list.
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100180 */
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100181void vdlog(const char *fmt, va_list args)
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100182{
183 static struct spinlock sl = SPINLOCK_INIT;
184 const char *p;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100185 size_t w;
186 int flags;
187 char buf[2];
188
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100189 sl_lock(&sl);
190
Andrew Scull020ae692018-07-19 16:20:14 +0100191 for (p = fmt; *p; p++) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100192 switch (*p) {
193 default:
194 arch_putchar(*p);
195 break;
196
197 case '%':
198 /* Read optional flags. */
199 flags = 0;
200 p = parse_flags(p + 1, &flags) - 1;
201
202 /* Read the minimum width, if one is specified. */
203 w = 0;
204 while (p[1] >= '0' && p[1] <= '9') {
205 w = (w * 10) + (p[1] - '0');
206 p++;
207 }
208
209 /* Read minimum width from arguments. */
210 if (w == 0 && p[1] == '*') {
211 int v = va_arg(args, int);
212 if (v >= 0) {
213 w = v;
214 } else {
215 w = -v;
216 flags |= FLAG_MINUS;
217 }
218 p++;
219 }
220
221 /* Handle the format specifier. */
222 switch (p[1]) {
Andrew Scull4f170f52018-07-19 12:58:20 +0100223 case 's': {
224 char *str = va_arg(args, char *);
225 print_string(str, str, 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 'd':
Andrew Scull4f170f52018-07-19 12:58:20 +0100230 case 'i': {
231 int v = va_arg(args, int);
232 if (v < 0) {
233 flags |= FLAG_NEG;
234 v = -v;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100235 }
Andrew Scull4f170f52018-07-19 12:58:20 +0100236
237 print_num((size_t)v, 10, w, flags);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100238 p++;
Andrew Scull4f170f52018-07-19 12:58:20 +0100239 } break;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100240
241 case 'X':
242 flags |= FLAG_UPPER;
243 print_num(va_arg(args, size_t), 16, w, flags);
244 break;
245
246 case 'p':
247 print_num(va_arg(args, size_t), 16,
248 sizeof(size_t) * 2, FLAG_ZERO);
249 p++;
250 break;
251
252 case 'x':
253 print_num(va_arg(args, size_t), 16, w, flags);
254 p++;
255 break;
256
257 case 'u':
258 print_num(va_arg(args, size_t), 10, w, flags);
259 p++;
260 break;
261
262 case 'o':
263 print_num(va_arg(args, size_t), 8, w, flags);
264 p++;
265 break;
266
267 case 'c':
268 buf[1] = 0;
269 buf[0] = va_arg(args, int);
270 print_string(buf, buf, w, flags, ' ');
271 p++;
272 break;
273
274 case '%':
275 break;
276
277 default:
278 arch_putchar('%');
279 }
280
281 break;
282 }
283 }
284
285 sl_unlock(&sl);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100286}
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100287
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100288/**
289 * Prints the given format string to the debug log.
290 */
291void dlog(const char *fmt, ...)
292{
293 va_list args;
294
295 va_start(args, fmt);
296 vdlog(fmt, args);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100297 va_end(args);
298}