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 
     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 {
     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 */
     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 */
     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
    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 */
    110         public int getThreadId() {
    111             return mBuffer.getInt(THREAD_OFFSET);
    112         }
    113 
    114         /** @return the wall clock time when the entry was written */
    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 */
    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. */
    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. */
    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 */
    192         public static Event fromBytes(byte[] data) {
    193             return new Event(data);
    194         }
    195 
    196         /** @hide */
    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
    207         public Exception getLastError() {
    208             return mLastWtf;
    209         }
    210 
    211         /**
    212          * Clear the error state for this object.
    213          * @hide
    214          */
    215         //VisibleForTesting
    216         public void clearError() {
    217             mLastWtf = null;
    218         }
    219 
    220         /**
    221          * @hide
    222          */
    223         @Override
    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
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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
    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      */
    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      */
    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      */
    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