1 /*
2 * Copyright (C) 2021 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 "pixelstats: ThermalStats"
18
19 #include <aidl/android/frameworks/stats/IStats.h>
20 #include <android-base/file.h>
21 #include <android-base/properties.h>
22 #include <android-base/stringprintf.h>
23 #include <android-base/strings.h>
24 #include <android/binder_manager.h>
25 #include <hardware/google/pixel/pixelstats/pixelatoms.pb.h>
26 #include <pixelstats/ThermalStatsReporter.h>
27 #include <utils/Log.h>
28
29 #include <cinttypes>
30
31 namespace android {
32 namespace hardware {
33 namespace google {
34 namespace pixel {
35
36 using aidl::android::frameworks::stats::IStats;
37 using aidl::android::frameworks::stats::VendorAtom;
38 using aidl::android::frameworks::stats::VendorAtomValue;
39 using android::base::ReadFileToString;
40 using android::hardware::google::pixel::PixelAtoms::ThermalDfsStats;
41
ThermalStatsReporter()42 ThermalStatsReporter::ThermalStatsReporter() {}
43
readDfsCount(const std::string & path,int64_t * val)44 bool ThermalStatsReporter::readDfsCount(const std::string &path, int64_t *val) {
45 std::string file_contents;
46
47 if (path.empty()) {
48 ALOGE("Empty path");
49 return false;
50 }
51
52 if (!ReadFileToString(path.c_str(), &file_contents)) {
53 ALOGE("Unable to read %s - %s", path.c_str(), strerror(errno));
54 return false;
55 } else {
56 int64_t trips[8];
57
58 if (sscanf(file_contents.c_str(),
59 "%" SCNd64 " %" SCNd64 " %" SCNd64 " %" SCNd64 " %" SCNd64 " %" SCNd64
60 " %" SCNd64 " %" SCNd64,
61 &trips[0], &trips[1], &trips[2], &trips[3], &trips[4], &trips[5], &trips[6],
62 &trips[7]) < 8) {
63 ALOGE("Unable to parse trip_counters %s from file %s", file_contents.c_str(),
64 path.c_str());
65 return false;
66 }
67
68 /* Trip#6 corresponds to DFS count */
69 *val = trips[6];
70 }
71
72 return true;
73 }
74
captureThermalDfsStats(const std::vector<std::string> & thermal_stats_paths,struct ThermalDfsCounts * pcur_data)75 bool ThermalStatsReporter::captureThermalDfsStats(
76 const std::vector<std::string> &thermal_stats_paths, struct ThermalDfsCounts *pcur_data) {
77 bool report_stats = false;
78 std::string path;
79
80 if (thermal_stats_paths.size() < kNumOfThermalDfsStats) {
81 ALOGE("Number of thermal stats paths (%zu) is less than expected (%d)",
82 thermal_stats_paths.size(), kNumOfThermalDfsStats);
83 return false;
84 }
85
86 path = thermal_stats_paths[ThermalDfsStats::kBigDfsCountFieldNumber - kVendorAtomOffset];
87 if (!readDfsCount(path, &(pcur_data->big_count))) {
88 pcur_data->big_count = prev_data.big_count;
89 } else {
90 report_stats |= (pcur_data->big_count > prev_data.big_count);
91 }
92
93 path = thermal_stats_paths[ThermalDfsStats::kMidDfsCountFieldNumber - kVendorAtomOffset];
94 if (!readDfsCount(path, &(pcur_data->mid_count))) {
95 pcur_data->mid_count = prev_data.mid_count;
96 } else {
97 report_stats |= (pcur_data->mid_count > prev_data.mid_count);
98 }
99
100 path = thermal_stats_paths[ThermalDfsStats::kLittleDfsCountFieldNumber - kVendorAtomOffset];
101 if (!readDfsCount(path, &(pcur_data->little_count))) {
102 pcur_data->little_count = prev_data.little_count;
103 } else {
104 report_stats |= (pcur_data->little_count > prev_data.little_count);
105 }
106
107 path = thermal_stats_paths[ThermalDfsStats::kGpuDfsCountFieldNumber - kVendorAtomOffset];
108 if (!readDfsCount(path, &(pcur_data->gpu_count))) {
109 pcur_data->gpu_count = prev_data.gpu_count;
110 } else {
111 report_stats |= (pcur_data->gpu_count > prev_data.gpu_count);
112 }
113
114 path = thermal_stats_paths[ThermalDfsStats::kTpuDfsCountFieldNumber - kVendorAtomOffset];
115 if (!readDfsCount(path, &(pcur_data->tpu_count))) {
116 pcur_data->tpu_count = prev_data.tpu_count;
117 } else {
118 report_stats |= (pcur_data->tpu_count > prev_data.tpu_count);
119 }
120
121 path = thermal_stats_paths[ThermalDfsStats::kAurDfsCountFieldNumber - kVendorAtomOffset];
122 if (!readDfsCount(path, &(pcur_data->aur_count))) {
123 pcur_data->aur_count = prev_data.aur_count;
124 } else {
125 report_stats |= (pcur_data->aur_count > prev_data.aur_count);
126 }
127
128 return report_stats;
129 }
130
logThermalDfsStats(const std::shared_ptr<IStats> & stats_client,const std::vector<std::string> & thermal_stats_paths)131 void ThermalStatsReporter::logThermalDfsStats(const std::shared_ptr<IStats> &stats_client,
132 const std::vector<std::string> &thermal_stats_paths) {
133 struct ThermalDfsCounts cur_data = prev_data;
134
135 if (!captureThermalDfsStats(thermal_stats_paths, &cur_data)) {
136 prev_data = cur_data;
137 ALOGI("No update found for thermal stats");
138 return;
139 }
140
141 VendorAtomValue tmp;
142 int64_t max_dfs_count = static_cast<int64_t>(INT32_MAX);
143 int dfs_count;
144 std::vector<VendorAtomValue> values(kNumOfThermalDfsStats);
145
146 dfs_count = std::min<int64_t>(cur_data.big_count - prev_data.big_count, max_dfs_count);
147 tmp.set<VendorAtomValue::intValue>(dfs_count);
148 values[ThermalDfsStats::kBigDfsCountFieldNumber - kVendorAtomOffset] = tmp;
149
150 dfs_count = std::min<int64_t>(cur_data.mid_count - prev_data.mid_count, max_dfs_count);
151 tmp.set<VendorAtomValue::intValue>(dfs_count);
152 values[ThermalDfsStats::kMidDfsCountFieldNumber - kVendorAtomOffset] = tmp;
153
154 dfs_count = std::min<int64_t>(cur_data.little_count - prev_data.little_count, max_dfs_count);
155 tmp.set<VendorAtomValue::intValue>(dfs_count);
156 values[ThermalDfsStats::kLittleDfsCountFieldNumber - kVendorAtomOffset] = tmp;
157
158 dfs_count = std::min<int64_t>(cur_data.gpu_count - prev_data.gpu_count, max_dfs_count);
159 tmp.set<VendorAtomValue::intValue>(dfs_count);
160 values[ThermalDfsStats::kGpuDfsCountFieldNumber - kVendorAtomOffset] = tmp;
161
162 dfs_count = std::min<int64_t>(cur_data.tpu_count - prev_data.tpu_count, max_dfs_count);
163 tmp.set<VendorAtomValue::intValue>(dfs_count);
164 values[ThermalDfsStats::kTpuDfsCountFieldNumber - kVendorAtomOffset] = tmp;
165
166 dfs_count = std::min<int64_t>(cur_data.aur_count - prev_data.aur_count, max_dfs_count);
167 tmp.set<VendorAtomValue::intValue>(dfs_count);
168 values[ThermalDfsStats::kAurDfsCountFieldNumber - kVendorAtomOffset] = tmp;
169
170 prev_data = cur_data;
171
172 ALOGD("Report updated thermal metrics to stats service");
173 // Send vendor atom to IStats HAL
174 VendorAtom event = {.reverseDomainName = "",
175 .atomId = PixelAtoms::Atom::kThermalDfsStats,
176 .values = std::move(values)};
177 const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
178 if (!ret.isOk())
179 ALOGE("Unable to report thermal DFS stats to Stats service");
180 }
181
logThermalStats(const std::shared_ptr<IStats> & stats_client,const std::vector<std::string> & thermal_stats_paths)182 void ThermalStatsReporter::logThermalStats(const std::shared_ptr<IStats> &stats_client,
183 const std::vector<std::string> &thermal_stats_paths) {
184 logThermalDfsStats(stats_client, thermal_stats_paths);
185 }
186
187 } // namespace pixel
188 } // namespace google
189 } // namespace hardware
190 } // namespace android
191