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