1 /*
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 
17 package android.util;
18 
19 import android.annotation.SystemApi;
20 import android.compat.annotation.UnsupportedAppUsage;
21 
22 import java.io.BufferedReader;
23 import java.io.FileReader;
24 import java.io.IOException;
25 import java.io.UnsupportedEncodingException;
26 import java.nio.BufferUnderflowException;
27 import java.nio.ByteBuffer;
28 import java.nio.ByteOrder;
29 import java.util.Arrays;
30 import java.util.Collection;
31 import java.util.HashMap;
32 import java.util.regex.Matcher;
33 import java.util.regex.Pattern;
34 
35 /**
36  * 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.
40  *
41  * <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.
43  *
44  * <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.
47  */
48 public class EventLog {
EventLog()49     /** @hide */ public EventLog() {}
50 
51     private static final String TAG = "EventLog";
52 
53     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;
58 
59     /** A previously logged event read from the logs. Instances are thread safe. */
60     public static final class Event {
61         private final ByteBuffer mBuffer;
62         private Exception mLastWtf;
63 
64         // Layout of event log entry received from Android logger.
65         //  see system/core/include/log/log.h
66         private static final int LENGTH_OFFSET = 0;
67         private static final int HEADER_SIZE_OFFSET = 2;
68         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;
72         private static final int UID_OFFSET = 24;
73 
74         // 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;
77 
78         // 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;
83         private static final byte FLOAT_TYPE = 4;
84 
85         /** @param data containing event, read from the system */
86         @UnsupportedAppUsage
Event(byte[] data)87         /*package*/ Event(byte[] data) {
88             mBuffer = ByteBuffer.wrap(data);
89             mBuffer.order(ByteOrder.nativeOrder());
90         }
91 
92         /** @return the process ID which wrote the log entry */
getProcessId()93         public int getProcessId() {
94             return mBuffer.getInt(PROCESS_OFFSET);
95         }
96 
97         /**
98          * @return the UID which wrote the log entry
99          * @hide
100          */
101         @SystemApi
getUid()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 
111         /** @return the thread ID which wrote the log entry */
getThreadId()112         public int getThreadId() {
113             return mBuffer.getInt(THREAD_OFFSET);
114         }
115 
116         /** @return the wall clock time when the entry was written */
getTimeNanos()117         public long getTimeNanos() {
118             return mBuffer.getInt(SECONDS_OFFSET) * 1000000000l
119                     + mBuffer.getInt(NANOSECONDS_OFFSET);
120         }
121 
122         /** @return the type tag code of the entry */
getTag()123         public int getTag() {
124             int offset = mBuffer.getShort(HEADER_SIZE_OFFSET);
125             if (offset == 0) {
126                 offset = V1_PAYLOAD_START;
127             }
128             return mBuffer.getInt(offset);
129         }
130 
131         /** @return one of Integer, Long, Float, String, null, or Object[] of same. */
getData()132         public synchronized Object getData() {
133             try {
134                 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));
139                 if ((offset + DATA_OFFSET) >= mBuffer.limit()) {
140                     // no payload
141                     return null;
142                 }
143                 mBuffer.position(offset + DATA_OFFSET); // Just after the tag.
144                 return decodeObject();
145             } catch (IllegalArgumentException e) {
146                 Log.wtf(TAG, "Illegal entry payload: tag=" + getTag(), e);
147                 mLastWtf = e;
148                 return null;
149             } catch (BufferUnderflowException e) {
150                 Log.wtf(TAG, "Truncated entry payload: tag=" + getTag(), e);
151                 mLastWtf = e;
152                 return null;
153             }
154         }
155 
156         /** @return the loggable item at the current position in mBuffer. */
decodeObject()157         private Object decodeObject() {
158             byte type = mBuffer.get();
159             switch (type) {
160             case INT_TYPE:
161                 return mBuffer.getInt();
162 
163             case LONG_TYPE:
164                 return mBuffer.getLong();
165 
166             case FLOAT_TYPE:
167                 return mBuffer.getFloat();
168 
169             case STRING_TYPE:
170                 try {
171                     int length = mBuffer.getInt();
172                     int start = mBuffer.position();
173                     mBuffer.position(start + length);
174                     return new String(mBuffer.array(), start, length, "UTF-8");
175                 } catch (UnsupportedEncodingException e) {
176                     Log.wtf(TAG, "UTF-8 is not supported", e);
177                     mLastWtf = e;
178                     return null;
179                 }
180 
181             case LIST_TYPE:
182                 int length = mBuffer.get();
183                 if (length < 0) length += 256;  // treat as signed byte
184                 Object[] array = new Object[length];
185                 for (int i = 0; i < length; ++i) array[i] = decodeObject();
186                 return array;
187 
188             default:
189                 throw new IllegalArgumentException("Unknown entry type: " + type);
190             }
191         }
192 
193         /** @hide */
fromBytes(byte[] data)194         public static Event fromBytes(byte[] data) {
195             return new Event(data);
196         }
197 
198         /** @hide */
getBytes()199         public byte[] getBytes() {
200             byte[] bytes = mBuffer.array();
201             return Arrays.copyOf(bytes, bytes.length);
202         }
203 
204         /**
205          * Retreive the last WTF error generated by this object.
206          * @hide
207          */
208         //VisibleForTesting
getLastError()209         public Exception getLastError() {
210             return mLastWtf;
211         }
212 
213         /**
214          * Clear the error state for this object.
215          * @hide
216          */
217         //VisibleForTesting
clearError()218         public void clearError() {
219             mLastWtf = null;
220         }
221 
222         /**
223          * @hide
224          */
225         @Override
equals(Object o)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
hashCode()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         }
244     }
245 
246     // We assume that the native methods deal with any concurrency issues.
247 
248     /**
249      * Record an event log message.
250      * @param tag The event type tag code
251      * @param value A value to log
252      * @return The number of bytes written
253      */
writeEvent(int tag, int value)254     public static native int writeEvent(int tag, int value);
255 
256     /**
257      * Record an event log message.
258      * @param tag The event type tag code
259      * @param value A value to log
260      * @return The number of bytes written
261      */
writeEvent(int tag, long value)262     public static native int writeEvent(int tag, long value);
263 
264     /**
265      * Record an event log message.
266      * @param tag The event type tag code
267      * @param value A value to log
268      * @return The number of bytes written
269      */
writeEvent(int tag, float value)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
275      * @param str A value to log
276      * @return The number of bytes written
277      */
writeEvent(int tag, String str)278     public static native int writeEvent(int tag, String str);
279 
280     /**
281      * Record an event log message.
282      * @param tag The event type tag code
283      * @param list A list of values to log
284      * @return The number of bytes written
285      */
writeEvent(int tag, Object... list)286     public static native int writeEvent(int tag, Object... list);
287 
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      */
readEvents(int[] tags, Collection<Event> output)294     public static native void readEvents(int[] tags, Collection<Event> output)
295             throws IOException;
296 
297     /**
298      * 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
readEventsOnWrapping(int[] tags, long timestamp, Collection<Event> output)306     public static native void readEventsOnWrapping(int[] tags, long timestamp,
307             Collection<Event> output)
308             throws IOException;
309 
310     /**
311      * 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
314      */
getTagName(int tag)315     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      */
getTagCode(String name)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      */
readTagsFile()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     }
372 }
373