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 17package android.util; 18 19import com.google.android.collect.Lists; 20 21import java.io.IOException; 22import java.io.UnsupportedEncodingException; 23import java.nio.ByteBuffer; 24import java.nio.ByteOrder; 25import java.util.ArrayList; 26import java.util.Collection; 27import java.util.List; 28 29/** 30 * {@hide} 31 * Dynamically defined (in terms of event types), space efficient (i.e. "tight") event logging 32 * to help instrument code for large scale stability and performance monitoring. 33 * 34 * Note that this class contains all static methods. This is done for efficiency reasons. 35 * 36 * Events for the event log are self-describing binary data structures. They start with a 20 byte 37 * header (generated automatically) which contains all of the following in order: 38 * 39 * <ul> 40 * <li> Payload length: 2 bytes - length of the non-header portion </li> 41 * <li> Padding: 2 bytes - no meaning at this time </li> 42 * <li> Timestamp: 43 * <ul> 44 * <li> Seconds: 4 bytes - seconds since Epoch </li> 45 * <li> Nanoseconds: 4 bytes - plus extra nanoseconds </li> 46 * </ul></li> 47 * <li> Process ID: 4 bytes - matching {@link android.os.Process#myPid} </li> 48 * <li> Thread ID: 4 bytes - matching {@link android.os.Process#myTid} </li> 49 * </li> 50 * </ul> 51 * 52 * The above is followed by a payload, comprised of the following: 53 * <ul> 54 * <li> Tag: 4 bytes - unique integer used to identify a particular event. This number is also 55 * used as a key to map to a string that can be displayed by log reading tools. 56 * </li> 57 * <li> Type: 1 byte - can be either {@link #INT}, {@link #LONG}, {@link #STRING}, 58 * or {@link #LIST}. </li> 59 * <li> Event log value: the size and format of which is one of: 60 * <ul> 61 * <li> INT: 4 bytes </li> 62 * <li> LONG: 8 bytes </li> 63 * <li> STRING: 64 * <ul> 65 * <li> Size of STRING: 4 bytes </li> 66 * <li> The string: n bytes as specified in the size fields above. </li> 67 * </ul></li> 68 * <li> {@link List LIST}: 69 * <ul> 70 * <li> Num items: 1 byte </li> 71 * <li> N value payloads, where N is the number of items specified above. </li> 72 * </ul></li> 73 * </ul> 74 * </li> 75 * <li> '\n': 1 byte - an automatically generated newline, used to help detect and recover from log 76 * corruption and enable standard unix tools like grep, tail and wc to operate 77 * on event logs. </li> 78 * </ul> 79 * 80 * Note that all output is done in the endian-ness of the device (as determined 81 * by {@link ByteOrder#nativeOrder()}). 82 */ 83 84public class EventLog { 85 86 // Value types 87 public static final byte INT = 0; 88 public static final byte LONG = 1; 89 public static final byte STRING = 2; 90 public static final byte LIST = 3; 91 92 /** 93 * An immutable tuple used to log a heterogeneous set of loggable items. 94 * The items can be Integer, Long, String, or {@link List}. 95 * The maximum number of items is 127 96 */ 97 public static final class List { 98 private Object[] mItems; 99 100 /** 101 * Get a particular tuple item 102 * @param pos The position of the item in the tuple 103 */ 104 public final Object getItem(int pos) { 105 return mItems[pos]; 106 } 107 108 /** 109 * Get the number of items in the tuple. 110 */ 111 public final byte getNumItems() { 112 return (byte) mItems.length; 113 } 114 115 /** 116 * Create a new tuple. 117 * @param items The items to create the tuple with, as varargs. 118 * @throws IllegalArgumentException if the arguments are too few (0), 119 * too many, or aren't loggable types. 120 */ 121 public List(Object... items) throws IllegalArgumentException { 122 if (items.length > Byte.MAX_VALUE) { 123 throw new IllegalArgumentException( 124 "A List must have fewer than " 125 + Byte.MAX_VALUE + " items in it."); 126 } 127 for (int i = 0; i < items.length; i++) { 128 final Object item = items[i]; 129 if (item == null) { 130 // Would be nice to be able to write null strings... 131 items[i] = ""; 132 } else if (!(item instanceof List || 133 item instanceof String || 134 item instanceof Integer || 135 item instanceof Long)) { 136 throw new IllegalArgumentException( 137 "Attempt to create a List with illegal item type."); 138 } 139 } 140 this.mItems = items; 141 } 142 } 143 144 /** 145 * A previously logged event read from the logs. 146 */ 147 public static final class Event { 148 private final ByteBuffer mBuffer; 149 150 // Layout of event log entry received from kernel. 151 private static final int LENGTH_OFFSET = 0; 152 private static final int PROCESS_OFFSET = 4; 153 private static final int THREAD_OFFSET = 8; 154 private static final int SECONDS_OFFSET = 12; 155 private static final int NANOSECONDS_OFFSET = 16; 156 157 private static final int PAYLOAD_START = 20; 158 private static final int TAG_OFFSET = 20; 159 private static final int DATA_START = 24; 160 161 /** @param data containing event, read from the system */ 162 public Event(byte[] data) { 163 mBuffer = ByteBuffer.wrap(data); 164 mBuffer.order(ByteOrder.nativeOrder()); 165 } 166 167 public int getProcessId() { 168 return mBuffer.getInt(PROCESS_OFFSET); 169 } 170 171 public int getThreadId() { 172 return mBuffer.getInt(THREAD_OFFSET); 173 } 174 175 public long getTimeNanos() { 176 return mBuffer.getInt(SECONDS_OFFSET) * 1000000000l 177 + mBuffer.getInt(NANOSECONDS_OFFSET); 178 } 179 180 public int getTag() { 181 return mBuffer.getInt(TAG_OFFSET); 182 } 183 184 /** @return one of Integer, Long, String, or List. */ 185 public synchronized Object getData() { 186 mBuffer.limit(PAYLOAD_START + mBuffer.getShort(LENGTH_OFFSET)); 187 mBuffer.position(DATA_START); // Just after the tag. 188 return decodeObject(); 189 } 190 191 public byte[] getRawData() { 192 return mBuffer.array(); 193 } 194 195 /** @return the loggable item at the current position in mBuffer. */ 196 private Object decodeObject() { 197 if (mBuffer.remaining() < 1) return null; 198 switch (mBuffer.get()) { 199 case INT: 200 if (mBuffer.remaining() < 4) return null; 201 return (Integer) mBuffer.getInt(); 202 203 case LONG: 204 if (mBuffer.remaining() < 8) return null; 205 return (Long) mBuffer.getLong(); 206 207 case STRING: 208 try { 209 if (mBuffer.remaining() < 4) return null; 210 int length = mBuffer.getInt(); 211 if (length < 0 || mBuffer.remaining() < length) return null; 212 int start = mBuffer.position(); 213 mBuffer.position(start + length); 214 return new String(mBuffer.array(), start, length, "UTF-8"); 215 } catch (UnsupportedEncodingException e) { 216 throw new RuntimeException(e); // UTF-8 is guaranteed. 217 } 218 219 case LIST: 220 if (mBuffer.remaining() < 1) return null; 221 int length = mBuffer.get(); 222 if (length < 0) return null; 223 Object[] array = new Object[length]; 224 for (int i = 0; i < length; ++i) { 225 array[i] = decodeObject(); 226 if (array[i] == null) return null; 227 } 228 return new List(array); 229 230 default: 231 return null; 232 } 233 } 234 } 235 236 // We assume that the native methods deal with any concurrency issues. 237 238 /** 239 * Send an event log message. 240 * @param tag An event identifer 241 * @param value A value to log 242 * @return The number of bytes written 243 */ 244 public static native int writeEvent(int tag, int value); 245 246 /** 247 * Send an event log message. 248 * @param tag An event identifer 249 * @param value A value to log 250 * @return The number of bytes written 251 */ 252 public static native int writeEvent(int tag, long value); 253 254 /** 255 * Send an event log message. 256 * @param tag An event identifer 257 * @param str A value to log 258 * @return The number of bytes written 259 */ 260 public static native int writeEvent(int tag, String str); 261 262 /** 263 * Send an event log message. 264 * @param tag An event identifer 265 * @param list A {@link List} to log 266 * @return The number of bytes written 267 */ 268 public static native int writeEvent(int tag, List list); 269 270 /** 271 * Send an event log message. 272 * @param tag An event identifer 273 * @param list A list of values to log 274 * @return The number of bytes written 275 */ 276 public static int writeEvent(int tag, Object... list) { 277 return writeEvent(tag, new List(list)); 278 } 279 280 /** 281 * Read events from the log, filtered by type. 282 * @param tags to search for 283 * @param output container to add events into 284 * @throws IOException if something goes wrong reading events 285 */ 286 public static native void readEvents(int[] tags, Collection<Event> output) 287 throws IOException; 288 289 /** 290 * Read events from a file. 291 * @param path to read from 292 * @param output container to add events into 293 * @throws IOException if something goes wrong reading events 294 */ 295 public static native void readEvents(String path, Collection<Event> output) 296 throws IOException; 297} 298