Home | History | Annotate | Download | only in util
      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 java.io.BufferedReader;
     20 import java.io.FileReader;
     21 import java.io.IOException;
     22 import java.io.UnsupportedEncodingException;
     23 import java.nio.BufferUnderflowException;
     24 import java.nio.ByteBuffer;
     25 import java.nio.ByteOrder;
     26 import java.util.Collection;
     27 import java.util.HashMap;
     28 import java.util.regex.Matcher;
     29 import java.util.regex.Pattern;
     30 
     31 /**
     32  * Access to the system diagnostic event record.  System diagnostic events are
     33  * used to record certain system-level events (such as garbage collection,
     34  * activity manager state, system watchdogs, and other low level activity),
     35  * which may be automatically collected and analyzed during system development.
     36  *
     37  * <p>This is <b>not</b> the main "logcat" debugging log ({@link android.util.Log})!
     38  * These diagnostic events are for system integrators, not application authors.
     39  *
     40  * <p>Events use integer tag codes corresponding to /system/etc/event-log-tags.
     41  * They carry a payload of one or more int, long, or String values.  The
     42  * event-log-tags file defines the payload contents for each type code.
     43  */
     44 public class EventLog {
     45     /** @hide */ public EventLog() {}
     46 
     47     private static final String TAG = "EventLog";
     48 
     49     private static final String TAGS_FILE = "/system/etc/event-log-tags";
     50     private static final String COMMENT_PATTERN = "^\\s*(#.*)?$";
     51     private static final String TAG_PATTERN = "^\\s*(\\d+)\\s+(\\w+)\\s*(\\(.*\\))?\\s*$";
     52     private static HashMap<String, Integer> sTagCodes = null;
     53     private static HashMap<Integer, String> sTagNames = null;
     54 
     55     /** A previously logged event read from the logs. */
     56     public static final class Event {
     57         private final ByteBuffer mBuffer;
     58 
     59         // Layout of event log entry received from kernel.
     60         private static final int LENGTH_OFFSET = 0;
     61         private static final int PROCESS_OFFSET = 4;
     62         private static final int THREAD_OFFSET = 8;
     63         private static final int SECONDS_OFFSET = 12;
     64         private static final int NANOSECONDS_OFFSET = 16;
     65 
     66         private static final int PAYLOAD_START = 20;
     67         private static final int TAG_OFFSET = 20;
     68         private static final int DATA_START = 24;
     69 
     70         // Value types
     71         private static final byte INT_TYPE    = 0;
     72         private static final byte LONG_TYPE   = 1;
     73         private static final byte STRING_TYPE = 2;
     74         private static final byte LIST_TYPE   = 3;
     75 
     76         /** @param data containing event, read from the system */
     77         /*package*/ Event(byte[] data) {
     78             mBuffer = ByteBuffer.wrap(data);
     79             mBuffer.order(ByteOrder.nativeOrder());
     80         }
     81 
     82         /** @return the process ID which wrote the log entry */
     83         public int getProcessId() {
     84             return mBuffer.getInt(PROCESS_OFFSET);
     85         }
     86 
     87         /** @return the thread ID which wrote the log entry */
     88         public int getThreadId() {
     89             return mBuffer.getInt(THREAD_OFFSET);
     90         }
     91 
     92         /** @return the wall clock time when the entry was written */
     93         public long getTimeNanos() {
     94             return mBuffer.getInt(SECONDS_OFFSET) * 1000000000l
     95                     + mBuffer.getInt(NANOSECONDS_OFFSET);
     96         }
     97 
     98         /** @return the type tag code of the entry */
     99         public int getTag() {
    100             return mBuffer.getInt(TAG_OFFSET);
    101         }
    102 
    103         /** @return one of Integer, Long, String, null, or Object[] of same. */
    104         public synchronized Object getData() {
    105             try {
    106                 mBuffer.limit(PAYLOAD_START + mBuffer.getShort(LENGTH_OFFSET));
    107                 mBuffer.position(DATA_START);  // Just after the tag.
    108                 return decodeObject();
    109             } catch (IllegalArgumentException e) {
    110                 Log.wtf(TAG, "Illegal entry payload: tag=" + getTag(), e);
    111                 return null;
    112             } catch (BufferUnderflowException e) {
    113                 Log.wtf(TAG, "Truncated entry payload: tag=" + getTag(), e);
    114                 return null;
    115             }
    116         }
    117 
    118         /** @return the loggable item at the current position in mBuffer. */
    119         private Object decodeObject() {
    120             byte type = mBuffer.get();
    121             switch (type) {
    122             case INT_TYPE:
    123                 return (Integer) mBuffer.getInt();
    124 
    125             case LONG_TYPE:
    126                 return (Long) mBuffer.getLong();
    127 
    128             case STRING_TYPE:
    129                 try {
    130                     int length = mBuffer.getInt();
    131                     int start = mBuffer.position();
    132                     mBuffer.position(start + length);
    133                     return new String(mBuffer.array(), start, length, "UTF-8");
    134                 } catch (UnsupportedEncodingException e) {
    135                     Log.wtf(TAG, "UTF-8 is not supported", e);
    136                     return null;
    137                 }
    138 
    139             case LIST_TYPE:
    140                 int length = mBuffer.get();
    141                 if (length < 0) length += 256;  // treat as signed byte
    142                 Object[] array = new Object[length];
    143                 for (int i = 0; i < length; ++i) array[i] = decodeObject();
    144                 return array;
    145 
    146             default:
    147                 throw new IllegalArgumentException("Unknown entry type: " + type);
    148             }
    149         }
    150     }
    151 
    152     // We assume that the native methods deal with any concurrency issues.
    153 
    154     /**
    155      * Record an event log message.
    156      * @param tag The event type tag code
    157      * @param value A value to log
    158      * @return The number of bytes written
    159      */
    160     public static native int writeEvent(int tag, int value);
    161 
    162     /**
    163      * Record an event log message.
    164      * @param tag The event type tag code
    165      * @param value A value to log
    166      * @return The number of bytes written
    167      */
    168     public static native int writeEvent(int tag, long value);
    169 
    170     /**
    171      * Record an event log message.
    172      * @param tag The event type tag code
    173      * @param str A value to log
    174      * @return The number of bytes written
    175      */
    176     public static native int writeEvent(int tag, String str);
    177 
    178     /**
    179      * Record an event log message.
    180      * @param tag The event type tag code
    181      * @param list A list of values to log
    182      * @return The number of bytes written
    183      */
    184     public static native int writeEvent(int tag, Object... list);
    185 
    186     /**
    187      * Read events from the log, filtered by type.
    188      * @param tags to search for
    189      * @param output container to add events into
    190      * @throws IOException if something goes wrong reading events
    191      */
    192     public static native void readEvents(int[] tags, Collection<Event> output)
    193             throws IOException;
    194 
    195     /**
    196      * Get the name associated with an event type tag code.
    197      * @param tag code to look up
    198      * @return the name of the tag, or null if no tag has that number
    199      */
    200     public static String getTagName(int tag) {
    201         readTagsFile();
    202         return sTagNames.get(tag);
    203     }
    204 
    205     /**
    206      * Get the event type tag code associated with an event name.
    207      * @param name of event to look up
    208      * @return the tag code, or -1 if no tag has that name
    209      */
    210     public static int getTagCode(String name) {
    211         readTagsFile();
    212         Integer code = sTagCodes.get(name);
    213         return code != null ? code : -1;
    214     }
    215 
    216     /**
    217      * Read TAGS_FILE, populating sTagCodes and sTagNames, if not already done.
    218      */
    219     private static synchronized void readTagsFile() {
    220         if (sTagCodes != null && sTagNames != null) return;
    221 
    222         sTagCodes = new HashMap<String, Integer>();
    223         sTagNames = new HashMap<Integer, String>();
    224 
    225         Pattern comment = Pattern.compile(COMMENT_PATTERN);
    226         Pattern tag = Pattern.compile(TAG_PATTERN);
    227         BufferedReader reader = null;
    228         String line;
    229 
    230         try {
    231             reader = new BufferedReader(new FileReader(TAGS_FILE), 256);
    232             while ((line = reader.readLine()) != null) {
    233                 if (comment.matcher(line).matches()) continue;
    234 
    235                 Matcher m = tag.matcher(line);
    236                 if (!m.matches()) {
    237                     Log.wtf(TAG, "Bad entry in " + TAGS_FILE + ": " + line);
    238                     continue;
    239                 }
    240 
    241                 try {
    242                     int num = Integer.parseInt(m.group(1));
    243                     String name = m.group(2);
    244                     sTagCodes.put(name, num);
    245                     sTagNames.put(num, name);
    246                 } catch (NumberFormatException e) {
    247                     Log.wtf(TAG, "Error in " + TAGS_FILE + ": " + line, e);
    248                 }
    249             }
    250         } catch (IOException e) {
    251             Log.wtf(TAG, "Error reading " + TAGS_FILE, e);
    252             // Leave the maps existing but unpopulated
    253         } finally {
    254             try { if (reader != null) reader.close(); } catch (IOException e) {}
    255         }
    256     }
    257 }
    258