]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: debug: detect CPU model and store it in post_mortem
authorWilly Tarreau <w@1wt.eu>
Wed, 22 Nov 2023 10:55:22 +0000 (11:55 +0100)
committerWilly Tarreau <w@1wt.eu>
Thu, 23 Nov 2023 14:39:21 +0000 (15:39 +0100)
The CPU model and type has significant impact on certain bugs, such
as contention issues caused by CPUs having split L3 caches, or stricter
memory models that exhibit some barrier issues. It's complicated though
because the info about the model depends on the arch. For example, x86
reports an SKU name while ARM rather reports the CPU core types, families
and versions for each CPU core. There, the SoC will sometimes be reported
in the device tree or DMI info instead. But we don't really care, it's
essentially useful to know if the code is running on an armv8.0 such as
A53, a 8.2 such as A55/A76/Neoverse etc. For MIPS the model appears to
generally be there, and in addition the SoC is often present in the
"system type" field before the first CPU, and the type of machine in the
"machine" field, to replace the missing DMI and DT, so they are also
collected. Note that only the first CPU is checked and reported, that's
expected to be vastly sufficient, since we're just trying to spot known
incompatibilities or issues.

src/debug.c

index a4dfc5ac1a9fd8a66229e055e0d1b6a326df46bc..fe383442ed0594f9bde22fee0877d13f607818d4 100644 (file)
@@ -83,6 +83,9 @@ struct post_mortem {
                char hw_model[64];      // hardware/hypervisor product/model when known
                char brd_vendor[64];    // mainboard vendor when known
                char brd_model[64];     // mainboard model when known
+               char soc_vendor[64];    // SoC/CPU vendor from cpuinfo
+               char soc_model[64];     // SoC model when known and relevant
+               char cpu_model[64];     // CPU model when different from SoC
                char cont_techno[16];   // empty, "no", "yes", "docker" or others
        } platform;
 } post_mortem ALIGNED(256) = { };
@@ -461,6 +464,12 @@ static int debug_parse_cli_show_dev(char **args, char *payload, struct appctx *a
                chunk_appendf(&trash, "  board vendor: %s\n", post_mortem.platform.brd_vendor);
        if (*post_mortem.platform.brd_model)
                chunk_appendf(&trash, "  board model: %s\n", post_mortem.platform.brd_model);
+       if (*post_mortem.platform.soc_vendor)
+               chunk_appendf(&trash, "  soc vendor: %s\n", post_mortem.platform.soc_vendor);
+       if (*post_mortem.platform.soc_model)
+               chunk_appendf(&trash, "  soc model: %s\n", post_mortem.platform.soc_model);
+       if (*post_mortem.platform.cpu_model)
+               chunk_appendf(&trash, "  cpu model: %s\n", post_mortem.platform.cpu_model);
        if (*post_mortem.platform.cont_techno)
                chunk_appendf(&trash, "  container: %s\n", post_mortem.platform.cont_techno);
        if (*post_mortem.platform.utsname.sysname)
@@ -1899,6 +1908,7 @@ static void feed_post_mortem_linux()
 {
 #if defined(__linux__)
        struct stat statbuf;
+       FILE *file;
 
        /* DMI reports either HW or hypervisor, this allows to detect most VMs.
         * On ARM the device-tree is often more precise for the model. Since many
@@ -1947,6 +1957,125 @@ static void feed_post_mortem_linux()
        else {
                strlcpy2(post_mortem.platform.cont_techno, "no", sizeof(post_mortem.platform.cont_techno));
        }
+
+       file = fopen("/proc/cpuinfo", "r");
+       if (file) {
+               uint cpu_implem = 0, cpu_arch = 0, cpu_variant = 0, cpu_part = 0, cpu_rev = 0; // arm
+               uint cpu_family = 0, model = 0, stepping = 0;                                  // x86
+               char vendor_id[64] = "", model_name[64] = "";                                  // x86
+               char machine[64] = "", system_type[64] = "", cpu_model[64] = "";               // mips
+               char *p, *e, *v, *lf;
+
+               /* let's figure what CPU we're working with */
+               while ((p = fgets(trash.area, trash.size, file)) != NULL) {
+                       lf = strchr(p, '\n');
+                       if (lf)
+                               *lf = 0;
+
+                       /* stop at first line break */
+                       if (!*p)
+                               break;
+
+                       /* skip colon and spaces and trim spaces after name */
+                       v = e = strchr(p, ':');
+                       if (!e)
+                               continue;
+
+                       do { *e-- = 0; } while (e >= p && (*e == ' ' || *e == '\t'));
+
+                       /* locate value after colon */
+                       do { v++; } while (*v == ' ' || *v == '\t');
+
+                       /* ARM */
+                       if (strcmp(p, "CPU implementer") == 0)
+                               cpu_implem = strtoul(v, NULL, 0);
+                       else if (strcmp(p, "CPU architecture") == 0)
+                               cpu_arch = strtoul(v, NULL, 0);
+                       else if (strcmp(p, "CPU variant") == 0)
+                               cpu_variant = strtoul(v, NULL, 0);
+                       else if (strcmp(p, "CPU part") == 0)
+                               cpu_part = strtoul(v, NULL, 0);
+                       else if (strcmp(p, "CPU revision") == 0)
+                               cpu_rev = strtoul(v, NULL, 0);
+
+                       /* x86 */
+                       else if (strcmp(p, "cpu family") == 0)
+                               cpu_family = strtoul(v, NULL, 0);
+                       else if (strcmp(p, "model") == 0)
+                               model = strtoul(v, NULL, 0);
+                       else if (strcmp(p, "stepping") == 0)
+                               stepping = strtoul(v, NULL, 0);
+                       else if (strcmp(p, "vendor_id") == 0)
+                               strlcpy2(vendor_id, v, sizeof(vendor_id));
+                       else if (strcmp(p, "model name") == 0)
+                               strlcpy2(model_name, v, sizeof(model_name));
+
+                       /* MIPS */
+                       else if (strcmp(p, "system type") == 0)
+                               strlcpy2(system_type, v, sizeof(system_type));
+                       else if (strcmp(p, "machine") == 0)
+                               strlcpy2(machine, v, sizeof(machine));
+                       else if (strcmp(p, "cpu model") == 0)
+                               strlcpy2(cpu_model, v, sizeof(cpu_model));
+               }
+               fclose(file);
+
+               /* Machine may replace hw_product on MIPS */
+               if (!*post_mortem.platform.hw_model)
+                       strlcpy2(post_mortem.platform.hw_model, machine, sizeof(post_mortem.platform.hw_model));
+
+               /* SoC vendor */
+               strlcpy2(post_mortem.platform.soc_vendor, vendor_id, sizeof(post_mortem.platform.soc_vendor));
+
+               /* SoC model */
+               if (*system_type) {
+                       /* MIPS */
+                       strlcpy2(post_mortem.platform.soc_model, system_type, sizeof(post_mortem.platform.soc_model));
+                       *system_type = 0;
+               } else if (*model_name) {
+                       /* x86 */
+                       strlcpy2(post_mortem.platform.soc_model, model_name, sizeof(post_mortem.platform.soc_model));
+                       *model_name = 0;
+               }
+
+               /* Create a CPU model name based on available IDs */
+               if (cpu_implem) // arm
+                       snprintf(cpu_model + strlen(cpu_model),
+                                sizeof(cpu_model) - strlen(cpu_model),
+                                "%sImpl %#02x", *cpu_model ? " " : "", cpu_implem);
+
+               if (cpu_family) // x86
+                       snprintf(cpu_model + strlen(cpu_model),
+                                sizeof(cpu_model) - strlen(cpu_model),
+                                "%sFam %u", *cpu_model ? " " : "", cpu_family);
+
+               if (model) // x86
+                       snprintf(cpu_model + strlen(cpu_model),
+                                sizeof(cpu_model) - strlen(cpu_model),
+                                "%sModel %u", *cpu_model ? " " : "", model);
+
+               if (stepping) // x86
+                       snprintf(cpu_model + strlen(cpu_model),
+                                sizeof(cpu_model) - strlen(cpu_model),
+                                "%sStep %u", *cpu_model ? " " : "", stepping);
+
+               if (cpu_arch) // arm
+                       snprintf(cpu_model + strlen(cpu_model),
+                                sizeof(cpu_model) - strlen(cpu_model),
+                                "%sArch %u", *cpu_model ? " " : "", cpu_arch);
+
+               if (cpu_part) // arm
+                       snprintf(cpu_model + strlen(cpu_model),
+                                sizeof(cpu_model) - strlen(cpu_model),
+                                "%sPart %#03x", *cpu_model ? " " : "", cpu_part);
+
+               if (cpu_variant || cpu_rev) // arm
+                       snprintf(cpu_model + strlen(cpu_model),
+                                sizeof(cpu_model) - strlen(cpu_model),
+                                "%sr%up%u", *cpu_model ? " " : "", cpu_variant, cpu_rev);
+
+               strlcpy2(post_mortem.platform.cpu_model, cpu_model, sizeof(post_mortem.platform.cpu_model));
+       }
 #endif // __linux__
 }