| Yiwei Zhang | 0102ad2 | 2018-05-02 17:37:17 -0700 | [diff] [blame] | 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 | #include <android-base/stringprintf.h> | 
|  | 17 | #include <timestatsproto/TimeStatsHelper.h> | 
|  | 18 |  | 
|  | 19 | #include <array> | 
| Yiwei Zhang | 0102ad2 | 2018-05-02 17:37:17 -0700 | [diff] [blame] | 20 |  | 
|  | 21 | #define HISTOGRAM_SIZE 85 | 
|  | 22 |  | 
|  | 23 | using android::base::StringAppendF; | 
|  | 24 | using android::base::StringPrintf; | 
|  | 25 |  | 
|  | 26 | namespace android { | 
|  | 27 | namespace surfaceflinger { | 
|  | 28 |  | 
|  | 29 | // Time buckets for histogram, the calculated time deltas will be lower bounded | 
|  | 30 | // to the buckets in this array. | 
|  | 31 | static const std::array<int32_t, HISTOGRAM_SIZE> histogramConfig = | 
|  | 32 | {0,   1,   2,   3,   4,   5,   6,   7,   8,   9,   10,  11,  12,  13,  14,  15,  16, | 
|  | 33 | 17,  18,  19,  20,  21,  22,  23,  24,  25,  26,  27,  28,  29,  30,  31,  32,  33, | 
|  | 34 | 34,  36,  38,  40,  42,  44,  46,  48,  50,  54,  58,  62,  66,  70,  74,  78,  82, | 
|  | 35 | 86,  90,  94,  98,  102, 106, 110, 114, 118, 122, 126, 130, 134, 138, 142, 146, 150, | 
|  | 36 | 200, 250, 300, 350, 400, 450, 500, 550, 600, 650, 700, 750, 800, 850, 900, 950, 1000}; | 
|  | 37 |  | 
|  | 38 | void TimeStatsHelper::Histogram::insert(int32_t delta) { | 
|  | 39 | if (delta < 0) return; | 
|  | 40 | // std::lower_bound won't work on out of range values | 
|  | 41 | if (delta > histogramConfig[HISTOGRAM_SIZE - 1]) { | 
|  | 42 | hist[histogramConfig[HISTOGRAM_SIZE - 1]]++; | 
|  | 43 | return; | 
|  | 44 | } | 
|  | 45 | auto iter = std::lower_bound(histogramConfig.begin(), histogramConfig.end(), delta); | 
|  | 46 | hist[*iter]++; | 
|  | 47 | } | 
|  | 48 |  | 
| Yiwei Zhang | 8a4015c | 2018-05-08 16:03:47 -0700 | [diff] [blame] | 49 | float TimeStatsHelper::Histogram::averageTime() const { | 
| Yiwei Zhang | 0102ad2 | 2018-05-02 17:37:17 -0700 | [diff] [blame] | 50 | int64_t ret = 0; | 
|  | 51 | int64_t count = 0; | 
| Yiwei Zhang | 8a4015c | 2018-05-08 16:03:47 -0700 | [diff] [blame] | 52 | for (auto& ele : hist) { | 
| Yiwei Zhang | 0102ad2 | 2018-05-02 17:37:17 -0700 | [diff] [blame] | 53 | count += ele.second; | 
|  | 54 | ret += ele.first * ele.second; | 
|  | 55 | } | 
|  | 56 | return static_cast<float>(ret) / count; | 
|  | 57 | } | 
|  | 58 |  | 
| Yiwei Zhang | 8a4015c | 2018-05-08 16:03:47 -0700 | [diff] [blame] | 59 | std::string TimeStatsHelper::Histogram::toString() const { | 
| Yiwei Zhang | 0102ad2 | 2018-05-02 17:37:17 -0700 | [diff] [blame] | 60 | std::string result; | 
|  | 61 | for (int32_t i = 0; i < HISTOGRAM_SIZE; ++i) { | 
|  | 62 | int32_t bucket = histogramConfig[i]; | 
| Yiwei Zhang | 8a4015c | 2018-05-08 16:03:47 -0700 | [diff] [blame] | 63 | int32_t count = (hist.count(bucket) == 0) ? 0 : hist.at(bucket); | 
| Yiwei Zhang | 0102ad2 | 2018-05-02 17:37:17 -0700 | [diff] [blame] | 64 | StringAppendF(&result, "%dms=%d ", bucket, count); | 
|  | 65 | } | 
|  | 66 | result.back() = '\n'; | 
|  | 67 | return result; | 
|  | 68 | } | 
|  | 69 |  | 
| Yiwei Zhang | 8a4015c | 2018-05-08 16:03:47 -0700 | [diff] [blame] | 70 | std::string TimeStatsHelper::TimeStatsLayer::toString() const { | 
| Yiwei Zhang | 0102ad2 | 2018-05-02 17:37:17 -0700 | [diff] [blame] | 71 | std::string result = ""; | 
|  | 72 | StringAppendF(&result, "layerName = %s\n", layerName.c_str()); | 
| Yiwei Zhang | 0102ad2 | 2018-05-02 17:37:17 -0700 | [diff] [blame] | 73 | StringAppendF(&result, "packageName = %s\n", packageName.c_str()); | 
|  | 74 | StringAppendF(&result, "statsStart = %lld\n", static_cast<long long int>(statsStart)); | 
|  | 75 | StringAppendF(&result, "statsEnd = %lld\n", static_cast<long long int>(statsEnd)); | 
|  | 76 | StringAppendF(&result, "totalFrames= %d\n", totalFrames); | 
| Yiwei Zhang | 8a4015c | 2018-05-08 16:03:47 -0700 | [diff] [blame] | 77 | auto iter = deltas.find("present2present"); | 
|  | 78 | if (iter != deltas.end()) { | 
|  | 79 | StringAppendF(&result, "averageFPS = %.3f\n", 1000.0 / iter->second.averageTime()); | 
| Yiwei Zhang | 0102ad2 | 2018-05-02 17:37:17 -0700 | [diff] [blame] | 80 | } | 
| Yiwei Zhang | 8a4015c | 2018-05-08 16:03:47 -0700 | [diff] [blame] | 81 | for (auto& ele : deltas) { | 
| Yiwei Zhang | 0102ad2 | 2018-05-02 17:37:17 -0700 | [diff] [blame] | 82 | StringAppendF(&result, "%s histogram is as below:\n", ele.first.c_str()); | 
|  | 83 | StringAppendF(&result, "%s", ele.second.toString().c_str()); | 
|  | 84 | } | 
|  | 85 |  | 
|  | 86 | return result; | 
|  | 87 | } | 
|  | 88 |  | 
| Yiwei Zhang | 8a4015c | 2018-05-08 16:03:47 -0700 | [diff] [blame] | 89 | std::string TimeStatsHelper::TimeStatsGlobal::toString(std::optional<uint32_t> maxLayers) const { | 
| Yiwei Zhang | 0102ad2 | 2018-05-02 17:37:17 -0700 | [diff] [blame] | 90 | std::string result = "SurfaceFlinger TimeStats:\n"; | 
|  | 91 | StringAppendF(&result, "statsStart = %lld\n", static_cast<long long int>(statsStart)); | 
|  | 92 | StringAppendF(&result, "statsEnd = %lld\n", static_cast<long long int>(statsEnd)); | 
|  | 93 | StringAppendF(&result, "totalFrames= %d\n", totalFrames); | 
|  | 94 | StringAppendF(&result, "missedFrames= %d\n", missedFrames); | 
|  | 95 | StringAppendF(&result, "clientCompositionFrames= %d\n", clientCompositionFrames); | 
|  | 96 | StringAppendF(&result, "TimeStats for each layer is as below:\n"); | 
| Yiwei Zhang | 8a4015c | 2018-05-08 16:03:47 -0700 | [diff] [blame] | 97 | const auto dumpStats = generateDumpStats(maxLayers); | 
|  | 98 | for (auto& ele : dumpStats) { | 
| Yiwei Zhang | 0102ad2 | 2018-05-02 17:37:17 -0700 | [diff] [blame] | 99 | StringAppendF(&result, "%s", ele->toString().c_str()); | 
|  | 100 | } | 
|  | 101 |  | 
|  | 102 | return result; | 
|  | 103 | } | 
|  | 104 |  | 
| Yiwei Zhang | 8a4015c | 2018-05-08 16:03:47 -0700 | [diff] [blame] | 105 | SFTimeStatsLayerProto TimeStatsHelper::TimeStatsLayer::toProto() const { | 
| Yiwei Zhang | 0102ad2 | 2018-05-02 17:37:17 -0700 | [diff] [blame] | 106 | SFTimeStatsLayerProto layerProto; | 
|  | 107 | layerProto.set_layer_name(layerName); | 
| Yiwei Zhang | 0102ad2 | 2018-05-02 17:37:17 -0700 | [diff] [blame] | 108 | layerProto.set_package_name(packageName); | 
|  | 109 | layerProto.set_stats_start(statsStart); | 
|  | 110 | layerProto.set_stats_end(statsEnd); | 
|  | 111 | layerProto.set_total_frames(totalFrames); | 
| Yiwei Zhang | 8a4015c | 2018-05-08 16:03:47 -0700 | [diff] [blame] | 112 | for (auto& ele : deltas) { | 
| Yiwei Zhang | 0102ad2 | 2018-05-02 17:37:17 -0700 | [diff] [blame] | 113 | SFTimeStatsDeltaProto* deltaProto = layerProto.add_deltas(); | 
|  | 114 | deltaProto->set_delta_name(ele.first); | 
| Yiwei Zhang | 8a4015c | 2018-05-08 16:03:47 -0700 | [diff] [blame] | 115 | for (auto& histEle : ele.second.hist) { | 
| Yiwei Zhang | 3bef395 | 2018-05-04 14:08:01 -0700 | [diff] [blame] | 116 | SFTimeStatsHistogramBucketProto* histProto = deltaProto->add_histograms(); | 
| Yiwei Zhang | 0102ad2 | 2018-05-02 17:37:17 -0700 | [diff] [blame] | 117 | histProto->set_render_millis(histEle.first); | 
|  | 118 | histProto->set_frame_count(histEle.second); | 
|  | 119 | } | 
|  | 120 | } | 
|  | 121 | return layerProto; | 
|  | 122 | } | 
|  | 123 |  | 
| Yiwei Zhang | 8a4015c | 2018-05-08 16:03:47 -0700 | [diff] [blame] | 124 | SFTimeStatsGlobalProto TimeStatsHelper::TimeStatsGlobal::toProto( | 
|  | 125 | std::optional<uint32_t> maxLayers) const { | 
| Yiwei Zhang | 0102ad2 | 2018-05-02 17:37:17 -0700 | [diff] [blame] | 126 | SFTimeStatsGlobalProto globalProto; | 
|  | 127 | globalProto.set_stats_start(statsStart); | 
|  | 128 | globalProto.set_stats_end(statsEnd); | 
|  | 129 | globalProto.set_total_frames(totalFrames); | 
|  | 130 | globalProto.set_missed_frames(missedFrames); | 
|  | 131 | globalProto.set_client_composition_frames(clientCompositionFrames); | 
| Yiwei Zhang | 8a4015c | 2018-05-08 16:03:47 -0700 | [diff] [blame] | 132 | const auto dumpStats = generateDumpStats(maxLayers); | 
|  | 133 | for (auto& ele : dumpStats) { | 
| Yiwei Zhang | 0102ad2 | 2018-05-02 17:37:17 -0700 | [diff] [blame] | 134 | SFTimeStatsLayerProto* layerProto = globalProto.add_stats(); | 
|  | 135 | layerProto->CopyFrom(ele->toProto()); | 
|  | 136 | } | 
|  | 137 | return globalProto; | 
|  | 138 | } | 
|  | 139 |  | 
| Yiwei Zhang | 8a4015c | 2018-05-08 16:03:47 -0700 | [diff] [blame] | 140 | std::vector<TimeStatsHelper::TimeStatsLayer const*> | 
|  | 141 | TimeStatsHelper::TimeStatsGlobal::generateDumpStats(std::optional<uint32_t> maxLayers) const { | 
|  | 142 | std::vector<TimeStatsLayer const*> dumpStats; | 
|  | 143 | for (auto& ele : stats) { | 
|  | 144 | dumpStats.push_back(&ele.second); | 
|  | 145 | } | 
|  | 146 |  | 
|  | 147 | std::sort(dumpStats.begin(), dumpStats.end(), | 
|  | 148 | [](TimeStatsHelper::TimeStatsLayer const* l, | 
|  | 149 | TimeStatsHelper::TimeStatsLayer const* r) { | 
|  | 150 | return l->totalFrames > r->totalFrames; | 
|  | 151 | }); | 
|  | 152 |  | 
|  | 153 | if (maxLayers && (*maxLayers < dumpStats.size())) { | 
|  | 154 | dumpStats.resize(*maxLayers); | 
|  | 155 | } | 
|  | 156 | return dumpStats; | 
|  | 157 | } | 
|  | 158 |  | 
| Yiwei Zhang | 0102ad2 | 2018-05-02 17:37:17 -0700 | [diff] [blame] | 159 | } // namespace surfaceflinger | 
|  | 160 | } // namespace android |