Coverage Report

Created: 2026-11-12 07:01

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
 */