Coverage Report

Created: 2025-12-27 06:52

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
#include "config.h"
12
#include <wsutil/cpu_info.h>
13
14
#include <string.h>
15
16
#include <wsutil/ws_cpuid.h>
17
#include <wsutil/file_util.h>
18
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
24
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
33
34
/*
35
 * Functions used for the GTree we use to keep a list of *unique*
36
 * model strings.
37
 */
38
static int
39
compare_model_names(const void *a, const void *b, void * user_data _U_)
40
434
{
41
434
    return strcmp((const char *)a, (const char *)b);
42
434
}
43
44
struct string_info {
45
    GString *str;
46
    const char *sep;
47
};
48
49
static gboolean
50
add_model_name_to_string(void * key, void * value _U_,
51
                         void * data)
52
14
{
53
14
    struct string_info *info = (struct string_info *)data;
54
55
    /* Separate this from the previous entry, if necessary. */
56
14
    if (info->sep != NULL)
57
0
        g_string_append(info->str, info->sep);
58
59
    /* Now add the model name. */
60
14
    g_string_append(info->str, g_strstrip((char *)key));
61
62
    /*
63
     * There will *definitely* need to be a separator for any subsequent
64
     * model string.
65
     */
66
14
    info->sep = ", ";
67
68
    /* Keep going. */
69
14
    return false;
70
14
}
71
72
static const char *
73
get_translator_name(void)
74
14
{
75
  #if defined(__APPLE__) /* Darwin */
76
    /*
77
     * OK, are we running x86(-64) code on Arm(64) under Rosetta?
78
     *
79
     * See
80
     *
81
     *    https://developer.apple.com/documentation/apple-silicon/about-the-rosetta-translation-environment#Determine-Whether-Your-App-Is-Running-as-a-Translated-Binary
82
     */
83
    int proc_translated;
84
    size_t proc_translated_len = sizeof proc_translated;
85
86
    if (sysctlbyname("sysctl.proc_translated", &proc_translated,
87
                     &proc_translated_len, NULL, 0) == -1) {
88
        /*
89
         * errno = ENOENT means there's no such name; presumably
90
         * that means there's no translator to check for, so we're
91
         * not running under translation.
92
         *
93
         * For other errors, we just punt.
94
         */
95
        return NULL;
96
    }
97
    return proc_translated ? "Rosetta 2" : NULL;
98
  #else
99
    /*
100
     * XXX - handle Windows x86-64-to-ARM64 translator, possibly with
101
     * IsWow64Process2(); see
102
     *
103
     *    https://learn.microsoft.com/en-us/windows/win32/api/wow64apiset/nf-wow64apiset-iswow64process2
104
     *
105
     * Any others?
106
     */
107
14
    return NULL;
108
14
  #endif
109
14
}
110
111
/*
112
 * Get the CPU info, and append it to the GString
113
 *
114
 * On at least some OSes, there's a call that will return this information
115
 * for all CPU types for which the OS determines that information, not just
116
 * x86 processors with CPUID and the brand string.  On those OSes, we use
117
 * that.
118
 *
119
 * On other OSes, we use ws_cpuid(), which will fail unconditionally on
120
 * non-x86 CPUs.
121
 */
122
void
123
get_cpu_info(GString *str)
124
14
{
125
14
    GTree *model_names = g_tree_new_full(compare_model_names, NULL, g_free, NULL);
126
14
    const char *translator_name;
127
128
14
#if defined(__linux__)
129
    /*
130
     * We scan /proc/cpuinfo looking for lines that begins with
131
     * "model name\t: ", and extract what comes after that prefix.
132
     *
133
     * /proc/cpuinfo can report information about multiple "CPU"s.
134
     * A "CPU" appears to be a CPU core, so this treats a multi-core
135
     * chip as multiple CPUs (which is arguably should), but doesn't
136
     * appear to treat a multi-threaded core as multiple CPUs.
137
     *
138
     * So we accumulate a table of *multiple* CPU strings, saving
139
     * one copy of each unique string, and glue them together at
140
     * the end.  We use a GTree for this.
141
     *
142
     * We test for Linux first, so that, even if you're on a Linux
143
     * that supports sysctl(), we don't use it, we scan /proc/cpuinfo,
144
     * as that's the right way to do this.
145
     */
146
14
    FILE *proc_cpuinfo;
147
148
14
    proc_cpuinfo = ws_fopen("/proc/cpuinfo", "r");
149
14
    if (proc_cpuinfo == NULL) {
150
        /* Just give up. */
151
0
        g_tree_destroy(model_names);
152
0
        return;
153
0
    }
154
155
14
    char *line = NULL;
156
14
    size_t linecap = 0;
157
14
    static const char prefix[] = "model name\t: ";
158
12.5k
    #define PREFIX_STRLEN (sizeof prefix - 1)
159
14
    ssize_t linelen;
160
161
    /*
162
     * Read lines from /proc/cpuinfo; stop when we either hit an EOF
163
     * or get an error.
164
     */
165
12.1k
    for (;;) {
166
12.1k
        linelen = getline(&line, &linecap, proc_cpuinfo);
167
12.1k
        if (linelen == -1) {
168
           /* EOF or error; just stop. */
169
14
           break;
170
14
        }
171
        /* Remove trailing newline. */
172
12.0k
        if (linelen != 0)
173
12.0k
            line[linelen - 1] = '\0';
174
12.0k
        if (strncmp(line, prefix, PREFIX_STRLEN) == 0) {
175
            /* OK, we have a model name. */
176
448
            char *model_name;
177
178
            /* Get everything after the prefix. */
179
448
            model_name = g_strdup(line + PREFIX_STRLEN);
180
181
            /*
182
             * Add an entry to the tree with the model name as key and
183
             * a null value.  There will only be one such entry in the
184
             * tree; if there's already such an entry, it will be left
185
             * alone, and model_name will be freed, otherwise a new
186
             * node will be created using model_name as the key.
187
             *
188
             * Thus, we don't free model_name; either it will be freed
189
             * for us, or it will be used in the tree and freed when we
190
             * free the tree.
191
             */
192
448
            g_tree_insert(model_names, model_name, NULL);
193
448
        }
194
12.0k
    }
195
196
14
    fclose(proc_cpuinfo);
197
14
#define xx_free free  /* hack so checkAPIs doesn't complain */
198
14
    xx_free(line);    /* yes, free(), as getline() mallocates it */
199
#elif defined(_WIN32)
200
    /*
201
     * They're in the Registry.  (Isn't everything?)
202
     */
203
    HKEY processors_key;
204
205
    if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,
206
                      L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor",
207
                      0, KEY_READ, &processors_key) != ERROR_SUCCESS) {
208
        /* Just give up. */
209
        g_tree_destroy(model_names);
210
        return;
211
    }
212
213
    /*
214
     * The processors appear under that key.  Enumerate all the keys
215
     * under it.
216
     */
217
    DWORD num_subkeys;
218
    DWORD max_subkey_len;
219
    wchar_t *subkey_buf;
220
221
    /*
222
     * How many subkeys are there, and what's the biggest subkey size?
223
     *
224
     * I assume that when the documentation says that some number is
225
     * in units of "Unicode characters" they mean "units of elements
226
     * of UTF-16 characters", i.e. "units of 2-octet items".
227
     */
228
    if (RegQueryInfoKeyW(processors_key, NULL, NULL, NULL, &num_subkeys,
229
                         &max_subkey_len, NULL, NULL, NULL, NULL, NULL,
230
                         NULL) != ERROR_SUCCESS) {
231
        /* Just give up. */
232
        g_tree_destroy(model_names);
233
        return;
234
    }
235
236
    /*
237
     * max_subkey_len does not count the trailing '\0'.  Add it.
238
     */
239
    max_subkey_len++;
240
241
    /*
242
     * Allocate a buffer for the subkey.
243
     */
244
    subkey_buf = g_new(wchar_t, max_subkey_len);
245
    if (subkey_buf == NULL) {
246
        /* Just give up. */
247
        g_tree_destroy(model_names);
248
        return;
249
    }
250
251
    for (DWORD processor_index = 0; processor_index < num_subkeys;
252
         processor_index++) {
253
        /*
254
         * The documentation says that this is "in characters"; I'm
255
         * assuming, for now, that they mean "Unicode characters",
256
         * meaning "2-octet items".
257
         */
258
        DWORD subkey_bufsize = max_subkey_len;
259
        if (RegEnumKeyExW(processors_key, processor_index, subkey_buf,
260
                          &subkey_bufsize, NULL, NULL, NULL,
261
                          NULL) != ERROR_SUCCESS) {
262
            /* Just exit the loop. */
263
            break;
264
        }
265
266
        /*
267
         * Get the length of processor name string for this processor.
268
         *
269
         * That's the "ProcessorNameString" value for the subkey of
270
         * processors_key with the name in subkey_buf.
271
         *
272
         * It's a string, so only allow REG_SZ values.
273
         */
274
        DWORD model_name_bufsize;
275
276
        model_name_bufsize = 0;
277
        if (RegGetValueW(processors_key, subkey_buf, L"ProcessorNameString",
278
                         RRF_RT_REG_SZ, NULL, NULL,
279
                         &model_name_bufsize) != ERROR_SUCCESS) {
280
            /* Just exit the loop. */
281
            break;
282
        }
283
284
        /*
285
         * Allocate a buffer for the string, as UTF-16.
286
         * The retrieved length includes the terminating '\0'.
287
         */
288
        wchar_t *model_name_wchar = g_malloc(model_name_bufsize);
289
        if (RegGetValueW(processors_key, subkey_buf, L"ProcessorNameString",
290
                         RRF_RT_REG_SZ, NULL, model_name_wchar,
291
                         &model_name_bufsize) != ERROR_SUCCESS) {
292
            /* Just exit the loop. */
293
            g_free(model_name_wchar);
294
            break;
295
        }
296
297
        /* Convert it to UTF-8. */
298
        char *model_name = g_utf16_to_utf8(model_name_wchar, -1, NULL, NULL, NULL);
299
        g_free(model_name_wchar);
300
301
        /*
302
         * Add an entry to the tree with the model name as key and
303
         * a null value.  There will only be one such entry in the
304
         * tree; if there's already such an entry, it will be left
305
         * alone, and model_name will be freed, otherwise a new
306
         * node will be created using model_name as the key.
307
         *
308
         * Thus, we don't free model_name; either it will be freed
309
         * for us, or it will be used in the tree and freed when we
310
         * free the tree.
311
         */
312
        g_tree_insert(model_names, model_name, NULL);
313
    }
314
315
    g_free(subkey_buf);
316
317
    /*
318
     * Close the registry key.
319
     */
320
    RegCloseKey(processors_key);
321
#elif defined(HAVE_SYSCTL)
322
    /*
323
     * Fetch the string using the appropriate sysctl.
324
     */
325
    size_t model_name_len;
326
    char *model_name;
327
  #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
328
    /*
329
     * Thanks, OpenBSD guys, for not having APIs to map MIB names to
330
     * MIB values!  Just consruct the MIB entry directly.
331
     *
332
     * We also do that for FreeBSD and DragonFly BSD, because we can.
333
     *
334
     * FreeBSD appears to support this for x86, PowerPC/Power ISA, and
335
     * Arm.  OpenBSD appears to support this for a number of
336
     * architectures.  DragonFly BSD appears to support it only for
337
     * x86, but I think they only run on x86-64 now, and may never
338
     * have run on anything non-x86.
339
     */
340
    int mib[2] = { CTL_HW, HW_MODEL };
341
    size_t miblen = 2;
342
  #else
343
    /* These require a lookup, as they don't have #defines. */
344
    #if defined(__APPLE__) /* Darwin */
345
        /*
346
         * The code seems to support this on both x86 and ARM.
347
         */
348
        #define BRAND_STRING_SYSCTL "machdep.cpu.brand_string"
349
        #define MIB_DEPTH 3
350
    #elif defined(__NetBSD__)
351
        /*
352
         * XXX - the "highly portable Unix-like Open Source operating
353
         * system" that "is available for a wide range of platforms"
354
         * doesn't seem to support this except on x86, and doesn't
355
         * seem to support any other MIB for, for example, ARM64.
356
         *
357
         * Maybe someday, so use it anyway.
358
         */
359
        #define BRAND_STRING_SYSCTL "machdep.cpu_brand"
360
        #define MIB_DEPTH 2
361
    #endif
362
    int mib[MIB_DEPTH];
363
    size_t miblen = MIB_DEPTH;
364
365
    /* Look up the sysctl name and get the MIB. */
366
    if (sysctlnametomib(BRAND_STRING_SYSCTL, mib, &miblen) == -1) {
367
        /*
368
         * Either there's no such string or something else went wrong.
369
         * Just give up.
370
         */
371
        g_tree_destroy(model_names);
372
        return;
373
    }
374
  #endif
375
    if (sysctl(mib, (u_int)miblen, NULL, &model_name_len, NULL, 0) == -1) {
376
        /*
377
         * Either there's no such string or something else went wrong.
378
         * Just give up.
379
         */
380
        g_tree_destroy(model_names);
381
        return;
382
    }
383
    model_name = g_malloc(model_name_len);
384
    if (sysctl(mib, (u_int)miblen, model_name, &model_name_len, NULL, 0) == -1) {
385
        /*
386
         * Either there's no such string or something else went wrong.
387
         * Just give up.
388
         */
389
        g_free(model_name);
390
        g_tree_destroy(model_names);
391
        return;
392
    }
393
    g_tree_insert(model_names, model_name, NULL);
394
#elif defined(HAVE_SYSINFO) && defined(SI_CPUBRAND)
395
    /*
396
     * Solaris.  Use sysinfo() with SI_CPUBRAND; the documentation
397
     * indicates that it works on SPARC as well as x86.
398
     *
399
     * Unfortunately, SI_CPUBRAND seems to be a recent addition, so
400
     * older versions of Solaris - dating back to some versions of
401
     * 11.3 - don't have it.
402
     */
403
    int model_name_len;
404
    char *model_name;
405
406
    /* How big is the model name? */
407
    model_name_len = sysinfo(SI_CPUBRAND, NULL, 0);
408
    if (model_name_len == -1) {
409
        g_tree_destroy(model_names);
410
        return;
411
    }
412
    model_name = g_malloc(model_name_len);
413
    if (sysinfo(SI_CPUBRAND, model_name, model_name_len) == -1) {
414
        g_tree_destroy(model_names);
415
        return;
416
    }
417
    g_tree_insert(model_names, model_name, NULL);
418
#else
419
    /*
420
     * OS for which we don't support the "get the CPU type" call; we
421
     * use ws_cpuid(), which uses CPUID on x86 and doesn't get any
422
     * information for other instruction sets.
423
     */
424
    uint32_t CPUInfo[4];
425
    char CPUBrandString[0x40];
426
    char *model_name;
427
    unsigned nExIds;
428
429
    /*
430
     * Calling ws_cpuid with 0x80000000 as the selector argument, i.e.
431
     * executing a cpuid instruction with EAX equal to 0x80000000 and
432
     * ECX equal to 0, gets the number of valid extended IDs.
433
     */
434
    if (!ws_cpuid(CPUInfo, 0x80000000)) {
435
        g_tree_destroy(model_names);
436
        return;
437
    }
438
439
    nExIds = CPUInfo[0];
440
441
    if (nExIds<0x80000005) {
442
        g_tree_destroy(model_names);
443
        return;
444
    }
445
446
    memset(CPUBrandString, 0, sizeof(CPUBrandString));
447
448
    /* Interpret CPU brand string */
449
    ws_cpuid(CPUInfo, 0x80000002);
450
    memcpy(CPUBrandString, CPUInfo, sizeof(CPUInfo));
451
    ws_cpuid(CPUInfo, 0x80000003);
452
    memcpy(CPUBrandString + 16, CPUInfo, sizeof(CPUInfo));
453
    ws_cpuid(CPUInfo, 0x80000004);
454
    memcpy(CPUBrandString + 32, CPUInfo, sizeof(CPUInfo));
455
456
    model_name = g_strdup(g_strstrip(CPUBrandString));
457
    g_tree_insert(model_names, model_name, NULL);
458
#endif
459
460
14
    int num_model_names = g_tree_nnodes(model_names);
461
462
14
    if (num_model_names > 0) {
463
        /*
464
         * We have at least one model name, so add the name(s) to
465
         * the string.
466
         */
467
14
        if (num_model_names > 1) {
468
            /*
469
             * There's more than one, so put the list inside curly
470
             * brackets.
471
             */
472
0
            g_string_append(str, "{ ");
473
0
        }
474
475
        /* Iterate over the tree, adding model names to the string. */
476
14
        struct string_info info;
477
14
        info.str = str;
478
14
        info.sep = NULL;
479
14
        g_tree_foreach(model_names, add_model_name_to_string, &info);
480
481
14
        if (num_model_names > 1) {
482
            /*
483
             * There's more than one, so put the list inside curly
484
             * brackets.
485
             */
486
0
            g_string_append(str, " }");
487
0
        }
488
14
    }
489
490
    /* We're done; get rid of the tree. */
491
14
    g_tree_destroy(model_names);
492
493
    /*
494
     * Are we running under a translator?
495
     */
496
14
    translator_name = get_translator_name();
497
14
    if (translator_name != NULL) {
498
        /*
499
         * Yes.
500
         */
501
0
         g_string_append(str, " running ");
502
0
         g_string_append(str, translator_name);
503
0
    }
504
505
    /*
506
     * We do this on all OSes and instruction sets, so that we don't
507
     * have to figure out how to dredge the "do we have SSE 4.2?"
508
     * information from whatever source provides it in the OS on
509
     * x86 processors.  We already have ws_cpuid_sse42() (which we
510
     * use to determine whether to use SSE 4.2 code to scan buffers
511
     * for strings), so use that; it always returns "false" on non-x86
512
     * processors.
513
     *
514
     * If you have multiple CPUs, some of which support it and some
515
     * of which don't, I'm not sure we can guarantee that buffer
516
     * scanning will work if, for example, the scanning code gets
517
     * preempted while running on an SSE-4.2-capable CPU  and, when
518
     * it gets rescheduled, gets rescheduled on a non-SSE-4.2-capable
519
     * CPU and tries to continue the SSE 4.2-based scan.  So we don't
520
     * worry about that case; constructing a CPU string is the *least*
521
     * of our worries in that case.
522
     */
523
14
    if (ws_cpuid_sse42())
524
14
        g_string_append(str, " (with SSE4.2)");
525
14
}
526
527
/*
528
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
529
 *
530
 * Local variables:
531
 * c-basic-offset: 4
532
 * tab-width: 8
533
 * indent-tabs-mode: nil
534
 * End:
535
 *
536
 * vi: set shiftwidth=4 tabstop=8 expandtab:
537
 * :indentSize=4:tabSize=8:noTabs=true:
538
 */