blob: 7e409474da6859f261e4738c5273144c9d3dacf2 [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"
Eric Tanace588c2018-09-12 11:44:43 -070018//#define LOG_NDEBUG 0
Sanna Catherine de Treville Wager80448082017-07-11 14:07:59 -070019
20#include <fstream>
21#include <iostream>
Eric Tanfefe3162018-09-07 10:09:11 -070022#include <memory>
Sanna Catherine de Treville Wager80448082017-07-11 14:07:59 -070023#include <queue>
24#include <stdarg.h>
25#include <stdint.h>
26#include <stdio.h>
27#include <string.h>
Sanna Catherine de Treville Wager23f89d32017-07-24 18:24:48 -070028#include <sstream>
Sanna Catherine de Treville Wager80448082017-07-11 14:07:59 -070029#include <sys/prctl.h>
Sanna Catherine de Treville Wager2a6a9452017-07-28 11:02:01 -070030#include <sys/time.h>
Sanna Catherine de Treville Wager80448082017-07-11 14:07:59 -070031#include <utility>
Eric Tanfefe3162018-09-07 10:09:11 -070032#include <json/json.h>
Eric Tand6eee712018-09-07 10:58:19 -070033#include <media/MediaAnalyticsItem.h>
Eric Tanace588c2018-09-12 11:44:43 -070034#include <media/nblog/Events.h>
Glenn Kasten8589ce72017-09-08 17:03:42 -070035#include <media/nblog/PerformanceAnalysis.h>
36#include <media/nblog/ReportPerformance.h>
Sanna Catherine de Treville Wager80448082017-07-11 14:07:59 -070037#include <utils/Log.h>
38#include <utils/String8.h>
39
40namespace android {
Eric Tand6eee712018-09-07 10:58:19 -070041namespace ReportPerformance {
42
Eric Tan0513b5d2018-09-17 10:32:48 -070043static std::unique_ptr<Json::Value> dumpToJson(const PerformanceData& data)
Eric Tanfefe3162018-09-07 10:09:11 -070044{
45 std::unique_ptr<Json::Value> rootPtr = std::make_unique<Json::Value>(Json::objectValue);
46 Json::Value& root = *rootPtr;
Eric Tan0513b5d2018-09-17 10:32:48 -070047 root["ioHandle"] = data.threadInfo.id;
48 root["type"] = NBLog::threadTypeToString(data.threadInfo.type);
49 root["frameCount"] = (Json::Value::Int)data.threadParams.frameCount;
50 root["sampleRate"] = (Json::Value::Int)data.threadParams.sampleRate;
Eric Tanfefe3162018-09-07 10:09:11 -070051 root["workMsHist"] = data.workHist.toString();
52 root["latencyMsHist"] = data.latencyHist.toString();
53 root["warmupMsHist"] = data.warmupHist.toString();
54 root["underruns"] = (Json::Value::Int64)data.underruns;
55 root["overruns"] = (Json::Value::Int64)data.overruns;
56 root["activeMs"] = (Json::Value::Int64)ns2ms(data.active);
57 root["durationMs"] = (Json::Value::Int64)ns2ms(systemTime() - data.start);
58 return rootPtr;
59}
Sanna Catherine de Treville Wager80448082017-07-11 14:07:59 -070060
Eric Tan0513b5d2018-09-17 10:32:48 -070061static std::string dumpHistogramsToString(const PerformanceData& data)
62{
63 std::stringstream ss;
64 ss << "==========================================\n";
65 ss << "Thread type=" << NBLog::threadTypeToString(data.threadInfo.type)
66 << " handle=" << data.threadInfo.id
67 << " sampleRate=" << data.threadParams.sampleRate
68 << " frameCount=" << data.threadParams.frameCount << "\n";
69 ss << " Thread work times in ms:\n" << data.workHist.asciiArtString(4 /*indent*/);
70 ss << " Thread latencies in ms:\n" << data.latencyHist.asciiArtString(4 /*indent*/);
71 ss << " Thread warmup times in ms:\n" << data.warmupHist.asciiArtString(4 /*indent*/);
72 return ss.str();
73}
74
75void dumpJson(int fd, const std::map<int, PerformanceData>& threadDataMap)
76{
77 if (fd < 0) {
78 return;
79 }
80
81 Json::Value root(Json::arrayValue);
82 for (const auto& item : threadDataMap) {
83 const ReportPerformance::PerformanceData& data = item.second;
84 // Skip threads that do not have performance data recorded yet.
85 if (data.empty()) {
86 continue;
87 }
88 std::unique_ptr<Json::Value> dataJson = ReportPerformance::dumpToJson(data);
89 if (dataJson == nullptr) {
90 continue;
91 }
92 (*dataJson)["threadNum"] = item.first;
93 root.append(*dataJson);
94 }
95 Json::StyledWriter writer;
96 std::string rootStr = writer.write(root);
97 write(fd, rootStr.c_str(), rootStr.size());
98}
99
100void dumpPlots(int fd, const std::map<int, PerformanceData>& threadDataMap)
101{
102 if (fd < 0) {
103 return;
104 }
105
106 for (const auto &item : threadDataMap) {
107 const ReportPerformance::PerformanceData& data = item.second;
108 if (data.empty()) {
109 continue;
110 }
111 std::string hists = ReportPerformance::dumpHistogramsToString(data);
112 write(fd, hists.c_str(), hists.size());
113 }
114}
115
Eric Tand6eee712018-09-07 10:58:19 -0700116bool sendToMediaMetrics(const PerformanceData& data)
117{
118 // See documentation for these metrics here:
119 // docs.google.com/document/d/11--6dyOXVOpacYQLZiaOY5QVtQjUyqNx2zT9cCzLKYE/edit?usp=sharing
120 static constexpr char kThreadType[] = "android.media.audiothread.type";
121 static constexpr char kThreadFrameCount[] = "android.media.audiothread.framecount";
122 static constexpr char kThreadSampleRate[] = "android.media.audiothread.samplerate";
123 static constexpr char kThreadWorkHist[] = "android.media.audiothread.workMs.hist";
124 static constexpr char kThreadLatencyHist[] = "android.media.audiothread.latencyMs.hist";
125 static constexpr char kThreadWarmupHist[] = "android.media.audiothread.warmupMs.hist";
126 static constexpr char kThreadUnderruns[] = "android.media.audiothread.underruns";
127 static constexpr char kThreadOverruns[] = "android.media.audiothread.overruns";
128 static constexpr char kThreadActive[] = "android.media.audiothread.activeMs";
129 static constexpr char kThreadDuration[] = "android.media.audiothread.durationMs";
Eric Tanfefe3162018-09-07 10:09:11 -0700130
Eric Tan0513b5d2018-09-17 10:32:48 -0700131 // Currently, we only allow FastMixer thread data to be sent to Media Metrics.
132 if (data.threadInfo.type != NBLog::FASTMIXER) {
133 return false;
134 }
135
Eric Tand6eee712018-09-07 10:58:19 -0700136 std::unique_ptr<MediaAnalyticsItem> item(new MediaAnalyticsItem("audiothread"));
137
138 const Histogram &workHist = data.workHist;
139 if (workHist.totalCount() > 0) {
140 item->setCString(kThreadWorkHist, workHist.toString().c_str());
141 }
142
143 const Histogram &latencyHist = data.latencyHist;
144 if (latencyHist.totalCount() > 0) {
145 item->setCString(kThreadLatencyHist, latencyHist.toString().c_str());
146 }
147
148 const Histogram &warmupHist = data.warmupHist;
149 if (warmupHist.totalCount() > 0) {
150 item->setCString(kThreadWarmupHist, warmupHist.toString().c_str());
151 }
152
153 if (data.underruns > 0) {
154 item->setInt64(kThreadUnderruns, data.underruns);
155 }
156
157 if (data.overruns > 0) {
158 item->setInt64(kThreadOverruns, data.overruns);
159 }
160
161 // Send to Media Metrics if the record is not empty.
162 // The thread and time info are added inside the if statement because
163 // we want to send them only if there are performance metrics to send.
164 if (item->count() > 0) {
165 // Add thread info fields.
Eric Tanace588c2018-09-12 11:44:43 -0700166 const char * const typeString = NBLog::threadTypeToString(data.threadInfo.type);
167 item->setCString(kThreadType, typeString);
Eric Tan0513b5d2018-09-17 10:32:48 -0700168 item->setInt32(kThreadFrameCount, data.threadParams.frameCount);
169 item->setInt32(kThreadSampleRate, data.threadParams.sampleRate);
Eric Tand6eee712018-09-07 10:58:19 -0700170 // Add time info fields.
171 item->setInt64(kThreadActive, data.active / 1000000);
172 item->setInt64(kThreadDuration, (systemTime() - data.start) / 1000000);
173 return item->selfrecord();
174 }
175 return false;
176}
177
178//------------------------------------------------------------------------------
Sanna Catherine de Treville Wager847b6e62017-08-03 11:35:51 -0700179
180// TODO: use a function like this to extract logic from writeToFile
181// https://stackoverflow.com/a/9279620
182
Sanna Catherine de Treville Wager2a6a9452017-07-28 11:02:01 -0700183// Writes outlier intervals, timestamps, and histograms spanning long time intervals to file.
184// TODO: write data in binary format
Eric Tand6eee712018-09-07 10:58:19 -0700185void writeToFile(const std::deque<std::pair<timestamp, Hist>> &hists,
Sanna Catherine de Treville Wager6ad40ee2017-07-28 10:10:55 -0700186 const std::deque<std::pair<msInterval, timestamp>> &outlierData,
Sanna Catherine de Treville Wagerf8c34282017-07-25 11:31:18 -0700187 const std::deque<timestamp> &peakTimestamps,
188 const char * directory, bool append, int author, log_hash_t hash) {
Sanna Catherine de Treville Wager2a6a9452017-07-28 11:02:01 -0700189
190 // TODO: remove old files, implement rotating files as in AudioFlinger.cpp
191
192 if (outlierData.empty() && hists.empty() && peakTimestamps.empty()) {
Sanna Catherine de Treville Wager23f89d32017-07-24 18:24:48 -0700193 ALOGW("No data, returning.");
Sanna Catherine de Treville Wager80448082017-07-11 14:07:59 -0700194 return;
195 }
196
Sanna Catherine de Treville Wager23f89d32017-07-24 18:24:48 -0700197 std::stringstream outlierName;
198 std::stringstream histogramName;
Sanna Catherine de Treville Wagerf8c34282017-07-25 11:31:18 -0700199 std::stringstream peakName;
Sanna Catherine de Treville Wager23f89d32017-07-24 18:24:48 -0700200
Sanna Catherine de Treville Wager2a6a9452017-07-28 11:02:01 -0700201 // get current time
202 char currTime[16]; //YYYYMMDDHHMMSS + '\0' + one unused
203 struct timeval tv;
204 gettimeofday(&tv, NULL);
205 struct tm tm;
206 localtime_r(&tv.tv_sec, &tm);
207 strftime(currTime, sizeof(currTime), "%Y%m%d%H%M%S", &tm);
208
209 // generate file names
210 std::stringstream common;
211 common << author << "_" << hash << "_" << currTime << ".csv";
212
213 histogramName << directory << "histograms_" << common.str();
214 outlierName << directory << "outliers_" << common.str();
215 peakName << directory << "peaks_" << common.str();
Sanna Catherine de Treville Wager23f89d32017-07-24 18:24:48 -0700216
217 std::ofstream hfs;
Sanna Catherine de Treville Wagerf8c34282017-07-25 11:31:18 -0700218 hfs.open(histogramName.str(), append ? std::ios::app : std::ios::trunc);
Sanna Catherine de Treville Wager23f89d32017-07-24 18:24:48 -0700219 if (!hfs.is_open()) {
220 ALOGW("couldn't open file %s", histogramName.str().c_str());
221 return;
222 }
Sanna Catherine de Treville Wager2a6a9452017-07-28 11:02:01 -0700223 // each histogram is written as a line where the first value is the timestamp and
224 // subsequent values are pairs of buckets and counts. Each value is separated
225 // by a comma, and each histogram is separated by a newline.
Sanna Catherine de Treville Wager847b6e62017-08-03 11:35:51 -0700226 for (auto hist = hists.begin(); hist != hists.end(); ++hist) {
227 hfs << hist->first << ", ";
228 for (auto bucket = hist->second.begin(); bucket != hist->second.end(); ++bucket) {
229 hfs << bucket->first / static_cast<double>(kJiffyPerMs)
230 << ", " << bucket->second;
231 if (std::next(bucket) != end(hist->second)) {
232 hfs << ", ";
233 }
Sanna Catherine de Treville Wager23f89d32017-07-24 18:24:48 -0700234 }
Sanna Catherine de Treville Wager847b6e62017-08-03 11:35:51 -0700235 if (std::next(hist) != end(hists)) {
236 hfs << "\n";
237 }
Sanna Catherine de Treville Wager23f89d32017-07-24 18:24:48 -0700238 }
239 hfs.close();
Sanna Catherine de Treville Wagerf8c34282017-07-25 11:31:18 -0700240
241 std::ofstream ofs;
242 ofs.open(outlierName.str(), append ? std::ios::app : std::ios::trunc);
243 if (!ofs.is_open()) {
244 ALOGW("couldn't open file %s", outlierName.str().c_str());
245 return;
246 }
Sanna Catherine de Treville Wager2a6a9452017-07-28 11:02:01 -0700247 // outliers are written as pairs separated by newlines, where each
248 // pair's values are separated by a comma
Sanna Catherine de Treville Wagerf8c34282017-07-25 11:31:18 -0700249 for (const auto &outlier : outlierData) {
Sanna Catherine de Treville Wager2a6a9452017-07-28 11:02:01 -0700250 ofs << outlier.first << ", " << outlier.second << "\n";
Sanna Catherine de Treville Wagerf8c34282017-07-25 11:31:18 -0700251 }
252 ofs.close();
253
254 std::ofstream pfs;
255 pfs.open(peakName.str(), append ? std::ios::app : std::ios::trunc);
256 if (!pfs.is_open()) {
257 ALOGW("couldn't open file %s", peakName.str().c_str());
258 return;
259 }
Sanna Catherine de Treville Wager2a6a9452017-07-28 11:02:01 -0700260 // peaks are simply timestamps separated by commas
Sanna Catherine de Treville Wager847b6e62017-08-03 11:35:51 -0700261 for (auto peak = peakTimestamps.begin(); peak != peakTimestamps.end(); ++peak) {
262 pfs << *peak;
263 if (std::next(peak) != end(peakTimestamps)) {
264 pfs << ", ";
265 }
Sanna Catherine de Treville Wagerf8c34282017-07-25 11:31:18 -0700266 }
267 pfs.close();
Sanna Catherine de Treville Wager80448082017-07-11 14:07:59 -0700268}
269
Eric Tanace588c2018-09-12 11:44:43 -0700270} // namespace ReportPerformance
Sanna Catherine de Treville Wager80448082017-07-11 14:07:59 -0700271} // namespace android