blob: 755dc8111844498c8d7b28efbfb9ca4b79f20cf0 [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
Elliott Hughes8f2a5a02013-03-15 15:30:25 -070029#include <../private/libc_logging.h> // Relative path so we can #include this .cpp file for testing.
30#include <../private/ScopedPthreadMutexLocker.h>
Elliott Hugheseababde2012-12-20 18:59:05 -080031
32#include <assert.h>
33#include <errno.h>
Elliott Hughes8f2a5a02013-03-15 15:30:25 -070034#include <pthread.h>
Elliott Hugheseababde2012-12-20 18:59:05 -080035#include <stdarg.h>
36#include <stddef.h>
Elliott Hughes8f2a5a02013-03-15 15:30:25 -070037#include <stdlib.h>
Elliott Hugheseababde2012-12-20 18:59:05 -080038#include <string.h>
39#include <unistd.h>
40
David 'Digit' Turner5c734642010-01-20 12:36:51 -080041/*** Generic output sink
42 ***/
43
Elliott Hughes18a206c2012-10-29 17:37:13 -070044struct Out {
45 void *opaque;
46 void (*send)(void *opaque, const char *data, int len);
47};
David 'Digit' Turner5c734642010-01-20 12:36:51 -080048
Elliott Hughes18a206c2012-10-29 17:37:13 -070049static void out_send(Out *o, const char *data, size_t len) {
David 'Digit' Turner5c734642010-01-20 12:36:51 -080050 o->send(o->opaque, data, (int)len);
51}
52
53static void
54out_send_repeat(Out *o, char ch, int count)
55{
56 char pad[8];
57 const int padSize = (int)sizeof(pad);
58
59 memset(pad, ch, sizeof(pad));
60 while (count > 0) {
61 int avail = count;
62 if (avail > padSize) {
63 avail = padSize;
64 }
65 o->send(o->opaque, pad, avail);
66 count -= avail;
67 }
68}
69
70/* forward declaration */
Elliott Hughes18a206c2012-10-29 17:37:13 -070071static void out_vformat(Out* o, const char* format, va_list args);
David 'Digit' Turner5c734642010-01-20 12:36:51 -080072
73/*** Bounded buffer output
74 ***/
75
Elliott Hughes18a206c2012-10-29 17:37:13 -070076struct BufOut {
77 Out out[1];
78 char *buffer;
79 char *pos;
80 char *end;
81 int total;
82};
David 'Digit' Turner5c734642010-01-20 12:36:51 -080083
Elliott Hughes18a206c2012-10-29 17:37:13 -070084static void buf_out_send(void *opaque, const char *data, int len) {
85 BufOut *bo = reinterpret_cast<BufOut*>(opaque);
David 'Digit' Turner5c734642010-01-20 12:36:51 -080086
Elliott Hughes18a206c2012-10-29 17:37:13 -070087 if (len < 0) {
David 'Digit' Turner5c734642010-01-20 12:36:51 -080088 len = strlen(data);
Elliott Hughes18a206c2012-10-29 17:37:13 -070089 }
David 'Digit' Turner5c734642010-01-20 12:36:51 -080090
91 bo->total += len;
92
93 while (len > 0) {
94 int avail = bo->end - bo->pos;
95 if (avail == 0)
96 break;
97 if (avail > len)
98 avail = len;
99 memcpy(bo->pos, data, avail);
100 bo->pos += avail;
101 bo->pos[0] = '\0';
102 len -= avail;
103 }
104}
105
106static Out*
107buf_out_init(BufOut *bo, char *buffer, size_t size)
108{
109 if (size == 0)
110 return NULL;
111
112 bo->out->opaque = bo;
113 bo->out->send = buf_out_send;
114 bo->buffer = buffer;
115 bo->end = buffer + size - 1;
116 bo->pos = bo->buffer;
117 bo->pos[0] = '\0';
118 bo->total = 0;
119
120 return bo->out;
121}
122
123static int
124buf_out_length(BufOut *bo)
125{
126 return bo->total;
127}
128
129static int
Elliott Hughes41b31792013-01-28 10:36:31 -0800130vformat_buffer(char *buff, size_t buf_size, const char *format, va_list args)
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800131{
132 BufOut bo;
133 Out *out;
134
Elliott Hughes41b31792013-01-28 10:36:31 -0800135 out = buf_out_init(&bo, buff, buf_size);
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800136 if (out == NULL)
137 return 0;
138
139 out_vformat(out, format, args);
140
141 return buf_out_length(&bo);
142}
143
Elliott Hughes1e980b62013-01-17 18:36:06 -0800144int __libc_format_buffer(char* buffer, size_t buffer_size, const char* format, ...) {
145 va_list args;
146 va_start(args, format);
147 int result = vformat_buffer(buffer, buffer_size, format, args);
148 va_end(args);
149 return result;
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800150}
151
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800152
153/*** File descriptor output
154 ***/
155
Elliott Hughes18a206c2012-10-29 17:37:13 -0700156struct FdOut {
157 Out out[1];
158 int fd;
159 int total;
160};
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800161
162static void
163fd_out_send(void *opaque, const char *data, int len)
164{
Elliott Hughes1e980b62013-01-17 18:36:06 -0800165 FdOut *fdo = reinterpret_cast<FdOut*>(opaque);
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800166
167 if (len < 0)
168 len = strlen(data);
169
170 while (len > 0) {
171 int ret = write(fdo->fd, data, len);
172 if (ret < 0) {
173 if (errno == EINTR)
174 continue;
175 break;
176 }
177 data += ret;
178 len -= ret;
179 fdo->total += ret;
180 }
181}
182
183static Out*
184fd_out_init(FdOut *fdo, int fd)
185{
186 fdo->out->opaque = fdo;
187 fdo->out->send = fd_out_send;
188 fdo->fd = fd;
189 fdo->total = 0;
190
191 return fdo->out;
192}
193
194static int
195fd_out_length(FdOut *fdo)
196{
197 return fdo->total;
198}
199
200
Elliott Hughes1e980b62013-01-17 18:36:06 -0800201int __libc_format_fd(int fd, const char* format, ...) {
202 FdOut fdo;
203 Out* out = fd_out_init(&fdo, fd);
204 if (out == NULL) {
205 return 0;
206 }
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800207
Elliott Hughes1e980b62013-01-17 18:36:06 -0800208 va_list args;
209 va_start(args, format);
210 out_vformat(out, format, args);
211 va_end(args);
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800212
Elliott Hughes1e980b62013-01-17 18:36:06 -0800213 return fd_out_length(&fdo);
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800214}
215
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800216/*** Log output
217 ***/
218
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800219#include <unistd.h>
220#include <fcntl.h>
221#include <sys/uio.h>
222
Elliott Hughes8f2a5a02013-03-15 15:30:25 -0700223static pthread_mutex_t gLogInitializationLock = PTHREAD_MUTEX_INITIALIZER;
224
Elliott Hughes1e980b62013-01-17 18:36:06 -0800225int __libc_format_log_va_list(int priority, const char* tag, const char* fmt, va_list args) {
226 char buf[1024];
Elliott Hughes8f2a5a02013-03-15 15:30:25 -0700227 int buf_strlen = vformat_buffer(buf, sizeof(buf), fmt, args);
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800228
Elliott Hughes8f2a5a02013-03-15 15:30:25 -0700229 static int main_log_fd = -1;
230 if (main_log_fd == -1) {
231 ScopedPthreadMutexLocker locker(&gLogInitializationLock);
232 main_log_fd = TEMP_FAILURE_RETRY(open("/dev/log/main", O_CLOEXEC | O_WRONLY));
233 if (main_log_fd == -1) {
234 return -1;
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800235 }
Elliott Hughes1e980b62013-01-17 18:36:06 -0800236 }
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800237
Elliott Hughes1e980b62013-01-17 18:36:06 -0800238 struct iovec vec[3];
Elliott Hughes8f2a5a02013-03-15 15:30:25 -0700239 vec[0].iov_base = &priority;
Elliott Hughes1e980b62013-01-17 18:36:06 -0800240 vec[0].iov_len = 1;
Elliott Hughes8f2a5a02013-03-15 15:30:25 -0700241 vec[1].iov_base = const_cast<char*>(tag);
Elliott Hughes1e980b62013-01-17 18:36:06 -0800242 vec[1].iov_len = strlen(tag) + 1;
Elliott Hughes8f2a5a02013-03-15 15:30:25 -0700243 vec[2].iov_base = const_cast<char*>(buf);
244 vec[2].iov_len = buf_strlen + 1;
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800245
Elliott Hughes8f2a5a02013-03-15 15:30:25 -0700246 return TEMP_FAILURE_RETRY(writev(main_log_fd, vec, 3));
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800247}
248
Elliott Hughes1e980b62013-01-17 18:36:06 -0800249int __libc_format_log(int priority, const char* tag, const char* format, ...) {
250 va_list args;
251 va_start(args, format);
252 int result = __libc_format_log_va_list(priority, tag, format, args);
253 va_end(args);
254 return result;
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800255}
256
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800257/*** formatted output implementation
258 ***/
259
260/* Parse a decimal string from 'format + *ppos',
261 * return the value, and writes the new position past
262 * the decimal string in '*ppos' on exit.
263 *
264 * NOTE: Does *not* handle a sign prefix.
265 */
266static unsigned
267parse_decimal(const char *format, int *ppos)
268{
269 const char* p = format + *ppos;
270 unsigned result = 0;
271
272 for (;;) {
273 int ch = *p;
274 unsigned d = (unsigned)(ch - '0');
275
276 if (d >= 10U)
277 break;
278
279 result = result*10 + d;
280 p++;
281 }
282 *ppos = p - format;
283 return result;
284}
285
Elliott Hugheseababde2012-12-20 18:59:05 -0800286// Writes number 'value' in base 'base' into buffer 'buf' of size 'buf_size' bytes.
287// Assumes that buf_size > 0.
Elliott Hughes41b31792013-01-28 10:36:31 -0800288static void format_unsigned(char* buf, size_t buf_size, uint64_t value, int base, bool caps) {
Elliott Hugheseababde2012-12-20 18:59:05 -0800289 char* p = buf;
290 char* end = buf + buf_size - 1;
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800291
Elliott Hugheseababde2012-12-20 18:59:05 -0800292 // Generate digit string in reverse order.
293 while (value) {
294 unsigned d = value % base;
295 value /= base;
296 if (p != end) {
297 char ch;
298 if (d < 10) {
299 ch = '0' + d;
300 } else {
301 ch = (caps ? 'A' : 'a') + (d - 10);
302 }
303 *p++ = ch;
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800304 }
Elliott Hugheseababde2012-12-20 18:59:05 -0800305 }
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800306
Elliott Hugheseababde2012-12-20 18:59:05 -0800307 // Special case for 0.
308 if (p == buf) {
309 if (p != end) {
310 *p++ = '0';
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800311 }
Elliott Hugheseababde2012-12-20 18:59:05 -0800312 }
313 *p = '\0';
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800314
Elliott Hugheseababde2012-12-20 18:59:05 -0800315 // Reverse digit string in-place.
316 size_t length = p - buf;
317 for (size_t i = 0, j = length - 1; i < j; ++i, --j) {
318 char ch = buf[i];
319 buf[i] = buf[j];
320 buf[j] = ch;
321 }
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800322}
323
Elliott Hughes41b31792013-01-28 10:36:31 -0800324static void format_integer(char* buf, size_t buf_size, uint64_t value, char conversion) {
325 // Decode the conversion specifier.
326 int is_signed = (conversion == 'd' || conversion == 'i' || conversion == 'o');
327 int base = 10;
328 if (conversion == 'x' || conversion == 'X') {
329 base = 16;
330 } else if (conversion == 'o') {
331 base = 8;
332 }
333 bool caps = (conversion == 'X');
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800334
Elliott Hughes41b31792013-01-28 10:36:31 -0800335 if (is_signed && static_cast<int64_t>(value) < 0) {
336 buf[0] = '-';
337 buf += 1;
338 buf_size -= 1;
339 value = static_cast<uint64_t>(-static_cast<int64_t>(value));
340 }
341 format_unsigned(buf, buf_size, value, base, caps);
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800342}
343
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800344/* Perform formatted output to an output target 'o' */
345static void
346out_vformat(Out *o, const char *format, va_list args)
347{
Andy McFaddenec92af82011-07-29 12:46:34 -0700348 int nn = 0;
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800349
350 for (;;) {
Andy McFaddenec92af82011-07-29 12:46:34 -0700351 int mm;
352 int padZero = 0;
353 int padLeft = 0;
354 char sign = '\0';
355 int width = -1;
356 int prec = -1;
357 size_t bytelen = sizeof(int);
Andy McFaddenec92af82011-07-29 12:46:34 -0700358 int slen;
359 char buffer[32]; /* temporary buffer used to format numbers */
360
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800361 char c;
362
363 /* first, find all characters that are not 0 or '%' */
364 /* then send them to the output directly */
365 mm = nn;
366 do {
367 c = format[mm];
368 if (c == '\0' || c == '%')
369 break;
370 mm++;
371 } while (1);
372
373 if (mm > nn) {
374 out_send(o, format+nn, mm-nn);
375 nn = mm;
376 }
377
378 /* is this it ? then exit */
379 if (c == '\0')
380 break;
381
382 /* nope, we are at a '%' modifier */
383 nn++; // skip it
384
385 /* parse flags */
386 for (;;) {
387 c = format[nn++];
388 if (c == '\0') { /* single trailing '%' ? */
389 c = '%';
390 out_send(o, &c, 1);
391 return;
392 }
393 else if (c == '0') {
394 padZero = 1;
395 continue;
396 }
397 else if (c == '-') {
398 padLeft = 1;
399 continue;
400 }
401 else if (c == ' ' || c == '+') {
402 sign = c;
403 continue;
404 }
405 break;
406 }
407
408 /* parse field width */
409 if ((c >= '0' && c <= '9')) {
410 nn --;
411 width = (int)parse_decimal(format, &nn);
412 c = format[nn++];
413 }
414
415 /* parse precision */
416 if (c == '.') {
417 prec = (int)parse_decimal(format, &nn);
418 c = format[nn++];
419 }
420
421 /* length modifier */
422 switch (c) {
423 case 'h':
424 bytelen = sizeof(short);
425 if (format[nn] == 'h') {
426 bytelen = sizeof(char);
427 nn += 1;
428 }
429 c = format[nn++];
430 break;
431 case 'l':
432 bytelen = sizeof(long);
433 if (format[nn] == 'l') {
434 bytelen = sizeof(long long);
435 nn += 1;
436 }
437 c = format[nn++];
438 break;
439 case 'z':
440 bytelen = sizeof(size_t);
441 c = format[nn++];
442 break;
443 case 't':
444 bytelen = sizeof(ptrdiff_t);
445 c = format[nn++];
446 break;
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800447 default:
448 ;
449 }
450
451 /* conversion specifier */
Elliott Hughes41b31792013-01-28 10:36:31 -0800452 const char* str = buffer;
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800453 if (c == 's') {
454 /* string */
455 str = va_arg(args, const char*);
Elliott Hughes239e7a02013-01-25 17:13:45 -0800456 if (str == NULL) {
457 str = "(null)";
458 }
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800459 } else if (c == 'c') {
460 /* character */
461 /* NOTE: char is promoted to int when passed through the stack */
462 buffer[0] = (char) va_arg(args, int);
463 buffer[1] = '\0';
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800464 } else if (c == 'p') {
Andy McFaddenec92af82011-07-29 12:46:34 -0700465 uint64_t value = (uintptr_t) va_arg(args, void*);
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800466 buffer[0] = '0';
467 buffer[1] = 'x';
Elliott Hughes41b31792013-01-28 10:36:31 -0800468 format_integer(buffer + 2, sizeof(buffer) - 2, value, 'x');
469 } else if (c == 'd' || c == 'i' || c == 'o' || c == 'x' || c == 'X') {
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800470 /* integers - first read value from stack */
471 uint64_t value;
Elliott Hughes41b31792013-01-28 10:36:31 -0800472 int is_signed = (c == 'd' || c == 'i' || c == 'o');
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800473
474 /* NOTE: int8_t and int16_t are promoted to int when passed
475 * through the stack
476 */
477 switch (bytelen) {
478 case 1: value = (uint8_t) va_arg(args, int); break;
479 case 2: value = (uint16_t) va_arg(args, int); break;
480 case 4: value = va_arg(args, uint32_t); break;
481 case 8: value = va_arg(args, uint64_t); break;
482 default: return; /* should not happen */
483 }
484
485 /* sign extension, if needed */
Elliott Hughes41b31792013-01-28 10:36:31 -0800486 if (is_signed) {
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800487 int shift = 64 - 8*bytelen;
488 value = (uint64_t)(((int64_t)(value << shift)) >> shift);
489 }
490
491 /* format the number properly into our buffer */
Elliott Hughes41b31792013-01-28 10:36:31 -0800492 format_integer(buffer, sizeof(buffer), value, c);
493 } else if (c == '%') {
494 buffer[0] = '%';
495 buffer[1] = '\0';
496 } else {
497 __assert(__FILE__, __LINE__, "conversion specifier unsupported");
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800498 }
499
500 /* if we are here, 'str' points to the content that must be
501 * outputted. handle padding and alignment now */
502
503 slen = strlen(str);
504
Elliott Hughes18a206c2012-10-29 17:37:13 -0700505 if (sign != '\0' || prec != -1) {
506 __assert(__FILE__, __LINE__, "sign/precision unsupported");
507 }
508
David 'Digit' Turner5c734642010-01-20 12:36:51 -0800509 if (slen < width && !padLeft) {
510 char padChar = padZero ? '0' : ' ';
511 out_send_repeat(o, padChar, width - slen);
512 }
513
514 out_send(o, str, slen);
515
516 if (slen < width && padLeft) {
517 char padChar = padZero ? '0' : ' ';
518 out_send_repeat(o, padChar, width - slen);
519 }
520 }
521}
Elliott Hughes8f2a5a02013-03-15 15:30:25 -0700522
523// must be kept in sync with frameworks/base/core/java/android/util/EventLog.java
524enum AndroidEventLogType {
525 EVENT_TYPE_INT = 0,
526 EVENT_TYPE_LONG = 1,
527 EVENT_TYPE_STRING = 2,
528 EVENT_TYPE_LIST = 3,
529};
530
531static int __libc_android_log_event(int32_t tag, char type, const void* payload, size_t len) {
532 struct iovec vec[3];
533 vec[0].iov_base = &tag;
534 vec[0].iov_len = sizeof(tag);
535 vec[1].iov_base = &type;
536 vec[1].iov_len = sizeof(type);
537 vec[2].iov_base = const_cast<void*>(payload);
538 vec[2].iov_len = len;
539
540 static int event_log_fd = -1;
541 if (event_log_fd == -1) {
542 ScopedPthreadMutexLocker locker(&gLogInitializationLock);
543 event_log_fd = TEMP_FAILURE_RETRY(open("/dev/log/events", O_CLOEXEC | O_WRONLY));
544 }
545 return TEMP_FAILURE_RETRY(writev(event_log_fd, vec, 3));
546}
547
548void __libc_android_log_event_int(int32_t tag, int value) {
549 __libc_android_log_event(tag, EVENT_TYPE_INT, &value, sizeof(value));
550}
551
552void __libc_android_log_event_uid(int32_t tag) {
553 __libc_android_log_event_int(tag, getuid());
554}
555
556void __fortify_chk_fail(const char *msg, uint32_t tag) {
557 __libc_format_log(ANDROID_LOG_FATAL, "libc", "FORTIFY_SOURCE: %s. Calling abort().\n", msg);
558 if (tag != 0) {
559 __libc_android_log_event_uid(tag);
560 }
561 abort();
562}