libc_logging.cpp revision eb847bc8666842a3cfc9c06e8458ad1abebebaf0
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 <fcntl.h>
35#include <pthread.h>
36#include <stdarg.h>
37#include <stddef.h>
38#include <stdlib.h>
39#include <string.h>
40#include <sys/mman.h>
41#include <sys/uio.h>
42#include <unistd.h>
43
44static pthread_mutex_t gAbortMsgLock = PTHREAD_MUTEX_INITIALIZER;
45
46__LIBC_HIDDEN__ abort_msg_t** __abort_message_ptr; // Accessible to __libc_init_common.
47
48// Must be kept in sync with frameworks/base/core/java/android/util/EventLog.java.
49enum AndroidEventLogType {
50  EVENT_TYPE_INT      = 0,
51  EVENT_TYPE_LONG     = 1,
52  EVENT_TYPE_STRING   = 2,
53  EVENT_TYPE_LIST     = 3,
54};
55
56struct BufferOutputStream {
57 public:
58  BufferOutputStream(char* buffer, size_t size) : total(0) {
59    buffer_ = buffer;
60    end_ = buffer + size - 1;
61    pos_ = buffer_;
62    pos_[0] = '\0';
63  }
64
65  ~BufferOutputStream() {
66  }
67
68  void Send(const char* data, int len) {
69    if (len < 0) {
70      len = strlen(data);
71    }
72
73    while (len > 0) {
74      int avail = end_ - pos_;
75      if (avail == 0) {
76        break;
77      }
78      if (avail > len) {
79        avail = len;
80      }
81      memcpy(pos_, data, avail);
82      pos_ += avail;
83      pos_[0] = '\0';
84      len -= avail;
85      total += avail;
86    }
87  }
88
89  int total;
90
91 private:
92  char* buffer_;
93  char* pos_;
94  char* end_;
95};
96
97struct FdOutputStream {
98 public:
99  FdOutputStream(int fd) : total(0), fd_(fd) {
100  }
101
102  void Send(const char* data, int len) {
103    if (len < 0) {
104      len = strlen(data);
105    }
106
107    while (len > 0) {
108      int rc = TEMP_FAILURE_RETRY(write(fd_, data, len));
109      if (rc == -1) {
110        break;
111      }
112      data += rc;
113      len -= rc;
114      total += rc;
115    }
116  }
117
118  int total;
119
120 private:
121  int fd_;
122};
123
124/*** formatted output implementation
125 ***/
126
127/* Parse a decimal string from 'format + *ppos',
128 * return the value, and writes the new position past
129 * the decimal string in '*ppos' on exit.
130 *
131 * NOTE: Does *not* handle a sign prefix.
132 */
133static unsigned parse_decimal(const char *format, int *ppos) {
134    const char* p = format + *ppos;
135    unsigned result = 0;
136
137    for (;;) {
138        int ch = *p;
139        unsigned d = (unsigned)(ch - '0');
140
141        if (d >= 10U) {
142            break;
143        }
144
145        result = result*10 + d;
146        p++;
147    }
148    *ppos = p - format;
149    return result;
150}
151
152// Writes number 'value' in base 'base' into buffer 'buf' of size 'buf_size' bytes.
153// Assumes that buf_size > 0.
154static void format_unsigned(char* buf, size_t buf_size, uint64_t value, int base, bool caps) {
155  char* p = buf;
156  char* end = buf + buf_size - 1;
157
158  // Generate digit string in reverse order.
159  while (value) {
160    unsigned d = value % base;
161    value /= base;
162    if (p != end) {
163      char ch;
164      if (d < 10) {
165        ch = '0' + d;
166      } else {
167        ch = (caps ? 'A' : 'a') + (d - 10);
168      }
169      *p++ = ch;
170    }
171  }
172
173  // Special case for 0.
174  if (p == buf) {
175    if (p != end) {
176      *p++ = '0';
177    }
178  }
179  *p = '\0';
180
181  // Reverse digit string in-place.
182  size_t length = p - buf;
183  for (size_t i = 0, j = length - 1; i < j; ++i, --j) {
184    char ch = buf[i];
185    buf[i] = buf[j];
186    buf[j] = ch;
187  }
188}
189
190static void format_integer(char* buf, size_t buf_size, uint64_t value, char conversion) {
191  // Decode the conversion specifier.
192  int is_signed = (conversion == 'd' || conversion == 'i' || conversion == 'o');
193  int base = 10;
194  if (conversion == 'x' || conversion == 'X') {
195    base = 16;
196  } else if (conversion == 'o') {
197    base = 8;
198  }
199  bool caps = (conversion == 'X');
200
201  if (is_signed && static_cast<int64_t>(value) < 0) {
202    buf[0] = '-';
203    buf += 1;
204    buf_size -= 1;
205    value = static_cast<uint64_t>(-static_cast<int64_t>(value));
206  }
207  format_unsigned(buf, buf_size, value, base, caps);
208}
209
210template <typename Out>
211static void SendRepeat(Out& o, char ch, int count) {
212  char pad[8];
213  memset(pad, ch, sizeof(pad));
214
215  const int pad_size = static_cast<int>(sizeof(pad));
216  while (count > 0) {
217    int avail = count;
218    if (avail > pad_size) {
219      avail = pad_size;
220    }
221    o.Send(pad, avail);
222    count -= avail;
223  }
224}
225
226/* Perform formatted output to an output target 'o' */
227template <typename Out>
228static void out_vformat(Out& o, const char* format, va_list args) {
229    int nn = 0;
230
231    for (;;) {
232        int mm;
233        int padZero = 0;
234        int padLeft = 0;
235        char sign = '\0';
236        int width = -1;
237        int prec  = -1;
238        size_t bytelen = sizeof(int);
239        int slen;
240        char buffer[32];  /* temporary buffer used to format numbers */
241
242        char  c;
243
244        /* first, find all characters that are not 0 or '%' */
245        /* then send them to the output directly */
246        mm = nn;
247        do {
248            c = format[mm];
249            if (c == '\0' || c == '%')
250                break;
251            mm++;
252        } while (1);
253
254        if (mm > nn) {
255            o.Send(format+nn, mm-nn);
256            nn = mm;
257        }
258
259        /* is this it ? then exit */
260        if (c == '\0')
261            break;
262
263        /* nope, we are at a '%' modifier */
264        nn++;  // skip it
265
266        /* parse flags */
267        for (;;) {
268            c = format[nn++];
269            if (c == '\0') {  /* single trailing '%' ? */
270                c = '%';
271                o.Send(&c, 1);
272                return;
273            }
274            else if (c == '0') {
275                padZero = 1;
276                continue;
277            }
278            else if (c == '-') {
279                padLeft = 1;
280                continue;
281            }
282            else if (c == ' ' || c == '+') {
283                sign = c;
284                continue;
285            }
286            break;
287        }
288
289        /* parse field width */
290        if ((c >= '0' && c <= '9')) {
291            nn --;
292            width = (int)parse_decimal(format, &nn);
293            c = format[nn++];
294        }
295
296        /* parse precision */
297        if (c == '.') {
298            prec = (int)parse_decimal(format, &nn);
299            c = format[nn++];
300        }
301
302        /* length modifier */
303        switch (c) {
304        case 'h':
305            bytelen = sizeof(short);
306            if (format[nn] == 'h') {
307                bytelen = sizeof(char);
308                nn += 1;
309            }
310            c = format[nn++];
311            break;
312        case 'l':
313            bytelen = sizeof(long);
314            if (format[nn] == 'l') {
315                bytelen = sizeof(long long);
316                nn += 1;
317            }
318            c = format[nn++];
319            break;
320        case 'z':
321            bytelen = sizeof(size_t);
322            c = format[nn++];
323            break;
324        case 't':
325            bytelen = sizeof(ptrdiff_t);
326            c = format[nn++];
327            break;
328        default:
329            ;
330        }
331
332        /* conversion specifier */
333        const char* str = buffer;
334        if (c == 's') {
335            /* string */
336            str = va_arg(args, const char*);
337            if (str == NULL) {
338                str = "(null)";
339            }
340        } else if (c == 'c') {
341            /* character */
342            /* NOTE: char is promoted to int when passed through the stack */
343            buffer[0] = (char) va_arg(args, int);
344            buffer[1] = '\0';
345        } else if (c == 'p') {
346            uint64_t  value = (uintptr_t) va_arg(args, void*);
347            buffer[0] = '0';
348            buffer[1] = 'x';
349            format_integer(buffer + 2, sizeof(buffer) - 2, value, 'x');
350        } else if (c == 'd' || c == 'i' || c == 'o' || c == 'u' || c == 'x' || c == 'X') {
351            /* integers - first read value from stack */
352            uint64_t value;
353            int is_signed = (c == 'd' || c == 'i' || c == 'o');
354
355            /* NOTE: int8_t and int16_t are promoted to int when passed
356             *       through the stack
357             */
358            switch (bytelen) {
359            case 1: value = (uint8_t)  va_arg(args, int); break;
360            case 2: value = (uint16_t) va_arg(args, int); break;
361            case 4: value = va_arg(args, uint32_t); break;
362            case 8: value = va_arg(args, uint64_t); break;
363            default: return;  /* should not happen */
364            }
365
366            /* sign extension, if needed */
367            if (is_signed) {
368                int shift = 64 - 8*bytelen;
369                value = (uint64_t)(((int64_t)(value << shift)) >> shift);
370            }
371
372            /* format the number properly into our buffer */
373            format_integer(buffer, sizeof(buffer), value, c);
374        } else if (c == '%') {
375            buffer[0] = '%';
376            buffer[1] = '\0';
377        } else {
378            __assert(__FILE__, __LINE__, "conversion specifier unsupported");
379        }
380
381        /* if we are here, 'str' points to the content that must be
382         * outputted. handle padding and alignment now */
383
384        slen = strlen(str);
385
386        if (sign != '\0' || prec != -1) {
387            __assert(__FILE__, __LINE__, "sign/precision unsupported");
388        }
389
390        if (slen < width && !padLeft) {
391            char padChar = padZero ? '0' : ' ';
392            SendRepeat(o, padChar, width - slen);
393        }
394
395        o.Send(str, slen);
396
397        if (slen < width && padLeft) {
398            char padChar = padZero ? '0' : ' ';
399            SendRepeat(o, padChar, width - slen);
400        }
401    }
402}
403
404int __libc_format_buffer(char* buffer, size_t buffer_size, const char* format, ...) {
405  BufferOutputStream os(buffer, buffer_size);
406  va_list args;
407  va_start(args, format);
408  out_vformat(os, format, args);
409  va_end(args);
410  return os.total;
411}
412
413int __libc_format_fd(int fd, const char* format, ...) {
414  FdOutputStream os(fd);
415  va_list args;
416  va_start(args, format);
417  out_vformat(os, format, args);
418  va_end(args);
419  return os.total;
420}
421
422static int __libc_write_stderr(const char* tag, const char* msg) {
423  int fd = TEMP_FAILURE_RETRY(open("/dev/stderr", O_CLOEXEC | O_WRONLY));
424  if (fd == -1) {
425    return -1;
426  }
427
428  iovec vec[4];
429  vec[0].iov_base = const_cast<char*>(tag);
430  vec[0].iov_len = strlen(tag);
431  vec[1].iov_base = const_cast<char*>(": ");
432  vec[1].iov_len = 2;
433  vec[2].iov_base = const_cast<char*>(msg);
434  vec[2].iov_len = strlen(msg) + 1;
435  vec[3].iov_base = const_cast<char*>("\n");
436  vec[3].iov_len = 1;
437
438  int result = TEMP_FAILURE_RETRY(writev(fd, vec, 4));
439  close(fd);
440  return result;
441}
442
443static int __libc_write_log(int priority, const char* tag, const char* msg) {
444  int main_log_fd = TEMP_FAILURE_RETRY(open("/dev/log/main", O_CLOEXEC | O_WRONLY));
445  if (main_log_fd == -1) {
446    if (errno == ENOTDIR) {
447      // /dev/log isn't a directory? Maybe we're running on the host? Try stderr instead.
448      return __libc_write_stderr(tag, msg);
449    }
450    return -1;
451  }
452
453  iovec vec[3];
454  vec[0].iov_base = &priority;
455  vec[0].iov_len = 1;
456  vec[1].iov_base = const_cast<char*>(tag);
457  vec[1].iov_len = strlen(tag) + 1;
458  vec[2].iov_base = const_cast<char*>(msg);
459  vec[2].iov_len = strlen(msg) + 1;
460
461  int result = TEMP_FAILURE_RETRY(writev(main_log_fd, vec, 3));
462  close(main_log_fd);
463  return result;
464}
465
466int __libc_format_log_va_list(int priority, const char* tag, const char* format, va_list args) {
467  char buffer[1024];
468  BufferOutputStream os(buffer, sizeof(buffer));
469  out_vformat(os, format, args);
470  return __libc_write_log(priority, tag, buffer);
471}
472
473int __libc_format_log(int priority, const char* tag, const char* format, ...) {
474  va_list args;
475  va_start(args, format);
476  int result = __libc_format_log_va_list(priority, tag, format, args);
477  va_end(args);
478  return result;
479}
480
481static int __libc_android_log_event(int32_t tag, char type, const void* payload, size_t len) {
482  iovec vec[3];
483  vec[0].iov_base = &tag;
484  vec[0].iov_len = sizeof(tag);
485  vec[1].iov_base = &type;
486  vec[1].iov_len = sizeof(type);
487  vec[2].iov_base = const_cast<void*>(payload);
488  vec[2].iov_len = len;
489
490  int event_log_fd = TEMP_FAILURE_RETRY(open("/dev/log/events", O_CLOEXEC | O_WRONLY));
491  if (event_log_fd == -1) {
492    return -1;
493  }
494  int result = TEMP_FAILURE_RETRY(writev(event_log_fd, vec, 3));
495  close(event_log_fd);
496  return result;
497}
498
499void __libc_android_log_event_int(int32_t tag, int value) {
500  __libc_android_log_event(tag, EVENT_TYPE_INT, &value, sizeof(value));
501}
502
503void __libc_android_log_event_uid(int32_t tag) {
504  __libc_android_log_event_int(tag, getuid());
505}
506
507void __fortify_chk_fail(const char *msg, uint32_t tag) {
508  if (tag != 0) {
509    __libc_android_log_event_uid(tag);
510  }
511  __libc_fatal("FORTIFY_SOURCE: %s. Calling abort().", msg);
512}
513
514static void __libc_fatal(const char* format, va_list args) {
515  char msg[1024];
516  BufferOutputStream os(msg, sizeof(msg));
517  out_vformat(os, format, args);
518
519  // TODO: log to stderr for the benefit of "adb shell" users.
520
521  // Log to the log for the benefit of regular app developers (whose stdout and stderr are closed).
522  __libc_write_log(ANDROID_LOG_FATAL, "libc", msg);
523
524  __libc_set_abort_message(msg);
525}
526
527void __libc_fatal_no_abort(const char* format, ...) {
528  va_list args;
529  va_start(args, format);
530  __libc_fatal(format, args);
531  va_end(args);
532}
533
534void __libc_fatal(const char* format, ...) {
535  va_list args;
536  va_start(args, format);
537  __libc_fatal(format, args);
538  va_end(args);
539  abort();
540}
541
542void __libc_set_abort_message(const char* msg) {
543  size_t size = sizeof(abort_msg_t) + strlen(msg) + 1;
544  void* map = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
545  if (map == MAP_FAILED) {
546    return;
547  }
548
549  if (__abort_message_ptr != NULL) {
550    ScopedPthreadMutexLocker locker(&gAbortMsgLock);
551    if (*__abort_message_ptr != NULL) {
552      munmap(*__abort_message_ptr, (*__abort_message_ptr)->size);
553    }
554    abort_msg_t* new_abort_message = reinterpret_cast<abort_msg_t*>(map);
555    new_abort_message->size = size;
556    strcpy(new_abort_message->msg, msg);
557    *__abort_message_ptr = new_abort_message;
558  }
559}
560