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 { 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 */ Event(byte[] data)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 */ getProcessId()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 getUid()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 */ getThreadId()110 public int getThreadId() { 111 return mBuffer.getInt(THREAD_OFFSET); 112 } 113 114 /** @return the wall clock time when the entry was written */ getTimeNanos()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 */ getTag()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. */ getData()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. */ decodeObject()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 */ fromBytes(byte[] data)192 public static Event fromBytes(byte[] data) { 193 return new Event(data); 194 } 195 196 /** @hide */ getBytes()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 getLastError()207 public Exception getLastError() { 208 return mLastWtf; 209 } 210 211 /** 212 * Clear the error state for this object. 213 * @hide 214 */ 215 //VisibleForTesting clearError()216 public void clearError() { 217 mLastWtf = null; 218 } 219 220 /** 221 * @hide 222 */ 223 @Override equals(Object o)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 hashCode()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 */ writeEvent(int tag, int value)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 */ writeEvent(int tag, long value)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 */ writeEvent(int tag, float value)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 */ writeEvent(int tag, String str)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 */ writeEvent(int tag, Object... list)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 */ readEvents(int[] tags, Collection<Event> output)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 readEventsOnWrapping(int[] tags, long timestamp, Collection<Event> output)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 */ getTagName(int tag)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 */ getTagCode(String name)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 */ readTagsFile()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