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