blob: 7c8724822961b7fbaa61ffca3decd02cf0846a38 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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 stansard 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 if (items.length < 1) {
128 throw new IllegalArgumentException(
129 "A List must have at least one item in it.");
130 }
131 for (int i = 0; i < items.length; i++) {
132 final Object item = items[i];
133 if (item == null) {
134 // Would be nice to be able to write null strings...
135 items[i] = "";
136 } else if (!(item instanceof List ||
137 item instanceof String ||
138 item instanceof Integer ||
139 item instanceof Long)) {
140 throw new IllegalArgumentException(
141 "Attempt to create a List with illegal item type.");
142 }
143 }
144 this.mItems = items;
145 }
146 }
147
148 /**
149 * A previously logged event read from the logs.
150 */
151 public static final class Event {
152 private final ByteBuffer mBuffer;
153
154 // Layout of event log entry received from kernel.
155 private static final int LENGTH_OFFSET = 0;
156 private static final int PROCESS_OFFSET = 4;
157 private static final int THREAD_OFFSET = 8;
158 private static final int SECONDS_OFFSET = 12;
159 private static final int NANOSECONDS_OFFSET = 16;
160
161 private static final int PAYLOAD_START = 20;
162 private static final int TAG_OFFSET = 20;
163 private static final int DATA_START = 24;
164
165 /** @param data containing event, read from the system */
166 public Event(byte[] data) {
167 mBuffer = ByteBuffer.wrap(data);
168 mBuffer.order(ByteOrder.nativeOrder());
169 }
170
171 public int getProcessId() {
172 return mBuffer.getInt(PROCESS_OFFSET);
173 }
174
175 public int getThreadId() {
176 return mBuffer.getInt(THREAD_OFFSET);
177 }
178
179 public long getTimeNanos() {
180 return mBuffer.getInt(SECONDS_OFFSET) * 1000000000l
181 + mBuffer.getInt(NANOSECONDS_OFFSET);
182 }
183
184 public int getTag() {
185 return mBuffer.getInt(TAG_OFFSET);
186 }
187
188 /** @return one of Integer, Long, String, or List. */
189 public synchronized Object getData() {
190 mBuffer.limit(PAYLOAD_START + mBuffer.getShort(LENGTH_OFFSET));
191 mBuffer.position(DATA_START); // Just after the tag.
192 return decodeObject();
193 }
194
Jim Miller30636872009-06-08 18:59:49 -0700195 public byte[] getRawData() {
196 return mBuffer.array();
197 }
198
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800199 /** @return the loggable item at the current position in mBuffer. */
200 private Object decodeObject() {
201 if (mBuffer.remaining() < 1) return null;
202 switch (mBuffer.get()) {
203 case INT:
204 if (mBuffer.remaining() < 4) return null;
Jim Miller30636872009-06-08 18:59:49 -0700205 return (Integer) mBuffer.getInt();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800206
207 case LONG:
208 if (mBuffer.remaining() < 8) return null;
Jim Miller30636872009-06-08 18:59:49 -0700209 return (Long) mBuffer.getLong();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800210
211 case STRING:
212 try {
213 if (mBuffer.remaining() < 4) return null;
214 int length = mBuffer.getInt();
215 if (length < 0 || mBuffer.remaining() < length) return null;
216 int start = mBuffer.position();
217 mBuffer.position(start + length);
218 return new String(mBuffer.array(), start, length, "UTF-8");
219 } catch (UnsupportedEncodingException e) {
220 throw new RuntimeException(e); // UTF-8 is guaranteed.
221 }
222
223 case LIST:
224 if (mBuffer.remaining() < 1) return null;
225 int length = mBuffer.get();
226 if (length <= 0) return null;
227 Object[] array = new Object[length];
228 for (int i = 0; i < length; ++i) {
229 array[i] = decodeObject();
230 if (array[i] == null) return null;
231 }
232 return new List(array);
233
234 default:
235 return null;
236 }
237 }
238 }
239
240 // We assume that the native methods deal with any concurrency issues.
241
242 /**
243 * Send an event log message.
244 * @param tag An event identifer
245 * @param value A value to log
246 * @return The number of bytes written
247 */
248 public static native int writeEvent(int tag, int value);
249
250 /**
251 * Send an event log message.
252 * @param tag An event identifer
253 * @param value A value to log
254 * @return The number of bytes written
255 */
256 public static native int writeEvent(int tag, long value);
257
258 /**
259 * Send an event log message.
260 * @param tag An event identifer
261 * @param str A value to log
262 * @return The number of bytes written
263 */
264 public static native int writeEvent(int tag, String str);
265
266 /**
267 * Send an event log message.
268 * @param tag An event identifer
269 * @param list A {@link List} to log
270 * @return The number of bytes written
271 */
272 public static native int writeEvent(int tag, List list);
273
274 /**
275 * Send an event log message.
276 * @param tag An event identifer
277 * @param list A list of values to log
278 * @return The number of bytes written
279 */
280 public static int writeEvent(int tag, Object... list) {
281 return writeEvent(tag, new List(list));
282 }
283
284 /**
285 * Read events from the log, filtered by type.
286 * @param tags to search for
287 * @param output container to add events into
288 * @throws IOException if something goes wrong reading events
289 */
290 public static native void readEvents(int[] tags, Collection<Event> output)
291 throws IOException;
292}