blob: 99f6c2a9c58d3666a2396960a833539a7b5fddfc [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;
Chris Wrend9f3d9e2016-12-16 14:08:34 -050059 private Exception mLastWtf;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080060
Mark Salyzyn747802f2014-04-25 13:25:18 -070061 // Layout of event log entry received from Android logger.
62 // see system/core/include/log/logger.h
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080063 private static final int LENGTH_OFFSET = 0;
Mark Salyzyn747802f2014-04-25 13:25:18 -070064 private static final int HEADER_SIZE_OFFSET = 2;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080065 private static final int PROCESS_OFFSET = 4;
66 private static final int THREAD_OFFSET = 8;
67 private static final int SECONDS_OFFSET = 12;
68 private static final int NANOSECONDS_OFFSET = 16;
69
Mark Salyzyn747802f2014-04-25 13:25:18 -070070 // Layout for event log v1 format, v2 and v3 use HEADER_SIZE_OFFSET
71 private static final int V1_PAYLOAD_START = 20;
72 private static final int DATA_OFFSET = 4;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080073
Dan Egnor62136d32010-01-05 19:09:08 -080074 // Value types
75 private static final byte INT_TYPE = 0;
76 private static final byte LONG_TYPE = 1;
77 private static final byte STRING_TYPE = 2;
78 private static final byte LIST_TYPE = 3;
Jeff Browne7e9cce2015-04-28 13:26:48 -070079 private static final byte FLOAT_TYPE = 4;
Dan Egnor62136d32010-01-05 19:09:08 -080080
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080081 /** @param data containing event, read from the system */
Dan Egnor62136d32010-01-05 19:09:08 -080082 /*package*/ Event(byte[] data) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080083 mBuffer = ByteBuffer.wrap(data);
84 mBuffer.order(ByteOrder.nativeOrder());
85 }
86
Dan Egnor62136d32010-01-05 19:09:08 -080087 /** @return the process ID which wrote the log entry */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080088 public int getProcessId() {
89 return mBuffer.getInt(PROCESS_OFFSET);
90 }
91
Dan Egnor62136d32010-01-05 19:09:08 -080092 /** @return the thread ID which wrote the log entry */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080093 public int getThreadId() {
94 return mBuffer.getInt(THREAD_OFFSET);
95 }
96
Dan Egnor62136d32010-01-05 19:09:08 -080097 /** @return the wall clock time when the entry was written */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080098 public long getTimeNanos() {
99 return mBuffer.getInt(SECONDS_OFFSET) * 1000000000l
100 + mBuffer.getInt(NANOSECONDS_OFFSET);
101 }
102
Dan Egnor62136d32010-01-05 19:09:08 -0800103 /** @return the type tag code of the entry */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800104 public int getTag() {
Mark Salyzyn747802f2014-04-25 13:25:18 -0700105 int offset = mBuffer.getShort(HEADER_SIZE_OFFSET);
106 if (offset == 0) {
107 offset = V1_PAYLOAD_START;
108 }
109 return mBuffer.getInt(offset);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800110 }
111
Jeff Browne7e9cce2015-04-28 13:26:48 -0700112 /** @return one of Integer, Long, Float, String, null, or Object[] of same. */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800113 public synchronized Object getData() {
Dan Egnor62136d32010-01-05 19:09:08 -0800114 try {
Mark Salyzyn747802f2014-04-25 13:25:18 -0700115 int offset = mBuffer.getShort(HEADER_SIZE_OFFSET);
116 if (offset == 0) {
117 offset = V1_PAYLOAD_START;
118 }
119 mBuffer.limit(offset + mBuffer.getShort(LENGTH_OFFSET));
Chris Wrend9f3d9e2016-12-16 14:08:34 -0500120 if ((offset + DATA_OFFSET) >= mBuffer.limit()) {
121 // no payload
122 return null;
123 }
Mark Salyzyn747802f2014-04-25 13:25:18 -0700124 mBuffer.position(offset + DATA_OFFSET); // Just after the tag.
Dan Egnor62136d32010-01-05 19:09:08 -0800125 return decodeObject();
126 } catch (IllegalArgumentException e) {
127 Log.wtf(TAG, "Illegal entry payload: tag=" + getTag(), e);
Chris Wrend9f3d9e2016-12-16 14:08:34 -0500128 mLastWtf = e;
Dan Egnor62136d32010-01-05 19:09:08 -0800129 return null;
130 } catch (BufferUnderflowException e) {
131 Log.wtf(TAG, "Truncated entry payload: tag=" + getTag(), e);
Chris Wrend9f3d9e2016-12-16 14:08:34 -0500132 mLastWtf = e;
Dan Egnor62136d32010-01-05 19:09:08 -0800133 return null;
134 }
Jim Miller30636872009-06-08 18:59:49 -0700135 }
136
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800137 /** @return the loggable item at the current position in mBuffer. */
138 private Object decodeObject() {
Dan Egnor62136d32010-01-05 19:09:08 -0800139 byte type = mBuffer.get();
140 switch (type) {
141 case INT_TYPE:
Jeff Browne7e9cce2015-04-28 13:26:48 -0700142 return mBuffer.getInt();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800143
Dan Egnor62136d32010-01-05 19:09:08 -0800144 case LONG_TYPE:
Jeff Browne7e9cce2015-04-28 13:26:48 -0700145 return mBuffer.getLong();
146
147 case FLOAT_TYPE:
148 return mBuffer.getFloat();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800149
Dan Egnor62136d32010-01-05 19:09:08 -0800150 case STRING_TYPE:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800151 try {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800152 int length = mBuffer.getInt();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800153 int start = mBuffer.position();
154 mBuffer.position(start + length);
155 return new String(mBuffer.array(), start, length, "UTF-8");
156 } catch (UnsupportedEncodingException e) {
Dan Egnor62136d32010-01-05 19:09:08 -0800157 Log.wtf(TAG, "UTF-8 is not supported", e);
Chris Wrend9f3d9e2016-12-16 14:08:34 -0500158 mLastWtf = e;
Dan Egnor62136d32010-01-05 19:09:08 -0800159 return null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800160 }
161
Dan Egnor62136d32010-01-05 19:09:08 -0800162 case LIST_TYPE:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800163 int length = mBuffer.get();
Dan Egnor69160892010-01-06 23:12:57 -0800164 if (length < 0) length += 256; // treat as signed byte
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800165 Object[] array = new Object[length];
Dan Egnor62136d32010-01-05 19:09:08 -0800166 for (int i = 0; i < length; ++i) array[i] = decodeObject();
167 return array;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800168
169 default:
Dan Egnor62136d32010-01-05 19:09:08 -0800170 throw new IllegalArgumentException("Unknown entry type: " + type);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800171 }
172 }
Rubin Xu75431fb2016-01-07 21:12:14 +0000173
174 /** @hide */
175 public static Event fromBytes(byte[] data) {
176 return new Event(data);
177 }
178
179 /** @hide */
180 public byte[] getBytes() {
181 byte[] bytes = mBuffer.array();
182 return Arrays.copyOf(bytes, bytes.length);
183 }
Chris Wrend9f3d9e2016-12-16 14:08:34 -0500184
185 /**
186 * Retreive the last WTF error generated by this object.
187 * @hide
188 */
189 //VisibleForTesting
190 public Exception getLastError() {
191 return mLastWtf;
192 }
193
194 /**
195 * Clear the error state for this object.
196 * @hide
197 */
198 //VisibleForTesting
199 public void clearError() {
200 mLastWtf = null;
201 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800202 }
203
204 // We assume that the native methods deal with any concurrency issues.
205
206 /**
Dan Egnor62136d32010-01-05 19:09:08 -0800207 * Record an event log message.
208 * @param tag The event type tag code
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800209 * @param value A value to log
210 * @return The number of bytes written
211 */
212 public static native int writeEvent(int tag, int value);
213
214 /**
Dan Egnor62136d32010-01-05 19:09:08 -0800215 * Record an event log message.
216 * @param tag The event type tag code
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800217 * @param value A value to log
218 * @return The number of bytes written
219 */
220 public static native int writeEvent(int tag, long value);
221
222 /**
Dan Egnor62136d32010-01-05 19:09:08 -0800223 * Record an event log message.
224 * @param tag The event type tag code
Jeff Browne7e9cce2015-04-28 13:26:48 -0700225 * @param value A value to log
226 * @return The number of bytes written
227 */
228 public static native int writeEvent(int tag, float value);
229
230 /**
231 * Record an event log message.
232 * @param tag The event type tag code
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800233 * @param str A value to log
234 * @return The number of bytes written
235 */
236 public static native int writeEvent(int tag, String str);
237
238 /**
Dan Egnor62136d32010-01-05 19:09:08 -0800239 * Record an event log message.
240 * @param tag The event type tag code
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800241 * @param list A list of values to log
242 * @return The number of bytes written
243 */
Dan Egnor62136d32010-01-05 19:09:08 -0800244 public static native int writeEvent(int tag, Object... list);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800245
246 /**
247 * Read events from the log, filtered by type.
248 * @param tags to search for
249 * @param output container to add events into
250 * @throws IOException if something goes wrong reading events
251 */
252 public static native void readEvents(int[] tags, Collection<Event> output)
253 throws IOException;
Jim Miller95ff2402009-07-01 18:03:41 -0700254
255 /**
Dan Egnor62136d32010-01-05 19:09:08 -0800256 * Get the name associated with an event type tag code.
257 * @param tag code to look up
258 * @return the name of the tag, or null if no tag has that number
Jim Miller95ff2402009-07-01 18:03:41 -0700259 */
Dan Egnor62136d32010-01-05 19:09:08 -0800260 public static String getTagName(int tag) {
261 readTagsFile();
262 return sTagNames.get(tag);
263 }
264
265 /**
266 * Get the event type tag code associated with an event name.
267 * @param name of event to look up
268 * @return the tag code, or -1 if no tag has that name
269 */
270 public static int getTagCode(String name) {
271 readTagsFile();
272 Integer code = sTagCodes.get(name);
273 return code != null ? code : -1;
274 }
275
276 /**
277 * Read TAGS_FILE, populating sTagCodes and sTagNames, if not already done.
278 */
279 private static synchronized void readTagsFile() {
280 if (sTagCodes != null && sTagNames != null) return;
281
282 sTagCodes = new HashMap<String, Integer>();
283 sTagNames = new HashMap<Integer, String>();
284
285 Pattern comment = Pattern.compile(COMMENT_PATTERN);
286 Pattern tag = Pattern.compile(TAG_PATTERN);
287 BufferedReader reader = null;
288 String line;
289
290 try {
291 reader = new BufferedReader(new FileReader(TAGS_FILE), 256);
292 while ((line = reader.readLine()) != null) {
293 if (comment.matcher(line).matches()) continue;
294
295 Matcher m = tag.matcher(line);
296 if (!m.matches()) {
297 Log.wtf(TAG, "Bad entry in " + TAGS_FILE + ": " + line);
298 continue;
299 }
300
301 try {
302 int num = Integer.parseInt(m.group(1));
303 String name = m.group(2);
304 sTagCodes.put(name, num);
305 sTagNames.put(num, name);
306 } catch (NumberFormatException e) {
307 Log.wtf(TAG, "Error in " + TAGS_FILE + ": " + line, e);
308 }
309 }
310 } catch (IOException e) {
311 Log.wtf(TAG, "Error reading " + TAGS_FILE, e);
312 // Leave the maps existing but unpopulated
313 } finally {
314 try { if (reader != null) reader.close(); } catch (IOException e) {}
315 }
316 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800317}