From 0cc799bdd1e7ad4856593711f3df3b5539a865d6 Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Wed, 22 Nov 2023 11:55:22 +0100 Subject: [PATCH] MINOR: debug: detect CPU model and store it in post_mortem 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 | 129 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) diff --git a/src/debug.c b/src/debug.c index a4dfc5ac1..fe383442e 100644 --- a/src/debug.c +++ b/src/debug.c @@ -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__ }