• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
21 import java.io.BufferedReader;
22 import java.io.FileReader;
23 import java.io.IOException;
24 import java.io.UnsupportedEncodingException;
25 import java.nio.BufferUnderflowException;
26 import java.nio.ByteBuffer;
27 import java.nio.ByteOrder;
28 import java.util.Arrays;
29 import java.util.Collection;
30 import java.util.HashMap;
31 import java.util.regex.Matcher;
32 import java.util.regex.Pattern;
33 
34 /**
35  * Access to the system diagnostic event record.  System diagnostic events are
36  * used to record certain system-level events (such as garbage collection,
37  * activity manager state, system watchdogs, and other low level activity),
38  * which may be automatically collected and analyzed during system development.
39  *
40  * <p>This is <b>not</b> the main "logcat" debugging log ({@link android.util.Log})!
41  * These diagnostic events are for system integrators, not application authors.
42  *
43  * <p>Events use integer tag codes corresponding to /system/etc/event-log-tags.
44  * They carry a payload of one or more int, long, or String values.  The
45  * event-log-tags file defines the payload contents for each type code.
46  */
47 public class EventLog {
EventLog()48     /** @hide */ public EventLog() {}
49 
50     private static final String TAG = "EventLog";
51 
52     private static final String TAGS_FILE = "/system/etc/event-log-tags";
53     private static final String COMMENT_PATTERN = "^\\s*(#.*)?$";
54     private static final String TAG_PATTERN = "^\\s*(\\d+)\\s+(\\w+)\\s*(\\(.*\\))?\\s*$";
55     private static HashMap<String, Integer> sTagCodes = null;
56     private static HashMap<Integer, String> sTagNames = null;
57 
58     /** A previously logged event read from the logs. Instances are thread safe. */
59     public static final class Event {
60         private final ByteBuffer mBuffer;
61         private Exception mLastWtf;
62 
63         // Layout of event log entry received from Android logger.
64         //  see system/core/include/log/log.h
65         private static final int LENGTH_OFFSET = 0;
66         private static final int HEADER_SIZE_OFFSET = 2;
67         private static final int PROCESS_OFFSET = 4;
68         private static final int THREAD_OFFSET = 8;
69         private static final int SECONDS_OFFSET = 12;
70         private static final int NANOSECONDS_OFFSET = 16;
71         private static final int UID_OFFSET = 24;
72 
73         // Layout for event log v1 format, v2 and v3 use HEADER_SIZE_OFFSET
74         private static final int V1_PAYLOAD_START = 20;
75         private static final int DATA_OFFSET = 4;
76 
77         // Value types
78         private static final byte INT_TYPE    = 0;
79         private static final byte LONG_TYPE   = 1;
80         private static final byte STRING_TYPE = 2;
81         private static final byte LIST_TYPE   = 3;
82         private static final byte FLOAT_TYPE = 4;
83 
84         /** @param data containing event, read from the system */
Event(byte[] data)85         /*package*/ Event(byte[] data) {
86             mBuffer = ByteBuffer.wrap(data);
87             mBuffer.order(ByteOrder.nativeOrder());
88         }
89 
90         /** @return the process ID which wrote the log entry */
getProcessId()91         public int getProcessId() {
92             return mBuffer.getInt(PROCESS_OFFSET);
93         }
94 
95         /**
96          * @return the UID which wrote the log entry
97          * @hide
98          */
99         @SystemApi
getUid()100         public int getUid() {
101             try {
102                 return mBuffer.getInt(UID_OFFSET);
103             } catch (IndexOutOfBoundsException e) {
104                 // buffer won't contain the UID if the caller doesn't have permission.
105                 return -1;
106             }
107         }
108 
109         /** @return the thread ID which wrote the log entry */
getThreadId()110         public int getThreadId() {
111             return mBuffer.getInt(THREAD_OFFSET);
112         }
113 
114         /** @return the wall clock time when the entry was written */
getTimeNanos()115         public long getTimeNanos() {
116             return mBuffer.getInt(SECONDS_OFFSET) * 1000000000l
117                     + mBuffer.getInt(NANOSECONDS_OFFSET);
118         }
119 
120         /** @return the type tag code of the entry */
getTag()121         public int getTag() {
122             int offset = mBuffer.getShort(HEADER_SIZE_OFFSET);
123             if (offset == 0) {
124                 offset = V1_PAYLOAD_START;
125             }
126             return mBuffer.getInt(offset);
127         }
128 
129         /** @return one of Integer, Long, Float, String, null, or Object[] of same. */
getData()130         public synchronized Object getData() {
131             try {
132                 int offset = mBuffer.getShort(HEADER_SIZE_OFFSET);
133                 if (offset == 0) {
134                     offset = V1_PAYLOAD_START;
135                 }
136                 mBuffer.limit(offset + mBuffer.getShort(LENGTH_OFFSET));
137                 if ((offset + DATA_OFFSET) >= mBuffer.limit()) {
138                     // no payload
139                     return null;
140                 }
141                 mBuffer.position(offset + DATA_OFFSET); // Just after the tag.
142                 return decodeObject();
143             } catch (IllegalArgumentException e) {
144                 Log.wtf(TAG, "Illegal entry payload: tag=" + getTag(), e);
145                 mLastWtf = e;
146                 return null;
147             } catch (BufferUnderflowException e) {
148                 Log.wtf(TAG, "Truncated entry payload: tag=" + getTag(), e);
149                 mLastWtf = e;
150                 return null;
151             }
152         }
153 
154         /** @return the loggable item at the current position in mBuffer. */
decodeObject()155         private Object decodeObject() {
156             byte type = mBuffer.get();
157             switch (type) {
158             case INT_TYPE:
159                 return mBuffer.getInt();
160 
161             case LONG_TYPE:
162                 return mBuffer.getLong();
163 
164             case FLOAT_TYPE:
165                 return mBuffer.getFloat();
166 
167             case STRING_TYPE:
168                 try {
169                     int length = mBuffer.getInt();
170                     int start = mBuffer.position();
171                     mBuffer.position(start + length);
172                     return new String(mBuffer.array(), start, length, "UTF-8");
173                 } catch (UnsupportedEncodingException e) {
174                     Log.wtf(TAG, "UTF-8 is not supported", e);
175                     mLastWtf = e;
176                     return null;
177                 }
178 
179             case LIST_TYPE:
180                 int length = mBuffer.get();
181                 if (length < 0) length += 256;  // treat as signed byte
182                 Object[] array = new Object[length];
183                 for (int i = 0; i < length; ++i) array[i] = decodeObject();
184                 return array;
185 
186             default:
187                 throw new IllegalArgumentException("Unknown entry type: " + type);
188             }
189         }
190 
191         /** @hide */
fromBytes(byte[] data)192         public static Event fromBytes(byte[] data) {
193             return new Event(data);
194         }
195 
196         /** @hide */
getBytes()197         public byte[] getBytes() {
198             byte[] bytes = mBuffer.array();
199             return Arrays.copyOf(bytes, bytes.length);
200         }
201 
202         /**
203          * Retreive the last WTF error generated by this object.
204          * @hide
205          */
206         //VisibleForTesting
getLastError()207         public Exception getLastError() {
208             return mLastWtf;
209         }
210 
211         /**
212          * Clear the error state for this object.
213          * @hide
214          */
215         //VisibleForTesting
clearError()216         public void clearError() {
217             mLastWtf = null;
218         }
219 
220         /**
221          * @hide
222          */
223         @Override
equals(Object o)224         public boolean equals(Object o) {
225             // Not using ByteBuffer.equals since it takes buffer position into account and we
226             // always use absolute positions here.
227             if (this == o) return true;
228             if (o == null || getClass() != o.getClass()) return false;
229             Event other = (Event) o;
230             return Arrays.equals(mBuffer.array(), other.mBuffer.array());
231         }
232 
233         /**
234          * @hide
235          */
236         @Override
hashCode()237         public int hashCode() {
238             // Not using ByteBuffer.hashCode since it takes buffer position into account and we
239             // always use absolute positions here.
240             return Arrays.hashCode(mBuffer.array());
241         }
242     }
243 
244     // We assume that the native methods deal with any concurrency issues.
245 
246     /**
247      * Record an event log message.
248      * @param tag The event type tag code
249      * @param value A value to log
250      * @return The number of bytes written
251      */
writeEvent(int tag, int value)252     public static native int writeEvent(int tag, int value);
253 
254     /**
255      * Record an event log message.
256      * @param tag The event type tag code
257      * @param value A value to log
258      * @return The number of bytes written
259      */
writeEvent(int tag, long value)260     public static native int writeEvent(int tag, long value);
261 
262     /**
263      * Record an event log message.
264      * @param tag The event type tag code
265      * @param value A value to log
266      * @return The number of bytes written
267      */
writeEvent(int tag, float value)268     public static native int writeEvent(int tag, float value);
269 
270     /**
271      * Record an event log message.
272      * @param tag The event type tag code
273      * @param str A value to log
274      * @return The number of bytes written
275      */
writeEvent(int tag, String str)276     public static native int writeEvent(int tag, String str);
277 
278     /**
279      * Record an event log message.
280      * @param tag The event type tag code
281      * @param list A list of values to log
282      * @return The number of bytes written
283      */
writeEvent(int tag, Object... list)284     public static native int writeEvent(int tag, Object... list);
285 
286     /**
287      * Read events from the log, filtered by type.
288      * @param tags to search for
289      * @param output container to add events into
290      * @throws IOException if something goes wrong reading events
291      */
readEvents(int[] tags, Collection<Event> output)292     public static native void readEvents(int[] tags, Collection<Event> output)
293             throws IOException;
294 
295     /**
296      * Read events from the log, filtered by type, blocking until logs are about to be overwritten.
297      * @param tags to search for
298      * @param timestamp timestamp allow logs before this time to be overwritten.
299      * @param output container to add events into
300      * @throws IOException if something goes wrong reading events
301      * @hide
302      */
303     @SystemApi
readEventsOnWrapping(int[] tags, long timestamp, Collection<Event> output)304     public static native void readEventsOnWrapping(int[] tags, long timestamp,
305             Collection<Event> output)
306             throws IOException;
307 
308     /**
309      * Get the name associated with an event type tag code.
310      * @param tag code to look up
311      * @return the name of the tag, or null if no tag has that number
312      */
getTagName(int tag)313     public static String getTagName(int tag) {
314         readTagsFile();
315         return sTagNames.get(tag);
316     }
317 
318     /**
319      * Get the event type tag code associated with an event name.
320      * @param name of event to look up
321      * @return the tag code, or -1 if no tag has that name
322      */
getTagCode(String name)323     public static int getTagCode(String name) {
324         readTagsFile();
325         Integer code = sTagCodes.get(name);
326         return code != null ? code : -1;
327     }
328 
329     /**
330      * Read TAGS_FILE, populating sTagCodes and sTagNames, if not already done.
331      */
readTagsFile()332     private static synchronized void readTagsFile() {
333         if (sTagCodes != null && sTagNames != null) return;
334 
335         sTagCodes = new HashMap<String, Integer>();
336         sTagNames = new HashMap<Integer, String>();
337 
338         Pattern comment = Pattern.compile(COMMENT_PATTERN);
339         Pattern tag = Pattern.compile(TAG_PATTERN);
340         BufferedReader reader = null;
341         String line;
342 
343         try {
344             reader = new BufferedReader(new FileReader(TAGS_FILE), 256);
345             while ((line = reader.readLine()) != null) {
346                 if (comment.matcher(line).matches()) continue;
347 
348                 Matcher m = tag.matcher(line);
349                 if (!m.matches()) {
350                     Log.wtf(TAG, "Bad entry in " + TAGS_FILE + ": " + line);
351                     continue;
352                 }
353 
354                 try {
355                     int num = Integer.parseInt(m.group(1));
356                     String name = m.group(2);
357                     sTagCodes.put(name, num);
358                     sTagNames.put(num, name);
359                 } catch (NumberFormatException e) {
360                     Log.wtf(TAG, "Error in " + TAGS_FILE + ": " + line, e);
361                 }
362             }
363         } catch (IOException e) {
364             Log.wtf(TAG, "Error reading " + TAGS_FILE, e);
365             // Leave the maps existing but unpopulated
366         } finally {
367             try { if (reader != null) reader.close(); } catch (IOException e) {}
368         }
369     }
370 }
371