1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372
|
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.util;
import android.annotation.SystemApi;
import android.annotation.UnsupportedAppUsage;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Access to the system diagnostic event record. System diagnostic events are
* used to record certain system-level events (such as garbage collection,
* activity manager state, system watchdogs, and other low level activity),
* which may be automatically collected and analyzed during system development.
*
* <p>This is <b>not</b> the main "logcat" debugging log ({@link android.util.Log})!
* These diagnostic events are for system integrators, not application authors.
*
* <p>Events use integer tag codes corresponding to /system/etc/event-log-tags.
* They carry a payload of one or more int, long, or String values. The
* event-log-tags file defines the payload contents for each type code.
*/
public class EventLog {
/** @hide */ public EventLog() {}
private static final String TAG = "EventLog";
private static final String TAGS_FILE = "/system/etc/event-log-tags";
private static final String COMMENT_PATTERN = "^\\s*(#.*)?$";
private static final String TAG_PATTERN = "^\\s*(\\d+)\\s+(\\w+)\\s*(\\(.*\\))?\\s*$";
private static HashMap<String, Integer> sTagCodes = null;
private static HashMap<Integer, String> sTagNames = null;
/** A previously logged event read from the logs. Instances are thread safe. */
public static final class Event {
private final ByteBuffer mBuffer;
private Exception mLastWtf;
// Layout of event log entry received from Android logger.
// see system/core/include/log/log.h
private static final int LENGTH_OFFSET = 0;
private static final int HEADER_SIZE_OFFSET = 2;
private static final int PROCESS_OFFSET = 4;
private static final int THREAD_OFFSET = 8;
private static final int SECONDS_OFFSET = 12;
private static final int NANOSECONDS_OFFSET = 16;
private static final int UID_OFFSET = 24;
// Layout for event log v1 format, v2 and v3 use HEADER_SIZE_OFFSET
private static final int V1_PAYLOAD_START = 20;
private static final int DATA_OFFSET = 4;
// Value types
private static final byte INT_TYPE = 0;
private static final byte LONG_TYPE = 1;
private static final byte STRING_TYPE = 2;
private static final byte LIST_TYPE = 3;
private static final byte FLOAT_TYPE = 4;
/** @param data containing event, read from the system */
@UnsupportedAppUsage
/*package*/ Event(byte[] data) {
mBuffer = ByteBuffer.wrap(data);
mBuffer.order(ByteOrder.nativeOrder());
}
/** @return the process ID which wrote the log entry */
public int getProcessId() {
return mBuffer.getInt(PROCESS_OFFSET);
}
/**
* @return the UID which wrote the log entry
* @hide
*/
@SystemApi
public int getUid() {
try {
return mBuffer.getInt(UID_OFFSET);
} catch (IndexOutOfBoundsException e) {
// buffer won't contain the UID if the caller doesn't have permission.
return -1;
}
}
/** @return the thread ID which wrote the log entry */
public int getThreadId() {
return mBuffer.getInt(THREAD_OFFSET);
}
/** @return the wall clock time when the entry was written */
public long getTimeNanos() {
return mBuffer.getInt(SECONDS_OFFSET) * 1000000000l
+ mBuffer.getInt(NANOSECONDS_OFFSET);
}
/** @return the type tag code of the entry */
public int getTag() {
int offset = mBuffer.getShort(HEADER_SIZE_OFFSET);
if (offset == 0) {
offset = V1_PAYLOAD_START;
}
return mBuffer.getInt(offset);
}
/** @return one of Integer, Long, Float, String, null, or Object[] of same. */
public synchronized Object getData() {
try {
int offset = mBuffer.getShort(HEADER_SIZE_OFFSET);
if (offset == 0) {
offset = V1_PAYLOAD_START;
}
mBuffer.limit(offset + mBuffer.getShort(LENGTH_OFFSET));
if ((offset + DATA_OFFSET) >= mBuffer.limit()) {
// no payload
return null;
}
mBuffer.position(offset + DATA_OFFSET); // Just after the tag.
return decodeObject();
} catch (IllegalArgumentException e) {
Log.wtf(TAG, "Illegal entry payload: tag=" + getTag(), e);
mLastWtf = e;
return null;
} catch (BufferUnderflowException e) {
Log.wtf(TAG, "Truncated entry payload: tag=" + getTag(), e);
mLastWtf = e;
return null;
}
}
/** @return the loggable item at the current position in mBuffer. */
private Object decodeObject() {
byte type = mBuffer.get();
switch (type) {
case INT_TYPE:
return mBuffer.getInt();
case LONG_TYPE:
return mBuffer.getLong();
case FLOAT_TYPE:
return mBuffer.getFloat();
case STRING_TYPE:
try {
int length = mBuffer.getInt();
int start = mBuffer.position();
mBuffer.position(start + length);
return new String(mBuffer.array(), start, length, "UTF-8");
} catch (UnsupportedEncodingException e) {
Log.wtf(TAG, "UTF-8 is not supported", e);
mLastWtf = e;
return null;
}
case LIST_TYPE:
int length = mBuffer.get();
if (length < 0) length += 256; // treat as signed byte
Object[] array = new Object[length];
for (int i = 0; i < length; ++i) array[i] = decodeObject();
return array;
default:
throw new IllegalArgumentException("Unknown entry type: " + type);
}
}
/** @hide */
public static Event fromBytes(byte[] data) {
return new Event(data);
}
/** @hide */
public byte[] getBytes() {
byte[] bytes = mBuffer.array();
return Arrays.copyOf(bytes, bytes.length);
}
/**
* Retreive the last WTF error generated by this object.
* @hide
*/
//VisibleForTesting
public Exception getLastError() {
return mLastWtf;
}
/**
* Clear the error state for this object.
* @hide
*/
//VisibleForTesting
public void clearError() {
mLastWtf = null;
}
/**
* @hide
*/
@Override
public boolean equals(Object o) {
// Not using ByteBuffer.equals since it takes buffer position into account and we
// always use absolute positions here.
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Event other = (Event) o;
return Arrays.equals(mBuffer.array(), other.mBuffer.array());
}
/**
* @hide
*/
@Override
public int hashCode() {
// Not using ByteBuffer.hashCode since it takes buffer position into account and we
// always use absolute positions here.
return Arrays.hashCode(mBuffer.array());
}
}
// We assume that the native methods deal with any concurrency issues.
/**
* Record an event log message.
* @param tag The event type tag code
* @param value A value to log
* @return The number of bytes written
*/
public static native int writeEvent(int tag, int value);
/**
* Record an event log message.
* @param tag The event type tag code
* @param value A value to log
* @return The number of bytes written
*/
public static native int writeEvent(int tag, long value);
/**
* Record an event log message.
* @param tag The event type tag code
* @param value A value to log
* @return The number of bytes written
*/
public static native int writeEvent(int tag, float value);
/**
* Record an event log message.
* @param tag The event type tag code
* @param str A value to log
* @return The number of bytes written
*/
public static native int writeEvent(int tag, String str);
/**
* Record an event log message.
* @param tag The event type tag code
* @param list A list of values to log
* @return The number of bytes written
*/
public static native int writeEvent(int tag, Object... list);
/**
* Read events from the log, filtered by type.
* @param tags to search for
* @param output container to add events into
* @throws IOException if something goes wrong reading events
*/
public static native void readEvents(int[] tags, Collection<Event> output)
throws IOException;
/**
* Read events from the log, filtered by type, blocking until logs are about to be overwritten.
* @param tags to search for
* @param timestamp timestamp allow logs before this time to be overwritten.
* @param output container to add events into
* @throws IOException if something goes wrong reading events
* @hide
*/
@SystemApi
public static native void readEventsOnWrapping(int[] tags, long timestamp,
Collection<Event> output)
throws IOException;
/**
* Get the name associated with an event type tag code.
* @param tag code to look up
* @return the name of the tag, or null if no tag has that number
*/
public static String getTagName(int tag) {
readTagsFile();
return sTagNames.get(tag);
}
/**
* Get the event type tag code associated with an event name.
* @param name of event to look up
* @return the tag code, or -1 if no tag has that name
*/
public static int getTagCode(String name) {
readTagsFile();
Integer code = sTagCodes.get(name);
return code != null ? code : -1;
}
/**
* Read TAGS_FILE, populating sTagCodes and sTagNames, if not already done.
*/
private static synchronized void readTagsFile() {
if (sTagCodes != null && sTagNames != null) return;
sTagCodes = new HashMap<String, Integer>();
sTagNames = new HashMap<Integer, String>();
Pattern comment = Pattern.compile(COMMENT_PATTERN);
Pattern tag = Pattern.compile(TAG_PATTERN);
BufferedReader reader = null;
String line;
try {
reader = new BufferedReader(new FileReader(TAGS_FILE), 256);
while ((line = reader.readLine()) != null) {
if (comment.matcher(line).matches()) continue;
Matcher m = tag.matcher(line);
if (!m.matches()) {
Log.wtf(TAG, "Bad entry in " + TAGS_FILE + ": " + line);
continue;
}
try {
int num = Integer.parseInt(m.group(1));
String name = m.group(2);
sTagCodes.put(name, num);
sTagNames.put(num, name);
} catch (NumberFormatException e) {
Log.wtf(TAG, "Error in " + TAGS_FILE + ": " + line, e);
}
}
} catch (IOException e) {
Log.wtf(TAG, "Error reading " + TAGS_FILE, e);
// Leave the maps existing but unpopulated
} finally {
try { if (reader != null) reader.close(); } catch (IOException e) {}
}
}
}
|