Ian Lance Taylor | 1d0c779 | 2015-01-09 11:31:23 -0800 | [diff] [blame] | 1 | // Copyright 2014 The Go Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style |
| 3 | // license that can be found in the LICENSE file. |
| 4 | |
David Crawshaw | bee8ae1 | 2014-12-10 09:29:24 -0500 | [diff] [blame] | 5 | package runtime |
| 6 | |
| 7 | import "unsafe" |
| 8 | |
| 9 | var ( |
| 10 | writeHeader = []byte{6 /* ANDROID_LOG_ERROR */, 'G', 'o', 0} |
| 11 | writePath = []byte("/dev/log/main\x00") |
Hyang-Ah Hana Kim | 3a87135 | 2015-01-24 17:51:42 -0500 | [diff] [blame] | 12 | writeLogd = []byte("/dev/socket/logdw\x00") |
| 13 | |
| 14 | // guarded by printlock/printunlock. |
| 15 | writeFD uintptr |
| 16 | writeBuf [1024]byte |
| 17 | writePos int |
David Crawshaw | bee8ae1 | 2014-12-10 09:29:24 -0500 | [diff] [blame] | 18 | ) |
| 19 | |
Hyang-Ah Hana Kim | 3a87135 | 2015-01-24 17:51:42 -0500 | [diff] [blame] | 20 | // Prior to Android-L, logging was done through writes to /dev/log files implemented |
| 21 | // in kernel ring buffers. In Android-L, those /dev/log files are no longer |
| 22 | // accessible and logging is done through a centralized user-mode logger, logd. |
| 23 | // |
| 24 | // https://android.googlesource.com/platform/system/core/+/master/liblog/logd_write.c |
| 25 | type loggerType int32 |
| 26 | |
| 27 | const ( |
| 28 | unknown loggerType = iota |
| 29 | legacy |
| 30 | logd |
| 31 | // TODO(hakim): logging for emulator? |
| 32 | ) |
| 33 | |
| 34 | var logger loggerType |
| 35 | |
David Crawshaw | bee8ae1 | 2014-12-10 09:29:24 -0500 | [diff] [blame] | 36 | func writeErr(b []byte) { |
Hyang-Ah Hana Kim | 3a87135 | 2015-01-24 17:51:42 -0500 | [diff] [blame] | 37 | if logger == unknown { |
| 38 | // Use logd if /dev/socket/logdw is available. |
| 39 | if v := uintptr(access(&writeLogd[0], 0x02 /* W_OK */)); v == 0 { |
| 40 | logger = logd |
| 41 | initLogd() |
| 42 | } else { |
| 43 | logger = legacy |
| 44 | initLegacy() |
| 45 | } |
| 46 | } |
| 47 | |
David Crawshaw | 84e200c | 2015-02-20 13:15:56 -0500 | [diff] [blame] | 48 | // Write to stderr for command-line programs. |
| 49 | write(2, unsafe.Pointer(&b[0]), int32(len(b))) |
| 50 | |
Hyang-Ah Hana Kim | 3a87135 | 2015-01-24 17:51:42 -0500 | [diff] [blame] | 51 | // Log format: "<header>\x00<message m bytes>\x00" |
| 52 | // |
| 53 | // <header> |
| 54 | // In legacy mode: "<priority 1 byte><tag n bytes>". |
| 55 | // In logd mode: "<android_log_header_t 11 bytes><priority 1 byte><tag n bytes>" |
| 56 | // |
David Crawshaw | bee8ae1 | 2014-12-10 09:29:24 -0500 | [diff] [blame] | 57 | // The entire log needs to be delivered in a single syscall (the NDK |
| 58 | // does this with writev). Each log is its own line, so we need to |
| 59 | // buffer writes until we see a newline. |
Hyang-Ah Hana Kim | 3a87135 | 2015-01-24 17:51:42 -0500 | [diff] [blame] | 60 | var hlen int |
| 61 | switch logger { |
| 62 | case logd: |
| 63 | hlen = writeLogdHeader() |
| 64 | case legacy: |
| 65 | hlen = len(writeHeader) |
David Crawshaw | bee8ae1 | 2014-12-10 09:29:24 -0500 | [diff] [blame] | 66 | } |
Hyang-Ah Hana Kim | 3a87135 | 2015-01-24 17:51:42 -0500 | [diff] [blame] | 67 | |
| 68 | dst := writeBuf[hlen:] |
David Crawshaw | bee8ae1 | 2014-12-10 09:29:24 -0500 | [diff] [blame] | 69 | for _, v := range b { |
| 70 | if v == 0 { // android logging won't print a zero byte |
| 71 | v = '0' |
| 72 | } |
| 73 | dst[writePos] = v |
| 74 | writePos++ |
| 75 | if v == '\n' || writePos == len(dst)-1 { |
| 76 | dst[writePos] = 0 |
Hyang-Ah Hana Kim | 3a87135 | 2015-01-24 17:51:42 -0500 | [diff] [blame] | 77 | write(writeFD, unsafe.Pointer(&writeBuf[0]), int32(hlen+writePos)) |
David Crawshaw | bee8ae1 | 2014-12-10 09:29:24 -0500 | [diff] [blame] | 78 | memclrBytes(dst) |
| 79 | writePos = 0 |
| 80 | } |
| 81 | } |
| 82 | } |
Hyang-Ah Hana Kim | 3a87135 | 2015-01-24 17:51:42 -0500 | [diff] [blame] | 83 | |
| 84 | func initLegacy() { |
| 85 | // In legacy mode, logs are written to /dev/log/main |
| 86 | writeFD = uintptr(open(&writePath[0], 0x1 /* O_WRONLY */, 0)) |
| 87 | if writeFD == 0 { |
| 88 | // It is hard to do anything here. Write to stderr just |
| 89 | // in case user has root on device and has run |
| 90 | // adb shell setprop log.redirect-stdio true |
| 91 | msg := []byte("runtime: cannot open /dev/log/main\x00") |
| 92 | write(2, unsafe.Pointer(&msg[0]), int32(len(msg))) |
| 93 | exit(2) |
| 94 | } |
| 95 | |
| 96 | // Prepopulate the invariant header part. |
| 97 | copy(writeBuf[:len(writeHeader)], writeHeader) |
| 98 | } |
| 99 | |
| 100 | // used in initLogdWrite but defined here to avoid heap allocation. |
| 101 | var logdAddr sockaddr_un |
| 102 | |
| 103 | func initLogd() { |
| 104 | // In logd mode, logs are sent to the logd via a unix domain socket. |
| 105 | logdAddr.family = _AF_UNIX |
| 106 | copy(logdAddr.path[:], writeLogd) |
| 107 | |
| 108 | // We are not using non-blocking I/O because writes taking this path |
| 109 | // are most likely triggered by panic, we cannot think of the advantage of |
| 110 | // non-blocking I/O for panic but see disadvantage (dropping panic message), |
| 111 | // and blocking I/O simplifies the code a lot. |
| 112 | fd := socket(_AF_UNIX, _SOCK_DGRAM|_O_CLOEXEC, 0) |
| 113 | if fd < 0 { |
| 114 | msg := []byte("runtime: cannot create a socket for logging\x00") |
| 115 | write(2, unsafe.Pointer(&msg[0]), int32(len(msg))) |
| 116 | exit(2) |
| 117 | } |
| 118 | |
| 119 | errno := connect(uintptr(fd), unsafe.Pointer(&logdAddr), int32(unsafe.Sizeof(logdAddr))) |
| 120 | if errno < 0 { |
| 121 | msg := []byte("runtime: cannot connect to /dev/socket/logdw\x00") |
| 122 | write(2, unsafe.Pointer(&msg[0]), int32(len(msg))) |
| 123 | // TODO(hakim): or should we just close fd and hope for better luck next time? |
| 124 | exit(2) |
| 125 | } |
| 126 | writeFD = uintptr(fd) |
| 127 | |
| 128 | // Prepopulate invariant part of the header. |
| 129 | // The first 11 bytes will be populated later in writeLogdHeader. |
| 130 | copy(writeBuf[11:11+len(writeHeader)], writeHeader) |
| 131 | } |
| 132 | |
| 133 | // writeLogdHeader populates the header and returns the length of the payload. |
| 134 | func writeLogdHeader() int { |
| 135 | hdr := writeBuf[:11] |
| 136 | |
| 137 | // The first 11 bytes of the header corresponds to android_log_header_t |
| 138 | // as defined in system/core/include/private/android_logger.h |
| 139 | // hdr[0] log type id (unsigned char), defined in <log/log.h> |
| 140 | // hdr[1:2] tid (uint16_t) |
| 141 | // hdr[3:11] log_time defined in <log/log_read.h> |
| 142 | // hdr[3:7] sec unsigned uint32, little endian. |
| 143 | // hdr[7:11] nsec unsigned uint32, little endian. |
| 144 | hdr[0] = 0 // LOG_ID_MAIN |
| 145 | sec, nsec := time_now() |
| 146 | packUint32(hdr[3:7], uint32(sec)) |
| 147 | packUint32(hdr[7:11], uint32(nsec)) |
| 148 | |
| 149 | // TODO(hakim): hdr[1:2] = gettid? |
| 150 | |
| 151 | return 11 + len(writeHeader) |
| 152 | } |
| 153 | |
| 154 | func packUint32(b []byte, v uint32) { |
| 155 | // little-endian. |
| 156 | b[0] = byte(v) |
| 157 | b[1] = byte(v >> 8) |
| 158 | b[2] = byte(v >> 16) |
| 159 | b[3] = byte(v >> 24) |
| 160 | } |