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
5package runtime
6
7import "unsafe"
8
9var (
10	writeHeader = []byte{6 /* ANDROID_LOG_ERROR */, 'G', 'o', 0}
11	writePath   = []byte("/dev/log/main\x00")
12	writeLogd   = []byte("/dev/socket/logdw\x00")
13
14	// guarded by printlock/printunlock.
15	writeFD  uintptr
16	writeBuf [1024]byte
17	writePos int
18)
19
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/+/refs/tags/android-6.0.1_r78/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
36func writeErr(b []byte) {
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
48	// Write to stderr for command-line programs.
49	write(2, unsafe.Pointer(&b[0]), int32(len(b)))
50
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	//
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.
60	var hlen int
61	switch logger {
62	case logd:
63		hlen = writeLogdHeader()
64	case legacy:
65		hlen = len(writeHeader)
66	}
67
68	dst := writeBuf[hlen:]
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
77			write(writeFD, unsafe.Pointer(&writeBuf[0]), int32(hlen+writePos))
78			for i := range dst {
79				dst[i] = 0
80			}
81			writePos = 0
82		}
83	}
84}
85
86func initLegacy() {
87	// In legacy mode, logs are written to /dev/log/main
88	writeFD = uintptr(open(&writePath[0], 0x1 /* O_WRONLY */, 0))
89	if writeFD == 0 {
90		// It is hard to do anything here. Write to stderr just
91		// in case user has root on device and has run
92		//	adb shell setprop log.redirect-stdio true
93		msg := []byte("runtime: cannot open /dev/log/main\x00")
94		write(2, unsafe.Pointer(&msg[0]), int32(len(msg)))
95		exit(2)
96	}
97
98	// Prepopulate the invariant header part.
99	copy(writeBuf[:len(writeHeader)], writeHeader)
100}
101
102// used in initLogdWrite but defined here to avoid heap allocation.
103var logdAddr sockaddr_un
104
105func initLogd() {
106	// In logd mode, logs are sent to the logd via a unix domain socket.
107	logdAddr.family = _AF_UNIX
108	copy(logdAddr.path[:], writeLogd)
109
110	// We are not using non-blocking I/O because writes taking this path
111	// are most likely triggered by panic, we cannot think of the advantage of
112	// non-blocking I/O for panic but see disadvantage (dropping panic message),
113	// and blocking I/O simplifies the code a lot.
114	fd := socket(_AF_UNIX, _SOCK_DGRAM|_O_CLOEXEC, 0)
115	if fd < 0 {
116		msg := []byte("runtime: cannot create a socket for logging\x00")
117		write(2, unsafe.Pointer(&msg[0]), int32(len(msg)))
118		exit(2)
119	}
120
121	errno := connect(fd, unsafe.Pointer(&logdAddr), int32(unsafe.Sizeof(logdAddr)))
122	if errno < 0 {
123		msg := []byte("runtime: cannot connect to /dev/socket/logdw\x00")
124		write(2, unsafe.Pointer(&msg[0]), int32(len(msg)))
125		// TODO(hakim): or should we just close fd and hope for better luck next time?
126		exit(2)
127	}
128	writeFD = uintptr(fd)
129
130	// Prepopulate invariant part of the header.
131	// The first 11 bytes will be populated later in writeLogdHeader.
132	copy(writeBuf[11:11+len(writeHeader)], writeHeader)
133}
134
135// writeLogdHeader populates the header and returns the length of the payload.
136func writeLogdHeader() int {
137	hdr := writeBuf[:11]
138
139	// The first 11 bytes of the header corresponds to android_log_header_t
140	// as defined in system/core/include/private/android_logger.h
141	//   hdr[0] log type id (unsigned char), defined in <log/log.h>
142	//   hdr[1:2] tid (uint16_t)
143	//   hdr[3:11] log_time defined in <log/log_read.h>
144	//      hdr[3:7] sec unsigned uint32, little endian.
145	//      hdr[7:11] nsec unsigned uint32, little endian.
146	hdr[0] = 0 // LOG_ID_MAIN
147	sec, nsec := walltime()
148	packUint32(hdr[3:7], uint32(sec))
149	packUint32(hdr[7:11], uint32(nsec))
150
151	// TODO(hakim):  hdr[1:2] = gettid?
152
153	return 11 + len(writeHeader)
154}
155
156func packUint32(b []byte, v uint32) {
157	// little-endian.
158	b[0] = byte(v)
159	b[1] = byte(v >> 8)
160	b[2] = byte(v >> 16)
161	b[3] = byte(v >> 24)
162}
163