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