aboutsummaryrefslogtreecommitdiff
path: root/libs/CommonLib/src/com/android/afwtest/common/test/StatsLogger.java
blob: 6131f9dfc8edab0598b2b740bf3ad51df1a96b17 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.afwtest.common.test;

import static android.os.Environment.getExternalStorageDirectory;

import android.text.TextUtils;
import android.util.Log;
import android.util.Range;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Singleton class to measure and save time stats in a local file in External Storage Directory.
 *
 * <p>writeStatsToFile() should be called at the end of the test.</p>
 */
public final class StatsLogger {

    private static final String TAG = "afwtest.StatsLogger";

    // Map stats file with corresponding {@link StatsLogger} object.
    private static Map<String, StatsLogger> sStasLogger = new HashMap<String, StatsLogger>();

    // File to save stats.
    private File mAfwStatsFile;

    // Time stats being logged by this StatsLogger.
    // The key of the map is the name of a metric.
    private Map<String, Range<Long>> mTimeStats = new HashMap<String, Range<Long>>();

    /**
     * Constructor.
     *
     * @param fileName Path of the file to save stats
     */
    private StatsLogger(String fileName) throws IOException {
        mAfwStatsFile = new File(getExternalStorageDirectory().getAbsolutePath(), fileName);

        if (!mAfwStatsFile.exists()){
            Log.d(TAG, String.format("%s does not exist, creating... ", fileName));
            mAfwStatsFile.createNewFile();
        }
    }

    /**
     * Gets {@link StatsLogger} object for given file, or create a new one if does not exist.
     *
     * @param fileName file relative to external storage.
     */
    public static StatsLogger getInstance(String fileName) throws IOException {
        if (sStasLogger.containsKey(fileName)){
            return sStasLogger.get(fileName);
        }

        StatsLogger statsLogger = new StatsLogger(fileName);
        sStasLogger.put(fileName, statsLogger);
        return statsLogger;
    }

    /**
     * Saves the stats to file.
     */
    public void writeStatsToFile() throws IOException {
        FileOutputStream outputStream = new FileOutputStream(mAfwStatsFile);
        String stats = toCsvFormatString(mTimeStats);

        try {
            outputStream.write(stats.getBytes());
        } catch (Exception e) {
            Log.e(TAG, "Failed to write stats to file", e);
        } finally {
            outputStream.close();
        }
    }

    /**
     * Converts set of time stats to cvs formatted string.
     *
     * @param timeStats time stats to convert, the key of the map is time stats name
     * @return String format:
     *             Key1,Key2,...\n
     *             Time1,Time2,...\n
     */
    private static String toCsvFormatString(Map<String, Range<Long>> timeStats) {
        List<String> keyList = new ArrayList();
        List<String> valueList = new ArrayList();
        for (Map.Entry<String, Range<Long>> entry : timeStats.entrySet()) {
            keyList.add(entry.getKey());
            long interval = entry.getValue().getUpper() - entry.getValue().getLower();
            valueList.add(String.format("%d", interval));
        }
        String keys = TextUtils.join(",", keyList);
        String values = TextUtils.join(",", valueList);

        return String.format("%s\n%s", keys, values);
    }

    /**
     * Start counting time associated with the passed metric.
     *
     * @param keyName name of the metric to measure.
     */
    public void startTime(String keyName){
        long currentTime = System.currentTimeMillis();
        Range<Long> range = Range.create(currentTime, currentTime);
        Log.d(TAG, String.format("%s start-time: %d", keyName, currentTime));

        mTimeStats.put(keyName, range);
        Log.d(TAG, String.format("HashMap for %s:%s", keyName, range.toString()));
    }

    /**
     * Stops counting time associated with the given metric and return the period.
     *
     * @param keyName name of the metric to measure
     * @return time between startTime and stopTime in milliseconds
     */
    public long stopTime(String keyName) throws Exception {
        // Find the key
        if (!mTimeStats.containsKey(keyName)) {
            throw new RuntimeException(String.format("%s was never started", keyName));
        }

        // Set end time
        Range<Long> range = mTimeStats.get(keyName).extend(System.currentTimeMillis());
        mTimeStats.put(keyName, range);
        Log.d(TAG, String.format("%s: %s", keyName, range.toString()));

        return range.getUpper() - range.getLower();
    }
}