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.
This commit is contained in:
Willy Tarreau 2023-11-22 11:55:22 +01:00
parent 2974f3e71b
commit 0cc799bdd1

View 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__
}