blob: dd45cd0b6b68d3abd823e4581a2f88f40ea6894b [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.util;
18
Dan Egnor62136d32010-01-05 19:09:08 -080019import java.io.BufferedReader;
20import java.io.FileReader;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080021import java.io.IOException;
22import java.io.UnsupportedEncodingException;
Dan Egnor62136d32010-01-05 19:09:08 -080023import java.nio.BufferUnderflowException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080024import java.nio.ByteBuffer;
25import java.nio.ByteOrder;
Rubin Xu75431fb2016-01-07 21:12:14 +000026import java.util.Arrays;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080027import java.util.Collection;
Dan Egnor62136d32010-01-05 19:09:08 -080028import java.util.HashMap;
29import java.util.regex.Matcher;
30import java.util.regex.Pattern;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080031
32/**
Dan Egnor62136d32010-01-05 19:09:08 -080033 * Access to the system diagnostic event record. System diagnostic events are
34 * used to record certain system-level events (such as garbage collection,
35 * activity manager state, system watchdogs, and other low level activity),
36 * which may be automatically collected and analyzed during system development.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080037 *
Dan Egnor62136d32010-01-05 19:09:08 -080038 * <p>This is <b>not</b> the main "logcat" debugging log ({@link android.util.Log})!
39 * These diagnostic events are for system integrators, not application authors.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080040 *
Dan Egnor62136d32010-01-05 19:09:08 -080041 * <p>Events use integer tag codes corresponding to /system/etc/event-log-tags.
42 * They carry a payload of one or more int, long, or String values. The
43 * event-log-tags file defines the payload contents for each type code.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080044 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080045public class EventLog {
Jesse Wilsona0f8bc52011-02-24 10:44:33 -080046 /** @hide */ public EventLog() {}
47
Dan Egnor62136d32010-01-05 19:09:08 -080048 private static final String TAG = "EventLog";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049
Dan Egnor62136d32010-01-05 19:09:08 -080050 private static final String TAGS_FILE = "/system/etc/event-log-tags";
51 private static final String COMMENT_PATTERN = "^\\s*(#.*)?$";
52 private static final String TAG_PATTERN = "^\\s*(\\d+)\\s+(\\w+)\\s*(\\(.*\\))?\\s*$";
53 private static HashMap<String, Integer> sTagCodes = null;
54 private static HashMap<Integer, String> sTagNames = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080055
Neil Fullerbf0dc0f2015-11-24 18:19:06 +000056 /** A previously logged event read from the logs. Instances are thread safe. */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080057 public static final class Event {
58 private final ByteBuffer mBuffer;
59
Mark Salyzyn747802f2014-04-25 13:25:18 -070060 // Layout of event log entry received from Android logger.
Mark Salyzyn52eb4e02016-09-28 16:15:30 -070061 // see system/core/include/log/log.h
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080062 private static final int LENGTH_OFFSET = 0;
Mark Salyzyn747802f2014-04-25 13:25:18 -070063 private static final int HEADER_SIZE_OFFSET = 2;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080064 private static final int PROCESS_OFFSET = 4;
65 private static final int THREAD_OFFSET = 8;
66 private static final int SECONDS_OFFSET = 12;
67 private static final int NANOSECONDS_OFFSET = 16;
68
Mark Salyzyn747802f2014-04-25 13:25:18 -070069 // Layout for event log v1 format, v2 and v3 use HEADER_SIZE_OFFSET
70 private static final int V1_PAYLOAD_START = 20;
71 private static final int DATA_OFFSET = 4;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080072
Dan Egnor62136d32010-01-05 19:09:08 -080073 // Value types
74 private static final byte INT_TYPE = 0;
75 private static final byte LONG_TYPE = 1;
76 private static final byte STRING_TYPE = 2;
77 private static final byte LIST_TYPE = 3;
Jeff Browne7e9cce2015-04-28 13:26:48 -070078 private static final byte FLOAT_TYPE = 4;
Dan Egnor62136d32010-01-05 19:09:08 -080079
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080080 /** @param data containing event, read from the system */
Dan Egnor62136d32010-01-05 19:09:08 -080081 /*package*/ Event(byte[] data) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080082 mBuffer = ByteBuffer.wrap(data);
83 mBuffer.order(ByteOrder.nativeOrder());
84 }
85
Dan Egnor62136d32010-01-05 19:09:08 -080086 /** @return the process ID which wrote the log entry */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080087 public int getProcessId() {
88 return mBuffer.getInt(PROCESS_OFFSET);
89 }
90
Dan Egnor62136d32010-01-05 19:09:08 -080091 /** @return the thread ID which wrote the log entry */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080092 public int getThreadId() {
93 return mBuffer.getInt(THREAD_OFFSET);
94 }
95
Dan Egnor62136d32010-01-05 19:09:08 -080096 /** @return the wall clock time when the entry was written */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080097 public long getTimeNanos() {
98 return mBuffer.getInt(SECONDS_OFFSET) * 1000000000l
99 + mBuffer.getInt(NANOSECONDS_OFFSET);
100 }
101
Dan Egnor62136d32010-01-05 19:09:08 -0800102 /** @return the type tag code of the entry */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800103 public int getTag() {
Mark Salyzyn747802f2014-04-25 13:25:18 -0700104 int offset = mBuffer.getShort(HEADER_SIZE_OFFSET);
105 if (offset == 0) {
106 offset = V1_PAYLOAD_START;
107 }
108 return mBuffer.getInt(offset);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800109 }
110
Jeff Browne7e9cce2015-04-28 13:26:48 -0700111 /** @return one of Integer, Long, Float, String, null, or Object[] of same. */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800112 public synchronized Object getData() {
Dan Egnor62136d32010-01-05 19:09:08 -0800113 try {
Mark Salyzyn747802f2014-04-25 13:25:18 -0700114 int offset = mBuffer.getShort(HEADER_SIZE_OFFSET);
115 if (offset == 0) {
116 offset = V1_PAYLOAD_START;
117 }
118 mBuffer.limit(offset + mBuffer.getShort(LENGTH_OFFSET));
119 mBuffer.position(offset + DATA_OFFSET); // Just after the tag.
Dan Egnor62136d32010-01-05 19:09:08 -0800120 return decodeObject();
121 } catch (IllegalArgumentException e) {
122 Log.wtf(TAG, "Illegal entry payload: tag=" + getTag(), e);
123 return null;
124 } catch (BufferUnderflowException e) {
125 Log.wtf(TAG, "Truncated entry payload: tag=" + getTag(), e);
126 return null;
127 }
Jim Miller30636872009-06-08 18:59:49 -0700128 }
129
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800130 /** @return the loggable item at the current position in mBuffer. */
131 private Object decodeObject() {
Dan Egnor62136d32010-01-05 19:09:08 -0800132 byte type = mBuffer.get();
133 switch (type) {
134 case INT_TYPE:
Jeff Browne7e9cce2015-04-28 13:26:48 -0700135 return mBuffer.getInt();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800136
Dan Egnor62136d32010-01-05 19:09:08 -0800137 case LONG_TYPE:
Jeff Browne7e9cce2015-04-28 13:26:48 -0700138 return mBuffer.getLong();
139
140 case FLOAT_TYPE:
141 return mBuffer.getFloat();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800142
Dan Egnor62136d32010-01-05 19:09:08 -0800143 case STRING_TYPE:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800144 try {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800145 int length = mBuffer.getInt();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800146 int start = mBuffer.position();
147 mBuffer.position(start + length);
148 return new String(mBuffer.array(), start, length, "UTF-8");
149 } catch (UnsupportedEncodingException e) {
Dan Egnor62136d32010-01-05 19:09:08 -0800150 Log.wtf(TAG, "UTF-8 is not supported", e);
151 return null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800152 }
153
Dan Egnor62136d32010-01-05 19:09:08 -0800154 case LIST_TYPE:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800155 int length = mBuffer.get();
Dan Egnor69160892010-01-06 23:12:57 -0800156 if (length < 0) length += 256; // treat as signed byte
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800157 Object[] array = new Object[length];
Dan Egnor62136d32010-01-05 19:09:08 -0800158 for (int i = 0; i < length; ++i) array[i] = decodeObject();
159 return array;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800160
161 default:
Dan Egnor62136d32010-01-05 19:09:08 -0800162 throw new IllegalArgumentException("Unknown entry type: " + type);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800163 }
164 }
Rubin Xu75431fb2016-01-07 21:12:14 +0000165
166 /** @hide */
167 public static Event fromBytes(byte[] data) {
168 return new Event(data);
169 }
170
171 /** @hide */
172 public byte[] getBytes() {
173 byte[] bytes = mBuffer.array();
174 return Arrays.copyOf(bytes, bytes.length);
175 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800176 }
177
178 // We assume that the native methods deal with any concurrency issues.
179
180 /**
Dan Egnor62136d32010-01-05 19:09:08 -0800181 * Record an event log message.
182 * @param tag The event type tag code
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800183 * @param value A value to log
184 * @return The number of bytes written
185 */
186 public static native int writeEvent(int tag, int value);
187
188 /**
Dan Egnor62136d32010-01-05 19:09:08 -0800189 * Record an event log message.
190 * @param tag The event type tag code
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800191 * @param value A value to log
192 * @return The number of bytes written
193 */
194 public static native int writeEvent(int tag, long value);
195
196 /**
Dan Egnor62136d32010-01-05 19:09:08 -0800197 * Record an event log message.
198 * @param tag The event type tag code
Jeff Browne7e9cce2015-04-28 13:26:48 -0700199 * @param value A value to log
200 * @return The number of bytes written
201 */
202 public static native int writeEvent(int tag, float value);
203
204 /**
205 * Record an event log message.
206 * @param tag The event type tag code
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800207 * @param str A value to log
208 * @return The number of bytes written
209 */
210 public static native int writeEvent(int tag, String str);
211
212 /**
Dan Egnor62136d32010-01-05 19:09:08 -0800213 * Record an event log message.
214 * @param tag The event type tag code
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800215 * @param list A list of values to log
216 * @return The number of bytes written
217 */
Dan Egnor62136d32010-01-05 19:09:08 -0800218 public static native int writeEvent(int tag, Object... list);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800219
220 /**
221 * Read events from the log, filtered by type.
222 * @param tags to search for
223 * @param output container to add events into
224 * @throws IOException if something goes wrong reading events
225 */
226 public static native void readEvents(int[] tags, Collection<Event> output)
227 throws IOException;
Jim Miller95ff2402009-07-01 18:03:41 -0700228
229 /**
Dan Egnor62136d32010-01-05 19:09:08 -0800230 * Get the name associated with an event type tag code.
231 * @param tag code to look up
232 * @return the name of the tag, or null if no tag has that number
Jim Miller95ff2402009-07-01 18:03:41 -0700233 */
Dan Egnor62136d32010-01-05 19:09:08 -0800234 public static String getTagName(int tag) {
235 readTagsFile();
236 return sTagNames.get(tag);
237 }
238
239 /**
240 * Get the event type tag code associated with an event name.
241 * @param name of event to look up
242 * @return the tag code, or -1 if no tag has that name
243 */
244 public static int getTagCode(String name) {
245 readTagsFile();
246 Integer code = sTagCodes.get(name);
247 return code != null ? code : -1;
248 }
249
250 /**
251 * Read TAGS_FILE, populating sTagCodes and sTagNames, if not already done.
252 */
253 private static synchronized void readTagsFile() {
254 if (sTagCodes != null && sTagNames != null) return;
255
256 sTagCodes = new HashMap<String, Integer>();
257 sTagNames = new HashMap<Integer, String>();
258
259 Pattern comment = Pattern.compile(COMMENT_PATTERN);
260 Pattern tag = Pattern.compile(TAG_PATTERN);
261 BufferedReader reader = null;
262 String line;
263
264 try {
265 reader = new BufferedReader(new FileReader(TAGS_FILE), 256);
266 while ((line = reader.readLine()) != null) {
267 if (comment.matcher(line).matches()) continue;
268
269 Matcher m = tag.matcher(line);
270 if (!m.matches()) {
271 Log.wtf(TAG, "Bad entry in " + TAGS_FILE + ": " + line);
272 continue;
273 }
274
275 try {
276 int num = Integer.parseInt(m.group(1));
277 String name = m.group(2);
278 sTagCodes.put(name, num);
279 sTagNames.put(num, name);
280 } catch (NumberFormatException e) {
281 Log.wtf(TAG, "Error in " + TAGS_FILE + ": " + line, e);
282 }
283 }
284 } catch (IOException e) {
285 Log.wtf(TAG, "Error reading " + TAGS_FILE, e);
286 // Leave the maps existing but unpopulated
287 } finally {
288 try { if (reader != null) reader.close(); } catch (IOException e) {}
289 }
290 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800291}