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 { 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 */ Event(byte[] data)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 */ getProcessId()87 public int getProcessId() { 88 return mBuffer.getInt(PROCESS_OFFSET); 89 } 90 91 /** @return the thread ID which wrote the log entry */ getThreadId()92 public int getThreadId() { 93 return mBuffer.getInt(THREAD_OFFSET); 94 } 95 96 /** @return the wall clock time when the entry was written */ getTimeNanos()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 */ getTag()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. */ getData()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. */ decodeObject()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 */ fromBytes(byte[] data)167 public static Event fromBytes(byte[] data) { 168 return new Event(data); 169 } 170 171 /** @hide */ getBytes()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 */ writeEvent(int tag, int value)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 */ writeEvent(int tag, long value)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 */ writeEvent(int tag, float value)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 */ writeEvent(int tag, String str)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 */ writeEvent(int tag, Object... list)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 */ readEvents(int[] tags, Collection<Event> output)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 */ getTagName(int tag)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 */ getTagCode(String name)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 */ readTagsFile()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