blob: 27f9a5dbf51cb77bcc9e5287005aa9454e121661 [file] [log] [blame]
Chris Wren26ca65d2016-11-29 10:43:28 -05001/*
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 */
Chris Wrenb6237142017-01-23 16:42:58 -050016package android.metrics;
17
18import android.annotation.SystemApi;
Wale Ogunwale691af682019-02-11 03:09:10 -080019import android.annotation.TestApi;
Chris Wrenf33926a2017-02-27 17:42:58 -050020import android.util.EventLog;
Chris Wren26ca65d2016-11-29 10:43:28 -050021
Chris Wren148805582017-03-17 17:18:11 -040022import com.android.internal.annotations.VisibleForTesting;
Chris Wrenf33926a2017-02-27 17:42:58 -050023import com.android.internal.logging.MetricsLogger;
Chris Wren148805582017-03-17 17:18:11 -040024import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Chris Wren26ca65d2016-11-29 10:43:28 -050025
Chris Wrenf33926a2017-02-27 17:42:58 -050026import java.io.IOException;
27import java.util.ArrayList;
Chris Wren148805582017-03-17 17:18:11 -040028import java.util.Collection;
Chris Wrenf33926a2017-02-27 17:42:58 -050029import java.util.LinkedList;
Chris Wren26ca65d2016-11-29 10:43:28 -050030import java.util.Queue;
Chris Wren5357e642017-04-12 16:19:57 -040031import java.util.concurrent.TimeUnit;
Chris Wren26ca65d2016-11-29 10:43:28 -050032
33/**
34 * Read platform logs.
Chris Wren148805582017-03-17 17:18:11 -040035 *
Chris Wrenb6237142017-01-23 16:42:58 -050036 * @hide
Chris Wren26ca65d2016-11-29 10:43:28 -050037 */
Chris Wrenb6237142017-01-23 16:42:58 -050038@SystemApi
Wale Ogunwale691af682019-02-11 03:09:10 -080039@TestApi
Chris Wren26ca65d2016-11-29 10:43:28 -050040public class MetricsReader {
Chris Wren148805582017-03-17 17:18:11 -040041 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
54 public void setLogReader(LogReader reader) {
55 mReader = reader;
56 }
Chris Wren26ca65d2016-11-29 10:43:28 -050057
Chris Wrenf33926a2017-02-27 17:42:58 -050058 /**
59 * Read the available logs into a new session.
Chris Wren26ca65d2016-11-29 10:43:28 -050060 *
Chris Wrenf33926a2017-02-27 17:42:58 -050061 * 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.
Chris Wren26ca65d2016-11-29 10:43:28 -050075 */
Chris Wrenf33926a2017-02-27 17:42:58 -050076 public void read(long horizonMs) {
77 ArrayList<Event> nativeEvents = new ArrayList<>();
78 try {
Chris Wren148805582017-03-17 17:18:11 -040079 mReader.readEvents(LOGTAGS, horizonMs, nativeEvents);
Chris Wrenf33926a2017-02-27 17:42:58 -050080 } catch (IOException e) {
81 e.printStackTrace();
82 }
Chris Wren148805582017-03-17 17:18:11 -040083 mPendingQueue.clear();
84 mSeenQueue.clear();
85 for (Event event : nativeEvents) {
Chris Wren5357e642017-04-12 16:19:57 -040086 final long eventTimestampMs = event.getTimeMillis();
Chris Wren148805582017-03-17 17:18:11 -040087 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)
Chris Wren4d6b54d2017-04-27 16:56:54 -040098 .setUid(event.getUid())
Chris Wren148805582017-03-17 17:18:11 -040099 .setProcessId(event.getProcessId());
100 if (log.getCategory() == MetricsEvent.METRICS_CHECKPOINT) {
101 if (log.getSubtype() == mCheckpointTag) {
102 mPendingQueue.clear();
Chris Wrenf33926a2017-02-27 17:42:58 -0500103 }
Chris Wren148805582017-03-17 17:18:11 -0400104 } else {
105 mPendingQueue.offer(log);
Chris Wrenf33926a2017-02-27 17:42:58 -0500106 }
107 }
Chris Wren26ca65d2016-11-29 10:43:28 -0500108 }
109
Chris Wren148805582017-03-17 17:18:11 -0400110 /**
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 */
Chris Wren26ca65d2016-11-29 10:43:28 -0500114 public void checkpoint() {
Chris Wren148805582017-03-17 17:18:11 -0400115 // write a checkpoint into the log stream
116 mCheckpointTag = (int) (System.currentTimeMillis() % 0x7fffffff);
117 mReader.writeCheckpoint(mCheckpointTag);
Chris Wrenf33926a2017-02-27 17:42:58 -0500118 // any queued event is now too old, so drop them.
Chris Wren148805582017-03-17 17:18:11 -0400119 mPendingQueue.clear();
120 mSeenQueue.clear();
Chris Wren26ca65d2016-11-29 10:43:28 -0500121 }
122
Chris Wrenf33926a2017-02-27 17:42:58 -0500123 /**
Chris Wren148805582017-03-17 17:18:11 -0400124 * Rewind the session to the beginning of time and replay all available logs.
Chris Wrenf33926a2017-02-27 17:42:58 -0500125 */
Chris Wren26ca65d2016-11-29 10:43:28 -0500126 public void reset() {
Chris Wren148805582017-03-17 17:18:11 -0400127 // 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;
Chris Wren26ca65d2016-11-29 10:43:28 -0500136 }
137
138 /* Does the current log session have another entry? */
139 public boolean hasNext() {
Chris Wren148805582017-03-17 17:18:11 -0400140 return !mPendingQueue.isEmpty();
Chris Wren26ca65d2016-11-29 10:43:28 -0500141 }
142
Chris Wrenf33926a2017-02-27 17:42:58 -0500143 /* Return the next entry in the current log session. */
Chris Wrenb6237142017-01-23 16:42:58 -0500144 public LogMaker next() {
Chris Wren148805582017-03-17 17:18:11 -0400145 final LogMaker next = mPendingQueue.poll();
146 if (next != null) {
147 mSeenQueue.offer(next);
148 }
149 return next;
Chris Wren26ca65d2016-11-29 10:43:28 -0500150 }
151
Chris Wren148805582017-03-17 17:18:11 -0400152 /**
153 * Wrapper for the Event object, to facilitate testing.
154 *
155 * @hide
156 */
157 @VisibleForTesting
158 public static class Event {
Chris Wren5357e642017-04-12 16:19:57 -0400159 long mTimeMillis;
Chris Wren148805582017-03-17 17:18:11 -0400160 int mPid;
Chris Wren4d6b54d2017-04-27 16:56:54 -0400161 int mUid;
Chris Wren148805582017-03-17 17:18:11 -0400162 Object mData;
163
Chris Wren4d6b54d2017-04-27 16:56:54 -0400164 public Event(long timeMillis, int pid, int uid, Object data) {
Chris Wren5357e642017-04-12 16:19:57 -0400165 mTimeMillis = timeMillis;
Chris Wren148805582017-03-17 17:18:11 -0400166 mPid = pid;
Chris Wren4d6b54d2017-04-27 16:56:54 -0400167 mUid = uid;
Chris Wren148805582017-03-17 17:18:11 -0400168 mData = data;
169 }
170
171 Event(EventLog.Event nativeEvent) {
Chris Wren5357e642017-04-12 16:19:57 -0400172 mTimeMillis = TimeUnit.MILLISECONDS.convert(
173 nativeEvent.getTimeNanos(), TimeUnit.NANOSECONDS);
Chris Wren148805582017-03-17 17:18:11 -0400174 mPid = nativeEvent.getProcessId();
Chris Wren4d6b54d2017-04-27 16:56:54 -0400175 mUid = nativeEvent.getUid();
Chris Wren148805582017-03-17 17:18:11 -0400176 mData = nativeEvent.getData();
177 }
178
Chris Wren5357e642017-04-12 16:19:57 -0400179 public long getTimeMillis() {
180 return mTimeMillis;
Chris Wren148805582017-03-17 17:18:11 -0400181 }
182
183 public int getProcessId() {
184 return mPid;
185 }
186
Chris Wren4d6b54d2017-04-27 16:56:54 -0400187 public int getUid() {
188 return mUid;
189 }
190
Chris Wren148805582017-03-17 17:18:11 -0400191 public Object getData() {
192 return mData;
193 }
194
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 {
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<>();
Chris Wren5357e642017-04-12 16:19:57 -0400211 long horizonNs = TimeUnit.NANOSECONDS.convert(horizonMs, TimeUnit.MILLISECONDS);
212 EventLog.readEventsOnWrapping(tags, horizonNs, nativeEvents);
Chris Wren148805582017-03-17 17:18:11 -0400213 for (EventLog.Event nativeEvent : nativeEvents) {
214 Event event = new Event(nativeEvent);
215 events.add(event);
216 }
217 }
218
219 public void writeCheckpoint(int tag) {
220 MetricsLogger logger = new MetricsLogger();
221 logger.action(MetricsEvent.METRICS_CHECKPOINT, tag);
222 }
223 }
Chris Wren26ca65d2016-11-29 10:43:28 -0500224}