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