126ca65d42523fca95081d21589f46708987d647cChris Wren/*
226ca65d42523fca95081d21589f46708987d647cChris Wren * Copyright (C) 2017 The Android Open Source Project
326ca65d42523fca95081d21589f46708987d647cChris Wren *
426ca65d42523fca95081d21589f46708987d647cChris Wren * Licensed under the Apache License, Version 2.0 (the "License");
526ca65d42523fca95081d21589f46708987d647cChris Wren * you may not use this file except in compliance with the License.
626ca65d42523fca95081d21589f46708987d647cChris Wren * You may obtain a copy of the License at
726ca65d42523fca95081d21589f46708987d647cChris Wren *
826ca65d42523fca95081d21589f46708987d647cChris Wren *      http://www.apache.org/licenses/LICENSE-2.0
926ca65d42523fca95081d21589f46708987d647cChris Wren *
1026ca65d42523fca95081d21589f46708987d647cChris Wren * Unless required by applicable law or agreed to in writing, software
1126ca65d42523fca95081d21589f46708987d647cChris Wren * distributed under the License is distributed on an "AS IS" BASIS,
1226ca65d42523fca95081d21589f46708987d647cChris Wren * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1326ca65d42523fca95081d21589f46708987d647cChris Wren * See the License for the specific language governing permissions and
1426ca65d42523fca95081d21589f46708987d647cChris Wren * limitations under the License.
1526ca65d42523fca95081d21589f46708987d647cChris Wren */
16b62371434c9b63560c78a85123fe9386edac1205Chris Wrenpackage android.metrics;
17b62371434c9b63560c78a85123fe9386edac1205Chris Wren
18b62371434c9b63560c78a85123fe9386edac1205Chris Wrenimport android.annotation.SystemApi;
19f33926abed907faf31093544303ea51723738214Chris Wrenimport android.util.EventLog;
2026ca65d42523fca95081d21589f46708987d647cChris Wren
2114880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wrenimport com.android.internal.annotations.VisibleForTesting;
22f33926abed907faf31093544303ea51723738214Chris Wrenimport com.android.internal.logging.MetricsLogger;
2314880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wrenimport com.android.internal.logging.nano.MetricsProto.MetricsEvent;
2426ca65d42523fca95081d21589f46708987d647cChris Wren
25f33926abed907faf31093544303ea51723738214Chris Wrenimport java.io.IOException;
26f33926abed907faf31093544303ea51723738214Chris Wrenimport java.util.ArrayList;
2714880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wrenimport java.util.Collection;
28f33926abed907faf31093544303ea51723738214Chris Wrenimport java.util.LinkedList;
2926ca65d42523fca95081d21589f46708987d647cChris Wrenimport java.util.Queue;
305357e642bcd0b97e6afd89ae005d17a2d813734bChris Wrenimport java.util.concurrent.TimeUnit;
3126ca65d42523fca95081d21589f46708987d647cChris Wren
3226ca65d42523fca95081d21589f46708987d647cChris Wren/**
3326ca65d42523fca95081d21589f46708987d647cChris Wren * Read platform logs.
3414880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren *
35b62371434c9b63560c78a85123fe9386edac1205Chris Wren * @hide
3626ca65d42523fca95081d21589f46708987d647cChris Wren */
37b62371434c9b63560c78a85123fe9386edac1205Chris Wren@SystemApi
3826ca65d42523fca95081d21589f46708987d647cChris Wrenpublic class MetricsReader {
3914880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren    private Queue<LogMaker> mPendingQueue = new LinkedList<>();
4014880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren    private Queue<LogMaker> mSeenQueue = new LinkedList<>();
4114880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren    private int[] LOGTAGS = {MetricsLogger.LOGTAG};
4214880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren
4314880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren    private LogReader mReader = new LogReader();
4414880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren    private int mCheckpointTag = -1;
4514880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren
4614880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren    /**
4714880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren     * Set the reader to isolate unit tests from the framework
4814880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren     *
4914880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren     * @hide
5014880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren     */
5114880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren    @VisibleForTesting
5214880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren    public void setLogReader(LogReader reader) {
5314880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren        mReader = reader;
5414880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren    }
5526ca65d42523fca95081d21589f46708987d647cChris Wren
56f33926abed907faf31093544303ea51723738214Chris Wren    /**
57f33926abed907faf31093544303ea51723738214Chris Wren     * Read the available logs into a new session.
5826ca65d42523fca95081d21589f46708987d647cChris Wren     *
59f33926abed907faf31093544303ea51723738214Chris Wren     * The session will contain events starting from the oldest available
60f33926abed907faf31093544303ea51723738214Chris Wren     * log on the system up to the most recent at the time of this call.
61f33926abed907faf31093544303ea51723738214Chris Wren     *
62f33926abed907faf31093544303ea51723738214Chris Wren     * A call to {@link #checkpoint()} will cause the session to contain
63f33926abed907faf31093544303ea51723738214Chris Wren     * only events that occured after that call.
64f33926abed907faf31093544303ea51723738214Chris Wren     *
65f33926abed907faf31093544303ea51723738214Chris Wren     * This call will not return until the system buffer overflows the
66f33926abed907faf31093544303ea51723738214Chris Wren     * specified timestamp. If the specified timestamp is 0, then the
67f33926abed907faf31093544303ea51723738214Chris Wren     * call will return immediately since any logs 1970 have already been
68f33926abed907faf31093544303ea51723738214Chris Wren     * overwritten (n.b. if the underlying system has the capability to
69f33926abed907faf31093544303ea51723738214Chris Wren     * store many decades of system logs, this call may fail in
70f33926abed907faf31093544303ea51723738214Chris Wren     * interesting ways.)
71f33926abed907faf31093544303ea51723738214Chris Wren     *
72f33926abed907faf31093544303ea51723738214Chris Wren     * @param horizonMs block until this timestamp is overwritten, 0 for non-blocking read.
7326ca65d42523fca95081d21589f46708987d647cChris Wren     */
74f33926abed907faf31093544303ea51723738214Chris Wren    public void read(long horizonMs) {
75f33926abed907faf31093544303ea51723738214Chris Wren        ArrayList<Event> nativeEvents = new ArrayList<>();
76f33926abed907faf31093544303ea51723738214Chris Wren        try {
7714880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren            mReader.readEvents(LOGTAGS, horizonMs, nativeEvents);
78f33926abed907faf31093544303ea51723738214Chris Wren        } catch (IOException e) {
79f33926abed907faf31093544303ea51723738214Chris Wren            e.printStackTrace();
80f33926abed907faf31093544303ea51723738214Chris Wren        }
8114880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren        mPendingQueue.clear();
8214880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren        mSeenQueue.clear();
8314880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren        for (Event event : nativeEvents) {
845357e642bcd0b97e6afd89ae005d17a2d813734bChris Wren            final long eventTimestampMs = event.getTimeMillis();
8514880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren            Object data = event.getData();
8614880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren            Object[] objects;
8714880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren            if (data instanceof Object[]) {
8814880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren                objects = (Object[]) data;
8914880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren            } else {
9014880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren                // wrap scalar objects
9114880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren                objects = new Object[1];
9214880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren                objects[0] = data;
9314880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren            }
9414880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren            final LogMaker log = new LogMaker(objects)
9514880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren                    .setTimestamp(eventTimestampMs)
964d6b54d2bfc27747974cd93b6d7f7d0a0ddcb91dChris Wren                    .setUid(event.getUid())
9714880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren                    .setProcessId(event.getProcessId());
9814880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren            if (log.getCategory() == MetricsEvent.METRICS_CHECKPOINT) {
9914880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren                if (log.getSubtype() == mCheckpointTag) {
10014880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren                    mPendingQueue.clear();
101f33926abed907faf31093544303ea51723738214Chris Wren                }
10214880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren            } else {
10314880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren                mPendingQueue.offer(log);
104f33926abed907faf31093544303ea51723738214Chris Wren            }
105f33926abed907faf31093544303ea51723738214Chris Wren        }
10626ca65d42523fca95081d21589f46708987d647cChris Wren    }
10726ca65d42523fca95081d21589f46708987d647cChris Wren
10814880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren    /**
10914880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren     * Empties the session and causes the next {@link #read(long)} to
11014880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren     * yeild a session containing only events that occur after this call.
11114880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren     */
11226ca65d42523fca95081d21589f46708987d647cChris Wren    public void checkpoint() {
11314880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren        // write a checkpoint into the log stream
11414880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren        mCheckpointTag = (int) (System.currentTimeMillis() % 0x7fffffff);
11514880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren        mReader.writeCheckpoint(mCheckpointTag);
116f33926abed907faf31093544303ea51723738214Chris Wren        // any queued event is now too old, so drop them.
11714880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren        mPendingQueue.clear();
11814880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren        mSeenQueue.clear();
11926ca65d42523fca95081d21589f46708987d647cChris Wren    }
12026ca65d42523fca95081d21589f46708987d647cChris Wren
121f33926abed907faf31093544303ea51723738214Chris Wren    /**
12214880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren     * Rewind the session to the beginning of time and replay all available logs.
123f33926abed907faf31093544303ea51723738214Chris Wren     */
12426ca65d42523fca95081d21589f46708987d647cChris Wren    public void reset() {
12514880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren        // flush the rest of hte pending events
12614880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren        mSeenQueue.addAll(mPendingQueue);
12714880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren        mPendingQueue.clear();
12814880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren        mCheckpointTag = -1;
12914880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren
13014880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren        // swap queues
13114880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren        Queue<LogMaker> tmp = mPendingQueue;
13214880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren        mPendingQueue = mSeenQueue;
13314880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren        mSeenQueue = tmp;
13426ca65d42523fca95081d21589f46708987d647cChris Wren    }
13526ca65d42523fca95081d21589f46708987d647cChris Wren
13626ca65d42523fca95081d21589f46708987d647cChris Wren    /* Does the current log session have another entry? */
13726ca65d42523fca95081d21589f46708987d647cChris Wren    public boolean hasNext() {
13814880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren        return !mPendingQueue.isEmpty();
13926ca65d42523fca95081d21589f46708987d647cChris Wren    }
14026ca65d42523fca95081d21589f46708987d647cChris Wren
141f33926abed907faf31093544303ea51723738214Chris Wren    /* Return the next entry in the current log session. */
142b62371434c9b63560c78a85123fe9386edac1205Chris Wren    public LogMaker next() {
14314880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren        final LogMaker next = mPendingQueue.poll();
14414880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren        if (next != null) {
14514880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren            mSeenQueue.offer(next);
14614880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren        }
14714880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren        return next;
14826ca65d42523fca95081d21589f46708987d647cChris Wren    }
14926ca65d42523fca95081d21589f46708987d647cChris Wren
15014880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren    /**
15114880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren     * Wrapper for the Event object, to facilitate testing.
15214880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren     *
15314880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren     * @hide
15414880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren     */
15514880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren    @VisibleForTesting
15614880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren    public static class Event {
1575357e642bcd0b97e6afd89ae005d17a2d813734bChris Wren        long mTimeMillis;
15814880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren        int mPid;
1594d6b54d2bfc27747974cd93b6d7f7d0a0ddcb91dChris Wren        int mUid;
16014880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren        Object mData;
16114880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren
1624d6b54d2bfc27747974cd93b6d7f7d0a0ddcb91dChris Wren        public Event(long timeMillis, int pid, int uid, Object data) {
1635357e642bcd0b97e6afd89ae005d17a2d813734bChris Wren            mTimeMillis = timeMillis;
16414880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren            mPid = pid;
1654d6b54d2bfc27747974cd93b6d7f7d0a0ddcb91dChris Wren            mUid = uid;
16614880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren            mData = data;
16714880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren        }
16814880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren
16914880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren        Event(EventLog.Event nativeEvent) {
1705357e642bcd0b97e6afd89ae005d17a2d813734bChris Wren            mTimeMillis = TimeUnit.MILLISECONDS.convert(
1715357e642bcd0b97e6afd89ae005d17a2d813734bChris Wren                    nativeEvent.getTimeNanos(), TimeUnit.NANOSECONDS);
17214880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren            mPid = nativeEvent.getProcessId();
1734d6b54d2bfc27747974cd93b6d7f7d0a0ddcb91dChris Wren            mUid = nativeEvent.getUid();
17414880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren            mData = nativeEvent.getData();
17514880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren        }
17614880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren
1775357e642bcd0b97e6afd89ae005d17a2d813734bChris Wren        public long getTimeMillis() {
1785357e642bcd0b97e6afd89ae005d17a2d813734bChris Wren            return mTimeMillis;
17914880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren        }
18014880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren
18114880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren        public int getProcessId() {
18214880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren            return mPid;
18314880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren        }
18414880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren
1854d6b54d2bfc27747974cd93b6d7f7d0a0ddcb91dChris Wren        public int getUid() {
1864d6b54d2bfc27747974cd93b6d7f7d0a0ddcb91dChris Wren            return mUid;
1874d6b54d2bfc27747974cd93b6d7f7d0a0ddcb91dChris Wren        }
1884d6b54d2bfc27747974cd93b6d7f7d0a0ddcb91dChris Wren
18914880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren        public Object getData() {
19014880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren            return mData;
19114880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren        }
19214880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren
19314880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren        public void setData(Object data) {
19414880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren            mData = data;
19514880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren        }
19614880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren    }
19714880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren
19814880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren    /**
19914880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren     * Wrapper for the Event reader, to facilitate testing.
20014880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren     *
20114880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren     * @hide
20214880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren     */
20314880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren    @VisibleForTesting
20414880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren    public static class LogReader {
20514880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren        public void readEvents(int[] tags, long horizonMs, Collection<Event> events)
20614880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren                throws IOException {
20714880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren            // Testing in Android: the Static Final Class Strikes Back!
20814880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren            ArrayList<EventLog.Event> nativeEvents = new ArrayList<>();
2095357e642bcd0b97e6afd89ae005d17a2d813734bChris Wren            long horizonNs = TimeUnit.NANOSECONDS.convert(horizonMs, TimeUnit.MILLISECONDS);
2105357e642bcd0b97e6afd89ae005d17a2d813734bChris Wren            EventLog.readEventsOnWrapping(tags, horizonNs, nativeEvents);
21114880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren            for (EventLog.Event nativeEvent : nativeEvents) {
21214880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren                Event event = new Event(nativeEvent);
21314880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren                events.add(event);
21414880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren            }
21514880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren        }
21614880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren
21714880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren        public void writeCheckpoint(int tag) {
21814880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren            MetricsLogger logger = new MetricsLogger();
21914880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren            logger.action(MetricsEvent.METRICS_CHECKPOINT, tag);
22014880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren        }
22114880558045fe55fcb8c86aeaac42f2f21a5b90eChris Wren    }
22226ca65d42523fca95081d21589f46708987d647cChris Wren}
223