libc_logging.cpp revision 8f2a5a0b40fc82126c691d5c30131d908772aab7
1/*
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
29#include <../private/libc_logging.h> // Relative path so we can #include this .cpp file for testing.
30#include <../private/ScopedPthreadMutexLocker.h>
31
32#include <assert.h>
33#include <errno.h>
34#include <pthread.h>
35#include <stdarg.h>
36#include <stddef.h>
37#include <stdlib.h>
38#include <string.h>
39#include <unistd.h>
40
41/*** Generic output sink
42 ***/
43
44struct Out {
45  void *opaque;
46  void (*send)(void *opaque, const char *data, int len);
47};
48
49static void out_send(Out *o, const char *data, size_t len) {
50    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 */
71static void out_vformat(Out* o, const char* format, va_list args);
72
73/*** Bounded buffer output
74 ***/
75
76struct BufOut {
77  Out out[1];
78  char *buffer;
79  char *pos;
80  char *end;
81  int total;
82};
83
84static void buf_out_send(void *opaque, const char *data, int len) {
85    BufOut *bo = reinterpret_cast<BufOut*>(opaque);
86
87    if (len < 0) {
88        len = strlen(data);
89    }
90
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
130vformat_buffer(char *buff, size_t buf_size, const char *format, va_list args)
131{
132    BufOut bo;
133    Out *out;
134
135    out = buf_out_init(&bo, buff, buf_size);
136    if (out == NULL)
137        return 0;
138
139    out_vformat(out, format, args);
140
141    return buf_out_length(&bo);
142}
143
144int __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;
150}
151
152
153/*** File descriptor output
154 ***/
155
156struct FdOut {
157  Out out[1];
158  int fd;
159  int total;
160};
161
162static void
163fd_out_send(void *opaque, const char *data, int len)
164{
165    FdOut *fdo = reinterpret_cast<FdOut*>(opaque);
166
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
201int __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  }
207
208  va_list args;
209  va_start(args, format);
210  out_vformat(out, format, args);
211  va_end(args);
212
213  return fd_out_length(&fdo);
214}
215
216/*** Log output
217 ***/
218
219#include <unistd.h>
220#include <fcntl.h>
221#include <sys/uio.h>
222
223static pthread_mutex_t gLogInitializationLock = PTHREAD_MUTEX_INITIALIZER;
224
225int __libc_format_log_va_list(int priority, const char* tag, const char* fmt, va_list args) {
226  char buf[1024];
227  int buf_strlen = vformat_buffer(buf, sizeof(buf), fmt, args);
228
229  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;
235    }
236  }
237
238  struct iovec vec[3];
239  vec[0].iov_base = &priority;
240  vec[0].iov_len = 1;
241  vec[1].iov_base = const_cast<char*>(tag);
242  vec[1].iov_len = strlen(tag) + 1;
243  vec[2].iov_base = const_cast<char*>(buf);
244  vec[2].iov_len = buf_strlen + 1;
245
246  return TEMP_FAILURE_RETRY(writev(main_log_fd, vec, 3));
247}
248
249int __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;
255}
256
257/*** 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
286// Writes number 'value' in base 'base' into buffer 'buf' of size 'buf_size' bytes.
287// Assumes that buf_size > 0.
288static void format_unsigned(char* buf, size_t buf_size, uint64_t value, int base, bool caps) {
289  char* p = buf;
290  char* end = buf + buf_size - 1;
291
292  // 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;
304    }
305  }
306
307  // Special case for 0.
308  if (p == buf) {
309    if (p != end) {
310      *p++ = '0';
311    }
312  }
313  *p = '\0';
314
315  // 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  }
322}
323
324static 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');
334
335  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);
342}
343
344/* Perform formatted output to an output target 'o' */
345static void
346out_vformat(Out *o, const char *format, va_list args)
347{
348    int nn = 0;
349
350    for (;;) {
351        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);
358        int slen;
359        char buffer[32];  /* temporary buffer used to format numbers */
360
361        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;
447        default:
448            ;
449        }
450
451        /* conversion specifier */
452        const char* str = buffer;
453        if (c == 's') {
454            /* string */
455            str = va_arg(args, const char*);
456            if (str == NULL) {
457                str = "(null)";
458            }
459        } 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';
464        } else if (c == 'p') {
465            uint64_t  value = (uintptr_t) va_arg(args, void*);
466            buffer[0] = '0';
467            buffer[1] = 'x';
468            format_integer(buffer + 2, sizeof(buffer) - 2, value, 'x');
469        } else if (c == 'd' || c == 'i' || c == 'o' || c == 'x' || c == 'X') {
470            /* integers - first read value from stack */
471            uint64_t value;
472            int is_signed = (c == 'd' || c == 'i' || c == 'o');
473
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 */
486            if (is_signed) {
487                int shift = 64 - 8*bytelen;
488                value = (uint64_t)(((int64_t)(value << shift)) >> shift);
489            }
490
491            /* format the number properly into our buffer */
492            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");
498        }
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
505        if (sign != '\0' || prec != -1) {
506            __assert(__FILE__, __LINE__, "sign/precision unsupported");
507        }
508
509        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}
522
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}
563