From 64b1b9090f96abf5294e184f7c58aaee5079aa4d Mon Sep 17 00:00:00 2001 From: Guillaume Chatelet Date: Tue, 12 Nov 2019 14:00:19 +0100 Subject: [PATCH] [NFC] Use a tree structure in list_cpu_features This is in preparation to include cache hierarchy in the dumped data. --- src/utils/list_cpu_features.c | 476 ++++++++++++++++++++++------------ 1 file changed, 308 insertions(+), 168 deletions(-) diff --git a/src/utils/list_cpu_features.c b/src/utils/list_cpu_features.c index acda5e7..41fc612 100644 --- a/src/utils/list_cpu_features.c +++ b/src/utils/list_cpu_features.c @@ -12,6 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. +// This program dumps current host data to the standard output. +// Output can be text or json if the `--json` flag is passed. + +#include +#include +#include #include #include #include @@ -30,7 +36,176 @@ #include "cpuinfo_ppc.h" #endif -static void PrintEscapedAscii(const char* str) { +// Design principles +// ----------------- +// We build a tree structure containing all the data to be displayed. +// Then depending on the output type (text or json) we walk the tree and display +// the data accordingly. + +// We use a bump allocator to allocate strings and nodes of the tree, +// Memory is not intented to be reclaimed. +typedef struct { + char* ptr; + size_t size; +} BumpAllocator; + +// Allocate a buffer of size `size`. +static BumpAllocator BA_Create(size_t size) { + char* const ptr = (char*)malloc(size); + BumpAllocator BA; + if (ptr) BA = (BumpAllocator){.ptr = ptr, .size = size}; + return BA; +} + +// Update the available memory left in the BumpAllocator. +static void* BA_Bump(BumpAllocator* BA, size_t size) { + assert(BA->size >= size); + void* ptr = BA->ptr; + BA->size -= size; + BA->ptr += size; + return ptr; +} + +// The type of the nodes in the tree. +typedef enum { + TNT_INVALID, + TNT_INT, + TNT_MAP, + TNT_MAP_ENTRY, + TNT_ARRAY, + TNT_ARRAY_ELEMENT, + TNT_STRING, +} TreeValueType; + +// The node in the tree. +typedef struct TreeValue { + TreeValueType type; + unsigned integer; + const char* string; + struct TreeValue* value; + struct TreeValue* next; +} TreeValue; + +// Allocates a node inside a BumpAllocator. +static TreeValue* BA_TreeValue(BumpAllocator* BA, TreeValueType type) { + TreeValue* TV = (TreeValue*)BA_Bump(BA, sizeof(TreeValue)); + assert(TV); + TV->type = type; + return TV; +} + +// Allocates an integer node inside a BumpAllocator. +static TreeValue* CreateInt(BumpAllocator* BA, int value) { + TreeValue* TV = BA_TreeValue(BA, TNT_INT); + TV->integer = value; + return TV; +} + +// Allocates a string node inside a BumpAllocator. +// `value` must outlive the tree. +static TreeValue* CreateConstantString(BumpAllocator* BA, const char* value) { + TreeValue* TV = BA_TreeValue(BA, TNT_STRING); + TV->string = value; + return TV; +} + +// Allocates a map node inside a BumpAllocator. +static TreeValue* CreateMap(BumpAllocator* BA) { + TreeValue* TV = BA_TreeValue(BA, TNT_MAP); + TV->next = NULL; + return TV; +} + +// Allocates an array node inside a BumpAllocator. +static TreeValue* CreateArray(BumpAllocator* BA) { + TreeValue* TV = BA_TreeValue(BA, TNT_ARRAY); + TV->next = NULL; + return TV; +} + +// Allocates a formatted string inside a BumpAllocator. +static TreeValue* CreatePrintfString(BumpAllocator* BA, const char* format, + ...) { + va_list arglist; + va_start(arglist, format); + char* const ptr = BA->ptr; + const int written = vsnprintf(ptr, BA->size, format, arglist); + va_end(arglist); + if (written < 0 || written >= BA->size) return NULL; + return CreateConstantString(BA, (char*)BA_Bump(BA, written)); +} + +static TreeValue* CreateString(BumpAllocator* BA, const char* value) { + return CreatePrintfString(BA, "%s", value); +} + +// Allocates a map entry node inside a BumpAllocator. +static void AddMapEntry(BumpAllocator* BA, TreeValue* map, const char* key, + TreeValue* value) { + assert(map && map->type == TNT_MAP); + TreeValue* current = map; + while (current->next) current = current->next; + current->next = (TreeValue*)BA_Bump(BA, sizeof(TreeValue)); + current->next->type = TNT_MAP_ENTRY; + current->next->string = key; + current->next->value = value; + current->next->next = NULL; +} + +// Allocates aan array element node inside a BumpAllocator. +static void AddArrayElement(BumpAllocator* BA, TreeValue* array, + TreeValue* value) { + assert(array && array->type == TNT_ARRAY); + TreeValue* current = array; + while (current->next) current = current->next; + current->next = (TreeValue*)BA_Bump(BA, sizeof(TreeValue)); + current->next->type = TNT_ARRAY_ELEMENT; + current->next->value = value; + current->next->next = NULL; +} + +static int cmp(const void* p1, const void* p2) { + return strcmp(*(const char* const*)p1, *(const char* const*)p2); +} + +#define DEFINE_ADD_FLAGS(HasFeature, FeatureName, FeatureType, LastEnum) \ + static void AddFlags(BumpAllocator* BA, TreeValue* map, \ + const FeatureType* features) { \ + size_t i; \ + const char* ptrs[LastEnum] = {0}; \ + size_t count = 0; \ + for (i = 0; i < LastEnum; ++i) { \ + if (HasFeature(features, i)) { \ + ptrs[count] = FeatureName(i); \ + ++count; \ + } \ + } \ + qsort((void*)ptrs, count, sizeof(char*), cmp); \ + TreeValue* const array = CreateArray(BA); \ + for (i = 0; i < count; ++i) \ + AddArrayElement(BA, array, CreateConstantString(BA, ptrs[i])); \ + AddMapEntry(BA, map, "flags", array); \ + } + +#if defined(CPU_FEATURES_ARCH_X86) +DEFINE_ADD_FLAGS(GetX86FeaturesEnumValue, GetX86FeaturesEnumName, X86Features, + X86_LAST_) +#elif defined(CPU_FEATURES_ARCH_ARM) +DEFINE_ADD_FLAGS(GetArmFeaturesEnumValue, GetArmFeaturesEnumName, ArmFeatures, + ARM_LAST_) +#elif defined(CPU_FEATURES_ARCH_AARCH64) +DEFINE_ADD_FLAGS(GetAarch64FeaturesEnumValue, GetAarch64FeaturesEnumName, + Aarch64Features, AARCH64_LAST_) +#elif defined(CPU_FEATURES_ARCH_MIPS) +DEFINE_ADD_FLAGS(GetMipsFeaturesEnumValue, GetMipsFeaturesEnumName, + MipsFeatures, MIPS_LAST_) +#elif defined(CPU_FEATURES_ARCH_PPC) +DEFINE_ADD_FLAGS(GetPPCFeaturesEnumValue, GetPPCFeaturesEnumName, PPCFeatures, + PPC_LAST_) +#endif + +// Prints a json string with characters escaping. +static void printJsonString(const char* str) { putchar('"'); for (; str && *str; ++str) { switch (*str) { @@ -49,170 +224,80 @@ static void PrintEscapedAscii(const char* str) { putchar('"'); } -static void PrintVoid(void) {} -static void PrintComma(void) { putchar(','); } -static void PrintLineFeed(void) { putchar('\n'); } -static void PrintOpenBrace(void) { putchar('{'); } -static void PrintCloseBrace(void) { putchar('}'); } -static void PrintOpenBracket(void) { putchar('['); } -static void PrintCloseBracket(void) { putchar(']'); } -static void PrintString(const char* field) { printf("%s", field); } -static void PrintAlignedHeader(const char* field) { printf("%-15s : ", field); } -static void PrintIntValue(int value) { printf("%d", value); } -static void PrintDecHexValue(int value) { - printf("%3d (0x%02X)", value, value); -} -static void PrintJsonHeader(const char* field) { - PrintEscapedAscii(field); - putchar(':'); -} - -typedef struct { - void (*Start)(void); - void (*ArrayStart)(void); - void (*ArraySeparator)(void); - void (*ArrayEnd)(void); - void (*PrintString)(const char* value); - void (*PrintValue)(int value); - void (*EndField)(void); - void (*StartField)(const char* field); - void (*End)(void); -} Printer; - -static Printer getJsonPrinter(void) { - return (Printer){ - .Start = &PrintOpenBrace, - .ArrayStart = &PrintOpenBracket, - .ArraySeparator = &PrintComma, - .ArrayEnd = &PrintCloseBracket, - .PrintString = &PrintEscapedAscii, - .PrintValue = &PrintIntValue, - .EndField = &PrintComma, - .StartField = &PrintJsonHeader, - .End = &PrintCloseBrace, - }; -} - -static Printer getTextPrinter(void) { - return (Printer){ - .Start = &PrintVoid, - .ArrayStart = &PrintVoid, - .ArraySeparator = &PrintComma, - .ArrayEnd = &PrintVoid, - .PrintString = &PrintString, - .PrintValue = &PrintDecHexValue, - .EndField = &PrintLineFeed, - .StartField = &PrintAlignedHeader, - .End = &PrintVoid, - }; -} - -// Prints a named numeric value in both decimal and hexadecimal. -static void PrintN(const Printer p, const char* field, int value) { - p.StartField(field); - p.PrintValue(value); - p.EndField(); -} - -// Prints a named string. -static void PrintS(const Printer p, const char* field, const char* value) { - p.StartField(field); - p.PrintString(value); - p.EndField(); -} - -static int cmp(const void* p1, const void* p2) { - return strcmp(*(const char* const*)p1, *(const char* const*)p2); -} - -#define DEFINE_PRINT_FLAGS(HasFeature, FeatureName, FeatureType, LastEnum) \ - static void PrintFlags(const Printer p, const FeatureType* features) { \ - size_t i; \ - const char* ptrs[LastEnum] = {0}; \ - size_t count = 0; \ - for (i = 0; i < LastEnum; ++i) { \ - if (HasFeature(features, i)) { \ - ptrs[count] = FeatureName(i); \ - ++count; \ - } \ - } \ - qsort((void*)ptrs, count, sizeof(char*), cmp); \ - p.StartField("flags"); \ - p.ArrayStart(); \ - for (i = 0; i < count; ++i) { \ - if (i > 0) p.ArraySeparator(); \ - p.PrintString(ptrs[i]); \ - } \ - p.ArrayEnd(); \ +// Walks a TreeValue and print it as json. +static void printJson(const TreeValue* current) { + assert(current); + switch (current->type) { + case TNT_INT: + printf("%d", current->integer); + break; + case TNT_STRING: + printJsonString(current->string); + break; + case TNT_ARRAY: + putchar('['); + if (current->next) printJson(current->next); + putchar(']'); + break; + case TNT_MAP: + putchar('{'); + if (current->next) printJson(current->next); + putchar('}'); + break; + case TNT_MAP_ENTRY: + printf("\"%s\":", current->string); + printJson(current->value); + if (current->next) { + putchar(','); + printJson(current->next); + } + break; + case TNT_ARRAY_ELEMENT: + printJson(current->value); + if (current->next) { + putchar(','); + printJson(current->next); + } + break; } - -#if defined(CPU_FEATURES_ARCH_X86) -DEFINE_PRINT_FLAGS(GetX86FeaturesEnumValue, GetX86FeaturesEnumName, X86Features, - X86_LAST_) -#elif defined(CPU_FEATURES_ARCH_ARM) -DEFINE_PRINT_FLAGS(GetArmFeaturesEnumValue, GetArmFeaturesEnumName, ArmFeatures, - ARM_LAST_) -#elif defined(CPU_FEATURES_ARCH_AARCH64) -DEFINE_PRINT_FLAGS(GetAarch64FeaturesEnumValue, GetAarch64FeaturesEnumName, - Aarch64Features, AARCH64_LAST_) -#elif defined(CPU_FEATURES_ARCH_MIPS) -DEFINE_PRINT_FLAGS(GetMipsFeaturesEnumValue, GetMipsFeaturesEnumName, - MipsFeatures, MIPS_LAST_) -#elif defined(CPU_FEATURES_ARCH_PPC) -DEFINE_PRINT_FLAGS(GetPPCFeaturesEnumValue, GetPPCFeaturesEnumName, PPCFeatures, - PPC_LAST_) -#endif - -static void PrintFeatures(const Printer printer) { -#if defined(CPU_FEATURES_ARCH_X86) - char brand_string[49]; - const X86Info info = GetX86Info(); - FillX86BrandString(brand_string); - PrintS(printer, "arch", "x86"); - PrintS(printer, "brand", brand_string); - PrintN(printer, "family", info.family); - PrintN(printer, "model", info.model); - PrintN(printer, "stepping", info.stepping); - PrintS(printer, "uarch", - GetX86MicroarchitectureName(GetX86Microarchitecture(&info))); - PrintFlags(printer, &info.features); -#elif defined(CPU_FEATURES_ARCH_ARM) - const ArmInfo info = GetArmInfo(); - PrintS(printer, "arch", "ARM"); - PrintN(printer, "implementer", info.implementer); - PrintN(printer, "architecture", info.architecture); - PrintN(printer, "variant", info.variant); - PrintN(printer, "part", info.part); - PrintN(printer, "revision", info.revision); - PrintFlags(printer, &info.features); -#elif defined(CPU_FEATURES_ARCH_AARCH64) - const Aarch64Info info = GetAarch64Info(); - PrintS(printer, "arch", "aarch64"); - PrintN(printer, "implementer", info.implementer); - PrintN(printer, "variant", info.variant); - PrintN(printer, "part", info.part); - PrintN(printer, "revision", info.revision); - PrintFlags(printer, &info.features); -#elif defined(CPU_FEATURES_ARCH_MIPS) - (void)&PrintN; // Remove unused function warning. - const MipsInfo info = GetMipsInfo(); - PrintS(printer, "arch", "mips"); - PrintFlags(printer, &info.features); -#elif defined(CPU_FEATURES_ARCH_PPC) - (void)&PrintN; // Remove unused function warning. - const PPCInfo info = GetPPCInfo(); - const PPCPlatformStrings strings = GetPPCPlatformStrings(); - PrintS(printer, "arch", "ppc"); - PrintS(printer, "platform", strings.platform); - PrintS(printer, "model", strings.model); - PrintS(printer, "machine", strings.machine); - PrintS(printer, "cpu", strings.cpu); - PrintS(printer, "instruction set", strings.type.platform); - PrintS(printer, "microarchitecture", strings.type.base_platform); - PrintFlags(printer, &info.features); -#endif } +// Walks a TreeValue and print it as text. +static void printTextField(const TreeValue* current) { + switch (current->type) { + case TNT_INT: + printf("%3d (0x%02X)", current->integer, current->integer); + break; + case TNT_STRING: + fputs(current->string, stdout); + break; + case TNT_ARRAY: + if (current->next) printTextField(current->next); + break; + case TNT_MAP: + if (current->next) printJson(current->next); + break; + case TNT_MAP_ENTRY: + printf("%-15s : ", current->string); + printTextField(current->value); + if (current->next) { + putchar('\n'); + printTextField(current->next); + } + break; + case TNT_ARRAY_ELEMENT: + printTextField(current->value); + if (current->next) { + putchar(','); + printTextField(current->next); + } + break; + } +} + +static void printTextRoot(const TreeValue* current) { + if (current->type == TNT_MAP && current->next) printTextField(current->next); +} static void showUsage(const char* name) { printf( "\n" @@ -224,13 +309,67 @@ static void showUsage(const char* name) { name); } +static TreeValue* CreateTree(BumpAllocator* BA) { + TreeValue* root = CreateMap(BA); +#if defined(CPU_FEATURES_ARCH_X86) + char brand_string[49]; + const X86Info info = GetX86Info(); + FillX86BrandString(brand_string); + AddMapEntry(BA, root, "arch", CreateString(BA, "x86")); + AddMapEntry(BA, root, "brand", CreateString(BA, brand_string)); + AddMapEntry(BA, root, "family", CreateInt(BA, info.family)); + AddMapEntry(BA, root, "model", CreateInt(BA, info.model)); + AddMapEntry(BA, root, "stepping", CreateInt(BA, info.stepping)); + AddMapEntry(BA, root, "uarch", + CreateString(BA, GetX86MicroarchitectureName( + GetX86Microarchitecture(&info)))); + AddFlags(BA, root, &info.features); +#elif defined(CPU_FEATURES_ARCH_ARM) + const ArmInfo info = GetArmInfo(); + AddMapEntry(BA, root, "arch", CreateString(BA, "ARM")); + AddMapEntry(BA, root, "implementer", CreateInt(BA, info.implementer)); + AddMapEntry(BA, root, "architecture", CreateInt(BA, info.architecture)); + AddMapEntry(BA, root, "variant", CreateInt(BA, info.variant)); + AddMapEntry(BA, root, "part", CreateInt(BA, info.part)); + AddMapEntry(BA, root, "revision", CreateInt(BA, info.revision)); + AddFlags(BA, root, &info.features); +#elif defined(CPU_FEATURES_ARCH_AARCH64) + const Aarch64Info info = GetAarch64Info(); + AddMapEntry(BA, root, "arch", CreateString(BA, "aarch64")); + AddMapEntry(BA, root, "implementer", CreateInt(BA, info.implementer)); + AddMapEntry(BA, root, "variant", CreateInt(BA, info.variant)); + AddMapEntry(BA, root, "part", CreateInt(BA, info.part)); + AddMapEntry(BA, root, "revision", CreateInt(BA, info.revision)); + AddFlags(BA, root, &info.features); +#elif defined(CPU_FEATURES_ARCH_MIPS) + const MipsInfo info = GetMipsInfo(); + AddMapEntry(BA, root, "arch", CreateString(BA, "mips")); + AddFlags(BA, root, &info.features); +#elif defined(CPU_FEATURES_ARCH_PPC) + const PPCInfo info = GetPPCInfo(); + const PPCPlatformStrings strings = GetPPCPlatformStrings(); + AddMapEntry(BA, root, "arch", CreateString(BA, "ppc")); + AddMapEntry(BA, root, "platform", CreateString(BA, strings.platform)); + AddMapEntry(BA, root, "model", CreateString(BA, strings.model)); + AddMapEntry(BA, root, "machine", CreateString(BA, strings.machine)); + AddMapEntry(BA, root, "cpu", CreateString(BA, strings.cpu)); + AddMapEntry(BA, root, "instruction", CreateString(BA, strings.type.platform)); + AddMapEntry(BA, root, "microarchitecture", + CreateString(BA, strings.type.base_platform)); + AddFlags(BA, root, &info.features); +#endif + return root; +} + int main(int argc, char** argv) { - Printer printer = getTextPrinter(); + BumpAllocator BA = BA_Create(64 * 1024); + const TreeValue* const root = CreateTree(&BA); + bool outputJson = false; int i = 1; for (; i < argc; ++i) { const char* arg = argv[i]; if (strcmp(arg, "-j") == 0 || strcmp(arg, "--json") == 0) { - printer = getJsonPrinter(); + outputJson = true; } else { showUsage(argv[0]); if (strcmp(arg, "-h") == 0 || strcmp(arg, "--help") == 0) @@ -238,9 +377,10 @@ int main(int argc, char** argv) { return EXIT_FAILURE; } } - printer.Start(); - PrintFeatures(printer); - printer.End(); - PrintLineFeed(); + if (outputJson) + printJson(root); + else + printTextRoot(root); + putchar('\n'); return EXIT_SUCCESS; }