blob: e8d6a45cdcef15e674d4d8d366236aa812c82ee9 [file] [log] [blame]
David 'Digit' Turner5c734642010-01-20 12:36:51 -08001/*
2 * Copyright (C) 2010 The Android Open Source Project
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in
12 * the documentation and/or other materials provided with the
13 * distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
18 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
19 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
22 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
25 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
Nick Kralevich83697b82012-11-15 15:21:43 -080029// Temporarily disable _FORTIFY_SOURCE to get this code to
30// compile under GCC 4.7
31#undef _FORTIFY_SOURCE
32
Elliott Hughes1e980b62013-01-17 18:36:06 -080033#include "debug_format.h"
Elliott Hugheseababde2012-12-20 18:59:05 -080034
35#include <assert.h>
36#include <errno.h>
37#include <stdarg.h>
38#include <stddef.h>
39#include <stdint.h>
40#include <string.h>
41#include <unistd.h>
42
David 'Digit' Turner5c734642010-01-20 12:36:51 -080043/* define UNIT_TESTS to build this file as a single executable that runs
44 * the formatter's unit tests
45 */
46#define xxUNIT_TESTS
47
48/*** Generic output sink
49 ***/
50
Elliott Hughes18a206c2012-10-29 17:37:13 -070051struct Out {
52 void *opaque;
53 void (*send)(void *opaque, const char *data, int len);
54};
David 'Digit' Turner5c734642010-01-20 12:36:51 -080055
Elliott Hughes18a206c2012-10-29 17:37:13 -070056static void out_send(Out *o, const char *data, size_t len) {
David 'Digit' Turner5c734642010-01-20 12:36:51 -080057 o->send(o->opaque, data, (int)len);
58}
59
60static void
61out_send_repeat(Out *o, char ch, int count)
62{
63 char pad[8];
64 const int padSize = (int)sizeof(pad);
65
66 memset(pad, ch, sizeof(pad));
67 while (count > 0) {
68 int avail = count;
69 if (avail > padSize) {
70 avail = padSize;
71 }
72 o->send(o->opaque, pad, avail);
73 count -= avail;
74 }
75}
76
77/* forward declaration */
Elliott Hughes18a206c2012-10-29 17:37:13 -070078static void out_vformat(Out* o, const char* format, va_list args);
David 'Digit' Turner5c734642010-01-20 12:36:51 -080079
80/*** Bounded buffer output
81 ***/
82
Elliott Hughes18a206c2012-10-29 17:37:13 -070083struct BufOut {
84 Out out[1];
85 char *buffer;
86 char *pos;
87 char *end;
88 int total;
89};
David 'Digit' Turner5c734642010-01-20 12:36:51 -080090
Elliott Hughes18a206c2012-10-29 17:37:13 -070091static void buf_out_send(void *opaque, const char *data, int len) {
92 BufOut *bo = reinterpret_cast<BufOut*>(opaque);
David 'Digit' Turner5c734642010-01-20 12:36:51 -080093
Elliott Hughes18a206c2012-10-29 17:37:13 -070094 if (len < 0) {
David 'Digit' Turner5c734642010-01-20 12:36:51 -080095 len = strlen(data);
Elliott Hughes18a206c2012-10-29 17:37:13 -070096 }
David 'Digit' Turner5c734642010-01-20 12:36:51 -080097
98 bo->total += len;
99
100 while (len > 0) {
101 int avail = bo->end - bo->pos;
102 if (avail == 0)
103 break;
104 if (avail > len)
105 avail = len;
106 memcpy(bo->pos, data, avail);
107 bo->pos += avail;
108 bo->pos[0] = '\0';
109 len -= avail;
110 }
111}
112
113static Out*
114buf_out_init(BufOut *bo, char *buffer, size_t size)
115{
116 if (size == 0)
117 return NULL;
118
119 bo->out->opaque = bo;
120 bo->out->send = buf_out_send;
121 bo->buffer = buffer;
122 bo->end = buffer + size - 1;
123 bo->pos = bo->buffer;
124 bo->pos[0] = '\0';
125 bo->total = 0;
126
127 return bo->out;
128}
129
130static int
131buf_out_length(BufOut *bo)
132{
133 return bo->total;
134}
135
136static int
137vformat_buffer(char *buff, size_t buffsize, const char *format, va_list args)
138{
139 BufOut bo;
140 Out *out;
141
142 out = buf_out_init(&bo, buff, buffsize);
143 if (out == NULL)
144 return 0;
145
146 out_vformat(out, format, args);
147
148 return buf_out_length(&bo);
149}
150
Elliott Hughes1e980b62013-01-17 18:36:06 -0800151int __libc_format_buffer(char* buffer, size_t buffer_size, const char* format, ...) {
152 va_list args;
153 va_start(args, format);
154 int result = vformat_buffer(buffer, buffer_size, format, args);
155 va_end(args);
156 return result;
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800157}
158
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800159
160/*** File descriptor output
161 ***/
162
Elliott Hughes18a206c2012-10-29 17:37:13 -0700163struct FdOut {
164 Out out[1];
165 int fd;
166 int total;
167};
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800168
169static void
170fd_out_send(void *opaque, const char *data, int len)
171{
Elliott Hughes1e980b62013-01-17 18:36:06 -0800172 FdOut *fdo = reinterpret_cast<FdOut*>(opaque);
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800173
174 if (len < 0)
175 len = strlen(data);
176
177 while (len > 0) {
178 int ret = write(fdo->fd, data, len);
179 if (ret < 0) {
180 if (errno == EINTR)
181 continue;
182 break;
183 }
184 data += ret;
185 len -= ret;
186 fdo->total += ret;
187 }
188}
189
190static Out*
191fd_out_init(FdOut *fdo, int fd)
192{
193 fdo->out->opaque = fdo;
194 fdo->out->send = fd_out_send;
195 fdo->fd = fd;
196 fdo->total = 0;
197
198 return fdo->out;
199}
200
201static int
202fd_out_length(FdOut *fdo)
203{
204 return fdo->total;
205}
206
207
Elliott Hughes1e980b62013-01-17 18:36:06 -0800208int __libc_format_fd(int fd, const char* format, ...) {
209 FdOut fdo;
210 Out* out = fd_out_init(&fdo, fd);
211 if (out == NULL) {
212 return 0;
213 }
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800214
Elliott Hughes1e980b62013-01-17 18:36:06 -0800215 va_list args;
216 va_start(args, format);
217 out_vformat(out, format, args);
218 va_end(args);
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800219
Elliott Hughes1e980b62013-01-17 18:36:06 -0800220 return fd_out_length(&fdo);
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800221}
222
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800223/*** Log output
224 ***/
225
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800226#include <unistd.h>
227#include <fcntl.h>
228#include <sys/uio.h>
229
Elliott Hughes1e980b62013-01-17 18:36:06 -0800230int __libc_format_log_va_list(int priority, const char* tag, const char* fmt, va_list args) {
231 char buf[1024];
232 int result = vformat_buffer(buf, sizeof buf, fmt, args);
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800233
Elliott Hughes1e980b62013-01-17 18:36:06 -0800234 static int log_fd = -1;
235 if (log_fd == -1) {
236 log_fd = open("/dev/log/main", O_WRONLY);
237 if (log_fd == -1) {
238 return result;
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800239 }
Elliott Hughes1e980b62013-01-17 18:36:06 -0800240 }
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800241
Elliott Hughes1e980b62013-01-17 18:36:06 -0800242 struct iovec vec[3];
243 vec[0].iov_base = (unsigned char *) &priority;
244 vec[0].iov_len = 1;
245 vec[1].iov_base = (void *) tag;
246 vec[1].iov_len = strlen(tag) + 1;
247 vec[2].iov_base = (void *) buf;
248 vec[2].iov_len = strlen(buf) + 1;
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800249
Elliott Hughes1e980b62013-01-17 18:36:06 -0800250 TEMP_FAILURE_RETRY(writev(log_fd, vec, 3));
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800251
Elliott Hughes1e980b62013-01-17 18:36:06 -0800252 return result;
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800253}
254
Elliott Hughes1e980b62013-01-17 18:36:06 -0800255int __libc_format_log(int priority, const char* tag, const char* format, ...) {
256 va_list args;
257 va_start(args, format);
258 int result = __libc_format_log_va_list(priority, tag, format, args);
259 va_end(args);
260 return result;
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800261}
262
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800263/*** formatted output implementation
264 ***/
265
266/* Parse a decimal string from 'format + *ppos',
267 * return the value, and writes the new position past
268 * the decimal string in '*ppos' on exit.
269 *
270 * NOTE: Does *not* handle a sign prefix.
271 */
272static unsigned
273parse_decimal(const char *format, int *ppos)
274{
275 const char* p = format + *ppos;
276 unsigned result = 0;
277
278 for (;;) {
279 int ch = *p;
280 unsigned d = (unsigned)(ch - '0');
281
282 if (d >= 10U)
283 break;
284
285 result = result*10 + d;
286 p++;
287 }
288 *ppos = p - format;
289 return result;
290}
291
Elliott Hugheseababde2012-12-20 18:59:05 -0800292// Writes number 'value' in base 'base' into buffer 'buf' of size 'buf_size' bytes.
293// Assumes that buf_size > 0.
294static void format_number(char* buf, size_t buf_size, uint64_t value, int base, bool caps) {
295 char* p = buf;
296 char* end = buf + buf_size - 1;
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800297
Elliott Hugheseababde2012-12-20 18:59:05 -0800298 // Generate digit string in reverse order.
299 while (value) {
300 unsigned d = value % base;
301 value /= base;
302 if (p != end) {
303 char ch;
304 if (d < 10) {
305 ch = '0' + d;
306 } else {
307 ch = (caps ? 'A' : 'a') + (d - 10);
308 }
309 *p++ = ch;
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800310 }
Elliott Hugheseababde2012-12-20 18:59:05 -0800311 }
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800312
Elliott Hugheseababde2012-12-20 18:59:05 -0800313 // Special case for 0.
314 if (p == buf) {
315 if (p != end) {
316 *p++ = '0';
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800317 }
Elliott Hugheseababde2012-12-20 18:59:05 -0800318 }
319 *p = '\0';
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800320
Elliott Hugheseababde2012-12-20 18:59:05 -0800321 // Reverse digit string in-place.
322 size_t length = p - buf;
323 for (size_t i = 0, j = length - 1; i < j; ++i, --j) {
324 char ch = buf[i];
325 buf[i] = buf[j];
326 buf[j] = ch;
327 }
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800328}
329
330/* Write an integer (octal or decimal) into a buffer, assumes buffsize > 2 */
331static void
332format_integer(char *buffer, size_t buffsize, uint64_t value, int base, int isSigned)
333{
Elliott Hugheseababde2012-12-20 18:59:05 -0800334 // TODO: this is incorrect for MIN_INT.
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800335 if (isSigned && (int64_t)value < 0) {
336 buffer[0] = '-';
337 buffer += 1;
338 buffsize -= 1;
339 value = (uint64_t)(-(int64_t)value);
340 }
341
Elliott Hugheseababde2012-12-20 18:59:05 -0800342 format_number(buffer, buffsize, value, base, false);
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800343}
344
Elliott Hugheseababde2012-12-20 18:59:05 -0800345// Assumes buf_size > 2.
346static void format_hex(char* buf, size_t buf_size, uint64_t value, bool caps) {
347 format_number(buf, buf_size, value, 16, caps);
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800348}
349
350
351/* Perform formatted output to an output target 'o' */
352static void
353out_vformat(Out *o, const char *format, va_list args)
354{
Andy McFaddenec92af82011-07-29 12:46:34 -0700355 int nn = 0;
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800356
357 for (;;) {
Andy McFaddenec92af82011-07-29 12:46:34 -0700358 int mm;
359 int padZero = 0;
360 int padLeft = 0;
361 char sign = '\0';
362 int width = -1;
363 int prec = -1;
364 size_t bytelen = sizeof(int);
365 const char* str;
366 int slen;
367 char buffer[32]; /* temporary buffer used to format numbers */
368
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800369 char c;
370
371 /* first, find all characters that are not 0 or '%' */
372 /* then send them to the output directly */
373 mm = nn;
374 do {
375 c = format[mm];
376 if (c == '\0' || c == '%')
377 break;
378 mm++;
379 } while (1);
380
381 if (mm > nn) {
382 out_send(o, format+nn, mm-nn);
383 nn = mm;
384 }
385
386 /* is this it ? then exit */
387 if (c == '\0')
388 break;
389
390 /* nope, we are at a '%' modifier */
391 nn++; // skip it
392
393 /* parse flags */
394 for (;;) {
395 c = format[nn++];
396 if (c == '\0') { /* single trailing '%' ? */
397 c = '%';
398 out_send(o, &c, 1);
399 return;
400 }
401 else if (c == '0') {
402 padZero = 1;
403 continue;
404 }
405 else if (c == '-') {
406 padLeft = 1;
407 continue;
408 }
409 else if (c == ' ' || c == '+') {
410 sign = c;
411 continue;
412 }
413 break;
414 }
415
416 /* parse field width */
417 if ((c >= '0' && c <= '9')) {
418 nn --;
419 width = (int)parse_decimal(format, &nn);
420 c = format[nn++];
421 }
422
423 /* parse precision */
424 if (c == '.') {
425 prec = (int)parse_decimal(format, &nn);
426 c = format[nn++];
427 }
428
429 /* length modifier */
430 switch (c) {
431 case 'h':
432 bytelen = sizeof(short);
433 if (format[nn] == 'h') {
434 bytelen = sizeof(char);
435 nn += 1;
436 }
437 c = format[nn++];
438 break;
439 case 'l':
440 bytelen = sizeof(long);
441 if (format[nn] == 'l') {
442 bytelen = sizeof(long long);
443 nn += 1;
444 }
445 c = format[nn++];
446 break;
447 case 'z':
448 bytelen = sizeof(size_t);
449 c = format[nn++];
450 break;
451 case 't':
452 bytelen = sizeof(ptrdiff_t);
453 c = format[nn++];
454 break;
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800455 default:
456 ;
457 }
458
459 /* conversion specifier */
460 if (c == 's') {
461 /* string */
462 str = va_arg(args, const char*);
463 } else if (c == 'c') {
464 /* character */
465 /* NOTE: char is promoted to int when passed through the stack */
466 buffer[0] = (char) va_arg(args, int);
467 buffer[1] = '\0';
468 str = buffer;
469 } else if (c == 'p') {
Andy McFaddenec92af82011-07-29 12:46:34 -0700470 uint64_t value = (uintptr_t) va_arg(args, void*);
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800471 buffer[0] = '0';
472 buffer[1] = 'x';
Elliott Hugheseababde2012-12-20 18:59:05 -0800473 format_hex(buffer + 2, sizeof buffer-2, value, false);
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800474 str = buffer;
475 } else {
476 /* integers - first read value from stack */
477 uint64_t value;
478 int isSigned = (c == 'd' || c == 'i' || c == 'o');
479
480 /* NOTE: int8_t and int16_t are promoted to int when passed
481 * through the stack
482 */
483 switch (bytelen) {
484 case 1: value = (uint8_t) va_arg(args, int); break;
485 case 2: value = (uint16_t) va_arg(args, int); break;
486 case 4: value = va_arg(args, uint32_t); break;
487 case 8: value = va_arg(args, uint64_t); break;
488 default: return; /* should not happen */
489 }
490
491 /* sign extension, if needed */
492 if (isSigned) {
493 int shift = 64 - 8*bytelen;
494 value = (uint64_t)(((int64_t)(value << shift)) >> shift);
495 }
496
497 /* format the number properly into our buffer */
498 switch (c) {
499 case 'i': case 'd':
500 format_integer(buffer, sizeof buffer, value, 10, isSigned);
501 break;
502 case 'o':
503 format_integer(buffer, sizeof buffer, value, 8, isSigned);
504 break;
505 case 'x': case 'X':
506 format_hex(buffer, sizeof buffer, value, (c == 'X'));
507 break;
508 default:
509 buffer[0] = '\0';
510 }
511 /* then point to it */
512 str = buffer;
513 }
514
515 /* if we are here, 'str' points to the content that must be
516 * outputted. handle padding and alignment now */
517
518 slen = strlen(str);
519
Elliott Hughes18a206c2012-10-29 17:37:13 -0700520 if (sign != '\0' || prec != -1) {
521 __assert(__FILE__, __LINE__, "sign/precision unsupported");
522 }
523
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800524 if (slen < width && !padLeft) {
525 char padChar = padZero ? '0' : ' ';
526 out_send_repeat(o, padChar, width - slen);
527 }
528
529 out_send(o, str, slen);
530
531 if (slen < width && padLeft) {
532 char padChar = padZero ? '0' : ' ';
533 out_send_repeat(o, padChar, width - slen);
534 }
535 }
536}
537
538
539#ifdef UNIT_TESTS
540
541#include <stdio.h>
542
543static int gFails = 0;
544
545#define MARGIN 40
546
547#define UTEST_CHECK(condition,message) \
548 printf("Checking %-*s: ", MARGIN, message); fflush(stdout); \
549 if (!(condition)) { \
550 printf("KO\n"); \
551 gFails += 1; \
552 } else { \
553 printf("ok\n"); \
554 }
555
556static void
557utest_BufOut(void)
558{
559 char buffer[16];
560 BufOut bo[1];
561 Out* out;
562 int ret;
563
564 buffer[0] = '1';
565 out = buf_out_init(bo, buffer, sizeof buffer);
566 UTEST_CHECK(buffer[0] == '\0', "buf_out_init clears initial byte");
567 out_send(out, "abc", 3);
568 UTEST_CHECK(!memcmp(buffer, "abc", 4), "out_send() works with BufOut");
569 out_send_repeat(out, 'X', 4);
570 UTEST_CHECK(!memcmp(buffer, "abcXXXX", 8), "out_send_repeat() works with BufOut");
571 buffer[sizeof buffer-1] = 'x';
572 out_send_repeat(out, 'Y', 2*sizeof(buffer));
573 UTEST_CHECK(buffer[sizeof buffer-1] == '\0', "overflows always zero-terminates");
574
575 out = buf_out_init(bo, buffer, sizeof buffer);
576 out_send_repeat(out, 'X', 2*sizeof(buffer));
577 ret = buf_out_length(bo);
578 UTEST_CHECK(ret == 2*sizeof(buffer), "correct size returned on overflow");
579}
580
581static void
582utest_expect(const char* result, const char* format, ...)
583{
584 va_list args;
585 BufOut bo[1];
586 char buffer[256];
587 Out* out = buf_out_init(bo, buffer, sizeof buffer);
588
589 printf("Checking %-*s: ", MARGIN, format); fflush(stdout);
590 va_start(args, format);
591 out_vformat(out, format, args);
592 va_end(args);
593
594 if (strcmp(result, buffer)) {
595 printf("KO. got '%s' expecting '%s'\n", buffer, result);
596 gFails += 1;
597 } else {
598 printf("ok. got '%s'\n", result);
599 }
600}
601
602int main(void)
603{
604 utest_BufOut();
605 utest_expect("", "");
606 utest_expect("a", "a");
607 utest_expect("01234", "01234", "");
608 utest_expect("01234", "%s", "01234");
609 utest_expect("aabbcc", "aa%scc", "bb");
610 utest_expect("a", "%c", 'a');
611 utest_expect("1234", "%d", 1234);
612 utest_expect("-8123", "%d", -8123);
613 utest_expect("16", "%hd", 0x7fff0010);
614 utest_expect("16", "%hhd", 0x7fffff10);
Andy McFaddenec92af82011-07-29 12:46:34 -0700615 utest_expect("68719476736", "%lld", 0x1000000000LL);
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800616 utest_expect("70000", "%ld", 70000);
617 utest_expect("0xb0001234", "%p", (void*)0xb0001234);
618 utest_expect("12ab", "%x", 0x12ab);
619 utest_expect("12AB", "%X", 0x12ab);
620 utest_expect("00123456", "%08x", 0x123456);
621 utest_expect("01234", "0%d", 1234);
622 utest_expect(" 1234", "%5d", 1234);
623 utest_expect("01234", "%05d", 1234);
624 utest_expect(" 1234", "%8d", 1234);
625 utest_expect("1234 ", "%-8d", 1234);
626 utest_expect("abcdef ", "%-11s", "abcdef");
627 utest_expect("something:1234", "%s:%d", "something", 1234);
Andy McFaddenec92af82011-07-29 12:46:34 -0700628 utest_expect("005:5:05", "%03d:%d:%02d", 5, 5, 5);
629 utest_expect("5,0x0", "%d,%p", 5, NULL);
630 utest_expect("68719476736,6,7,8", "%lld,%d,%d,%d", 0x1000000000LL, 6, 7, 8);
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800631 return gFails != 0;
632}
633
634#endif /* UNIT_TESTS */