/src/wireshark/wsutil/cpu_info.c
Line | Count | Source |
1 | | /* cpu_info.c |
2 | | * Routines to report CPU information |
3 | | * |
4 | | * Wireshark - Network traffic analyzer |
5 | | * By Gerald Combs <gerald@wireshark.org> |
6 | | * Copyright 1998 Gerald Combs |
7 | | * |
8 | | * SPDX-License-Identifier: GPL-2.0-or-later |
9 | | */ |
10 | | |
11 | | #define WS_LOG_DOMAIN LOG_DOMAIN_WSUTIL |
12 | | |
13 | | #include "config.h" |
14 | | #include <wsutil/cpu_info.h> |
15 | | |
16 | | #include <errno.h> |
17 | | #include <string.h> |
18 | | #include <stdlib.h> |
19 | | |
20 | | #include <wsutil/ws_cpuid.h> |
21 | | #include <wsutil/file_util.h> |
22 | | |
23 | | #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__APPLE__) |
24 | | #define HAVE_SYSCTL |
25 | | #elif defined(sun) || defined(__sun) |
26 | | #define HAVE_SYSINFO |
27 | | #endif |
28 | | |
29 | | #if defined(_WIN32) |
30 | | #include <windows.h> |
31 | | #include <wsutil/win32-utils.h> |
32 | | #elif defined(HAVE_SYSCTL) |
33 | | #include <sys/types.h> |
34 | | #include <sys/sysctl.h> |
35 | | #elif defined(HAVE_SYSINFO) |
36 | | #include <sys/systeminfo.h> |
37 | | #endif |
38 | | |
39 | | #ifdef HAVE_CPUINFO |
40 | | #include <cpuinfo.h> |
41 | | static bool cpuinfo_is_initialized; |
42 | | #endif |
43 | | |
44 | | /* |
45 | | * Functions used for the GTree we use to keep a list of *unique* |
46 | | * model strings. |
47 | | */ |
48 | | static int |
49 | | compare_model_names(const void *a, const void *b, void * user_data _U_) |
50 | 496 | { |
51 | 496 | return strcmp((const char *)a, (const char *)b); |
52 | 496 | } |
53 | | |
54 | | struct string_info { |
55 | | GString *str; |
56 | | const char *sep; |
57 | | }; |
58 | | |
59 | | static gboolean |
60 | | add_model_name_to_string(void * key, void * value _U_, |
61 | | void * data) |
62 | 16 | { |
63 | 16 | struct string_info *info = (struct string_info *)data; |
64 | | |
65 | | /* Separate this from the previous entry, if necessary. */ |
66 | 16 | if (info->sep != NULL) |
67 | 16 | g_string_append(info->str, info->sep); |
68 | | |
69 | | /* Now add the model name. */ |
70 | 16 | g_string_append(info->str, g_strstrip((char *)key)); |
71 | | |
72 | | /* |
73 | | * There will *definitely* need to be a separator for any subsequent |
74 | | * model string. |
75 | | */ |
76 | 16 | info->sep = ", "; |
77 | | |
78 | | /* Keep going. */ |
79 | 16 | return false; |
80 | 16 | } |
81 | | |
82 | | static const char * |
83 | | get_translator_name(void) |
84 | 16 | { |
85 | | #if defined(__APPLE__) /* Darwin */ |
86 | | /* |
87 | | * OK, are we running x86(-64) code on Arm(64) under Rosetta? |
88 | | * |
89 | | * See |
90 | | * |
91 | | * https://developer.apple.com/documentation/apple-silicon/about-the-rosetta-translation-environment#Determine-Whether-Your-App-Is-Running-as-a-Translated-Binary |
92 | | */ |
93 | | int proc_translated; |
94 | | size_t proc_translated_len = sizeof proc_translated; |
95 | | |
96 | | if (sysctlbyname("sysctl.proc_translated", &proc_translated, |
97 | | &proc_translated_len, NULL, 0) == -1) { |
98 | | /* |
99 | | * errno = ENOENT means there's no such name; presumably |
100 | | * that means there's no translator to check for, so we're |
101 | | * not running under translation. |
102 | | * |
103 | | * For other errors, we just punt. |
104 | | */ |
105 | | return NULL; |
106 | | } |
107 | | return proc_translated ? "Rosetta 2" : NULL; |
108 | | #else |
109 | | /* |
110 | | * XXX - handle Windows x86-64-to-ARM64 translator, possibly with |
111 | | * IsWow64Process2(); see |
112 | | * |
113 | | * https://learn.microsoft.com/en-us/windows/win32/api/wow64apiset/nf-wow64apiset-iswow64process2 |
114 | | * |
115 | | * Any others? |
116 | | */ |
117 | 16 | return NULL; |
118 | 16 | #endif |
119 | 16 | } |
120 | | |
121 | | #ifdef HAVE_CPUINFO |
122 | | /* |
123 | | * Depending on how it was built, cpuinfo logs debugging and informational |
124 | | * messages to the standard output, and warnings and errors to the standard |
125 | | * error; for example, the vcpkg debug build logs debugging messages. It |
126 | | * writes them with write() on UN*X, and with WriteFile() on the handles |
127 | | * from GetStdHandle() on Windows, bypassing stdio. That can corrupt what we |
128 | | * write to those streams, such as a capture file that dumpcap writes to |
129 | | * the standard output, or dumpcap's messages to its parent. |
130 | | * |
131 | | * So while cpuinfo initializes, which is when it logs, point the standard |
132 | | * output and error at temporary files, and afterwards log what it wrote |
133 | | * with our own logging. This affects the whole process, so it should be |
134 | | * done as briefly as possible. It only happened at startup in our programs; |
135 | | * later calls don't reinitialize and just fetch the existing info. |
136 | | */ |
137 | | |
138 | | /* Don't log more than this much of a stream. */ |
139 | | #define STD_STREAM_CAPTURE_MAX (64 * 1024) |
140 | | |
141 | | struct std_stream_capture { |
142 | | enum ws_log_level level; /* level at which to log what was written */ |
143 | | bool active; |
144 | | #ifdef _WIN32 |
145 | | DWORD std_handle; /* STD_OUTPUT_HANDLE or STD_ERROR_HANDLE */ |
146 | | HANDLE saved_handle; |
147 | | HANDLE file; /* deleted when closed */ |
148 | | #else |
149 | | int std_fd; /* STDOUT_FILENO or STDERR_FILENO */ |
150 | | int saved_fd; |
151 | | int file_fd; /* already unlinked */ |
152 | | #endif |
153 | | }; |
154 | | |
155 | | static void |
156 | | std_stream_capture_start(struct std_stream_capture *capture, FILE *stream, |
157 | | enum ws_log_level level) |
158 | | { |
159 | | capture->level = level; |
160 | | capture->active = false; |
161 | | /* Write out anything we've buffered to the real stream first. */ |
162 | | fflush(stream); |
163 | | #ifdef _WIN32 |
164 | | WCHAR dir[MAX_PATH + 1]; |
165 | | WCHAR path[MAX_PATH + 1]; |
166 | | |
167 | | capture->std_handle = (stream == stdout) ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE; |
168 | | if (GetTempPathW((DWORD)G_N_ELEMENTS(dir), dir) == 0 || |
169 | | GetTempFileNameW(dir, L"wsc", 0, path) == 0) { |
170 | | ws_debug("Can't get a temporary file name for cpuinfo output: %s", |
171 | | win32strerror(GetLastError())); |
172 | | return; |
173 | | } |
174 | | capture->file = CreateFileW(path, GENERIC_READ | GENERIC_WRITE, 0, NULL, |
175 | | CREATE_ALWAYS, |
176 | | FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, |
177 | | NULL); |
178 | | if (capture->file == INVALID_HANDLE_VALUE) { |
179 | | ws_debug("Can't create a temporary file for cpuinfo output: %s", |
180 | | win32strerror(GetLastError())); |
181 | | DeleteFileW(path); |
182 | | return; |
183 | | } |
184 | | capture->saved_handle = GetStdHandle(capture->std_handle); |
185 | | if (!SetStdHandle(capture->std_handle, capture->file)) { |
186 | | ws_debug("Can't redirect a standard stream for cpuinfo: %s", |
187 | | win32strerror(GetLastError())); |
188 | | CloseHandle(capture->file); |
189 | | return; |
190 | | } |
191 | | #else |
192 | | GError *err = NULL; |
193 | | char *path; |
194 | | |
195 | | capture->std_fd = fileno(stream); |
196 | | capture->file_fd = g_file_open_tmp("wireshark_cpuinfo_XXXXXX", &path, &err); |
197 | | if (capture->file_fd == -1) { |
198 | | ws_debug("Can't create a temporary file for cpuinfo output: %s", |
199 | | err->message); |
200 | | g_error_free(err); |
201 | | return; |
202 | | } |
203 | | ws_unlink(path); |
204 | | g_free(path); |
205 | | capture->saved_fd = ws_dup(capture->std_fd); |
206 | | if (capture->saved_fd == -1) { |
207 | | ws_debug("Can't save a standard stream for cpuinfo: %s", g_strerror(errno)); |
208 | | ws_close(capture->file_fd); |
209 | | return; |
210 | | } |
211 | | if (dup2(capture->file_fd, capture->std_fd) == -1) { |
212 | | ws_debug("Can't redirect a standard stream for cpuinfo: %s", g_strerror(errno)); |
213 | | ws_close(capture->saved_fd); |
214 | | ws_close(capture->file_fd); |
215 | | return; |
216 | | } |
217 | | #endif |
218 | | capture->active = true; |
219 | | } |
220 | | |
221 | | static void |
222 | | std_stream_capture_finish(struct std_stream_capture *capture) |
223 | | { |
224 | | char *buf; |
225 | | size_t len = 0; |
226 | | |
227 | | if (!capture->active) { |
228 | | return; |
229 | | } |
230 | | buf = g_malloc(STD_STREAM_CAPTURE_MAX + 1); |
231 | | /* Restore the stream, and read back what was written to it. */ |
232 | | #ifdef _WIN32 |
233 | | DWORD bytes_read; |
234 | | |
235 | | SetStdHandle(capture->std_handle, capture->saved_handle); |
236 | | if (SetFilePointer(capture->file, 0, NULL, FILE_BEGIN) != INVALID_SET_FILE_POINTER) { |
237 | | while (len < STD_STREAM_CAPTURE_MAX && |
238 | | ReadFile(capture->file, buf + len, (DWORD)(STD_STREAM_CAPTURE_MAX - len), |
239 | | &bytes_read, NULL) && bytes_read > 0) { |
240 | | len += bytes_read; |
241 | | } |
242 | | } |
243 | | CloseHandle(capture->file); |
244 | | #else |
245 | | ws_file_ssize_t bytes_read; |
246 | | |
247 | | dup2(capture->saved_fd, capture->std_fd); |
248 | | ws_close(capture->saved_fd); |
249 | | if (ws_lseek64(capture->file_fd, 0, SEEK_SET) == 0) { |
250 | | while (len < STD_STREAM_CAPTURE_MAX && |
251 | | (bytes_read = ws_read(capture->file_fd, buf + len, |
252 | | STD_STREAM_CAPTURE_MAX - len)) > 0) { |
253 | | len += (size_t)bytes_read; |
254 | | } |
255 | | } |
256 | | ws_close(capture->file_fd); |
257 | | #endif |
258 | | capture->active = false; |
259 | | |
260 | | /* Log each line, without the line ending. */ |
261 | | buf[len] = '\0'; |
262 | | char **lines = g_strsplit(buf, "\n", -1); |
263 | | for (char **line = lines; *line != NULL; line++) { |
264 | | g_strchomp(*line); |
265 | | if (**line != '\0') { |
266 | | ws_log(WS_LOG_DOMAIN, capture->level, "%s", *line); |
267 | | } |
268 | | } |
269 | | g_strfreev(lines); |
270 | | g_free(buf); |
271 | | } |
272 | | |
273 | | static bool |
274 | | try_cpuinfo(GTree* model_names) |
275 | | { |
276 | | static bool tried_cpuinfo_init = false; |
277 | | if (!tried_cpuinfo_init) { |
278 | | struct std_stream_capture out_capture, err_capture; |
279 | | |
280 | | /* XXX - If we really cared, we could string match against the |
281 | | * common prefixes cpuinfo used, remove them and send to the |
282 | | * exact same log level. Probably overkill. Also note we'll |
283 | | * log all the debug messages before all the warnings at the |
284 | | * end, instead of preserving the exact order. */ |
285 | | std_stream_capture_start(&out_capture, stdout, LOG_LEVEL_DEBUG); |
286 | | std_stream_capture_start(&err_capture, stderr, LOG_LEVEL_WARNING); |
287 | | cpuinfo_is_initialized = cpuinfo_initialize(); |
288 | | std_stream_capture_finish(&err_capture); |
289 | | std_stream_capture_finish(&out_capture); |
290 | | if (cpuinfo_is_initialized) { |
291 | | atexit(cpuinfo_deinitialize); |
292 | | } |
293 | | tried_cpuinfo_init = true; |
294 | | } |
295 | | if (cpuinfo_is_initialized) { |
296 | | char* model_name; |
297 | | for (uint32_t i = 0; i < cpuinfo_get_packages_count(); i++) { |
298 | | model_name = g_strdup(cpuinfo_get_package(i)->name); |
299 | | |
300 | | /* |
301 | | * Add an entry to the tree with the model name as key and |
302 | | * a null value. There will only be one such entry in the |
303 | | * tree; if there's already such an entry, it will be left |
304 | | * alone, and model_name will be freed, otherwise a new |
305 | | * node will be created using model_name as the key. |
306 | | * |
307 | | * Thus, we don't free model_name; either it will be freed |
308 | | * for us, or it will be used in the tree and freed when we |
309 | | * free the tree. |
310 | | */ |
311 | | g_tree_insert(model_names, model_name, NULL); |
312 | | } |
313 | | return true; |
314 | | } |
315 | | return false; |
316 | | } |
317 | | #else |
318 | | static bool |
319 | | try_cpuinfo(GTree* model_names _U_) |
320 | 16 | { |
321 | 16 | return false; |
322 | 16 | } |
323 | | #endif |
324 | | |
325 | | static bool |
326 | 16 | try_platform_cpuinfo(GTree *model_names) { |
327 | 16 | #if defined(__linux__) |
328 | | /* |
329 | | * We scan /proc/cpuinfo looking for lines that begins with |
330 | | * "model name\t: ", and extract what comes after that prefix. |
331 | | * |
332 | | * /proc/cpuinfo can report information about multiple "CPU"s. |
333 | | * A "CPU" appears to be a CPU core, so this treats a multi-core |
334 | | * chip as multiple CPUs (which is arguably should), but doesn't |
335 | | * appear to treat a multi-threaded core as multiple CPUs. |
336 | | * |
337 | | * So we accumulate a table of *multiple* CPU strings, saving |
338 | | * one copy of each unique string, and glue them together at |
339 | | * the end. We use a GTree for this. |
340 | | * |
341 | | * We test for Linux first, so that, even if you're on a Linux |
342 | | * that supports sysctl(), we don't use it, we scan /proc/cpuinfo, |
343 | | * as that's the right way to do this. |
344 | | */ |
345 | 16 | FILE *proc_cpuinfo; |
346 | | |
347 | 16 | proc_cpuinfo = ws_fopen("/proc/cpuinfo", "r"); |
348 | 16 | if (proc_cpuinfo == NULL) { |
349 | | /* Just give up. */ |
350 | 0 | return false; |
351 | 0 | } |
352 | | |
353 | 16 | char *line = NULL; |
354 | 16 | size_t linecap = 0; |
355 | 16 | static const char prefix[] = "model name\t: "; |
356 | 13.8k | #define PREFIX_STRLEN (sizeof prefix - 1) |
357 | 16 | ssize_t linelen; |
358 | | |
359 | | /* |
360 | | * Read lines from /proc/cpuinfo; stop when we either hit an EOF |
361 | | * or get an error. |
362 | | */ |
363 | 13.8k | for (;;) { |
364 | 13.8k | linelen = getline(&line, &linecap, proc_cpuinfo); |
365 | 13.8k | if (linelen == -1) { |
366 | | /* EOF or error; just stop. */ |
367 | 16 | break; |
368 | 16 | } |
369 | | /* Remove trailing newline. */ |
370 | 13.8k | if (linelen != 0) |
371 | 13.8k | line[linelen - 1] = '\0'; |
372 | 13.8k | if (strncmp(line, prefix, PREFIX_STRLEN) == 0) { |
373 | | /* OK, we have a model name. */ |
374 | 512 | char *model_name; |
375 | | |
376 | | /* Get everything after the prefix. */ |
377 | 512 | model_name = g_strdup(line + PREFIX_STRLEN); |
378 | | |
379 | | /* |
380 | | * Add an entry to the tree with the model name as key and |
381 | | * a null value. There will only be one such entry in the |
382 | | * tree; if there's already such an entry, it will be left |
383 | | * alone, and model_name will be freed, otherwise a new |
384 | | * node will be created using model_name as the key. |
385 | | * |
386 | | * Thus, we don't free model_name; either it will be freed |
387 | | * for us, or it will be used in the tree and freed when we |
388 | | * free the tree. |
389 | | */ |
390 | 512 | g_tree_insert(model_names, model_name, NULL); |
391 | 512 | } |
392 | 13.8k | } |
393 | | |
394 | 16 | fclose(proc_cpuinfo); |
395 | 16 | #define xx_free free /* hack so checkAPIs doesn't complain */ |
396 | 16 | xx_free(line); /* yes, free(), as getline() mallocates it */ |
397 | | #elif defined(_WIN32) |
398 | | /* |
399 | | * They're in the Registry. (Isn't everything?) |
400 | | */ |
401 | | HKEY processors_key; |
402 | | |
403 | | if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, |
404 | | L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor", |
405 | | 0, KEY_READ, &processors_key) != ERROR_SUCCESS) { |
406 | | /* Just give up. */ |
407 | | return false; |
408 | | } |
409 | | |
410 | | /* |
411 | | * The processors appear under that key. Enumerate all the keys |
412 | | * under it. |
413 | | */ |
414 | | DWORD num_subkeys; |
415 | | DWORD max_subkey_len; |
416 | | wchar_t *subkey_buf; |
417 | | |
418 | | /* |
419 | | * How many subkeys are there, and what's the biggest subkey size? |
420 | | * |
421 | | * I assume that when the documentation says that some number is |
422 | | * in units of "Unicode characters" they mean "units of elements |
423 | | * of UTF-16 characters", i.e. "units of 2-octet items". |
424 | | */ |
425 | | if (RegQueryInfoKeyW(processors_key, NULL, NULL, NULL, &num_subkeys, |
426 | | &max_subkey_len, NULL, NULL, NULL, NULL, NULL, |
427 | | NULL) != ERROR_SUCCESS) { |
428 | | /* Just give up. */ |
429 | | return false; |
430 | | } |
431 | | |
432 | | /* |
433 | | * max_subkey_len does not count the trailing '\0'. Add it. |
434 | | */ |
435 | | max_subkey_len++; |
436 | | |
437 | | /* |
438 | | * Allocate a buffer for the subkey. |
439 | | */ |
440 | | subkey_buf = g_new(wchar_t, max_subkey_len); |
441 | | if (subkey_buf == NULL) { |
442 | | /* Just give up. */ |
443 | | return false; |
444 | | } |
445 | | |
446 | | for (DWORD processor_index = 0; processor_index < num_subkeys; |
447 | | processor_index++) { |
448 | | /* |
449 | | * The documentation says that this is "in characters"; I'm |
450 | | * assuming, for now, that they mean "Unicode characters", |
451 | | * meaning "2-octet items". |
452 | | */ |
453 | | DWORD subkey_bufsize = max_subkey_len; |
454 | | if (RegEnumKeyExW(processors_key, processor_index, subkey_buf, |
455 | | &subkey_bufsize, NULL, NULL, NULL, |
456 | | NULL) != ERROR_SUCCESS) { |
457 | | /* Just exit the loop. */ |
458 | | break; |
459 | | } |
460 | | |
461 | | /* |
462 | | * Get the length of processor name string for this processor. |
463 | | * |
464 | | * That's the "ProcessorNameString" value for the subkey of |
465 | | * processors_key with the name in subkey_buf. |
466 | | * |
467 | | * It's a string, so only allow REG_SZ values. |
468 | | */ |
469 | | DWORD model_name_bufsize; |
470 | | |
471 | | model_name_bufsize = 0; |
472 | | if (RegGetValueW(processors_key, subkey_buf, L"ProcessorNameString", |
473 | | RRF_RT_REG_SZ, NULL, NULL, |
474 | | &model_name_bufsize) != ERROR_SUCCESS) { |
475 | | /* Just exit the loop. */ |
476 | | break; |
477 | | } |
478 | | |
479 | | /* |
480 | | * Allocate a buffer for the string, as UTF-16. |
481 | | * The retrieved length includes the terminating '\0'. |
482 | | */ |
483 | | wchar_t *model_name_wchar = g_malloc(model_name_bufsize); |
484 | | if (RegGetValueW(processors_key, subkey_buf, L"ProcessorNameString", |
485 | | RRF_RT_REG_SZ, NULL, model_name_wchar, |
486 | | &model_name_bufsize) != ERROR_SUCCESS) { |
487 | | /* Just exit the loop. */ |
488 | | g_free(model_name_wchar); |
489 | | break; |
490 | | } |
491 | | |
492 | | /* Convert it to UTF-8. */ |
493 | | char *model_name = g_utf16_to_utf8(model_name_wchar, -1, NULL, NULL, NULL); |
494 | | g_free(model_name_wchar); |
495 | | |
496 | | /* |
497 | | * Add an entry to the tree with the model name as key and |
498 | | * a null value. There will only be one such entry in the |
499 | | * tree; if there's already such an entry, it will be left |
500 | | * alone, and model_name will be freed, otherwise a new |
501 | | * node will be created using model_name as the key. |
502 | | * |
503 | | * Thus, we don't free model_name; either it will be freed |
504 | | * for us, or it will be used in the tree and freed when we |
505 | | * free the tree. |
506 | | */ |
507 | | g_tree_insert(model_names, model_name, NULL); |
508 | | } |
509 | | |
510 | | g_free(subkey_buf); |
511 | | |
512 | | /* |
513 | | * Close the registry key. |
514 | | */ |
515 | | RegCloseKey(processors_key); |
516 | | #elif defined(HAVE_SYSCTL) |
517 | | /* |
518 | | * Fetch the string using the appropriate sysctl. |
519 | | */ |
520 | | size_t model_name_len; |
521 | | char *model_name; |
522 | | #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) |
523 | | /* |
524 | | * Thanks, OpenBSD guys, for not having APIs to map MIB names to |
525 | | * MIB values! Just construct the MIB entry directly. |
526 | | * |
527 | | * We also do that for FreeBSD and DragonFly BSD, because we can. |
528 | | * |
529 | | * FreeBSD appears to support this for x86, PowerPC/Power ISA, and |
530 | | * Arm. OpenBSD appears to support this for a number of |
531 | | * architectures. DragonFly BSD appears to support it only for |
532 | | * x86, but I think they only run on x86-64 now, and may never |
533 | | * have run on anything non-x86. |
534 | | */ |
535 | | int mib[2] = { CTL_HW, HW_MODEL }; |
536 | | size_t miblen = 2; |
537 | | #else |
538 | | /* These require a lookup, as they don't have #defines. */ |
539 | | #if defined(__APPLE__) /* Darwin */ |
540 | | /* |
541 | | * The code seems to support this on both x86 and ARM. |
542 | | */ |
543 | | #define BRAND_STRING_SYSCTL "machdep.cpu.brand_string" |
544 | | #define MIB_DEPTH 3 |
545 | | #elif defined(__NetBSD__) |
546 | | /* |
547 | | * XXX - the "highly portable Unix-like Open Source operating |
548 | | * system" that "is available for a wide range of platforms" |
549 | | * doesn't seem to support this except on x86, and doesn't |
550 | | * seem to support any other MIB for, for example, ARM64. |
551 | | * |
552 | | * Maybe someday, so use it anyway. |
553 | | */ |
554 | | #define BRAND_STRING_SYSCTL "machdep.cpu_brand" |
555 | | #define MIB_DEPTH 2 |
556 | | #endif |
557 | | int mib[MIB_DEPTH]; |
558 | | size_t miblen = MIB_DEPTH; |
559 | | |
560 | | /* Look up the sysctl name and get the MIB. */ |
561 | | if (sysctlnametomib(BRAND_STRING_SYSCTL, mib, &miblen) == -1) { |
562 | | /* |
563 | | * Either there's no such string or something else went wrong. |
564 | | * Just give up. |
565 | | */ |
566 | | return false; |
567 | | } |
568 | | #endif |
569 | | if (sysctl(mib, (u_int)miblen, NULL, &model_name_len, NULL, 0) == -1) { |
570 | | /* |
571 | | * Either there's no such string or something else went wrong. |
572 | | * Just give up. |
573 | | */ |
574 | | return false; |
575 | | } |
576 | | model_name = g_malloc(model_name_len); |
577 | | if (sysctl(mib, (u_int)miblen, model_name, &model_name_len, NULL, 0) == -1) { |
578 | | /* |
579 | | * Either there's no such string or something else went wrong. |
580 | | * Just give up. |
581 | | */ |
582 | | g_free(model_name); |
583 | | return false; |
584 | | } |
585 | | g_tree_insert(model_names, model_name, NULL); |
586 | | #elif defined(HAVE_SYSINFO) && defined(SI_CPUBRAND) |
587 | | /* |
588 | | * Solaris. Use sysinfo() with SI_CPUBRAND; the documentation |
589 | | * indicates that it works on SPARC as well as x86. |
590 | | * |
591 | | * Unfortunately, SI_CPUBRAND seems to be a recent addition, so |
592 | | * older versions of Solaris - dating back to some versions of |
593 | | * 11.3 - don't have it. |
594 | | */ |
595 | | int model_name_len; |
596 | | char *model_name; |
597 | | |
598 | | /* How big is the model name? */ |
599 | | model_name_len = sysinfo(SI_CPUBRAND, NULL, 0); |
600 | | if (model_name_len == -1) { |
601 | | return false; |
602 | | } |
603 | | model_name = g_malloc(model_name_len); |
604 | | if (sysinfo(SI_CPUBRAND, model_name, model_name_len) == -1) { |
605 | | return false; |
606 | | } |
607 | | g_tree_insert(model_names, model_name, NULL); |
608 | | #else |
609 | | /* |
610 | | * OS for which we don't support the "get the CPU type" call; we |
611 | | * use ws_cpuid(), which uses CPUID on x86 and doesn't get any |
612 | | * information for other instruction sets. |
613 | | */ |
614 | | uint32_t CPUInfo[4]; |
615 | | char CPUBrandString[0x40]; |
616 | | char *model_name; |
617 | | unsigned nExIds; |
618 | | |
619 | | /* |
620 | | * Calling ws_cpuid with 0x80000000 as the selector argument, i.e. |
621 | | * executing a cpuid instruction with EAX equal to 0x80000000 and |
622 | | * ECX equal to 0, gets the number of valid extended IDs. |
623 | | */ |
624 | | if (!ws_cpuid(CPUInfo, 0x80000000)) { |
625 | | return false; |
626 | | } |
627 | | |
628 | | nExIds = CPUInfo[0]; |
629 | | |
630 | | if (nExIds<0x80000005) { |
631 | | return false; |
632 | | } |
633 | | |
634 | | memset(CPUBrandString, 0, sizeof(CPUBrandString)); |
635 | | |
636 | | /* Interpret CPU brand string */ |
637 | | ws_cpuid(CPUInfo, 0x80000002); |
638 | | memcpy(CPUBrandString, CPUInfo, sizeof(CPUInfo)); |
639 | | ws_cpuid(CPUInfo, 0x80000003); |
640 | | memcpy(CPUBrandString + 16, CPUInfo, sizeof(CPUInfo)); |
641 | | ws_cpuid(CPUInfo, 0x80000004); |
642 | | memcpy(CPUBrandString + 32, CPUInfo, sizeof(CPUInfo)); |
643 | | |
644 | | model_name = g_strdup(g_strstrip(CPUBrandString)); |
645 | | g_tree_insert(model_names, model_name, NULL); |
646 | | #endif |
647 | | |
648 | 16 | return true; |
649 | 16 | } |
650 | | |
651 | | /* |
652 | | * Get the CPU info, and append it to the GString |
653 | | * |
654 | | * On at least some OSes, there's a call that will return this information |
655 | | * for all CPU types for which the OS determines that information, not just |
656 | | * x86 processors with CPUID and the brand string. On those OSes, we use |
657 | | * that. |
658 | | * |
659 | | * On other OSes, we use ws_cpuid(), which will fail unconditionally on |
660 | | * non-x86 CPUs. |
661 | | */ |
662 | | void |
663 | | get_cpu_info(GString *str) |
664 | 16 | { |
665 | 16 | GTree *model_names = g_tree_new_full(compare_model_names, NULL, g_free, NULL); |
666 | 16 | const char *translator_name; |
667 | | |
668 | 16 | if (!try_cpuinfo(model_names) && !try_platform_cpuinfo(model_names)) { |
669 | 0 | g_tree_destroy(model_names); |
670 | 0 | return; |
671 | 0 | } |
672 | | |
673 | 16 | int num_model_names = g_tree_nnodes(model_names); |
674 | | |
675 | 16 | if (num_model_names > 0) { |
676 | | /* |
677 | | * We have at least one model name, so add the name(s) to |
678 | | * the string. |
679 | | */ |
680 | 16 | if (num_model_names > 1) { |
681 | | /* |
682 | | * There's more than one, so put the list inside curly |
683 | | * brackets. |
684 | | */ |
685 | 0 | g_string_append(str, "{ "); |
686 | 0 | } |
687 | | |
688 | | /* Iterate over the tree, adding model names to the string. */ |
689 | 16 | struct string_info info; |
690 | 16 | info.str = str; |
691 | 16 | info.sep = NULL; |
692 | 16 | g_tree_foreach(model_names, add_model_name_to_string, &info); |
693 | | |
694 | 16 | if (num_model_names > 1) { |
695 | | /* |
696 | | * There's more than one, so put the list inside curly |
697 | | * brackets. |
698 | | */ |
699 | 0 | g_string_append(str, " }"); |
700 | 0 | } |
701 | 16 | } |
702 | | |
703 | | /* We're done; get rid of the tree. */ |
704 | 16 | g_tree_destroy(model_names); |
705 | | |
706 | | /* |
707 | | * Are we running under a translator? |
708 | | */ |
709 | 16 | translator_name = get_translator_name(); |
710 | 16 | if (translator_name != NULL) { |
711 | | /* |
712 | | * Yes. |
713 | | */ |
714 | 0 | g_string_append(str, " running "); |
715 | 0 | g_string_append(str, translator_name); |
716 | 0 | } |
717 | | |
718 | | /* |
719 | | * We do this on all OSes and instruction sets, so that we don't |
720 | | * have to figure out how to dredge the "do we have SSE 4.2?" |
721 | | * information from whatever source provides it in the OS on |
722 | | * x86 processors. We already have ws_cpuid_sse42() (which we |
723 | | * use to determine whether to use SSE 4.2 code to scan buffers |
724 | | * for strings), so use that; it always returns "false" on non-x86 |
725 | | * processors. |
726 | | * |
727 | | * If you have multiple CPUs, some of which support it and some |
728 | | * of which don't, I'm not sure we can guarantee that buffer |
729 | | * scanning will work if, for example, the scanning code gets |
730 | | * preempted while running on an SSE-4.2-capable CPU and, when |
731 | | * it gets rescheduled, gets rescheduled on a non-SSE-4.2-capable |
732 | | * CPU and tries to continue the SSE 4.2-based scan. So we don't |
733 | | * worry about that case; constructing a CPU string is the *least* |
734 | | * of our worries in that case. |
735 | | */ |
736 | | #ifdef HAVE_CPUINFO |
737 | | if (cpuinfo_is_initialized) { |
738 | | if (cpuinfo_has_x86_sse4_2()) { |
739 | | g_string_append(str, " (with SSE4.2)"); |
740 | | } |
741 | | } else |
742 | | #endif |
743 | 16 | if (ws_cpuid_sse42()) |
744 | | g_string_append(str, " (with SSE4.2)"); |
745 | 16 | } |
746 | | |
747 | | /* |
748 | | * Editor modelines - https://www.wireshark.org/tools/modelines.html |
749 | | * |
750 | | * Local variables: |
751 | | * c-basic-offset: 4 |
752 | | * tab-width: 8 |
753 | | * indent-tabs-mode: nil |
754 | | * End: |
755 | | * |
756 | | * vi: set shiftwidth=4 tabstop=8 expandtab: |
757 | | * :indentSize=4:tabSize=8:noTabs=true: |
758 | | */ |