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