Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit be5460cb authored by Dan Egnor's avatar Dan Egnor Committed by Android (Google) Code Review
Browse files

Merge "Simplify EventLog interface -- remove supported for nested sequences...

Merge "Simplify EventLog interface -- remove supported for nested sequences (which nobody used) and streamline the API, adding documentation in preparation for inclusion in the SDK."
parents 7e6ca580 62136d3e
Loading
Loading
Loading
Loading
+126 −167
Original line number Diff line number Diff line
@@ -16,134 +16,43 @@

package android.util;

import com.google.android.collect.Lists;

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.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.HashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * {@hide}
 * Dynamically defined (in terms of event types), space efficient (i.e. "tight") event logging
 * to help instrument code for large scale stability and performance monitoring.
 *
 * Note that this class contains all static methods.  This is done for efficiency reasons.
 * 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.
 *
 * Events for the event log are self-describing binary data structures.  They start with a 20 byte
 * header (generated automatically) which contains all of the following in order:
 * <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.
 *
 * <ul>
 * <li> Payload length: 2 bytes - length of the non-header portion </li>
 * <li> Padding: 2 bytes - no meaning at this time </li>
 * <li> Timestamp:
 *   <ul>
 *   <li> Seconds: 4 bytes - seconds since Epoch </li>
 *   <li> Nanoseconds: 4 bytes - plus extra nanoseconds </li>
 *   </ul></li>
 * <li> Process ID: 4 bytes - matching {@link android.os.Process#myPid} </li>
 * <li> Thread ID: 4 bytes - matching {@link android.os.Process#myTid} </li>
 * </li>
 * </ul>
 * <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.
 *
 * The above is followed by a payload, comprised of the following:
 * <ul>
 * <li> Tag: 4 bytes - unique integer used to identify a particular event.  This number is also
 *                     used as a key to map to a string that can be displayed by log reading tools.
 * </li>
 * <li> Type: 1 byte - can be either {@link #INT}, {@link #LONG}, {@link #STRING},
 *                     or {@link #LIST}. </li>
 * <li> Event log value: the size and format of which is one of:
 *   <ul>
 *   <li> INT: 4 bytes </li>
 *   <li> LONG: 8 bytes </li>
 *   <li> STRING:
 *     <ul>
 *     <li> Size of STRING: 4 bytes </li>
 *     <li> The string:  n bytes as specified in the size fields above. </li>
 *     </ul></li>
 *   <li> {@link List LIST}:
 *     <ul>
 *     <li> Num items: 1 byte </li>
 *     <li> N value payloads, where N is the number of items specified above. </li>
 *     </ul></li>
 *   </ul>
 * </li>
 * <li> '\n': 1 byte - an automatically generated newline, used to help detect and recover from log
 *                     corruption and enable standard unix tools like grep, tail and wc to operate
 *                     on event logs. </li>
 * </ul>
 *
 * Note that all output is done in the endian-ness of the device (as determined
 * by {@link ByteOrder#nativeOrder()}).
 * @pending
 */

public class EventLog {
    private static final String TAG = "EventLog";

    // Value types
    public static final byte INT    = 0;
    public static final byte LONG   = 1;
    public static final byte STRING = 2;
    public static final byte LIST   = 3;

    /**
     * An immutable tuple used to log a heterogeneous set of loggable items.
     * The items can be Integer, Long, String, or {@link List}.
     * The maximum number of items is 127
     */
    public static final class List {
        private Object[] mItems;

        /**
         * Get a particular tuple item
         * @param pos The position of the item in the tuple
         */
        public final Object getItem(int pos) {
            return mItems[pos];
        }

        /**
         * Get the number of items in the tuple.
         */
        public final byte getNumItems() {
            return (byte) mItems.length;
        }
    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;

        /**
         * Create a new tuple.
         * @param items The items to create the tuple with, as varargs.
         * @throws IllegalArgumentException if the arguments are too few (0),
         *         too many, or aren't loggable types.
         */
        public List(Object... items) throws IllegalArgumentException {
            if (items.length > Byte.MAX_VALUE) {
                throw new IllegalArgumentException(
                        "A List must have fewer than "
                        + Byte.MAX_VALUE + " items in it.");
            }
            for (int i = 0; i < items.length; i++) {
                final Object item = items[i];
                if (item == null) {
                    // Would be nice to be able to write null strings...
                    items[i] = "";
                } else if (!(item instanceof List ||
                      item instanceof String ||
                      item instanceof Integer ||
                      item instanceof Long)) {
                    throw new IllegalArgumentException(
                            "Attempt to create a List with illegal item type.");
                }
            }
            this.mItems = items;
        }
    }

    /**
     * A previously logged event read from the logs.
     */
    /** A previously logged event read from the logs. */
    public static final class Event {
        private final ByteBuffer mBuffer;

@@ -158,77 +67,83 @@ public class EventLog {
        private static final int TAG_OFFSET = 20;
        private static final int DATA_START = 24;

        // 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;

        /** @param data containing event, read from the system */
        public Event(byte[] data) {
        /*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 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() {
            return mBuffer.getInt(TAG_OFFSET);
        }

        /** @return one of Integer, Long, String, or List. */
        /** @return one of Integer, Long, String, null, or Object[] of same. */
        public synchronized Object getData() {
            try {
                mBuffer.limit(PAYLOAD_START + mBuffer.getShort(LENGTH_OFFSET));
                mBuffer.position(DATA_START);  // Just after the tag.
                return decodeObject();
            } catch (IllegalArgumentException e) {
                Log.wtf(TAG, "Illegal entry payload: tag=" + getTag(), e);
                return null;
            } catch (BufferUnderflowException e) {
                Log.wtf(TAG, "Truncated entry payload: tag=" + getTag(), e);
                return null;
            }

        public byte[] getRawData() {
            return mBuffer.array();
        }

        /** @return the loggable item at the current position in mBuffer. */
        private Object decodeObject() {
            if (mBuffer.remaining() < 1) return null;
            switch (mBuffer.get()) {
            case INT:
                if (mBuffer.remaining() < 4) return null;
            byte type = mBuffer.get();
            switch (type) {
            case INT_TYPE:
                return (Integer) mBuffer.getInt();

            case LONG:
                if (mBuffer.remaining() < 8) return null;
            case LONG_TYPE:
                return (Long) mBuffer.getLong();

            case STRING:
            case STRING_TYPE:
                try {
                    if (mBuffer.remaining() < 4) return null;
                    int length = mBuffer.getInt();
                    if (length < 0 || mBuffer.remaining() < length) return null;
                    int start = mBuffer.position();
                    mBuffer.position(start + length);
                    return new String(mBuffer.array(), start, length, "UTF-8");
                } catch (UnsupportedEncodingException e) {
                    throw new RuntimeException(e);  // UTF-8 is guaranteed.
                    Log.wtf(TAG, "UTF-8 is not supported", e);
                    return null;
                }

            case LIST:
                if (mBuffer.remaining() < 1) return null;
            case LIST_TYPE:
                int length = mBuffer.get();
                if (length < 0) return null;
                Object[] array = new Object[length];
                for (int i = 0; i < length; ++i) {
                    array[i] = decodeObject();
                    if (array[i] == null) return null;
                }
                return new List(array);
                for (int i = 0; i < length; ++i) array[i] = decodeObject();
                return array;

            default:
                return null;
                throw new IllegalArgumentException("Unknown entry type: " + type);
            }
        }
    }
@@ -236,46 +151,36 @@ public class EventLog {
    // We assume that the native methods deal with any concurrency issues.

    /**
     * Send an event log message.
     * @param tag An event identifer
     * 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);

    /**
     * Send an event log message.
     * @param tag An event identifer
     * 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);

    /**
     * Send an event log message.
     * @param tag An event identifer
     * 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);

    /**
     * Send an event log message.
     * @param tag An event identifer
     * @param list A {@link List} to log
     * @return The number of bytes written
     */
    public static native int writeEvent(int tag, List list);

    /**
     * Send an event log message.
     * @param tag An event identifer
     * 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 int writeEvent(int tag, Object... list) {
        return writeEvent(tag, new List(list));
    }
    public static native int writeEvent(int tag, Object... list);

    /**
     * Read events from the log, filtered by type.
@@ -287,11 +192,65 @@ public class EventLog {
            throws IOException;

    /**
     * Read events from a file.
     * @param path to read from
     * @param output container to add events into
     * @throws IOException if something goes wrong reading events
     * 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 native void readEvents(String path, Collection<Event> output)
            throws IOException;
    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) {}
        }
    }
}
+8 −48
Original line number Diff line number Diff line
@@ -25,16 +25,14 @@ import java.util.HashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/** Parsed representation of /etc/event-log-tags. */
/**
 * to-bo-deprecated: This class is no longer functional.
 * Use {@link android.util.EventLog} instead.
 */
public class EventLogTags {
    private final static String TAG = "EventLogTags";

    private final static String TAGS_FILE = "/etc/event-log-tags";

    public static class Description {
        public final int mTag;
        public final String mName;
        // TODO: Parse parameter descriptions when anyone has a use for them.

        Description(int tag, String name) {
            mTag = tag;
@@ -42,49 +40,11 @@ public class EventLogTags {
        }
    }

    private final static Pattern COMMENT_PATTERN = Pattern.compile(
            "^\\s*(#.*)?$");

    private final static Pattern TAG_PATTERN = Pattern.compile(
            "^\\s*(\\d+)\\s+(\\w+)\\s*(\\(.*\\))?\\s*$");
    public EventLogTags() throws IOException {}

    private final HashMap<String, Description> mNameMap =
            new HashMap<String, Description>();
    public EventLogTags(BufferedReader input) throws IOException {}

    private final HashMap<Integer, Description> mTagMap =
            new HashMap<Integer, Description>();
    public Description get(String name) { return null; }

    public EventLogTags() throws IOException {
        this(new BufferedReader(new FileReader(TAGS_FILE), 256));
    }

    public EventLogTags(BufferedReader input) throws IOException {
        String line;
        while ((line = input.readLine()) != null) {
            Matcher m = COMMENT_PATTERN.matcher(line);
            if (m.matches()) continue;

            m = TAG_PATTERN.matcher(line);
            if (m.matches()) {
                try {
                    int tag = Integer.parseInt(m.group(1));
                    Description d = new Description(tag, m.group(2));
                    mNameMap.put(d.mName, d);
                    mTagMap.put(d.mTag, d);
                } catch (NumberFormatException e) {
                    Log.e(TAG, "Error in event log tags entry: " + line, e);
                }
            } else {
                Log.e(TAG, "Can't parse event log tags entry: " + line);
            }
        }
    }

    public Description get(String name) {
        return mNameMap.get(name);
    }

    public Description get(int tag) {
        return mTagMap.get(tag);
    }
    public Description get(int tag) { return null; }
}
+67 −218

File changed.

Preview size limit exceeded, changes collapsed.

+4 −7
Original line number Diff line number Diff line
@@ -822,11 +822,9 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
                mRetryMgr.resetRetryCount();

                CdmaCellLocation loc = (CdmaCellLocation)(phone.getCellLocation());
                int bsid = (loc != null) ? loc.getBaseStationId() : -1;

                EventLog.List val = new EventLog.List(bsid,
                EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_CDMA_DATA_SETUP_FAILED,
                        loc != null ? loc.getBaseStationId() : -1,
                        TelephonyManager.getDefault().getNetworkType());
                EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_CDMA_DATA_SETUP_FAILED, val);
            }
            trySetupData(Phone.REASON_CDMA_DATA_DETACHED);
        }
@@ -865,10 +863,9 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {

    private void writeEventLogCdmaDataDrop() {
        CdmaCellLocation loc = (CdmaCellLocation)(phone.getCellLocation());
        int bsid = (loc != null) ? loc.getBaseStationId() : -1;
        EventLog.List val = new EventLog.List(bsid,
        EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_CDMA_DATA_DROP,
                loc != null ? loc.getBaseStationId() : -1,
                TelephonyManager.getDefault().getNetworkType());
        EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_CDMA_DATA_DROP, val);
    }

    protected void onDataStateChanged(AsyncResult ar) {
+2 −4
Original line number Diff line number Diff line
@@ -537,11 +537,9 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
        } else if (!mDesiredPowerState && cm.getRadioState().isOn()) {
            DataConnectionTracker dcTracker = phone.mDataConnection;
            if (! dcTracker.isDataConnectionAsDesired()) {

                EventLog.List val = new EventLog.List(
                EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_DATA_STATE_RADIO_OFF,
                        dcTracker.getStateInString(),
                        (dcTracker.getAnyDataEnabled() ? 1 : 0) );
                EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_DATA_STATE_RADIO_OFF, val);
                        dcTracker.getAnyDataEnabled() ? 1 : 0);
            }
            Message msg = dcTracker.obtainMessage(DataConnectionTracker.EVENT_CLEAN_UP_CONNECTION);
            msg.arg1 = 1; // tearDown is true
Loading