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: TempResidencyStats"
18
19 #include <aidl/android/frameworks/stats/IStats.h>
20 #include <android-base/chrono_utils.h>
21 #include <android-base/file.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/TempResidencyReporter.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
42 /**
43 * Parse file_contents and read residency stats into cur_stats.
44 */
parse_file_contents(std::string file_contents,std::map<std::string,std::vector<int64_t>> * cur_stats)45 bool parse_file_contents(std::string file_contents,
46 std::map<std::string, std::vector<int64_t>> *cur_stats) {
47 const char *data = file_contents.c_str();
48 int data_len = file_contents.length();
49 char sensor_name[32];
50 int offset = 0;
51 int bytes_read;
52
53 while (sscanf(data + offset, "THERMAL ZONE: %31s\n%n", sensor_name, &bytes_read) == 1) {
54 int64_t temp_res_value;
55 int num_stats_buckets;
56 int index = 0;
57 offset += bytes_read;
58 if (offset >= data_len)
59 return false;
60
61 std::string sensor_name_str = sensor_name;
62
63 if (!sscanf(data + offset, "NUM_TEMP_RESIDENCY_BUCKETS: %d\n%n", &num_stats_buckets,
64 &bytes_read))
65 return false;
66 offset += bytes_read;
67 if (offset >= data_len)
68 return false;
69 while (index < num_stats_buckets) {
70 if (sscanf(data + offset, "-inf - %*d ====> %ldms\n%n", &temp_res_value, &bytes_read) !=
71 1 &&
72 sscanf(data + offset, "%*d - %*d ====> %ldms\n%n", &temp_res_value, &bytes_read) !=
73 1 &&
74 sscanf(data + offset, "%*d - inf ====> %ldms\n\n%n", &temp_res_value,
75 &bytes_read) != 1)
76 return false;
77
78 (*cur_stats)[sensor_name_str].push_back(temp_res_value);
79 index++;
80
81 offset += bytes_read;
82 if ((offset >= data_len) && (index < num_stats_buckets))
83 return false;
84 }
85 }
86 return true;
87 }
88
89 /**
90 * Logs the Temperature residency stats for every thermal zone.
91 */
logTempResidencyStats(const std::shared_ptr<IStats> & stats_client,const char * const temperature_residency_path)92 void TempResidencyReporter::logTempResidencyStats(const std::shared_ptr<IStats> &stats_client,
93 const char *const temperature_residency_path) {
94 if (!temperature_residency_path) {
95 ALOGV("TempResidencyStatsPath path not specified");
96 return;
97 }
98 std::string file_contents;
99 if (!ReadFileToString(temperature_residency_path, &file_contents)) {
100 ALOGE("Unable to read TempResidencyStatsPath %s - %s", temperature_residency_path,
101 strerror(errno));
102 return;
103 }
104 std::map<std::string, std::vector<int64_t>> cur_stats_map;
105 if (!parse_file_contents(file_contents, &cur_stats_map)) {
106 ALOGE("Fail to parse TempResidencyStatsPath");
107 return;
108 }
109 if (!cur_stats_map.size())
110 return;
111 ::android::base::boot_clock::time_point curTime = ::android::base::boot_clock::now();
112 int64_t since_last_update_ms =
113 std::chrono::duration_cast<std::chrono::milliseconds>(curTime - prevTime).count();
114
115 auto cur_stats_map_iterator = cur_stats_map.begin();
116 VendorAtomValue tmp_atom_value;
117
118 // Iterate through cur_stats_map by sensor_name
119 while (cur_stats_map_iterator != cur_stats_map.end()) {
120 std::vector<VendorAtomValue> values;
121 std::string sensor_name_str = cur_stats_map_iterator->first;
122 std::vector<int64_t> residency_stats = cur_stats_map_iterator->second;
123 tmp_atom_value.set<VendorAtomValue::stringValue>(sensor_name_str);
124 values.push_back(tmp_atom_value);
125 tmp_atom_value.set<VendorAtomValue::longValue>(since_last_update_ms);
126 values.push_back(tmp_atom_value);
127
128 bool key_in_map = (prev_stats.find(sensor_name_str)) != prev_stats.end();
129 bool stat_len_match = (residency_stats.size() == prev_stats[sensor_name_str].size());
130 if (key_in_map && !stat_len_match)
131 prev_stats[sensor_name_str].clear();
132
133 int64_t sum_residency = 0;
134 if (residency_stats.size() > kMaxBucketLen) {
135 cur_stats_map_iterator++;
136 continue;
137 }
138 // Iterate over every temperature residency buckets
139 for (int index = 0; index < residency_stats.size(); index++) {
140 // Get diff if stats arr length match previous stats
141 // Otherwise use raw stats as temperature residency stats per day
142 if (key_in_map && stat_len_match) {
143 int64_t diff_residency =
144 residency_stats[index] - prev_stats[sensor_name_str][index];
145 tmp_atom_value.set<VendorAtomValue::longValue>(diff_residency);
146 sum_residency += diff_residency;
147 prev_stats[sensor_name_str][index] = residency_stats[index];
148 } else {
149 tmp_atom_value.set<VendorAtomValue::longValue>(residency_stats[index]);
150 sum_residency += residency_stats[index];
151 prev_stats[sensor_name_str].push_back(residency_stats[index]);
152 }
153 values.push_back(tmp_atom_value);
154 }
155 if (abs(since_last_update_ms - sum_residency) > kMaxResidencyDiffMs)
156 ALOGI("Thermal zone: %s Temperature residency stats not good!\ndevice sum_residency: "
157 "%ldms, since_last_update_ms %ldms\n",
158 sensor_name_str.c_str(), sum_residency, since_last_update_ms);
159
160 // Send vendor atom to IStats HAL
161 VendorAtom event = {.reverseDomainName = "",
162 .atomId = PixelAtoms::Atom::kVendorTempResidencyStats,
163 .values = std::move(values)};
164 ndk::ScopedAStatus ret = stats_client->reportVendorAtom(event);
165 if (!ret.isOk())
166 ALOGE("Unable to report VendorTempResidencyStats to Stats service");
167
168 cur_stats_map_iterator++;
169 }
170 prevTime = curTime;
171 }
172
173 } // namespace pixel
174 } // namespace google
175 } // namespace hardware
176 } // namespace android
177