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