1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package android.metrics; 17 18 import android.annotation.SystemApi; 19 import android.annotation.TestApi; 20 import android.util.EventLog; 21 22 import com.android.internal.annotations.VisibleForTesting; 23 import com.android.internal.logging.MetricsLogger; 24 import com.android.internal.logging.nano.MetricsProto.MetricsEvent; 25 26 import java.io.IOException; 27 import java.util.ArrayList; 28 import java.util.Collection; 29 import java.util.LinkedList; 30 import java.util.Queue; 31 import java.util.concurrent.TimeUnit; 32 33 /** 34 * Read platform logs. 35 * 36 * @hide 37 */ 38 @SystemApi 39 @TestApi 40 public class MetricsReader { 41 private Queue<LogMaker> mPendingQueue = new LinkedList<>(); 42 private Queue<LogMaker> mSeenQueue = new LinkedList<>(); 43 private int[] LOGTAGS = {MetricsLogger.LOGTAG}; 44 45 private LogReader mReader = new LogReader(); 46 private int mCheckpointTag = -1; 47 48 /** 49 * Set the reader to isolate unit tests from the framework 50 * 51 * @hide 52 */ 53 @VisibleForTesting setLogReader(LogReader reader)54 public void setLogReader(LogReader reader) { 55 mReader = reader; 56 } 57 58 /** 59 * Read the available logs into a new session. 60 * 61 * The session will contain events starting from the oldest available 62 * log on the system up to the most recent at the time of this call. 63 * 64 * A call to {@link #checkpoint()} will cause the session to contain 65 * only events that occured after that call. 66 * 67 * This call will not return until the system buffer overflows the 68 * specified timestamp. If the specified timestamp is 0, then the 69 * call will return immediately since any logs 1970 have already been 70 * overwritten (n.b. if the underlying system has the capability to 71 * store many decades of system logs, this call may fail in 72 * interesting ways.) 73 * 74 * @param horizonMs block until this timestamp is overwritten, 0 for non-blocking read. 75 */ read(long horizonMs)76 public void read(long horizonMs) { 77 ArrayList<Event> nativeEvents = new ArrayList<>(); 78 try { 79 mReader.readEvents(LOGTAGS, horizonMs, nativeEvents); 80 } catch (IOException e) { 81 e.printStackTrace(); 82 } 83 mPendingQueue.clear(); 84 mSeenQueue.clear(); 85 for (Event event : nativeEvents) { 86 final long eventTimestampMs = event.getTimeMillis(); 87 Object data = event.getData(); 88 Object[] objects; 89 if (data instanceof Object[]) { 90 objects = (Object[]) data; 91 } else { 92 // wrap scalar objects 93 objects = new Object[1]; 94 objects[0] = data; 95 } 96 final LogMaker log = new LogMaker(objects) 97 .setTimestamp(eventTimestampMs) 98 .setUid(event.getUid()) 99 .setProcessId(event.getProcessId()); 100 if (log.getCategory() == MetricsEvent.METRICS_CHECKPOINT) { 101 if (log.getSubtype() == mCheckpointTag) { 102 mPendingQueue.clear(); 103 } 104 } else { 105 mPendingQueue.offer(log); 106 } 107 } 108 } 109 110 /** 111 * Empties the session and causes the next {@link #read(long)} to 112 * yeild a session containing only events that occur after this call. 113 */ checkpoint()114 public void checkpoint() { 115 // write a checkpoint into the log stream 116 mCheckpointTag = (int) (System.currentTimeMillis() % 0x7fffffff); 117 mReader.writeCheckpoint(mCheckpointTag); 118 // any queued event is now too old, so drop them. 119 mPendingQueue.clear(); 120 mSeenQueue.clear(); 121 } 122 123 /** 124 * Rewind the session to the beginning of time and replay all available logs. 125 */ reset()126 public void reset() { 127 // flush the rest of hte pending events 128 mSeenQueue.addAll(mPendingQueue); 129 mPendingQueue.clear(); 130 mCheckpointTag = -1; 131 132 // swap queues 133 Queue<LogMaker> tmp = mPendingQueue; 134 mPendingQueue = mSeenQueue; 135 mSeenQueue = tmp; 136 } 137 138 /* Does the current log session have another entry? */ hasNext()139 public boolean hasNext() { 140 return !mPendingQueue.isEmpty(); 141 } 142 143 /* Return the next entry in the current log session. */ next()144 public LogMaker next() { 145 final LogMaker next = mPendingQueue.poll(); 146 if (next != null) { 147 mSeenQueue.offer(next); 148 } 149 return next; 150 } 151 152 /** 153 * Wrapper for the Event object, to facilitate testing. 154 * 155 * @hide 156 */ 157 @VisibleForTesting 158 public static class Event { 159 long mTimeMillis; 160 int mPid; 161 int mUid; 162 Object mData; 163 Event(long timeMillis, int pid, int uid, Object data)164 public Event(long timeMillis, int pid, int uid, Object data) { 165 mTimeMillis = timeMillis; 166 mPid = pid; 167 mUid = uid; 168 mData = data; 169 } 170 Event(EventLog.Event nativeEvent)171 Event(EventLog.Event nativeEvent) { 172 mTimeMillis = TimeUnit.MILLISECONDS.convert( 173 nativeEvent.getTimeNanos(), TimeUnit.NANOSECONDS); 174 mPid = nativeEvent.getProcessId(); 175 mUid = nativeEvent.getUid(); 176 mData = nativeEvent.getData(); 177 } 178 getTimeMillis()179 public long getTimeMillis() { 180 return mTimeMillis; 181 } 182 getProcessId()183 public int getProcessId() { 184 return mPid; 185 } 186 getUid()187 public int getUid() { 188 return mUid; 189 } 190 getData()191 public Object getData() { 192 return mData; 193 } 194 setData(Object data)195 public void setData(Object data) { 196 mData = data; 197 } 198 } 199 200 /** 201 * Wrapper for the Event reader, to facilitate testing. 202 * 203 * @hide 204 */ 205 @VisibleForTesting 206 public static class LogReader { readEvents(int[] tags, long horizonMs, Collection<Event> events)207 public void readEvents(int[] tags, long horizonMs, Collection<Event> events) 208 throws IOException { 209 // Testing in Android: the Static Final Class Strikes Back! 210 ArrayList<EventLog.Event> nativeEvents = new ArrayList<>(); 211 long horizonNs = TimeUnit.NANOSECONDS.convert(horizonMs, TimeUnit.MILLISECONDS); 212 EventLog.readEventsOnWrapping(tags, horizonNs, nativeEvents); 213 for (EventLog.Event nativeEvent : nativeEvents) { 214 Event event = new Event(nativeEvent); 215 events.add(event); 216 } 217 } 218 writeCheckpoint(int tag)219 public void writeCheckpoint(int tag) { 220 MetricsLogger logger = new MetricsLogger(); 221 logger.action(MetricsEvent.METRICS_CHECKPOINT, tag); 222 } 223 } 224 } 225