Browse Source

[LibOS] Make /proc/cpuinfo output exactly like in Linux kernel

Some Linux applications get the number of CPU cores by querying
/proc/cpuinfo.  Before, such apps would be confused by a wrong
format of /proc/cpuinfo in Graphene. This commit updates format
to exactly the same as in Linux kernel.

This commit also adds a LibOS regression test for /proc/cpuinfo.
Biao Lu 6 years ago
parent
commit
2c87e294fc

+ 10 - 8
LibOS/shim/src/fs/proc/info.c

@@ -93,15 +93,17 @@ static int proc_cpuinfo_open (struct shim_handle * hdl, const char * name,
     char * str = NULL;
 
     struct { const char * fmt; unsigned long val; }
+        /* below strings must match exactly the strings retrieved from
+         * /proc/cpuinfo (see Linux's arch/x86/kernel/cpu/proc.c) */
         cpuinfo[] = {
-            { "processor      : %lu\n", 0, },
-            { "vendor_id      : %s\n",  (unsigned long) pal_control.cpu_info.cpu_vendor, },
-            { "cpu_family     : %lu\n", pal_control.cpu_info.cpu_family, },
-            { "model          : %lu\n", pal_control.cpu_info.cpu_model, },
-            { "model name     : %s\n",  (unsigned long) pal_control.cpu_info.cpu_brand, },
-            { "stepping       : %lu\n", pal_control.cpu_info.cpu_stepping, },
-            { "core id        : %lu\n", 0, },
-            { "cpu_core       : %lu\n", pal_control.cpu_info.cpu_num, },
+            { "processor\t: %lu\n",  0, },
+            { "vendor_id\t: %s\n",   (unsigned long) pal_control.cpu_info.cpu_vendor, },
+            { "cpu family\t: %lu\n", pal_control.cpu_info.cpu_family, },
+            { "model\t\t: %lu\n",    pal_control.cpu_info.cpu_model, },
+            { "model name\t: %s\n",  (unsigned long) pal_control.cpu_info.cpu_brand, },
+            { "stepping\t: %lu\n",   pal_control.cpu_info.cpu_stepping, },
+            { "core id\t\t: %lu\n",  0, },
+            { "cpu cores\t: %lu\n",  pal_control.cpu_info.cpu_num, },
         };
 
 retry:

+ 12 - 0
LibOS/shim/test/regression/40_proc_cpuinfo.py

@@ -0,0 +1,12 @@
+import sys
+from regression import Regression
+
+loader = sys.argv[1]
+
+regression = Regression(loader, "proc_cpuinfo", None, 50000)
+
+regression.add_check(name="proc/cpuinfo Linux-based formatting",
+    check=lambda res: "cpuinfo test passed" in res[0].out)
+
+rv = regression.run_checks()
+if rv: sys.exit(rv)

+ 134 - 0
LibOS/shim/test/regression/proc_cpuinfo.c

@@ -0,0 +1,134 @@
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+#define CPUINFO_FILE    "/proc/cpuinfo"
+#define BUFFSIZE        2048
+
+/* vendor_id, model_name size reference Linux kernel struct cpuinfo_x86
+ * (see Linux's arch/x86/include/asm/processor.h) */
+struct cpuinfo {
+    int processor;
+    char vendor_id[16];
+    int cpu_family;
+    int model;
+    char model_name[64];
+    int stepping;
+    int core_id;
+    int cpu_cores;
+};
+
+static void init_cpuinfo(struct cpuinfo *ci) {
+    ci->processor = -1;
+    memset(&ci->vendor_id, 0, sizeof(ci->vendor_id));
+    ci->cpu_family = -1;
+    ci->model = -1;
+    memset(&ci->model_name, 0, sizeof(ci->model_name));
+    ci->stepping = -1;
+    ci->core_id = -1;
+    ci->cpu_cores = -1;
+}
+
+static int parse_line(char *line, struct cpuinfo *ci) {
+    char *k, *v, *p;
+
+    if ((p = strchr(line, ':')) == NULL)
+        goto fmt_err;
+
+    /* if the line does not have value string, p[1] should be '\n', otherwise
+     * p[1] should be ' ' */
+    if (p[1] == '\n' && !p[2])
+        return 0;  /* No value string */
+
+    /* ':' should always be followed by a space */
+    if (p[1] != ' ')
+        goto fmt_err;
+
+    /* skip ": " to get value string */
+    v = p + 2;
+
+    /* get key string */
+    *p = '\0';
+    if ((p = strchr(line, '\t')) != NULL)
+        *p = '\0';
+    k = line;
+
+    if (!strcmp(k, "processor")) {
+        sscanf(v, "%d\n", &ci->processor);
+    } else if (!strcmp(k, "cpu family")) {
+        sscanf(v, "%d\n", &ci->cpu_family);
+    } else if (!strcmp(k, "model")) {
+        sscanf(v, "%d\n", &ci->model);
+    } else if (!strcmp(k, "stepping")) {
+        sscanf(v, "%d\n", &ci->stepping);
+    } else if (!strcmp(k, "core id")) {
+        sscanf(v, "%d\n", &ci->core_id);
+    } else if (!strcmp(k, "cpu cores")) {
+        sscanf(v, "%d\n", &ci->cpu_cores);
+    } else if (!strcmp(k, "vendor_id")) {
+        snprintf(ci->vendor_id, sizeof(ci->vendor_id), "%s", v);
+    } else if (!strcmp(k, "model name")) {
+        snprintf(ci->model_name, sizeof(ci->model_name), "%s", v);
+    }
+    return 0;
+
+fmt_err:
+    fprintf(stderr, "format error in line: %s\n", line);
+    return -1;
+};
+
+static int check_cpuinfo(struct cpuinfo *ci) {
+    if (ci->processor == -1) {
+        fprintf(stderr, "Could not get cpu index\n");
+        return -1;
+    }
+    if (ci->core_id == -1) {
+        fprintf(stderr, "Could not get core id\n");
+        return -1;
+    }
+    if (ci->cpu_cores == -1) {
+        fprintf(stderr, "Could not get cpu cores\n");
+        return -1;
+    }
+
+    return 0;
+}
+
+int main (int argc, char *argv[]) {
+    FILE *fp = NULL;
+    char line[BUFFSIZE];
+    struct cpuinfo ci;
+    int cpu_cnt = 0, rv = 0;
+
+    init_cpuinfo(&ci);
+
+    if ((fp = fopen(CPUINFO_FILE, "r")) == NULL) {
+        perror("fopen");
+        return 1;
+    }
+
+    while (fgets(line, sizeof(line), fp) != NULL) {
+        if (line[0] == '\n') {
+            if ((rv = check_cpuinfo(&ci)) != 0)
+                break;
+            cpu_cnt++;
+            init_cpuinfo(&ci);
+            continue;
+        }
+        if ((rv = parse_line(line, &ci)) != 0)
+            break;
+    }
+
+    fclose(fp);
+
+    if (rv != 0)
+        return 1;
+
+    if (cpu_cnt == 0) {
+        fprintf(stderr, "Could not get online cpu info.\n");
+        return 1;
+    }
+
+    printf("cpuinfo test passed\n");
+    return 0;
+}