1 /*
2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
8
9 #include "CPUInfoFreebsd.h"
10
11 #include "utils/Temperature.h"
12 #include "utils/log.h"
13
14 #include <array>
15 #include <vector>
16
17 #if defined(__i386__) || defined(__x86_64__)
18 #include <cpuid.h>
19 #elif __has_include(<sys/auxv.h>)
20 #include <sys/auxv.h>
21 #endif
22
23 #include <sys/resource.h>
24 #include <sys/sysctl.h>
25 #include <sys/types.h>
26
27 namespace
28 {
29
30 struct CpuData
31 {
32 public:
GetActiveTime__anon575c8d8f0111::CpuData33 std::size_t GetActiveTime() const { return state[CP_USER] + state[CP_NICE] + state[CP_SYS]; }
34
GetIdleTime__anon575c8d8f0111::CpuData35 std::size_t GetIdleTime() const { return state[CP_INTR] + state[CP_IDLE]; }
36
GetTotalTime__anon575c8d8f0111::CpuData37 std::size_t GetTotalTime() const { return GetActiveTime() + GetIdleTime(); }
38
39 std::size_t state[CPUSTATES];
40 };
41
42 } // namespace
43
GetCPUInfo()44 std::shared_ptr<CCPUInfo> CCPUInfo::GetCPUInfo()
45 {
46 return std::make_shared<CCPUInfoFreebsd>();
47 }
48
CCPUInfoFreebsd()49 CCPUInfoFreebsd::CCPUInfoFreebsd()
50 {
51 int count = 0;
52 size_t countLength = sizeof(count);
53 if (sysctlbyname("hw.ncpu", &count, &countLength, nullptr, 0) == 0)
54 m_cpuCount = count;
55 else
56 m_cpuCount = 1;
57
58 std::array<char, 512> cpuModel;
59 size_t length = cpuModel.size();
60 if (sysctlbyname("hw.model", cpuModel.data(), &length, nullptr, 0) == 0)
61 m_cpuModel = cpuModel.data();
62
63 for (size_t i = 0; i < m_cpuCount; i++)
64 {
65 CoreInfo core;
66 core.m_id = i;
67 m_cores.emplace_back(core);
68 }
69 #if defined(__i386__) || defined(__x86_64__)
70 uint32_t eax, ebx, ecx, edx;
71
72 m_cpuVendor.clear();
73
74 if (__get_cpuid(CPUID_INFOTYPE_MANUFACTURER, &eax, &ebx, &ecx, &edx))
75 {
76 m_cpuVendor.append(reinterpret_cast<const char*>(&ebx), 4);
77 m_cpuVendor.append(reinterpret_cast<const char*>(&edx), 4);
78 m_cpuVendor.append(reinterpret_cast<const char*>(&ecx), 4);
79 }
80
81 if (__get_cpuid(CPUID_INFOTYPE_EXTENDED_IMPLEMENTED, &eax, &ebx, &ecx, &edx))
82 {
83 if (eax >= CPUID_INFOTYPE_PROCESSOR_3)
84 {
85 m_cpuModel.clear();
86
87 if (__get_cpuid(CPUID_INFOTYPE_PROCESSOR_1, &eax, &ebx, &ecx, &edx))
88 {
89 m_cpuModel.append(reinterpret_cast<const char*>(&eax), 4);
90 m_cpuModel.append(reinterpret_cast<const char*>(&ebx), 4);
91 m_cpuModel.append(reinterpret_cast<const char*>(&ecx), 4);
92 m_cpuModel.append(reinterpret_cast<const char*>(&edx), 4);
93 }
94
95 if (__get_cpuid(CPUID_INFOTYPE_PROCESSOR_2, &eax, &ebx, &ecx, &edx))
96 {
97 m_cpuModel.append(reinterpret_cast<const char*>(&eax), 4);
98 m_cpuModel.append(reinterpret_cast<const char*>(&ebx), 4);
99 m_cpuModel.append(reinterpret_cast<const char*>(&ecx), 4);
100 m_cpuModel.append(reinterpret_cast<const char*>(&edx), 4);
101 }
102
103 if (__get_cpuid(CPUID_INFOTYPE_PROCESSOR_3, &eax, &ebx, &ecx, &edx))
104 {
105 m_cpuModel.append(reinterpret_cast<const char*>(&eax), 4);
106 m_cpuModel.append(reinterpret_cast<const char*>(&ebx), 4);
107 m_cpuModel.append(reinterpret_cast<const char*>(&ecx), 4);
108 m_cpuModel.append(reinterpret_cast<const char*>(&edx), 4);
109 }
110 }
111 }
112
113 if (__get_cpuid(CPUID_INFOTYPE_STANDARD, &eax, &eax, &ecx, &edx))
114 {
115 if (edx & CPUID_00000001_EDX_MMX)
116 m_cpuFeatures |= CPU_FEATURE_MMX;
117
118 // Set MMX2 when SSE is present as SSE is a superset of MMX2 and Intel doesn't set the MMX2 cap
119 if (edx & CPUID_00000001_EDX_SSE)
120 m_cpuFeatures |= (CPU_FEATURE_SSE | CPU_FEATURE_MMX2);
121
122 if (edx & CPUID_00000001_EDX_SSE2)
123 m_cpuFeatures |= CPU_FEATURE_SSE2;
124
125 if (ecx & CPUID_00000001_ECX_SSE3)
126 m_cpuFeatures |= CPU_FEATURE_SSE3;
127
128 if (ecx & CPUID_00000001_ECX_SSSE3)
129 m_cpuFeatures |= CPU_FEATURE_SSSE3;
130
131 if (ecx & CPUID_00000001_ECX_SSE4)
132 m_cpuFeatures |= CPU_FEATURE_SSE4;
133
134 if (ecx & CPUID_00000001_ECX_SSE42)
135 m_cpuFeatures |= CPU_FEATURE_SSE42;
136 }
137
138 if (__get_cpuid(CPUID_INFOTYPE_EXTENDED_IMPLEMENTED, &eax, &eax, &ecx, &edx))
139 {
140 if (eax >= CPUID_INFOTYPE_EXTENDED)
141 {
142 if (edx & CPUID_80000001_EDX_MMX)
143 m_cpuFeatures |= CPU_FEATURE_MMX;
144
145 if (edx & CPUID_80000001_EDX_MMX2)
146 m_cpuFeatures |= CPU_FEATURE_MMX2;
147
148 if (edx & CPUID_80000001_EDX_3DNOW)
149 m_cpuFeatures |= CPU_FEATURE_3DNOW;
150
151 if (edx & CPUID_80000001_EDX_3DNOWEXT)
152 m_cpuFeatures |= CPU_FEATURE_3DNOWEXT;
153 }
154 }
155 #endif
156
157 #if defined(HAS_NEON)
158 #if defined(__ARM_NEON)
159 m_cpuFeatures |= CPU_FEATURE_NEON;
160 #elif __has_include(<sys/auxv.h>)
161 unsigned long hwcap = 0;
162 elf_aux_info(AT_HWCAP, &hwcap, sizeof(hwcap));
163 if (hwcap & HWCAP_NEON)
164 m_cpuFeatures |= CPU_FEATURE_NEON;
165 #endif
166 #endif
167 }
168
GetUsedPercentage()169 int CCPUInfoFreebsd::GetUsedPercentage()
170 {
171 if (!m_nextUsedReadTime.IsTimePast())
172 return m_lastUsedPercentage;
173
174 size_t len = sizeof(long);
175
176 if (sysctlbyname("kern.cp_times", nullptr, &len, nullptr, 0) != 0)
177 return false;
178
179 std::vector<long> cptimes(len);
180 size_t cptimesLength = cptimes.size();
181 if (sysctlbyname("kern.cp_times", cptimes.data(), &cptimesLength, nullptr, 0) != 0)
182 return false;
183
184 size_t activeTime{0};
185 size_t idleTime{0};
186 size_t totalTime{0};
187
188 std::vector<CpuData> cpuData;
189
190 for (size_t i = 0; i < m_cpuCount; i++)
191 {
192 CpuData info;
193
194 for (size_t state = 0; state < CPUSTATES; state++)
195 {
196 info.state[state] = cptimes[i * CPUSTATES + state];
197 }
198
199 activeTime += info.GetActiveTime();
200 idleTime += info.GetIdleTime();
201 totalTime += info.GetTotalTime();
202
203 cpuData.emplace_back(info);
204 }
205
206 activeTime -= m_activeTime;
207 idleTime -= m_idleTime;
208 totalTime -= m_totalTime;
209
210 m_activeTime += activeTime;
211 m_idleTime += idleTime;
212 m_totalTime += totalTime;
213
214 m_lastUsedPercentage = activeTime * 100.0f / totalTime;
215 m_nextUsedReadTime.Set(MINIMUM_TIME_BETWEEN_READS);
216
217 for (size_t core = 0; core < cpuData.size(); core++)
218 {
219 auto activeTime = cpuData[core].GetActiveTime() - m_cores[core].m_activeTime;
220 auto idleTime = cpuData[core].GetIdleTime() - m_cores[core].m_idleTime;
221 auto totalTime = cpuData[core].GetTotalTime() - m_cores[core].m_totalTime;
222
223 m_cores[core].m_usagePercent = activeTime * 100.0f / totalTime;
224
225 m_cores[core].m_activeTime += activeTime;
226 m_cores[core].m_idleTime += idleTime;
227 m_cores[core].m_totalTime += totalTime;
228 }
229
230 return static_cast<int>(m_lastUsedPercentage);
231 }
232
GetCPUFrequency()233 float CCPUInfoFreebsd::GetCPUFrequency()
234 {
235 int hz = 0;
236 size_t len = sizeof(hz);
237 if (sysctlbyname("dev.cpu.0.freq", &hz, &len, nullptr, 0) != 0)
238 hz = 0;
239
240 return static_cast<float>(hz);
241 }
242
243
GetTemperature(CTemperature & temperature)244 bool CCPUInfoFreebsd::GetTemperature(CTemperature& temperature)
245 {
246 int value;
247 size_t len = sizeof(value);
248
249 /* Temperature is in Kelvin * 10 */
250 if (sysctlbyname("dev.cpu.0.temperature", &value, &len, nullptr, 0) != 0)
251 return CCPUInfoPosix::GetTemperature(temperature);
252 temperature = CTemperature::CreateFromKelvin(static_cast<double>(value) / 10.0);
253 temperature.SetValid(true);
254
255 return true;
256 }
257