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 android.annotation.SystemApi;
     20 import android.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 {
     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
     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 */
     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
    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 */
    112         public int getThreadId() {
    113             return mBuffer.getInt(THREAD_OFFSET);
    114         }
    115 
    116         /** @return the wall clock time when the entry was written */
    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 */
    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. */
    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. */
    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 */
    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         }
    203 
    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         }
    221 
    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         }
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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
    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      */
    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      */
    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     }
    372 }
    373