update epan/dissectors/pidl/drsuapi/drsuapi.idl from samba
[wireshark-sm.git] / wsutil / cpu_info.c
blob7c31a3bdfb93c7f080fe36f54ac1693146818566
1 /* cpu_info.c
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
9 */
11 #include "config.h"
12 #include <wsutil/cpu_info.h>
14 #include <string.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__)
20 #define HAVE_SYSCTL
21 #elif defined(sun) || defined(__sun)
22 #define HAVE_SYSINFO
23 #endif
25 #if defined(_WIN32)
26 #include <windows.h>
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>
32 #endif
35 * Functions used for the GTree we use to keep a list of *unique*
36 * model strings.
38 static int
39 compare_model_names(const void *a, const void *b, void * user_data _U_)
41 return strcmp((const char *)a, (const char *)b);
44 struct string_info {
45 GString *str;
46 const char *sep;
49 static gboolean
50 add_model_name_to_string(void * key, void * value _U_,
51 void * data)
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
64 * model string.
66 info->sep = ", ";
68 /* Keep going. */
69 return false;
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
78 * that.
80 * On other OSes, we use ws_cpuid(), which will fail unconditionally on
81 * non-x86 CPUs.
83 void
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.
106 FILE *proc_cpuinfo;
108 proc_cpuinfo = ws_fopen("/proc/cpuinfo", "r");
109 if (proc_cpuinfo == NULL) {
110 /* Just give up. */
111 g_tree_destroy(model_names);
112 return;
115 char *line = NULL;
116 size_t linecap = 0;
117 static const char prefix[] = "model name\t: ";
118 #define PREFIX_STRLEN (sizeof prefix - 1)
119 ssize_t linelen;
122 * Read lines from /proc/cpuinfo; stop when we either hit an EOF
123 * or get an error.
125 for (;;) {
126 linelen = getline(&line, &linecap, proc_cpuinfo);
127 if (linelen == -1) {
128 /* EOF or error; just stop. */
129 break;
131 /* Remove trailing newline. */
132 if (linelen != 0)
133 line[linelen - 1] = '\0';
134 if (strncmp(line, prefix, PREFIX_STRLEN) == 0) {
135 /* OK, we have a model name. */
136 char *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
150 * free the tree.
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?)
163 HKEY processors_key;
165 if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,
166 L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor",
167 0, KEY_READ, &processors_key) != ERROR_SUCCESS) {
168 /* Just give up. */
169 g_tree_destroy(model_names);
170 return;
174 * The processors appear under that key. Enumerate all the keys
175 * under it.
177 DWORD num_subkeys;
178 DWORD max_subkey_len;
179 wchar_t *subkey_buf;
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) {
191 /* Just give up. */
192 g_tree_destroy(model_names);
193 return;
197 * max_subkey_len does not count the trailing '\0'. Add it.
199 max_subkey_len++;
202 * Allocate a buffer for the subkey.
204 subkey_buf = g_new(wchar_t, max_subkey_len);
205 if (subkey_buf == NULL) {
206 /* Just give up. */
207 g_tree_destroy(model_names);
208 return;
211 for (DWORD processor_index = 0; processor_index < num_subkeys;
212 processor_index++) {
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. */
223 break;
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. */
241 break;
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);
254 break;
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
270 * free the tree.
272 g_tree_insert(model_names, model_name, NULL);
275 g_free(subkey_buf);
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;
286 char *model_name;
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 };
301 size_t miblen = 2;
302 #else
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"
309 #define MIB_DEPTH 3
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"
320 #define MIB_DEPTH 2
321 #endif
322 int mib[MIB_DEPTH];
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.
329 * Just give up.
331 g_tree_destroy(model_names);
332 return;
334 #endif
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.
338 * Just give up.
340 g_tree_destroy(model_names);
341 return;
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.
347 * Just give up.
349 g_free(model_name);
350 g_tree_destroy(model_names);
351 return;
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.
363 int model_name_len;
364 char *model_name;
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);
370 return;
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);
375 return;
377 g_tree_insert(model_names, model_name, NULL);
378 #else
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.
384 uint32_t CPUInfo[4];
385 char CPUBrandString[0x40];
386 char *model_name;
387 unsigned nExIds;
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);
396 return;
399 nExIds = CPUInfo[0];
401 if (nExIds<0x80000005) {
402 g_tree_destroy(model_names);
403 return;
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);
418 #endif
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
425 * the string.
427 * If the string is not empty, separate the name(s) from
428 * what precedes it.
430 if (str->len > 0)
431 g_string_append(str, ", with ");
433 if (num_model_names > 1) {
435 * There's more than one, so put the list inside curly
436 * brackets.
438 g_string_append(str, "{ ");
441 /* Iterate over the tree, adding model names to the string. */
442 struct string_info info;
443 info.str = str;
444 info.sep = NULL;
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
450 * brackets.
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
466 * processors.
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
484 * Local variables:
485 * c-basic-offset: 4
486 * tab-width: 8
487 * indent-tabs-mode: nil
488 * End:
490 * vi: set shiftwidth=4 tabstop=8 expandtab:
491 * :indentSize=4:tabSize=8:noTabs=true: