1 /*
2  * Copyright (c) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "rs_profiler_telemetry.h"
17 
18 #include "rs_profiler_settings.h"
19 
20 namespace OHOS::Rosen {
21 
22 static const StringParameter TERMAL("paths.termal");
23 static const StringParameter CURRENT("paths.current");
24 static const StringParameter VOLTAGE("paths.voltage");
25 static const StringParameter MEMORY("paths.memory");
26 // cpu
27 static const StringParameter CPU_USAGE("paths.cpu.time");
28 static const StringParameter CPU_CORES("paths.cpu.cores");
29 static const StringParameter CPU_FREQUENCY("paths.cpu.frequency");
30 static const StringParameter CPU_FREQUENCY_POLICY("paths.cpu.frequency.policy");
31 // gpu
32 static const StringParameter GPU_FREQUENCY("paths.gpu.frequency");
33 static const StringParameter GPU_FREQUENCY_MIN("paths.gpu.frequency.min");
34 static const StringParameter GPU_FREQUENCY_MAX("paths.gpu.frequency.max");
35 static const StringParameter GPU_LOAD("paths.gpu.load");
36 
37 struct CpuTime final {
38     static const uint32_t COUNT = 7;
39     double times[COUNT] = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 };
40     double total = 0;
41 };
42 
GetTemperaturePath()43 static std::string GetTemperaturePath()
44 {
45     static std::string path;
46     if (!path.empty()) {
47         return path;
48     }
49 
50     std::vector<std::string> directories;
51     Utils::IterateDirectory(*TERMAL, directories);
52 
53     std::string type;
54     for (const std::string& directory : directories) {
55         Utils::LoadContent(Utils::MakePath(directory, "type"), type);
56         if (type.find("soc_thermal") != std::string::npos) {
57             path = Utils::MakePath(directory, "temp");
58             break;
59         }
60     }
61 
62     return path;
63 }
64 
65 // cpufreq
GetCpuFrequencyPath(uint32_t cpu)66 static std::string GetCpuFrequencyPath(uint32_t cpu)
67 {
68     return Utils::MakePath(*CPU_FREQUENCY + std::to_string(cpu), "cpufreq");
69 }
70 
GetCpuCurrentFrequencyPath(uint32_t cpu)71 static std::string GetCpuCurrentFrequencyPath(uint32_t cpu)
72 {
73     return Utils::MakePath(GetCpuFrequencyPath(cpu), "scaling_cur_freq");
74 }
75 
76 // cpupolicy
GetCpuFrequencyPolicyPath(uint32_t cpu)77 static std::string GetCpuFrequencyPolicyPath(uint32_t cpu)
78 {
79     return *CPU_FREQUENCY_POLICY + std::to_string(cpu);
80 }
81 
GetCpuCurrentFrequencyPolicyPath(uint32_t cpu)82 static std::string GetCpuCurrentFrequencyPolicyPath(uint32_t cpu)
83 {
84     return Utils::MakePath(GetCpuFrequencyPolicyPath(cpu), "scaling_cur_freq");
85 }
86 
GetCpuMinFrequencyPolicyPath(uint32_t cpu)87 static std::string GetCpuMinFrequencyPolicyPath(uint32_t cpu)
88 {
89     return Utils::MakePath(GetCpuFrequencyPolicyPath(cpu), "scaling_min_freq");
90 }
91 
GetCpuMaxFrequencyPolicyPath(uint32_t cpu)92 static std::string GetCpuMaxFrequencyPolicyPath(uint32_t cpu)
93 {
94     return Utils::MakePath(GetCpuFrequencyPolicyPath(cpu), "scaling_max_freq");
95 }
96 
TemperatureToString(float temperature)97 static std::string TemperatureToString(float temperature)
98 {
99     return std::to_string(temperature) + " °C";
100 }
101 
CurrentToString(float current)102 static std::string CurrentToString(float current)
103 {
104     return std::to_string(current) + " mA";
105 }
106 
VoltageToString(float voltage)107 static std::string VoltageToString(float voltage)
108 {
109     return std::to_string(voltage) + " V";
110 }
111 
MemoryToString(uint64_t memory)112 static std::string MemoryToString(uint64_t memory)
113 {
114     return std::to_string(memory * Utils::MICRO) + " GB";
115 }
116 
FrequencyToString(float frequency)117 static std::string FrequencyToString(float frequency)
118 {
119     return std::to_string(frequency) + " GHz";
120 }
121 
LoadToString(float load)122 static std::string LoadToString(float load)
123 {
124     return std::to_string(load) + " %";
125 }
126 
FrequencyLoadToString(const FrequencyLoadInfo & info)127 static std::string FrequencyLoadToString(const FrequencyLoadInfo& info)
128 {
129     return FrequencyToString(info.current) + "(min: " + FrequencyToString(info.min) +
130            " max: " + FrequencyToString(info.max) + " load: " + LoadToString(info.load) + ")";
131 }
132 
GetMetric(const std::string & name)133 static std::string GetMetric(const std::string& name)
134 {
135     std::string metric("0");
136     Utils::LoadContent(name, metric);
137     return metric;
138 }
139 
GetMetricFloat(const std::string & name)140 static float GetMetricFloat(const std::string& name)
141 {
142     return Utils::ToFp32(GetMetric(name));
143 }
144 
GetCPUTemperature(CPUInfo & cpu)145 static void GetCPUTemperature(CPUInfo& cpu)
146 {
147     cpu.temperature = GetMetricFloat(GetTemperaturePath()) * Utils::MILLI;
148 }
149 
GetBattery(CPUInfo & cpu)150 static void GetBattery(CPUInfo& cpu)
151 {
152     cpu.current = GetMetricFloat(*CURRENT) * Utils::MICRO;
153     cpu.voltage = GetMetricFloat(*VOLTAGE) * Utils::MICRO;
154 }
155 
GetValue(const std::string & name,const std::vector<std::string> & lines,uint64_t & value)156 static void GetValue(const std::string& name, const std::vector<std::string>& lines, uint64_t& value)
157 {
158     for (const std::string& line : lines) {
159         if (line.find(name) != std::string::npos) {
160             value = Utils::ToUint64(Utils::ExtractNumber(line));
161             return;
162         }
163     }
164 }
165 
GetCPUMemory(CPUInfo & cpu)166 static void GetCPUMemory(CPUInfo& cpu)
167 {
168     std::vector<std::string> lines;
169     Utils::LoadLines(*MEMORY, lines);
170 
171     if (!lines.empty()) {
172         GetValue("MemTotal", lines, cpu.ramTotal);
173         GetValue("MemFree", lines, cpu.ramFree);
174     }
175 }
176 
GetCpuTime(const std::vector<std::string> & args,CpuTime & info)177 static uint32_t GetCpuTime(const std::vector<std::string>& args, CpuTime& info)
178 {
179     constexpr size_t cpuWordSize = 3;
180     if (args[0].size() == cpuWordSize) {
181         // this is cpu total line
182         return UINT_MAX;
183     }
184 
185     info.total = 0;
186     for (uint32_t i = 0; i < CpuTime::COUNT; i++) {
187         info.times[i] = Utils::ToUint64(args[i + 1]);
188         info.total += info.times[i];
189     }
190 
191     return Utils::ToUint64(args[0].data() + cpuWordSize);
192 }
193 
IsCpuLine(const std::vector<std::string> & args)194 static bool IsCpuLine(const std::vector<std::string>& args)
195 {
196     constexpr size_t required = 11;
197     return (args.size() == required) && (args[0].find("cpu") == 0);
198 }
199 
GetCpuTime(std::vector<CpuTime> & infos)200 static void GetCpuTime(std::vector<CpuTime>& infos)
201 {
202     std::vector<std::string> lines;
203     Utils::LoadLines(*CPU_USAGE, lines);
204     if (lines.empty()) {
205         return;
206     }
207 
208     size_t processed = 0u;
209     for (const auto& line : lines) {
210         const std::vector<std::string> args = Utils::Split(line);
211         if (!IsCpuLine(args) || (processed == infos.size())) {
212             // either there are no more cpu lines in the file
213             // or all cpus were processed
214             return;
215         }
216 
217         CpuTime info;
218         const size_t id = GetCpuTime(args, info);
219         if (id < infos.size()) {
220             infos[id] = info;
221             processed++;
222         }
223     }
224 }
225 
GetCpuTotalUsage(const CpuTime & info,const CpuTime & lastInfo)226 static double GetCpuTotalUsage(const CpuTime& info, const CpuTime& lastInfo)
227 {
228     const double deltaTotal = info.total - lastInfo.total;
229     if (deltaTotal <= 0.0) {
230         return 0.0;
231     }
232 
233     double usage = 0.0;
234     for (uint32_t i = 0; i < CpuTime::COUNT; i++) {
235         usage += std::max(info.times[i] - lastInfo.times[i], 0.0);
236     }
237 
238     constexpr double ratioToPercent = 100.0;
239     return usage * ratioToPercent / deltaTotal;
240 }
241 
GetCoreCount()242 static uint32_t GetCoreCount()
243 {
244     std::string line;
245     Utils::LoadLine(*CPU_CORES, line);
246 
247     if (line.empty()) {
248         return 0;
249     }
250 
251     if (line.size() == 1) {
252         return 1;
253     }
254 
255     constexpr size_t multiCoreLineLength = 3;
256     if (line.size() >= multiCoreLineLength) {
257         constexpr uint32_t maxCoreIndex = 2;
258         return Utils::ToUint32(line.substr(maxCoreIndex)) + 1;
259     }
260 
261     return 0;
262 }
263 
GetCPUCores(CPUInfo & cpu)264 static void GetCPUCores(CPUInfo& cpu)
265 {
266     static const uint32_t CORE_COUNT = GetCoreCount();
267     cpu.cores = std::min(CPUInfo::MAX_CORES, CORE_COUNT);
268 
269     for (uint32_t i = 0; i < cpu.cores; i++) {
270         cpu.coreFrequencyLoad[i].current = GetMetricFloat(GetCpuCurrentFrequencyPath(i)) * Utils::MICRO;
271     }
272 
273     static std::vector<CpuTime> cpuTimeInfos;
274     if (cpuTimeInfos.empty()) {
275         cpuTimeInfos.resize(cpu.cores);
276     }
277 
278     static std::vector<CpuTime> lastCpuTimeInfos;
279     if (lastCpuTimeInfos.empty()) {
280         lastCpuTimeInfos.resize(cpu.cores);
281     }
282 
283     GetCpuTime(cpuTimeInfos);
284     for (size_t i = 0; i < cpu.cores; i++) {
285         cpu.coreFrequencyLoad[i].load = GetCpuTotalUsage(cpuTimeInfos[i], lastCpuTimeInfos[i]);
286     }
287     lastCpuTimeInfos = cpuTimeInfos;
288 }
289 
GetGPUFrequencyLoad(GPUInfo & gpu)290 static void GetGPUFrequencyLoad(GPUInfo& gpu)
291 {
292     gpu.frequencyLoad.current = GetMetricFloat(*GPU_FREQUENCY) * Utils::NANO;
293     gpu.frequencyLoad.min = GetMetricFloat(*GPU_FREQUENCY_MIN) * Utils::NANO;
294     gpu.frequencyLoad.max = GetMetricFloat(*GPU_FREQUENCY_MAX) * Utils::NANO;
295     gpu.frequencyLoad.load = GetMetricFloat(*GPU_LOAD);
296 }
297 
GetDeviceInfo()298 const DeviceInfo& RSTelemetry::GetDeviceInfo()
299 {
300     static DeviceInfo info;
301     GetBattery(info.cpu);
302     GetCPUTemperature(info.cpu);
303     GetCPUMemory(info.cpu);
304     GetCPUCores(info.cpu);
305     GetGPUFrequencyLoad(info.gpu);
306     return info;
307 }
308 
GetDeviceInfoString()309 std::string RSTelemetry::GetDeviceInfoString()
310 {
311     const DeviceInfo info = GetDeviceInfo();
312 
313     std::string out;
314     for (size_t i = 0; i < info.cpu.cores; i++) {
315         out += +"\nCPU" + std::to_string(i) + ": " + FrequencyLoadToString(info.cpu.coreFrequencyLoad[i]);
316     }
317 
318     out += "\nTemperature: " + TemperatureToString(info.cpu.temperature) +
319            "\nCurrent: " + CurrentToString(info.cpu.current) + "\nVoltage: " + VoltageToString(info.cpu.voltage) +
320            "\nRAM Total: " + MemoryToString(info.cpu.ramTotal) + "\nRAM Free: " + MemoryToString(info.cpu.ramFree) +
321            "\nGPU: " + FrequencyLoadToString(info.gpu.frequencyLoad);
322 
323     return out;
324 }
325 
GetDeviceFrequencyString()326 std::string RSTelemetry::GetDeviceFrequencyString()
327 {
328     std::string out;
329 
330     constexpr int32_t count = 3;
331     for (int32_t i = 0; i < count; i++) {
332         const auto current = GetMetricFloat(GetCpuCurrentFrequencyPolicyPath(i)) * Utils::MICRO;
333         const auto max = GetMetricFloat(GetCpuMaxFrequencyPolicyPath(i)) * Utils::MICRO;
334         const auto min = GetMetricFloat(GetCpuMinFrequencyPolicyPath(i)) * Utils::MICRO;
335         out += "CPU" + std::to_string(i) + ": " + FrequencyToString(current) + "(min=" + FrequencyToString(min) +
336                " max=" + FrequencyToString(max) + ")\n";
337     }
338 
339     const DeviceInfo info = GetDeviceInfo();
340     out += "GPU: " + FrequencyLoadToString(info.gpu.frequencyLoad);
341 
342     return out;
343 }
344 
GetCpuAffinityString()345 std::string RSTelemetry::GetCpuAffinityString()
346 {
347     std::string out = "Cpu affinity mask: ";
348     const uint32_t cores = GetCoreCount();
349     for (uint32_t i = 0; i < cores; i++) {
350         out += Utils::GetCpuAffinity(i) ? "1" : "0";
351     }
352 
353     return out;
354 }
355 } // namespace OHOS::Rosen