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