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

Commit 62136d3e authored by Dan Egnor's avatar Dan Egnor
Browse files

Simplify EventLog interface -- remove supported for nested

sequences (which nobody used) and streamline the API, adding
documentation in preparation for inclusion in the SDK.

Gut and deprecate EventLogTags, which unfortunately was put
into the public SDK (an oversight).  Include the functionality
in EventLog proper, in a simpler and easier to use manner.

This change doesn't actually un-@hide anything, but it does
change it to @pending.
parent 432bff01
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