blob: ead4e46cd28ba4d4109b05f8cd2adb3da5fc480a [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
Rubin Xu1480ce72020-02-05 11:38:08 +000019import android.annotation.NonNull;
20import android.annotation.Nullable;
Chris Wrend09bf822016-12-02 17:43:23 -050021import android.annotation.SystemApi;
Artur Satayevdf439592019-12-10 17:47:53 +000022import android.compat.annotation.UnsupportedAppUsage;
Chris Wrend09bf822016-12-02 17:43:23 -050023
Dan Egnor62136d32010-01-05 19:09:08 -080024import java.io.BufferedReader;
25import java.io.FileReader;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080026import java.io.IOException;
27import java.io.UnsupportedEncodingException;
Dan Egnor62136d32010-01-05 19:09:08 -080028import java.nio.BufferUnderflowException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080029import java.nio.ByteBuffer;
30import java.nio.ByteOrder;
Rubin Xu75431fb2016-01-07 21:12:14 +000031import java.util.Arrays;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080032import java.util.Collection;
Dan Egnor62136d32010-01-05 19:09:08 -080033import java.util.HashMap;
34import java.util.regex.Matcher;
35import java.util.regex.Pattern;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080036
37/**
Dan Egnor62136d32010-01-05 19:09:08 -080038 * Access to the system diagnostic event record. System diagnostic events are
39 * used to record certain system-level events (such as garbage collection,
40 * activity manager state, system watchdogs, and other low level activity),
41 * which may be automatically collected and analyzed during system development.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080042 *
Dan Egnor62136d32010-01-05 19:09:08 -080043 * <p>This is <b>not</b> the main "logcat" debugging log ({@link android.util.Log})!
44 * These diagnostic events are for system integrators, not application authors.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080045 *
Dan Egnor62136d32010-01-05 19:09:08 -080046 * <p>Events use integer tag codes corresponding to /system/etc/event-log-tags.
47 * They carry a payload of one or more int, long, or String values. The
48 * event-log-tags file defines the payload contents for each type code.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080050public class EventLog {
Jesse Wilsona0f8bc52011-02-24 10:44:33 -080051 /** @hide */ public EventLog() {}
52
Dan Egnor62136d32010-01-05 19:09:08 -080053 private static final String TAG = "EventLog";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080054
Dan Egnor62136d32010-01-05 19:09:08 -080055 private static final String TAGS_FILE = "/system/etc/event-log-tags";
56 private static final String COMMENT_PATTERN = "^\\s*(#.*)?$";
57 private static final String TAG_PATTERN = "^\\s*(\\d+)\\s+(\\w+)\\s*(\\(.*\\))?\\s*$";
58 private static HashMap<String, Integer> sTagCodes = null;
59 private static HashMap<Integer, String> sTagNames = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080060
Neil Fullerbf0dc0f2015-11-24 18:19:06 +000061 /** A previously logged event read from the logs. Instances are thread safe. */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080062 public static final class Event {
63 private final ByteBuffer mBuffer;
Chris Wrend9f3d9e2016-12-16 14:08:34 -050064 private Exception mLastWtf;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080065
Mark Salyzyn747802f2014-04-25 13:25:18 -070066 // Layout of event log entry received from Android logger.
Rubin Xu1480ce72020-02-05 11:38:08 +000067 // see system/core/liblog/include/log/log_read.h
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080068 private static final int LENGTH_OFFSET = 0;
Mark Salyzyn747802f2014-04-25 13:25:18 -070069 private static final int HEADER_SIZE_OFFSET = 2;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080070 private static final int PROCESS_OFFSET = 4;
71 private static final int THREAD_OFFSET = 8;
72 private static final int SECONDS_OFFSET = 12;
73 private static final int NANOSECONDS_OFFSET = 16;
Chris Wren4d6b54d2017-04-27 16:56:54 -040074 private static final int UID_OFFSET = 24;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080075
Mark Salyzyn747802f2014-04-25 13:25:18 -070076 // Layout for event log v1 format, v2 and v3 use HEADER_SIZE_OFFSET
77 private static final int V1_PAYLOAD_START = 20;
Rubin Xu1480ce72020-02-05 11:38:08 +000078 private static final int TAG_LENGTH = 4;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080079
Dan Egnor62136d32010-01-05 19:09:08 -080080 // Value types
81 private static final byte INT_TYPE = 0;
82 private static final byte LONG_TYPE = 1;
83 private static final byte STRING_TYPE = 2;
84 private static final byte LIST_TYPE = 3;
Jeff Browne7e9cce2015-04-28 13:26:48 -070085 private static final byte FLOAT_TYPE = 4;
Dan Egnor62136d32010-01-05 19:09:08 -080086
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080087 /** @param data containing event, read from the system */
Mathew Inwood4eb56ab2018-08-14 17:24:32 +010088 @UnsupportedAppUsage
Dan Egnor62136d32010-01-05 19:09:08 -080089 /*package*/ Event(byte[] data) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080090 mBuffer = ByteBuffer.wrap(data);
91 mBuffer.order(ByteOrder.nativeOrder());
92 }
93
Dan Egnor62136d32010-01-05 19:09:08 -080094 /** @return the process ID which wrote the log entry */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080095 public int getProcessId() {
96 return mBuffer.getInt(PROCESS_OFFSET);
97 }
98
Chris Wren4d6b54d2017-04-27 16:56:54 -040099 /**
100 * @return the UID which wrote the log entry
101 * @hide
102 */
103 @SystemApi
104 public int getUid() {
105 try {
106 return mBuffer.getInt(UID_OFFSET);
107 } catch (IndexOutOfBoundsException e) {
108 // buffer won't contain the UID if the caller doesn't have permission.
109 return -1;
110 }
111 }
112
Dan Egnor62136d32010-01-05 19:09:08 -0800113 /** @return the thread ID which wrote the log entry */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800114 public int getThreadId() {
115 return mBuffer.getInt(THREAD_OFFSET);
116 }
117
Dan Egnor62136d32010-01-05 19:09:08 -0800118 /** @return the wall clock time when the entry was written */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800119 public long getTimeNanos() {
120 return mBuffer.getInt(SECONDS_OFFSET) * 1000000000l
121 + mBuffer.getInt(NANOSECONDS_OFFSET);
122 }
123
Dan Egnor62136d32010-01-05 19:09:08 -0800124 /** @return the type tag code of the entry */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800125 public int getTag() {
Rubin Xu1480ce72020-02-05 11:38:08 +0000126 return mBuffer.getInt(getHeaderSize());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800127 }
128
Rubin Xu1480ce72020-02-05 11:38:08 +0000129 private int getHeaderSize() {
130 int length = mBuffer.getShort(HEADER_SIZE_OFFSET);
131 if (length != 0) {
132 return length;
133 }
134 return V1_PAYLOAD_START;
135 }
Jeff Browne7e9cce2015-04-28 13:26:48 -0700136 /** @return one of Integer, Long, Float, String, null, or Object[] of same. */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800137 public synchronized Object getData() {
Dan Egnor62136d32010-01-05 19:09:08 -0800138 try {
Rubin Xu1480ce72020-02-05 11:38:08 +0000139 int offset = getHeaderSize();
Mark Salyzyn747802f2014-04-25 13:25:18 -0700140 mBuffer.limit(offset + mBuffer.getShort(LENGTH_OFFSET));
Rubin Xu1480ce72020-02-05 11:38:08 +0000141 if ((offset + TAG_LENGTH) >= mBuffer.limit()) {
Chris Wrend9f3d9e2016-12-16 14:08:34 -0500142 // no payload
143 return null;
144 }
Rubin Xu1480ce72020-02-05 11:38:08 +0000145 mBuffer.position(offset + TAG_LENGTH); // Just after the tag.
Dan Egnor62136d32010-01-05 19:09:08 -0800146 return decodeObject();
147 } catch (IllegalArgumentException e) {
148 Log.wtf(TAG, "Illegal entry payload: tag=" + getTag(), e);
Chris Wrend9f3d9e2016-12-16 14:08:34 -0500149 mLastWtf = e;
Dan Egnor62136d32010-01-05 19:09:08 -0800150 return null;
151 } catch (BufferUnderflowException e) {
152 Log.wtf(TAG, "Truncated entry payload: tag=" + getTag(), e);
Chris Wrend9f3d9e2016-12-16 14:08:34 -0500153 mLastWtf = e;
Dan Egnor62136d32010-01-05 19:09:08 -0800154 return null;
155 }
Jim Miller30636872009-06-08 18:59:49 -0700156 }
157
Rubin Xu1480ce72020-02-05 11:38:08 +0000158 /**
159 * Construct a new EventLog object from the current object, copying all log metadata
160 * but replacing the actual payload with the content provided.
161 * @hide
162 */
163 public Event withNewData(@Nullable Object object) {
164 byte[] payload = encodeObject(object);
165 if (payload.length > 65535 - TAG_LENGTH) {
166 throw new IllegalArgumentException("Payload too long");
167 }
168 int headerLength = getHeaderSize();
169 byte[] newBytes = new byte[headerLength + TAG_LENGTH + payload.length];
170 // Copy header (including the 4 bytes of tag integer at the beginning of payload)
171 System.arraycopy(mBuffer.array(), 0, newBytes, 0, headerLength + TAG_LENGTH);
172 // Fill in encoded objects
173 System.arraycopy(payload, 0, newBytes, headerLength + TAG_LENGTH, payload.length);
174 Event result = new Event(newBytes);
175 // Patch payload length in header
176 result.mBuffer.putShort(LENGTH_OFFSET, (short) (payload.length + TAG_LENGTH));
177 return result;
178 }
179
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800180 /** @return the loggable item at the current position in mBuffer. */
181 private Object decodeObject() {
Dan Egnor62136d32010-01-05 19:09:08 -0800182 byte type = mBuffer.get();
183 switch (type) {
184 case INT_TYPE:
Jeff Browne7e9cce2015-04-28 13:26:48 -0700185 return mBuffer.getInt();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800186
Dan Egnor62136d32010-01-05 19:09:08 -0800187 case LONG_TYPE:
Jeff Browne7e9cce2015-04-28 13:26:48 -0700188 return mBuffer.getLong();
189
190 case FLOAT_TYPE:
191 return mBuffer.getFloat();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800192
Dan Egnor62136d32010-01-05 19:09:08 -0800193 case STRING_TYPE:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800194 try {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800195 int length = mBuffer.getInt();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800196 int start = mBuffer.position();
197 mBuffer.position(start + length);
198 return new String(mBuffer.array(), start, length, "UTF-8");
199 } catch (UnsupportedEncodingException e) {
Dan Egnor62136d32010-01-05 19:09:08 -0800200 Log.wtf(TAG, "UTF-8 is not supported", e);
Chris Wrend9f3d9e2016-12-16 14:08:34 -0500201 mLastWtf = e;
Dan Egnor62136d32010-01-05 19:09:08 -0800202 return null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800203 }
204
Dan Egnor62136d32010-01-05 19:09:08 -0800205 case LIST_TYPE:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800206 int length = mBuffer.get();
Dan Egnor69160892010-01-06 23:12:57 -0800207 if (length < 0) length += 256; // treat as signed byte
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800208 Object[] array = new Object[length];
Dan Egnor62136d32010-01-05 19:09:08 -0800209 for (int i = 0; i < length; ++i) array[i] = decodeObject();
210 return array;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800211
212 default:
Dan Egnor62136d32010-01-05 19:09:08 -0800213 throw new IllegalArgumentException("Unknown entry type: " + type);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800214 }
215 }
Rubin Xu75431fb2016-01-07 21:12:14 +0000216
Rubin Xu1480ce72020-02-05 11:38:08 +0000217 private static @NonNull byte[] encodeObject(@Nullable Object object) {
218 if (object == null) {
219 return new byte[0];
220 }
221 if (object instanceof Integer) {
222 return ByteBuffer.allocate(1 + 4)
223 .order(ByteOrder.nativeOrder())
224 .put(INT_TYPE)
225 .putInt((Integer) object)
226 .array();
227 } else if (object instanceof Long) {
228 return ByteBuffer.allocate(1 + 8)
229 .order(ByteOrder.nativeOrder())
230 .put(LONG_TYPE)
231 .putLong((Long) object)
232 .array();
233 } else if (object instanceof Float) {
234 return ByteBuffer.allocate(1 + 4)
235 .order(ByteOrder.nativeOrder())
236 .put(FLOAT_TYPE)
237 .putFloat((Float) object)
238 .array();
239 } else if (object instanceof String) {
240 String string = (String) object;
241 byte[] bytes;
242 try {
243 bytes = string.getBytes("UTF-8");
244 } catch (UnsupportedEncodingException e) {
245 bytes = new byte[0];
246 }
247 return ByteBuffer.allocate(1 + 4 + bytes.length)
248 .order(ByteOrder.nativeOrder())
249 .put(STRING_TYPE)
250 .putInt(bytes.length)
251 .put(bytes)
252 .array();
253 } else if (object instanceof Object[]) {
254 Object[] objects = (Object[]) object;
255 if (objects.length > 255) {
256 throw new IllegalArgumentException("Object array too long");
257 }
258 byte[][] bytes = new byte[objects.length][];
259 int totalLength = 0;
260 for (int i = 0; i < objects.length; i++) {
261 bytes[i] = encodeObject(objects[i]);
262 totalLength += bytes[i].length;
263 }
264 ByteBuffer buffer = ByteBuffer.allocate(1 + 1 + totalLength)
265 .order(ByteOrder.nativeOrder())
266 .put(LIST_TYPE)
267 .put((byte) objects.length);
268 for (int i = 0; i < objects.length; i++) {
269 buffer.put(bytes[i]);
270 }
271 return buffer.array();
272 } else {
273 throw new IllegalArgumentException("Unknown object type " + object);
274 }
275 }
276
Rubin Xu75431fb2016-01-07 21:12:14 +0000277 /** @hide */
278 public static Event fromBytes(byte[] data) {
279 return new Event(data);
280 }
281
282 /** @hide */
283 public byte[] getBytes() {
284 byte[] bytes = mBuffer.array();
285 return Arrays.copyOf(bytes, bytes.length);
286 }
Chris Wrend9f3d9e2016-12-16 14:08:34 -0500287
288 /**
289 * Retreive the last WTF error generated by this object.
290 * @hide
291 */
292 //VisibleForTesting
293 public Exception getLastError() {
294 return mLastWtf;
295 }
296
297 /**
298 * Clear the error state for this object.
299 * @hide
300 */
301 //VisibleForTesting
302 public void clearError() {
303 mLastWtf = null;
304 }
Pavel Grafov4ce59d42017-02-25 19:45:43 +0000305
306 /**
307 * @hide
308 */
309 @Override
310 public boolean equals(Object o) {
311 // Not using ByteBuffer.equals since it takes buffer position into account and we
312 // always use absolute positions here.
313 if (this == o) return true;
314 if (o == null || getClass() != o.getClass()) return false;
315 Event other = (Event) o;
316 return Arrays.equals(mBuffer.array(), other.mBuffer.array());
317 }
318
319 /**
320 * @hide
321 */
322 @Override
323 public int hashCode() {
324 // Not using ByteBuffer.hashCode since it takes buffer position into account and we
325 // always use absolute positions here.
326 return Arrays.hashCode(mBuffer.array());
327 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800328 }
329
330 // We assume that the native methods deal with any concurrency issues.
331
332 /**
Dan Egnor62136d32010-01-05 19:09:08 -0800333 * Record an event log message.
334 * @param tag The event type tag code
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800335 * @param value A value to log
336 * @return The number of bytes written
337 */
338 public static native int writeEvent(int tag, int value);
339
340 /**
Dan Egnor62136d32010-01-05 19:09:08 -0800341 * Record an event log message.
342 * @param tag The event type tag code
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800343 * @param value A value to log
344 * @return The number of bytes written
345 */
346 public static native int writeEvent(int tag, long value);
347
348 /**
Dan Egnor62136d32010-01-05 19:09:08 -0800349 * Record an event log message.
350 * @param tag The event type tag code
Jeff Browne7e9cce2015-04-28 13:26:48 -0700351 * @param value A value to log
352 * @return The number of bytes written
353 */
354 public static native int writeEvent(int tag, float value);
355
356 /**
357 * Record an event log message.
358 * @param tag The event type tag code
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800359 * @param str A value to log
360 * @return The number of bytes written
361 */
362 public static native int writeEvent(int tag, String str);
363
364 /**
Dan Egnor62136d32010-01-05 19:09:08 -0800365 * Record an event log message.
366 * @param tag The event type tag code
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800367 * @param list A list of values to log
368 * @return The number of bytes written
369 */
Dan Egnor62136d32010-01-05 19:09:08 -0800370 public static native int writeEvent(int tag, Object... list);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800371
372 /**
373 * Read events from the log, filtered by type.
374 * @param tags to search for
375 * @param output container to add events into
376 * @throws IOException if something goes wrong reading events
377 */
378 public static native void readEvents(int[] tags, Collection<Event> output)
379 throws IOException;
Jim Miller95ff2402009-07-01 18:03:41 -0700380
381 /**
Chris Wrend09bf822016-12-02 17:43:23 -0500382 * Read events from the log, filtered by type, blocking until logs are about to be overwritten.
383 * @param tags to search for
384 * @param timestamp timestamp allow logs before this time to be overwritten.
385 * @param output container to add events into
386 * @throws IOException if something goes wrong reading events
387 * @hide
388 */
389 @SystemApi
390 public static native void readEventsOnWrapping(int[] tags, long timestamp,
391 Collection<Event> output)
392 throws IOException;
393
394 /**
Dan Egnor62136d32010-01-05 19:09:08 -0800395 * Get the name associated with an event type tag code.
396 * @param tag code to look up
397 * @return the name of the tag, or null if no tag has that number
Jim Miller95ff2402009-07-01 18:03:41 -0700398 */
Dan Egnor62136d32010-01-05 19:09:08 -0800399 public static String getTagName(int tag) {
400 readTagsFile();
401 return sTagNames.get(tag);
402 }
403
404 /**
405 * Get the event type tag code associated with an event name.
406 * @param name of event to look up
407 * @return the tag code, or -1 if no tag has that name
408 */
409 public static int getTagCode(String name) {
410 readTagsFile();
411 Integer code = sTagCodes.get(name);
412 return code != null ? code : -1;
413 }
414
415 /**
416 * Read TAGS_FILE, populating sTagCodes and sTagNames, if not already done.
417 */
418 private static synchronized void readTagsFile() {
419 if (sTagCodes != null && sTagNames != null) return;
420
421 sTagCodes = new HashMap<String, Integer>();
422 sTagNames = new HashMap<Integer, String>();
423
424 Pattern comment = Pattern.compile(COMMENT_PATTERN);
425 Pattern tag = Pattern.compile(TAG_PATTERN);
426 BufferedReader reader = null;
427 String line;
428
429 try {
430 reader = new BufferedReader(new FileReader(TAGS_FILE), 256);
431 while ((line = reader.readLine()) != null) {
432 if (comment.matcher(line).matches()) continue;
433
434 Matcher m = tag.matcher(line);
435 if (!m.matches()) {
436 Log.wtf(TAG, "Bad entry in " + TAGS_FILE + ": " + line);
437 continue;
438 }
439
440 try {
441 int num = Integer.parseInt(m.group(1));
442 String name = m.group(2);
443 sTagCodes.put(name, num);
444 sTagNames.put(num, name);
445 } catch (NumberFormatException e) {
446 Log.wtf(TAG, "Error in " + TAGS_FILE + ": " + line, e);
447 }
448 }
449 } catch (IOException e) {
450 Log.wtf(TAG, "Error reading " + TAGS_FILE, e);
451 // Leave the maps existing but unpopulated
452 } finally {
453 try { if (reader != null) reader.close(); } catch (IOException e) {}
454 }
455 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800456}