blob: 54d4826375e7114c73c123ebdd9fae1ad628f3d8 [file] [log] [blame]
Ian Lance Taylor1d0c7792015-01-09 11:31:23 -08001// 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 Crawshawbee8ae12014-12-10 09:29:24 -05005package runtime
6
7import "unsafe"
8
9var (
10 writeHeader = []byte{6 /* ANDROID_LOG_ERROR */, 'G', 'o', 0}
11 writePath = []byte("/dev/log/main\x00")
Hyang-Ah Hana Kim3a871352015-01-24 17:51:42 -050012 writeLogd = []byte("/dev/socket/logdw\x00")
13
14 // guarded by printlock/printunlock.
15 writeFD uintptr
16 writeBuf [1024]byte
17 writePos int
David Crawshawbee8ae12014-12-10 09:29:24 -050018)
19
Hyang-Ah Hana Kim3a871352015-01-24 17:51:42 -050020// 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
25type loggerType int32
26
27const (
28 unknown loggerType = iota
29 legacy
30 logd
31 // TODO(hakim): logging for emulator?
32)
33
34var logger loggerType
35
David Crawshawbee8ae12014-12-10 09:29:24 -050036func writeErr(b []byte) {
Hyang-Ah Hana Kim3a871352015-01-24 17:51:42 -050037 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 Crawshaw84e200c2015-02-20 13:15:56 -050048 // Write to stderr for command-line programs.
49 write(2, unsafe.Pointer(&b[0]), int32(len(b)))
50
Hyang-Ah Hana Kim3a871352015-01-24 17:51:42 -050051 // 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 Crawshawbee8ae12014-12-10 09:29:24 -050057 // 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 Kim3a871352015-01-24 17:51:42 -050060 var hlen int
61 switch logger {
62 case logd:
63 hlen = writeLogdHeader()
64 case legacy:
65 hlen = len(writeHeader)
David Crawshawbee8ae12014-12-10 09:29:24 -050066 }
Hyang-Ah Hana Kim3a871352015-01-24 17:51:42 -050067
68 dst := writeBuf[hlen:]
David Crawshawbee8ae12014-12-10 09:29:24 -050069 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 Kim3a871352015-01-24 17:51:42 -050077 write(writeFD, unsafe.Pointer(&writeBuf[0]), int32(hlen+writePos))
David Crawshawbee8ae12014-12-10 09:29:24 -050078 memclrBytes(dst)
79 writePos = 0
80 }
81 }
82}
Hyang-Ah Hana Kim3a871352015-01-24 17:51:42 -050083
84func 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.
101var logdAddr sockaddr_un
102
103func 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.
134func 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
154func 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}