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