2 * Routines to report CPU information
4 * Wireshark - Network traffic analyzer
5 * By Gerald Combs <gerald@wireshark.org>
6 * Copyright 1998 Gerald Combs
8 * SPDX-License-Identifier: GPL-2.0-or-later
12 #include <wsutil/cpu_info.h>
16 #include <wsutil/ws_cpuid.h>
17 #include <wsutil/file_util.h>
19 #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__APPLE__)
21 #elif defined(sun) || defined(__sun)
27 #elif defined(HAVE_SYSCTL)
28 #include <sys/types.h>
29 #include <sys/sysctl.h>
30 #elif defined(HAVE_SYSINFO)
31 #include <sys/systeminfo.h>
35 * Functions used for the GTree we use to keep a list of *unique*
39 compare_model_names(const void *a
, const void *b
, void * user_data _U_
)
41 return strcmp((const char *)a
, (const char *)b
);
50 add_model_name_to_string(void * key
, void * value _U_
,
53 struct string_info
*info
= (struct string_info
*)data
;
55 /* Separate this from the previous entry, if necessary. */
56 if (info
->sep
!= NULL
)
57 g_string_append(info
->str
, info
->sep
);
59 /* Now add the model name. */
60 g_string_append(info
->str
, g_strstrip((char *)key
));
63 * There will *definitely* need to be a separator for any subsequent
73 * Get the CPU info, and append it to the GString
75 * On at least some OSes, there's a call that will return this information
76 * for all CPU types for which the OS determines that information, not just
77 * x86 processors with CPUID and the brand string. On those OSes, we use
80 * On other OSes, we use ws_cpuid(), which will fail unconditionally on
84 get_cpu_info(GString
*str
)
86 GTree
*model_names
= g_tree_new_full(compare_model_names
, NULL
, g_free
, NULL
);
88 #if defined(__linux__)
90 * We scan /proc/cpuinfo looking for lines that begins with
91 * "model name\t: ", and extract what comes after that prefix.
93 * /proc/cpuinfo can report information about multiple "CPU"s.
94 * A "CPU" appears to be a CPU core, so this treats a multi-core
95 * chip as multiple CPUs (which is arguably should), but doesn't
96 * appear to treat a multi-threaded core as multiple CPUs.
98 * So we accumulate a table of *multiple* CPU strings, saving
99 * one copy of each unique string, and glue them together at
100 * the end. We use a GTree for this.
102 * We test for Linux first, so that, even if you're on a Linux
103 * that supports sysctl(), we don't use it, we scan /proc/cpuinfo,
104 * as that's the right way to do this.
108 proc_cpuinfo
= ws_fopen("/proc/cpuinfo", "r");
109 if (proc_cpuinfo
== NULL
) {
111 g_tree_destroy(model_names
);
117 static const char prefix
[] = "model name\t: ";
118 #define PREFIX_STRLEN (sizeof prefix - 1)
122 * Read lines from /proc/cpuinfo; stop when we either hit an EOF
126 linelen
= getline(&line
, &linecap
, proc_cpuinfo
);
128 /* EOF or error; just stop. */
131 /* Remove trailing newline. */
133 line
[linelen
- 1] = '\0';
134 if (strncmp(line
, prefix
, PREFIX_STRLEN
) == 0) {
135 /* OK, we have a model name. */
138 /* Get everything after the prefix. */
139 model_name
= g_strdup(line
+ PREFIX_STRLEN
);
142 * Add an entry to the tree with the model name as key and
143 * a null value. There will only be one such entry in the
144 * tree; if there's already such an entry, it will be left
145 * alone, and model_name will be freed, otherwise a new
146 * node will be created using model_name as the key.
148 * Thus, we don't free model_name; either it will be freed
149 * for us, or it will be used in the tree and freed when we
152 g_tree_insert(model_names
, model_name
, NULL
);
156 fclose(proc_cpuinfo
);
157 #define xx_free free /* hack so checkAPIs doesn't complain */
158 xx_free(line
); /* yes, free(), as getline() mallocates it */
159 #elif defined(_WIN32)
161 * They're in the Registry. (Isn't everything?)
165 if (RegOpenKeyExW(HKEY_LOCAL_MACHINE
,
166 L
"HARDWARE\\DESCRIPTION\\System\\CentralProcessor",
167 0, KEY_READ
, &processors_key
) != ERROR_SUCCESS
) {
169 g_tree_destroy(model_names
);
174 * The processors appear under that key. Enumerate all the keys
178 DWORD max_subkey_len
;
182 * How many subkeys are there, and what's the biggest subkey size?
184 * I assume that when the documentation says that some number is
185 * in units of "Unicode characters" they mean "units of elements
186 * of UTF-16 characters", i.e. "units of 2-octet items".
188 if (RegQueryInfoKeyW(processors_key
, NULL
, NULL
, NULL
, &num_subkeys
,
189 &max_subkey_len
, NULL
, NULL
, NULL
, NULL
, NULL
,
190 NULL
) != ERROR_SUCCESS
) {
192 g_tree_destroy(model_names
);
197 * max_subkey_len does not count the trailing '\0'. Add it.
202 * Allocate a buffer for the subkey.
204 subkey_buf
= g_new(wchar_t, max_subkey_len
);
205 if (subkey_buf
== NULL
) {
207 g_tree_destroy(model_names
);
211 for (DWORD processor_index
= 0; processor_index
< num_subkeys
;
214 * The documentation says that this is "in characters"; I'm
215 * assuming, for now, that they mean "Unicode characters",
216 * meaning "2-octet items".
218 DWORD subkey_bufsize
= max_subkey_len
;
219 if (RegEnumKeyExW(processors_key
, processor_index
, subkey_buf
,
220 &subkey_bufsize
, NULL
, NULL
, NULL
,
221 NULL
) != ERROR_SUCCESS
) {
222 /* Just exit the loop. */
227 * Get the length of processor name string for this processor.
229 * That's the "ProcessorNameString" value for the subkey of
230 * processors_key with the name in subkey_buf.
232 * It's a string, so only allow REG_SZ values.
234 DWORD model_name_bufsize
;
236 model_name_bufsize
= 0;
237 if (RegGetValueW(processors_key
, subkey_buf
, L
"ProcessorNameString",
238 RRF_RT_REG_SZ
, NULL
, NULL
,
239 &model_name_bufsize
) != ERROR_SUCCESS
) {
240 /* Just exit the loop. */
245 * Allocate a buffer for the string, as UTF-16.
246 * The retrieved length includes the terminating '\0'.
248 wchar_t *model_name_wchar
= g_malloc(model_name_bufsize
);
249 if (RegGetValueW(processors_key
, subkey_buf
, L
"ProcessorNameString",
250 RRF_RT_REG_SZ
, NULL
, model_name_wchar
,
251 &model_name_bufsize
) != ERROR_SUCCESS
) {
252 /* Just exit the loop. */
253 g_free(model_name_wchar
);
257 /* Convert it to UTF-8. */
258 char *model_name
= g_utf16_to_utf8(model_name_wchar
, -1, NULL
, NULL
, NULL
);
259 g_free(model_name_wchar
);
262 * Add an entry to the tree with the model name as key and
263 * a null value. There will only be one such entry in the
264 * tree; if there's already such an entry, it will be left
265 * alone, and model_name will be freed, otherwise a new
266 * node will be created using model_name as the key.
268 * Thus, we don't free model_name; either it will be freed
269 * for us, or it will be used in the tree and freed when we
272 g_tree_insert(model_names
, model_name
, NULL
);
278 * Close the registry key.
280 RegCloseKey(processors_key
);
281 #elif defined(HAVE_SYSCTL)
283 * Fetch the string using the appropriate sysctl.
285 size_t model_name_len
;
287 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
289 * Thanks, OpenBSD guys, for not having APIs to map MIB names to
290 * MIB values! Just consruct the MIB entry directly.
292 * We also do that for FreeBSD and DragonFly BSD, because we can.
294 * FreeBSD appears to support this for x86, PowerPC/Power ISA, and
295 * Arm. OpenBSD appears to support this for a number of
296 * architectures. DragonFly BSD appears to support it only for
297 * x86, but I think they only run on x86-64 now, and may never
298 * have run on anything non-x86.
300 int mib
[2] = { CTL_HW
, HW_MODEL
};
303 /* These require a lookup, as they don't have #defines. */
304 #if defined(__APPLE__) /* Darwin */
306 * The code seems to support this on both x86 and ARM.
308 #define BRAND_STRING_SYSCTL "machdep.cpu.brand_string"
310 #elif defined(__NetBSD__)
312 * XXX - the "highly portable Unix-like Open Source operating
313 * system" that "is available for a wide range of platforms"
314 * doesn't seem to support this except on x86, and doesn't
315 * seem to support any other MIB for, for example, ARM64.
317 * Maybe someday, so use it anyway.
319 #define BRAND_STRING_SYSCTL "machdep.cpu_brand"
323 size_t miblen
= MIB_DEPTH
;
325 /* Look up the sysctl name and get the MIB. */
326 if (sysctlnametomib(BRAND_STRING_SYSCTL
, mib
, &miblen
) == -1) {
328 * Either there's no such string or something else went wrong.
331 g_tree_destroy(model_names
);
335 if (sysctl(mib
, (u_int
)miblen
, NULL
, &model_name_len
, NULL
, 0) == -1) {
337 * Either there's no such string or something else went wrong.
340 g_tree_destroy(model_names
);
343 model_name
= g_malloc(model_name_len
);
344 if (sysctl(mib
, (u_int
)miblen
, model_name
, &model_name_len
, NULL
, 0) == -1) {
346 * Either there's no such string or something else went wrong.
350 g_tree_destroy(model_names
);
353 g_tree_insert(model_names
, model_name
, NULL
);
354 #elif defined(HAVE_SYSINFO) && defined(SI_CPUBRAND)
356 * Solaris. Use sysinfo() with SI_CPUBRAND; the documentation
357 * indicates that it works on SPARC as well as x86.
359 * Unfortunately, SI_CPUBRAND seems to be a recent addition, so
360 * older versions of Solaris - dating back to some versions of
361 * 11.3 - don't have it.
366 /* How big is the model name? */
367 model_name_len
= sysinfo(SI_CPUBRAND
, NULL
, 0);
368 if (model_name_len
== -1) {
369 g_tree_destroy(model_names
);
372 model_name
= g_malloc(model_name_len
);
373 if (sysinfo(SI_CPUBRAND
, model_name
, model_name_len
) == -1) {
374 g_tree_destroy(model_names
);
377 g_tree_insert(model_names
, model_name
, NULL
);
380 * OS for which we don't support the "get the CPU type" call; we
381 * use ws_cpuid(), which uses CPUID on x86 and doesn't get any
382 * information for other instruction sets.
385 char CPUBrandString
[0x40];
390 * Calling ws_cpuid with 0x80000000 as the selector argument, i.e.
391 * executing a cpuid instruction with EAX equal to 0x80000000 and
392 * ECX equal to 0, gets the number of valid extended IDs.
394 if (!ws_cpuid(CPUInfo
, 0x80000000)) {
395 g_tree_destroy(model_names
);
401 if (nExIds
<0x80000005) {
402 g_tree_destroy(model_names
);
406 memset(CPUBrandString
, 0, sizeof(CPUBrandString
));
408 /* Interpret CPU brand string */
409 ws_cpuid(CPUInfo
, 0x80000002);
410 memcpy(CPUBrandString
, CPUInfo
, sizeof(CPUInfo
));
411 ws_cpuid(CPUInfo
, 0x80000003);
412 memcpy(CPUBrandString
+ 16, CPUInfo
, sizeof(CPUInfo
));
413 ws_cpuid(CPUInfo
, 0x80000004);
414 memcpy(CPUBrandString
+ 32, CPUInfo
, sizeof(CPUInfo
));
416 model_name
= g_strdup(g_strstrip(CPUBrandString
));
417 g_tree_insert(model_names
, model_name
, NULL
);
420 int num_model_names
= g_tree_nnodes(model_names
);
422 if (num_model_names
> 0) {
424 * We have at least one model name, so add the name(s) to
427 * If the string is not empty, separate the name(s) from
431 g_string_append(str
, ", with ");
433 if (num_model_names
> 1) {
435 * There's more than one, so put the list inside curly
438 g_string_append(str
, "{ ");
441 /* Iterate over the tree, adding model names to the string. */
442 struct string_info info
;
445 g_tree_foreach(model_names
, add_model_name_to_string
, &info
);
447 if (num_model_names
> 1) {
449 * There's more than one, so put the list inside curly
452 g_string_append(str
, " }");
456 /* We're done; get rid of the tree. */
457 g_tree_destroy(model_names
);
460 * We do this on all OSes and instruction sets, so that we don't
461 * have to figure out how to dredge the "do we have SSE 4.2?"
462 * information from whatever source provides it in the OS on
463 * x86 processors. We already have ws_cpuid_sse42() (which we
464 * use to determine whether to use SSE 4.2 code to scan buffers
465 * for strings), so use that; it always returns "false" on non-x86
468 * If you have multiple CPUs, some of which support it and some
469 * of which don't, I'm not sure we can guarantee that buffer
470 * scanning will work if, for example, the scanning code gets
471 * preempted while running on an SSE-4.2-capable CPU and, when
472 * it gets rescheduled, gets rescheduled on a non-SSE-4.2-capable
473 * CPU and tries to continue the SSE 4.2-based scan. So we don't
474 * worry about that case; constructing a CPU string is the *least*
475 * of our worries in that case.
477 if (ws_cpuid_sse42())
478 g_string_append(str
, " (with SSE4.2)");
482 * Editor modelines - https://www.wireshark.org/tools/modelines.html
487 * indent-tabs-mode: nil
490 * vi: set shiftwidth=4 tabstop=8 expandtab:
491 * :indentSize=4:tabSize=8:noTabs=true: