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