blob: c9dc05b5aeb5f378f80ba9453535b806637d407b [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
Chris Wrend09bf822016-12-02 17:43:23 -050019import android.annotation.SystemApi;
Artur Satayevdf439592019-12-10 17:47:53 +000020import android.compat.annotation.UnsupportedAppUsage;
Chris Wrend09bf822016-12-02 17:43:23 -050021
Dan Egnor62136d32010-01-05 19:09:08 -080022import java.io.BufferedReader;
23import java.io.FileReader;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080024import java.io.IOException;
25import java.io.UnsupportedEncodingException;
Dan Egnor62136d32010-01-05 19:09:08 -080026import java.nio.BufferUnderflowException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080027import java.nio.ByteBuffer;
28import java.nio.ByteOrder;
Rubin Xu75431fb2016-01-07 21:12:14 +000029import java.util.Arrays;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080030import java.util.Collection;
Dan Egnor62136d32010-01-05 19:09:08 -080031import java.util.HashMap;
32import java.util.regex.Matcher;
33import java.util.regex.Pattern;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080034
35/**
Dan Egnor62136d32010-01-05 19:09:08 -080036 * Access to the system diagnostic event record. System diagnostic events are
37 * used to record certain system-level events (such as garbage collection,
38 * activity manager state, system watchdogs, and other low level activity),
39 * which may be automatically collected and analyzed during system development.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080040 *
Dan Egnor62136d32010-01-05 19:09:08 -080041 * <p>This is <b>not</b> the main "logcat" debugging log ({@link android.util.Log})!
42 * These diagnostic events are for system integrators, not application authors.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043 *
Dan Egnor62136d32010-01-05 19:09:08 -080044 * <p>Events use integer tag codes corresponding to /system/etc/event-log-tags.
45 * They carry a payload of one or more int, long, or String values. The
46 * event-log-tags file defines the payload contents for each type code.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080048public class EventLog {
Jesse Wilsona0f8bc52011-02-24 10:44:33 -080049 /** @hide */ public EventLog() {}
50
Dan Egnor62136d32010-01-05 19:09:08 -080051 private static final String TAG = "EventLog";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080052
Dan Egnor62136d32010-01-05 19:09:08 -080053 private static final String TAGS_FILE = "/system/etc/event-log-tags";
54 private static final String COMMENT_PATTERN = "^\\s*(#.*)?$";
55 private static final String TAG_PATTERN = "^\\s*(\\d+)\\s+(\\w+)\\s*(\\(.*\\))?\\s*$";
56 private static HashMap<String, Integer> sTagCodes = null;
57 private static HashMap<Integer, String> sTagNames = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080058
Neil Fullerbf0dc0f2015-11-24 18:19:06 +000059 /** A previously logged event read from the logs. Instances are thread safe. */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080060 public static final class Event {
61 private final ByteBuffer mBuffer;
Chris Wrend9f3d9e2016-12-16 14:08:34 -050062 private Exception mLastWtf;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080063
Mark Salyzyn747802f2014-04-25 13:25:18 -070064 // Layout of event log entry received from Android logger.
Mark Salyzyn52eb4e02016-09-28 16:15:30 -070065 // see system/core/include/log/log.h
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080066 private static final int LENGTH_OFFSET = 0;
Mark Salyzyn747802f2014-04-25 13:25:18 -070067 private static final int HEADER_SIZE_OFFSET = 2;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080068 private static final int PROCESS_OFFSET = 4;
69 private static final int THREAD_OFFSET = 8;
70 private static final int SECONDS_OFFSET = 12;
71 private static final int NANOSECONDS_OFFSET = 16;
Chris Wren4d6b54d2017-04-27 16:56:54 -040072 private static final int UID_OFFSET = 24;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080073
Mark Salyzyn747802f2014-04-25 13:25:18 -070074 // Layout for event log v1 format, v2 and v3 use HEADER_SIZE_OFFSET
75 private static final int V1_PAYLOAD_START = 20;
76 private static final int DATA_OFFSET = 4;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080077
Dan Egnor62136d32010-01-05 19:09:08 -080078 // Value types
79 private static final byte INT_TYPE = 0;
80 private static final byte LONG_TYPE = 1;
81 private static final byte STRING_TYPE = 2;
82 private static final byte LIST_TYPE = 3;
Jeff Browne7e9cce2015-04-28 13:26:48 -070083 private static final byte FLOAT_TYPE = 4;
Dan Egnor62136d32010-01-05 19:09:08 -080084
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080085 /** @param data containing event, read from the system */
Mathew Inwood4eb56ab2018-08-14 17:24:32 +010086 @UnsupportedAppUsage
Dan Egnor62136d32010-01-05 19:09:08 -080087 /*package*/ Event(byte[] data) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080088 mBuffer = ByteBuffer.wrap(data);
89 mBuffer.order(ByteOrder.nativeOrder());
90 }
91
Dan Egnor62136d32010-01-05 19:09:08 -080092 /** @return the process ID which wrote the log entry */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080093 public int getProcessId() {
94 return mBuffer.getInt(PROCESS_OFFSET);
95 }
96
Chris Wren4d6b54d2017-04-27 16:56:54 -040097 /**
98 * @return the UID which wrote the log entry
99 * @hide
100 */
101 @SystemApi
102 public int getUid() {
103 try {
104 return mBuffer.getInt(UID_OFFSET);
105 } catch (IndexOutOfBoundsException e) {
106 // buffer won't contain the UID if the caller doesn't have permission.
107 return -1;
108 }
109 }
110
Dan Egnor62136d32010-01-05 19:09:08 -0800111 /** @return the thread ID which wrote the log entry */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800112 public int getThreadId() {
113 return mBuffer.getInt(THREAD_OFFSET);
114 }
115
Dan Egnor62136d32010-01-05 19:09:08 -0800116 /** @return the wall clock time when the entry was written */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800117 public long getTimeNanos() {
118 return mBuffer.getInt(SECONDS_OFFSET) * 1000000000l
119 + mBuffer.getInt(NANOSECONDS_OFFSET);
120 }
121
Dan Egnor62136d32010-01-05 19:09:08 -0800122 /** @return the type tag code of the entry */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800123 public int getTag() {
Mark Salyzyn747802f2014-04-25 13:25:18 -0700124 int offset = mBuffer.getShort(HEADER_SIZE_OFFSET);
125 if (offset == 0) {
126 offset = V1_PAYLOAD_START;
127 }
128 return mBuffer.getInt(offset);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800129 }
130
Jeff Browne7e9cce2015-04-28 13:26:48 -0700131 /** @return one of Integer, Long, Float, String, null, or Object[] of same. */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800132 public synchronized Object getData() {
Dan Egnor62136d32010-01-05 19:09:08 -0800133 try {
Mark Salyzyn747802f2014-04-25 13:25:18 -0700134 int offset = mBuffer.getShort(HEADER_SIZE_OFFSET);
135 if (offset == 0) {
136 offset = V1_PAYLOAD_START;
137 }
138 mBuffer.limit(offset + mBuffer.getShort(LENGTH_OFFSET));
Chris Wrend9f3d9e2016-12-16 14:08:34 -0500139 if ((offset + DATA_OFFSET) >= mBuffer.limit()) {
140 // no payload
141 return null;
142 }
Mark Salyzyn747802f2014-04-25 13:25:18 -0700143 mBuffer.position(offset + DATA_OFFSET); // Just after the tag.
Dan Egnor62136d32010-01-05 19:09:08 -0800144 return decodeObject();
145 } catch (IllegalArgumentException e) {
146 Log.wtf(TAG, "Illegal entry payload: tag=" + getTag(), e);
Chris Wrend9f3d9e2016-12-16 14:08:34 -0500147 mLastWtf = e;
Dan Egnor62136d32010-01-05 19:09:08 -0800148 return null;
149 } catch (BufferUnderflowException e) {
150 Log.wtf(TAG, "Truncated entry payload: tag=" + getTag(), e);
Chris Wrend9f3d9e2016-12-16 14:08:34 -0500151 mLastWtf = e;
Dan Egnor62136d32010-01-05 19:09:08 -0800152 return null;
153 }
Jim Miller30636872009-06-08 18:59:49 -0700154 }
155
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800156 /** @return the loggable item at the current position in mBuffer. */
157 private Object decodeObject() {
Dan Egnor62136d32010-01-05 19:09:08 -0800158 byte type = mBuffer.get();
159 switch (type) {
160 case INT_TYPE:
Jeff Browne7e9cce2015-04-28 13:26:48 -0700161 return mBuffer.getInt();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800162
Dan Egnor62136d32010-01-05 19:09:08 -0800163 case LONG_TYPE:
Jeff Browne7e9cce2015-04-28 13:26:48 -0700164 return mBuffer.getLong();
165
166 case FLOAT_TYPE:
167 return mBuffer.getFloat();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800168
Dan Egnor62136d32010-01-05 19:09:08 -0800169 case STRING_TYPE:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800170 try {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800171 int length = mBuffer.getInt();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800172 int start = mBuffer.position();
173 mBuffer.position(start + length);
174 return new String(mBuffer.array(), start, length, "UTF-8");
175 } catch (UnsupportedEncodingException e) {
Dan Egnor62136d32010-01-05 19:09:08 -0800176 Log.wtf(TAG, "UTF-8 is not supported", e);
Chris Wrend9f3d9e2016-12-16 14:08:34 -0500177 mLastWtf = e;
Dan Egnor62136d32010-01-05 19:09:08 -0800178 return null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800179 }
180
Dan Egnor62136d32010-01-05 19:09:08 -0800181 case LIST_TYPE:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800182 int length = mBuffer.get();
Dan Egnor69160892010-01-06 23:12:57 -0800183 if (length < 0) length += 256; // treat as signed byte
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800184 Object[] array = new Object[length];
Dan Egnor62136d32010-01-05 19:09:08 -0800185 for (int i = 0; i < length; ++i) array[i] = decodeObject();
186 return array;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800187
188 default:
Dan Egnor62136d32010-01-05 19:09:08 -0800189 throw new IllegalArgumentException("Unknown entry type: " + type);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800190 }
191 }
Rubin Xu75431fb2016-01-07 21:12:14 +0000192
193 /** @hide */
194 public static Event fromBytes(byte[] data) {
195 return new Event(data);
196 }
197
198 /** @hide */
199 public byte[] getBytes() {
200 byte[] bytes = mBuffer.array();
201 return Arrays.copyOf(bytes, bytes.length);
202 }
Chris Wrend9f3d9e2016-12-16 14:08:34 -0500203
204 /**
205 * Retreive the last WTF error generated by this object.
206 * @hide
207 */
208 //VisibleForTesting
209 public Exception getLastError() {
210 return mLastWtf;
211 }
212
213 /**
214 * Clear the error state for this object.
215 * @hide
216 */
217 //VisibleForTesting
218 public void clearError() {
219 mLastWtf = null;
220 }
Pavel Grafov4ce59d42017-02-25 19:45:43 +0000221
222 /**
223 * @hide
224 */
225 @Override
226 public boolean equals(Object o) {
227 // Not using ByteBuffer.equals since it takes buffer position into account and we
228 // always use absolute positions here.
229 if (this == o) return true;
230 if (o == null || getClass() != o.getClass()) return false;
231 Event other = (Event) o;
232 return Arrays.equals(mBuffer.array(), other.mBuffer.array());
233 }
234
235 /**
236 * @hide
237 */
238 @Override
239 public int hashCode() {
240 // Not using ByteBuffer.hashCode since it takes buffer position into account and we
241 // always use absolute positions here.
242 return Arrays.hashCode(mBuffer.array());
243 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800244 }
245
246 // We assume that the native methods deal with any concurrency issues.
247
248 /**
Dan Egnor62136d32010-01-05 19:09:08 -0800249 * Record an event log message.
250 * @param tag The event type tag code
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800251 * @param value A value to log
252 * @return The number of bytes written
253 */
254 public static native int writeEvent(int tag, int value);
255
256 /**
Dan Egnor62136d32010-01-05 19:09:08 -0800257 * Record an event log message.
258 * @param tag The event type tag code
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800259 * @param value A value to log
260 * @return The number of bytes written
261 */
262 public static native int writeEvent(int tag, long value);
263
264 /**
Dan Egnor62136d32010-01-05 19:09:08 -0800265 * Record an event log message.
266 * @param tag The event type tag code
Jeff Browne7e9cce2015-04-28 13:26:48 -0700267 * @param value A value to log
268 * @return The number of bytes written
269 */
270 public static native int writeEvent(int tag, float value);
271
272 /**
273 * Record an event log message.
274 * @param tag The event type tag code
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800275 * @param str A value to log
276 * @return The number of bytes written
277 */
278 public static native int writeEvent(int tag, String str);
279
280 /**
Dan Egnor62136d32010-01-05 19:09:08 -0800281 * Record an event log message.
282 * @param tag The event type tag code
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800283 * @param list A list of values to log
284 * @return The number of bytes written
285 */
Dan Egnor62136d32010-01-05 19:09:08 -0800286 public static native int writeEvent(int tag, Object... list);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800287
288 /**
289 * Read events from the log, filtered by type.
290 * @param tags to search for
291 * @param output container to add events into
292 * @throws IOException if something goes wrong reading events
293 */
294 public static native void readEvents(int[] tags, Collection<Event> output)
295 throws IOException;
Jim Miller95ff2402009-07-01 18:03:41 -0700296
297 /**
Chris Wrend09bf822016-12-02 17:43:23 -0500298 * Read events from the log, filtered by type, blocking until logs are about to be overwritten.
299 * @param tags to search for
300 * @param timestamp timestamp allow logs before this time to be overwritten.
301 * @param output container to add events into
302 * @throws IOException if something goes wrong reading events
303 * @hide
304 */
305 @SystemApi
306 public static native void readEventsOnWrapping(int[] tags, long timestamp,
307 Collection<Event> output)
308 throws IOException;
309
310 /**
Dan Egnor62136d32010-01-05 19:09:08 -0800311 * Get the name associated with an event type tag code.
312 * @param tag code to look up
313 * @return the name of the tag, or null if no tag has that number
Jim Miller95ff2402009-07-01 18:03:41 -0700314 */
Dan Egnor62136d32010-01-05 19:09:08 -0800315 public static String getTagName(int tag) {
316 readTagsFile();
317 return sTagNames.get(tag);
318 }
319
320 /**
321 * Get the event type tag code associated with an event name.
322 * @param name of event to look up
323 * @return the tag code, or -1 if no tag has that name
324 */
325 public static int getTagCode(String name) {
326 readTagsFile();
327 Integer code = sTagCodes.get(name);
328 return code != null ? code : -1;
329 }
330
331 /**
332 * Read TAGS_FILE, populating sTagCodes and sTagNames, if not already done.
333 */
334 private static synchronized void readTagsFile() {
335 if (sTagCodes != null && sTagNames != null) return;
336
337 sTagCodes = new HashMap<String, Integer>();
338 sTagNames = new HashMap<Integer, String>();
339
340 Pattern comment = Pattern.compile(COMMENT_PATTERN);
341 Pattern tag = Pattern.compile(TAG_PATTERN);
342 BufferedReader reader = null;
343 String line;
344
345 try {
346 reader = new BufferedReader(new FileReader(TAGS_FILE), 256);
347 while ((line = reader.readLine()) != null) {
348 if (comment.matcher(line).matches()) continue;
349
350 Matcher m = tag.matcher(line);
351 if (!m.matches()) {
352 Log.wtf(TAG, "Bad entry in " + TAGS_FILE + ": " + line);
353 continue;
354 }
355
356 try {
357 int num = Integer.parseInt(m.group(1));
358 String name = m.group(2);
359 sTagCodes.put(name, num);
360 sTagNames.put(num, name);
361 } catch (NumberFormatException e) {
362 Log.wtf(TAG, "Error in " + TAGS_FILE + ": " + line, e);
363 }
364 }
365 } catch (IOException e) {
366 Log.wtf(TAG, "Error reading " + TAGS_FILE, e);
367 // Leave the maps existing but unpopulated
368 } finally {
369 try { if (reader != null) reader.close(); } catch (IOException e) {}
370 }
371 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800372}