2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
9 #include "CPUInfoLinux.h"
11 #include "utils/StringUtils.h"
12 #include "utils/Temperature.h"
14 #include "platform/linux/SysfsPath.h"
21 #if (defined(__arm__) && defined(HAS_NEON)) || defined(__aarch64__)
22 #include <asm/hwcap.h>
24 #elif defined(__i386__) || defined(__x86_64__)
50 std::size_t GetActiveTime() const
52 return state
[STATE_USER
] + state
[STATE_NICE
] + state
[STATE_SYSTEM
] + state
[STATE_IRQ
] +
53 state
[STATE_SOFTIRQ
] + state
[STATE_STEAL
] + state
[STATE_GUEST
] + state
[STATE_GUEST_NICE
];
56 std::size_t GetIdleTime() const { return state
[STATE_IDLE
] + state
[STATE_IOWAIT
]; }
58 std::size_t GetTotalTime() const { return GetActiveTime() + GetIdleTime(); }
61 std::size_t state
[STATE_MAX
];
65 std::shared_ptr
<CCPUInfo
> CCPUInfo::GetCPUInfo()
67 return std::make_shared
<CCPUInfoLinux
>();
70 CCPUInfoLinux::CCPUInfoLinux()
72 CSysfsPath machinePath
{"/sys/bus/soc/devices/soc0/machine"};
73 if (machinePath
.Exists())
74 m_cpuHardware
= machinePath
.Get
<std::string
>();
76 CSysfsPath familyPath
{"/sys/bus/soc/devices/soc0/family"};
77 if (familyPath
.Exists())
78 m_cpuSoC
= familyPath
.Get
<std::string
>();
80 CSysfsPath socPath
{"/sys/bus/soc/devices/soc0/soc_id"};
82 m_cpuSoC
+= " " + socPath
.Get
<std::string
>();
84 CSysfsPath revisionPath
{"/sys/bus/soc/devices/soc0/revision"};
85 if (revisionPath
.Exists())
86 m_cpuRevision
+= revisionPath
.Get
<std::string
>();
88 CSysfsPath serialPath
{"/sys/bus/soc/devices/soc0/serial_number"};
89 if (serialPath
.Exists())
90 m_cpuSerial
+= serialPath
.Get
<std::string
>();
92 const std::string freqStr
{"/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq"};
93 CSysfsPath freqPath
{freqStr
};
94 if (freqPath
.Exists())
97 const std::array
<std::string
, 4> modules
= {
104 for (int i
= 0; i
< 20; i
++)
106 CSysfsPath path
{"/sys/class/hwmon/hwmon" + std::to_string(i
) + "/name"};
110 auto name
= path
.Get
<std::string
>();
115 for (const auto& module
: modules
)
119 std::string tempStr
{"/sys/class/hwmon/hwmon" + std::to_string(i
) + "/temp1_input"};
120 CSysfsPath tempPath
{tempStr
};
121 if (!tempPath
.Exists())
124 m_tempPath
= tempStr
;
129 if (!m_tempPath
.empty())
133 m_cpuCount
= sysconf(_SC_NPROCESSORS_ONLN
);
135 for (int core
= 0; core
< m_cpuCount
; core
++)
138 coreInfo
.m_id
= core
;
139 m_cores
.emplace_back(coreInfo
);
142 #if defined(__i386__) || defined(__x86_64__)
150 if (__get_cpuid(CPUID_INFOTYPE_MANUFACTURER
, &eax
, &ebx
, &ecx
, &edx
))
152 m_cpuVendor
.append(reinterpret_cast<const char*>(&ebx
), 4);
153 m_cpuVendor
.append(reinterpret_cast<const char*>(&edx
), 4);
154 m_cpuVendor
.append(reinterpret_cast<const char*>(&ecx
), 4);
157 if (__get_cpuid(CPUID_INFOTYPE_EXTENDED_IMPLEMENTED
, &eax
, &ebx
, &ecx
, &edx
))
159 if (eax
>= CPUID_INFOTYPE_PROCESSOR_3
)
163 if (__get_cpuid(CPUID_INFOTYPE_PROCESSOR_1
, &eax
, &ebx
, &ecx
, &edx
))
165 m_cpuModel
.append(reinterpret_cast<const char*>(&eax
), 4);
166 m_cpuModel
.append(reinterpret_cast<const char*>(&ebx
), 4);
167 m_cpuModel
.append(reinterpret_cast<const char*>(&ecx
), 4);
168 m_cpuModel
.append(reinterpret_cast<const char*>(&edx
), 4);
171 if (__get_cpuid(CPUID_INFOTYPE_PROCESSOR_2
, &eax
, &ebx
, &ecx
, &edx
))
173 m_cpuModel
.append(reinterpret_cast<const char*>(&eax
), 4);
174 m_cpuModel
.append(reinterpret_cast<const char*>(&ebx
), 4);
175 m_cpuModel
.append(reinterpret_cast<const char*>(&ecx
), 4);
176 m_cpuModel
.append(reinterpret_cast<const char*>(&edx
), 4);
179 if (__get_cpuid(CPUID_INFOTYPE_PROCESSOR_3
, &eax
, &ebx
, &ecx
, &edx
))
181 m_cpuModel
.append(reinterpret_cast<const char*>(&eax
), 4);
182 m_cpuModel
.append(reinterpret_cast<const char*>(&ebx
), 4);
183 m_cpuModel
.append(reinterpret_cast<const char*>(&ecx
), 4);
184 m_cpuModel
.append(reinterpret_cast<const char*>(&edx
), 4);
189 if (__get_cpuid(CPUID_INFOTYPE_STANDARD
, &eax
, &eax
, &ecx
, &edx
))
191 if (edx
& CPUID_00000001_EDX_MMX
)
192 m_cpuFeatures
|= CPU_FEATURE_MMX
;
194 if (edx
& CPUID_00000001_EDX_SSE
)
195 m_cpuFeatures
|= CPU_FEATURE_SSE
;
197 if (edx
& CPUID_00000001_EDX_SSE2
)
198 m_cpuFeatures
|= CPU_FEATURE_SSE2
;
200 if (ecx
& CPUID_00000001_ECX_SSE3
)
201 m_cpuFeatures
|= CPU_FEATURE_SSE3
;
203 if (ecx
& CPUID_00000001_ECX_SSSE3
)
204 m_cpuFeatures
|= CPU_FEATURE_SSSE3
;
206 if (ecx
& CPUID_00000001_ECX_SSE4
)
207 m_cpuFeatures
|= CPU_FEATURE_SSE4
;
209 if (ecx
& CPUID_00000001_ECX_SSE42
)
210 m_cpuFeatures
|= CPU_FEATURE_SSE42
;
213 if (__get_cpuid(CPUID_INFOTYPE_EXTENDED_IMPLEMENTED
, &eax
, &eax
, &ecx
, &edx
))
215 if (eax
>= CPUID_INFOTYPE_EXTENDED
)
217 if (edx
& CPUID_80000001_EDX_MMX
)
218 m_cpuFeatures
|= CPU_FEATURE_MMX
;
220 if (edx
& CPUID_80000001_EDX_MMX2
)
221 m_cpuFeatures
|= CPU_FEATURE_MMX2
;
223 if (edx
& CPUID_80000001_EDX_3DNOW
)
224 m_cpuFeatures
|= CPU_FEATURE_3DNOW
;
226 if (edx
& CPUID_80000001_EDX_3DNOWEXT
)
227 m_cpuFeatures
|= CPU_FEATURE_3DNOWEXT
;
231 std::ifstream
cpuinfo("/proc/cpuinfo");
232 std::regex
re(".*: (.*)$");
234 for (std::string line
; std::getline(cpuinfo
, line
);)
238 if (std::regex_match(line
, match
, re
))
240 if (match
.size() == 2)
242 std::ssub_match value
= match
[1];
244 if (line
.find("model name") != std::string::npos
)
246 if (m_cpuModel
.empty())
247 m_cpuModel
= value
.str();
250 if (line
.find("BogoMIPS") != std::string::npos
)
252 if (m_cpuBogoMips
.empty())
253 m_cpuBogoMips
= value
.str();
256 if (line
.find("Hardware") != std::string::npos
)
258 if (m_cpuHardware
.empty())
259 m_cpuHardware
= value
.str();
262 if (line
.find("Serial") != std::string::npos
)
264 if (m_cpuSerial
.empty())
265 m_cpuSerial
= value
.str();
268 if (line
.find("Revision") != std::string::npos
)
270 if (m_cpuRevision
.empty())
271 m_cpuRevision
= value
.str();
278 m_cpuModel
= m_cpuModel
.substr(0, m_cpuModel
.find(char(0))); // remove extra null terminations
280 #if defined(HAS_NEON) && defined(__arm__)
281 if (getauxval(AT_HWCAP
) & HWCAP_NEON
)
282 m_cpuFeatures
|= CPU_FEATURE_NEON
;
285 #if defined(HAS_NEON) && defined(__aarch64__)
286 if (getauxval(AT_HWCAP
) & HWCAP_ASIMD
)
287 m_cpuFeatures
|= CPU_FEATURE_NEON
;
290 // Set MMX2 when SSE is present as SSE is a superset of MMX2 and Intel doesn't set the MMX2 cap
291 if (m_cpuFeatures
& CPU_FEATURE_SSE
)
292 m_cpuFeatures
|= CPU_FEATURE_MMX2
;
295 int CCPUInfoLinux::GetUsedPercentage()
297 if (!m_nextUsedReadTime
.IsTimePast())
298 return m_lastUsedPercentage
;
300 std::vector
<CpuData
> cpuData
;
302 std::ifstream
infile("/proc/stat");
304 for (std::string line
; std::getline(infile
, line
);)
306 if (line
.find("cpu") != std::string::npos
)
308 std::istringstream
ss(line
);
313 for (int i
= 0; i
< STATE_MAX
; i
++)
318 cpuData
.emplace_back(info
);
322 auto activeTime
= cpuData
.front().GetActiveTime() - m_activeTime
;
323 auto idleTime
= cpuData
.front().GetIdleTime() - m_idleTime
;
324 auto totalTime
= cpuData
.front().GetTotalTime() - m_totalTime
;
326 m_activeTime
+= activeTime
;
327 m_idleTime
+= idleTime
;
328 m_totalTime
+= totalTime
;
330 m_lastUsedPercentage
= activeTime
* 100.0f
/ totalTime
;
331 m_nextUsedReadTime
.Set(MINIMUM_TIME_BETWEEN_READS
);
333 cpuData
.erase(cpuData
.begin());
335 for (std::size_t core
= 0; core
< cpuData
.size(); core
++)
337 auto activeTime
= cpuData
[core
].GetActiveTime() - m_cores
[core
].m_activeTime
;
338 auto idleTime
= cpuData
[core
].GetIdleTime() - m_cores
[core
].m_idleTime
;
339 auto totalTime
= cpuData
[core
].GetTotalTime() - m_cores
[core
].m_totalTime
;
341 m_cores
[core
].m_usagePercent
= activeTime
* 100.0 / totalTime
;
343 m_cores
[core
].m_activeTime
+= activeTime
;
344 m_cores
[core
].m_idleTime
+= idleTime
;
345 m_cores
[core
].m_totalTime
+= totalTime
;
348 return static_cast<int>(m_lastUsedPercentage
);
351 float CCPUInfoLinux::GetCPUFrequency()
353 if (m_freqPath
.empty())
356 CSysfsPath path
{m_freqPath
};
357 return path
.Get
<float>() / 1000.0f
;
360 bool CCPUInfoLinux::GetTemperature(CTemperature
& temperature
)
362 if (CheckUserTemperatureCommand(temperature
))
365 if (m_tempPath
.empty())
368 CSysfsPath path
{m_tempPath
};
369 double value
= path
.Get
<double>() / 1000.0;
371 temperature
= CTemperature::CreateFromCelsius(value
);
372 temperature
.SetValid(true);