blob: aab67c6317ad0a94705513a28f2517eefdbf20e0 [file] [log] [blame]
Sanna Catherine de Treville Wager80448082017-07-11 14:07:59 -07001/*
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
17#define LOG_TAG "ReportPerformance"
18
19#include <fstream>
20#include <iostream>
Eric Tanfefe3162018-09-07 10:09:11 -070021#include <memory>
Sanna Catherine de Treville Wager80448082017-07-11 14:07:59 -070022#include <queue>
23#include <stdarg.h>
24#include <stdint.h>
25#include <stdio.h>
26#include <string.h>
Sanna Catherine de Treville Wager23f89d32017-07-24 18:24:48 -070027#include <sstream>
Sanna Catherine de Treville Wager80448082017-07-11 14:07:59 -070028#include <sys/prctl.h>
Sanna Catherine de Treville Wager2a6a9452017-07-28 11:02:01 -070029#include <sys/time.h>
Sanna Catherine de Treville Wager80448082017-07-11 14:07:59 -070030#include <utility>
Eric Tanfefe3162018-09-07 10:09:11 -070031#include <json/json.h>
Eric Tand6eee712018-09-07 10:58:19 -070032#include <media/MediaAnalyticsItem.h>
Glenn Kasten8589ce72017-09-08 17:03:42 -070033#include <media/nblog/NBLog.h>
34#include <media/nblog/PerformanceAnalysis.h>
35#include <media/nblog/ReportPerformance.h>
Sanna Catherine de Treville Wager80448082017-07-11 14:07:59 -070036#include <utils/Log.h>
37#include <utils/String8.h>
38
39namespace android {
40
Eric Tand6eee712018-09-07 10:58:19 -070041namespace ReportPerformance {
42
Eric Tanfefe3162018-09-07 10:09:11 -070043std::unique_ptr<Json::Value> dumpToJson(const PerformanceData& data)
44{
45 std::unique_ptr<Json::Value> rootPtr = std::make_unique<Json::Value>(Json::objectValue);
46 Json::Value& root = *rootPtr;
47 root["type"] = data.type;
48 root["frameCount"] = (Json::Value::Int)data.frameCount;
49 root["sampleRate"] = (Json::Value::Int)data.sampleRate;
50 root["workMsHist"] = data.workHist.toString();
51 root["latencyMsHist"] = data.latencyHist.toString();
52 root["warmupMsHist"] = data.warmupHist.toString();
53 root["underruns"] = (Json::Value::Int64)data.underruns;
54 root["overruns"] = (Json::Value::Int64)data.overruns;
55 root["activeMs"] = (Json::Value::Int64)ns2ms(data.active);
56 root["durationMs"] = (Json::Value::Int64)ns2ms(systemTime() - data.start);
57 return rootPtr;
58}
Sanna Catherine de Treville Wager80448082017-07-11 14:07:59 -070059
Eric Tand6eee712018-09-07 10:58:19 -070060bool sendToMediaMetrics(const PerformanceData& data)
61{
62 // See documentation for these metrics here:
63 // docs.google.com/document/d/11--6dyOXVOpacYQLZiaOY5QVtQjUyqNx2zT9cCzLKYE/edit?usp=sharing
64 static constexpr char kThreadType[] = "android.media.audiothread.type";
65 static constexpr char kThreadFrameCount[] = "android.media.audiothread.framecount";
66 static constexpr char kThreadSampleRate[] = "android.media.audiothread.samplerate";
67 static constexpr char kThreadWorkHist[] = "android.media.audiothread.workMs.hist";
68 static constexpr char kThreadLatencyHist[] = "android.media.audiothread.latencyMs.hist";
69 static constexpr char kThreadWarmupHist[] = "android.media.audiothread.warmupMs.hist";
70 static constexpr char kThreadUnderruns[] = "android.media.audiothread.underruns";
71 static constexpr char kThreadOverruns[] = "android.media.audiothread.overruns";
72 static constexpr char kThreadActive[] = "android.media.audiothread.activeMs";
73 static constexpr char kThreadDuration[] = "android.media.audiothread.durationMs";
Eric Tanfefe3162018-09-07 10:09:11 -070074
Eric Tand6eee712018-09-07 10:58:19 -070075 std::unique_ptr<MediaAnalyticsItem> item(new MediaAnalyticsItem("audiothread"));
76
77 const Histogram &workHist = data.workHist;
78 if (workHist.totalCount() > 0) {
79 item->setCString(kThreadWorkHist, workHist.toString().c_str());
80 }
81
82 const Histogram &latencyHist = data.latencyHist;
83 if (latencyHist.totalCount() > 0) {
84 item->setCString(kThreadLatencyHist, latencyHist.toString().c_str());
85 }
86
87 const Histogram &warmupHist = data.warmupHist;
88 if (warmupHist.totalCount() > 0) {
89 item->setCString(kThreadWarmupHist, warmupHist.toString().c_str());
90 }
91
92 if (data.underruns > 0) {
93 item->setInt64(kThreadUnderruns, data.underruns);
94 }
95
96 if (data.overruns > 0) {
97 item->setInt64(kThreadOverruns, data.overruns);
98 }
99
100 // Send to Media Metrics if the record is not empty.
101 // The thread and time info are added inside the if statement because
102 // we want to send them only if there are performance metrics to send.
103 if (item->count() > 0) {
104 // Add thread info fields.
105 const int type = data.type;
106 // TODO have a int-to-string mapping defined somewhere else for other thread types.
107 if (type == 2) {
108 item->setCString(kThreadType, "FASTMIXER");
109 } else {
110 item->setCString(kThreadType, "UNKNOWN");
111 }
112 item->setInt32(kThreadFrameCount, data.frameCount);
113 item->setInt32(kThreadSampleRate, data.sampleRate);
114 // Add time info fields.
115 item->setInt64(kThreadActive, data.active / 1000000);
116 item->setInt64(kThreadDuration, (systemTime() - data.start) / 1000000);
117 return item->selfrecord();
118 }
119 return false;
120}
121
122//------------------------------------------------------------------------------
Sanna Catherine de Treville Wager847b6e62017-08-03 11:35:51 -0700123
124// TODO: use a function like this to extract logic from writeToFile
125// https://stackoverflow.com/a/9279620
126
Sanna Catherine de Treville Wager2a6a9452017-07-28 11:02:01 -0700127// Writes outlier intervals, timestamps, and histograms spanning long time intervals to file.
128// TODO: write data in binary format
Eric Tand6eee712018-09-07 10:58:19 -0700129void writeToFile(const std::deque<std::pair<timestamp, Hist>> &hists,
Sanna Catherine de Treville Wager6ad40ee2017-07-28 10:10:55 -0700130 const std::deque<std::pair<msInterval, timestamp>> &outlierData,
Sanna Catherine de Treville Wagerf8c34282017-07-25 11:31:18 -0700131 const std::deque<timestamp> &peakTimestamps,
132 const char * directory, bool append, int author, log_hash_t hash) {
Sanna Catherine de Treville Wager2a6a9452017-07-28 11:02:01 -0700133
134 // TODO: remove old files, implement rotating files as in AudioFlinger.cpp
135
136 if (outlierData.empty() && hists.empty() && peakTimestamps.empty()) {
Sanna Catherine de Treville Wager23f89d32017-07-24 18:24:48 -0700137 ALOGW("No data, returning.");
Sanna Catherine de Treville Wager80448082017-07-11 14:07:59 -0700138 return;
139 }
140
Sanna Catherine de Treville Wager23f89d32017-07-24 18:24:48 -0700141 std::stringstream outlierName;
142 std::stringstream histogramName;
Sanna Catherine de Treville Wagerf8c34282017-07-25 11:31:18 -0700143 std::stringstream peakName;
Sanna Catherine de Treville Wager23f89d32017-07-24 18:24:48 -0700144
Sanna Catherine de Treville Wager2a6a9452017-07-28 11:02:01 -0700145 // get current time
146 char currTime[16]; //YYYYMMDDHHMMSS + '\0' + one unused
147 struct timeval tv;
148 gettimeofday(&tv, NULL);
149 struct tm tm;
150 localtime_r(&tv.tv_sec, &tm);
151 strftime(currTime, sizeof(currTime), "%Y%m%d%H%M%S", &tm);
152
153 // generate file names
154 std::stringstream common;
155 common << author << "_" << hash << "_" << currTime << ".csv";
156
157 histogramName << directory << "histograms_" << common.str();
158 outlierName << directory << "outliers_" << common.str();
159 peakName << directory << "peaks_" << common.str();
Sanna Catherine de Treville Wager23f89d32017-07-24 18:24:48 -0700160
161 std::ofstream hfs;
Sanna Catherine de Treville Wagerf8c34282017-07-25 11:31:18 -0700162 hfs.open(histogramName.str(), append ? std::ios::app : std::ios::trunc);
Sanna Catherine de Treville Wager23f89d32017-07-24 18:24:48 -0700163 if (!hfs.is_open()) {
164 ALOGW("couldn't open file %s", histogramName.str().c_str());
165 return;
166 }
Sanna Catherine de Treville Wager2a6a9452017-07-28 11:02:01 -0700167 // each histogram is written as a line where the first value is the timestamp and
168 // subsequent values are pairs of buckets and counts. Each value is separated
169 // by a comma, and each histogram is separated by a newline.
Sanna Catherine de Treville Wager847b6e62017-08-03 11:35:51 -0700170 for (auto hist = hists.begin(); hist != hists.end(); ++hist) {
171 hfs << hist->first << ", ";
172 for (auto bucket = hist->second.begin(); bucket != hist->second.end(); ++bucket) {
173 hfs << bucket->first / static_cast<double>(kJiffyPerMs)
174 << ", " << bucket->second;
175 if (std::next(bucket) != end(hist->second)) {
176 hfs << ", ";
177 }
Sanna Catherine de Treville Wager23f89d32017-07-24 18:24:48 -0700178 }
Sanna Catherine de Treville Wager847b6e62017-08-03 11:35:51 -0700179 if (std::next(hist) != end(hists)) {
180 hfs << "\n";
181 }
Sanna Catherine de Treville Wager23f89d32017-07-24 18:24:48 -0700182 }
183 hfs.close();
Sanna Catherine de Treville Wagerf8c34282017-07-25 11:31:18 -0700184
185 std::ofstream ofs;
186 ofs.open(outlierName.str(), append ? std::ios::app : std::ios::trunc);
187 if (!ofs.is_open()) {
188 ALOGW("couldn't open file %s", outlierName.str().c_str());
189 return;
190 }
Sanna Catherine de Treville Wager2a6a9452017-07-28 11:02:01 -0700191 // outliers are written as pairs separated by newlines, where each
192 // pair's values are separated by a comma
Sanna Catherine de Treville Wagerf8c34282017-07-25 11:31:18 -0700193 for (const auto &outlier : outlierData) {
Sanna Catherine de Treville Wager2a6a9452017-07-28 11:02:01 -0700194 ofs << outlier.first << ", " << outlier.second << "\n";
Sanna Catherine de Treville Wagerf8c34282017-07-25 11:31:18 -0700195 }
196 ofs.close();
197
198 std::ofstream pfs;
199 pfs.open(peakName.str(), append ? std::ios::app : std::ios::trunc);
200 if (!pfs.is_open()) {
201 ALOGW("couldn't open file %s", peakName.str().c_str());
202 return;
203 }
Sanna Catherine de Treville Wager2a6a9452017-07-28 11:02:01 -0700204 // peaks are simply timestamps separated by commas
Sanna Catherine de Treville Wager847b6e62017-08-03 11:35:51 -0700205 for (auto peak = peakTimestamps.begin(); peak != peakTimestamps.end(); ++peak) {
206 pfs << *peak;
207 if (std::next(peak) != end(peakTimestamps)) {
208 pfs << ", ";
209 }
Sanna Catherine de Treville Wagerf8c34282017-07-25 11:31:18 -0700210 }
211 pfs.close();
Sanna Catherine de Treville Wager80448082017-07-11 14:07:59 -0700212}
213
214} // namespace ReportPerformance
215
216} // namespace android