Home | History | Annotate | Download | only in metrics
      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
     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      */
     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      */
    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      */
    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? */
    139     public boolean hasNext() {
    140         return !mPendingQueue.isEmpty();
    141     }
    142 
    143     /* Return the next entry in the current log session. */
    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 
    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 
    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 
    179         public long getTimeMillis() {
    180             return mTimeMillis;
    181         }
    182 
    183         public int getProcessId() {
    184             return mPid;
    185         }
    186 
    187         public int getUid() {
    188             return mUid;
    189         }
    190 
    191         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<>();
    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 
    219         public void writeCheckpoint(int tag) {
    220             MetricsLogger logger = new MetricsLogger();
    221             logger.action(MetricsEvent.METRICS_CHECKPOINT, tag);
    222         }
    223     }
    224 }
    225