/
logger_reader.go
170 lines (145 loc) · 4.58 KB
/
logger_reader.go
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
163
164
165
166
167
168
169
170
package alog
// #include <sys/ioctl.h>
// #define __LOGGERIO 0xAE
// #define LOGGER_SET_VERSION _IO(__LOGGERIO, 6) /* abi version */
//
// int RequestLoggerAbiV2(int fd)
// {
// static int version = 2;
// return ioctl(fd, LOGGER_SET_VERSION, &version);
// }
import "C"
import (
"bytes"
"encoding/binary"
"errors"
"io"
"io/ioutil"
"path/filepath"
"strings"
"syscall"
"time"
"github.com/npat-efault/poller"
)
const (
maxEntrySize = 5 * 1024 // Max size of a log entry when reading
)
// wire bundles all the fields available on the wire.
// Used for parsing a single entry received from the Android logging
// facilities.
type wire struct {
Len uint16
_ uint16
Pid int32
Tid int32
Sec int32
Nsec int32
}
// requestExtendedLoggerAbi issues an ioctl on fd to request AOSP Logger wire format v2.
//
// Returns an error if the ioctl on fd fails.
func requestExtendedLoggerAbi(fd int) error {
if rc := C.RequestLoggerAbiV2(C.int(fd)); rc != 0 {
return syscall.Errno(rc)
}
return nil
}
// A LoggerAbiExtension models additions to the logger v1 wire format defined by
// AOSP.
type LoggerAbiExtension interface {
// Read allows implementations to unmarshal addition fields from a reader
// into the buffer returned by a single read call to Android's logger facilities.
Read(reader io.Reader) (map[string]interface{}, error)
}
// A LoggerAbiV2Extension implements LoggerAbiExtension, reading the
// additional euid field.
type LoggerAbiV2Extension struct {
}
// Read unmarshals the additional euid field from reader, and return it in the extension map under key 'euid'.
//
// Returns an error if unmarshaling the euid field from reader fails.
func (self LoggerAbiV2Extension) Read(reader io.Reader) (map[string]interface{}, error) {
euid := uint32(0)
if err := binary.Read(reader, binary.LittleEndian, &euid); err != nil {
return nil, err
}
return map[string]interface{}{"euid": euid}, nil
}
// A LoggerReader connects to pre-Lollipop kernel logging facilities.
type LoggerReader struct {
abiExtension LoggerAbiExtension // ABI extension handler
f *poller.FD // The file we read entries from
buf []byte // Buffer for reading raw bytes from f
}
// NewLoggerReader returns a new LoggerReader reading from the log stream
// identified by id. If abiExtension is not nil it is used to parse additional fields from
// a buffer read from Android's logging facilities.
//
// Returns an error if accessing the underlying Android log facilities fails.
func NewLoggerReader(id LogId, abiExtension LoggerAbiExtension) (*LoggerReader, error) {
fn := filepath.Join("/dev", "alog", id.String())
p, err := poller.Open(fn, poller.O_RO)
if err != nil {
return nil, err
}
p.Lock()
defer p.Unlock()
if abiExtension != nil {
if err = requestExtendedLoggerAbi(p.Sysfd()); err != nil {
return nil, err
}
}
return &LoggerReader{abiExtension: abiExtension, f: p, buf: make([]byte, maxEntrySize, maxEntrySize)}, nil
}
// Close() closes the underlying connection to the Android logger facilities.
//
// Outstanding ReadNext operations are cancelled and return an error.
func (self *LoggerReader) Close() error {
return self.f.Close()
}
// SetDeadline adjusts the deadline for reading for a LoggerReader.
//
// Returns an error if an issue arises in talking to the underlying
// Android log facilities.
func (self *LoggerReader) SetDeadline(t time.Time) error {
return self.f.SetReadDeadline(t)
}
// ReadNext reads the next entry from a LaggerReader. Extension fields (if any)
// are placed into the Ext field of Entry.
//
// Returns an error if reading from the underlying Android facilities fails,
// specifically if the read operation times out.
func (self *LoggerReader) ReadNext() (*Entry, error) {
n, err := self.f.Read(self.buf)
if err != nil {
return nil, err
}
reader := bytes.NewReader(self.buf[:n])
w := wire{}
if err = binary.Read(reader, binary.LittleEndian, &w); err != nil {
return nil, err
}
var ext map[string]interface{}
if self.abiExtension != nil {
ext, err = self.abiExtension.Read(reader)
if err != nil {
return nil, err
}
}
if buf, err := ioutil.ReadAll(reader); err != nil {
return nil, err
} else if len(buf) > 3 { // We need at least a priority, and two \0.
tagEnd := bytes.IndexAny(buf[1:], "\x00")
return &Entry{
Pid: w.Pid,
Tid: w.Tid,
When: Timestamp{Seconds: w.Sec, Nanoseconds: w.Nsec},
Priority: Priority(buf[0]),
Tag: Tag(buf[1 : tagEnd+1]),
Message: strings.TrimSpace(string(buf[tagEnd+1 : w.Len-1])),
Ext: ext,
}, nil
} else {
return nil, errors.New("Invalid log entry")
}
}