1 // Copyright 2017 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 // This program dumps current host data to the standard output.
16 // Output can be text or json if the `--json` flag is passed.
17
18 #include <assert.h>
19 #include <stdarg.h>
20 #include <stdbool.h>
21 #include <stdint.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25
26 #include "cpu_features_macros.h"
27
28 #if defined(CPU_FEATURES_ARCH_X86)
29 #include "cpuinfo_x86.h"
30 #elif defined(CPU_FEATURES_ARCH_ARM)
31 #include "cpuinfo_arm.h"
32 #elif defined(CPU_FEATURES_ARCH_AARCH64)
33 #include "cpuinfo_aarch64.h"
34 #elif defined(CPU_FEATURES_ARCH_MIPS)
35 #include "cpuinfo_mips.h"
36 #elif defined(CPU_FEATURES_ARCH_PPC)
37 #include "cpuinfo_ppc.h"
38 #elif defined(CPU_FEATURES_ARCH_S390X)
39 #include "cpuinfo_s390x.h"
40 #elif defined(CPU_FEATURES_ARCH_RISCV)
41 #include "cpuinfo_riscv.h"
42 #elif defined(CPU_FEATURES_ARCH_LOONGARCH)
43 #include "cpuinfo_loongarch.h"
44 #endif
45
46 // Design principles
47 // -----------------
48 // We build a tree structure containing all the data to be displayed.
49 // Then depending on the output type (text or json) we walk the tree and display
50 // the data accordingly.
51
52 // We use a bump allocator to allocate strings and nodes of the tree,
53 // Memory is not intended to be reclaimed.
54 typedef struct {
55 char* ptr;
56 size_t size;
57 } BumpAllocator;
58
59 char gGlobalBuffer[64 * 1024];
60 BumpAllocator gBumpAllocator = {.ptr = gGlobalBuffer,
61 .size = sizeof(gGlobalBuffer)};
62
internal_error(void)63 static void internal_error(void) {
64 fputs("internal error\n", stderr);
65 exit(EXIT_FAILURE);
66 }
67
68 #define ALIGN 8
69
assertAligned(void)70 static void assertAligned(void) {
71 if ((uintptr_t)(gBumpAllocator.ptr) % ALIGN) internal_error();
72 }
73
BA_Align(void)74 static void BA_Align(void) {
75 while (gBumpAllocator.size && (uintptr_t)(gBumpAllocator.ptr) % ALIGN) {
76 --gBumpAllocator.size;
77 ++gBumpAllocator.ptr;
78 }
79 assertAligned();
80 }
81
82 // Update the available memory left in the BumpAllocator.
BA_Bump(size_t size)83 static void* BA_Bump(size_t size) {
84 assertAligned();
85 // Align size to next 8B boundary.
86 size = (size + ALIGN - 1) / ALIGN * ALIGN;
87 if (gBumpAllocator.size < size) internal_error();
88 void* ptr = gBumpAllocator.ptr;
89 gBumpAllocator.size -= size;
90 gBumpAllocator.ptr += size;
91 return ptr;
92 }
93
94 // The type of the nodes in the tree.
95 typedef enum {
96 NT_INVALID,
97 NT_INT,
98 NT_MAP,
99 NT_MAP_ENTRY,
100 NT_ARRAY,
101 NT_ARRAY_ELEMENT,
102 NT_STRING,
103 } NodeType;
104
105 // The node in the tree.
106 typedef struct Node {
107 NodeType type;
108 unsigned integer;
109 const char* string;
110 struct Node* value;
111 struct Node* next;
112 } Node;
113
114 // Creates an initialized Node.
BA_CreateNode(NodeType type)115 static Node* BA_CreateNode(NodeType type) {
116 Node* tv = (Node*)BA_Bump(sizeof(Node));
117 assert(tv);
118 *tv = (Node){.type = type};
119 return tv;
120 }
121
122 // Adds an integer node.
CreateInt(int value)123 static Node* CreateInt(int value) {
124 Node* tv = BA_CreateNode(NT_INT);
125 tv->integer = value;
126 return tv;
127 }
128
129 // Adds a string node.
130 // `value` must outlive the tree.
CreateConstantString(const char * value)131 static Node* CreateConstantString(const char* value) {
132 Node* tv = BA_CreateNode(NT_STRING);
133 tv->string = value;
134 return tv;
135 }
136
137 // Adds a map node.
CreateMap(void)138 static Node* CreateMap(void) { return BA_CreateNode(NT_MAP); }
139
140 // Adds an array node.
CreateArray(void)141 static Node* CreateArray(void) { return BA_CreateNode(NT_ARRAY); }
142
143 // Adds a formatted string node.
CreatePrintfString(const char * format,...)144 static Node* CreatePrintfString(const char* format, ...) {
145 va_list arglist;
146 va_start(arglist, format);
147 char* const ptr = gBumpAllocator.ptr;
148 const int written = vsnprintf(ptr, gBumpAllocator.size, format, arglist);
149 va_end(arglist);
150 if (written < 0 || written >= (int)gBumpAllocator.size) internal_error();
151 return CreateConstantString((char*)BA_Bump(written));
152 }
153
154 // Adds a string node.
CreateString(const char * value)155 static Node* CreateString(const char* value) {
156 return CreatePrintfString("%s", value);
157 }
158
159 // Adds a map entry node.
AddMapEntry(Node * map,const char * key,Node * value)160 static void AddMapEntry(Node* map, const char* key, Node* value) {
161 assert(map && map->type == NT_MAP);
162 Node* current = map;
163 while (current->next) current = current->next;
164 current->next = (Node*)BA_Bump(sizeof(Node));
165 *current->next = (Node){.type = NT_MAP_ENTRY, .string = key, .value = value};
166 }
167
168 // Adds an array element node.
AddArrayElement(Node * array,Node * value)169 static void AddArrayElement(Node* array, Node* value) {
170 assert(array && array->type == NT_ARRAY);
171 Node* current = array;
172 while (current->next) current = current->next;
173 current->next = (Node*)BA_Bump(sizeof(Node));
174 *current->next = (Node){.type = NT_ARRAY_ELEMENT, .value = value};
175 }
176
cmp(const void * p1,const void * p2)177 static int cmp(const void* p1, const void* p2) {
178 return strcmp(*(const char* const*)p1, *(const char* const*)p2);
179 }
180
181 #define DEFINE_ADD_FLAGS(HasFeature, FeatureName, FeatureType, LastEnum) \
182 static void AddFlags(Node* map, const FeatureType* features) { \
183 size_t i; \
184 const char* ptrs[LastEnum] = {0}; \
185 size_t count = 0; \
186 for (i = 0; i < LastEnum; ++i) { \
187 if (HasFeature(features, i)) { \
188 ptrs[count] = FeatureName(i); \
189 ++count; \
190 } \
191 } \
192 qsort((void*)ptrs, count, sizeof(char*), cmp); \
193 Node* const array = CreateArray(); \
194 for (i = 0; i < count; ++i) \
195 AddArrayElement(array, CreateConstantString(ptrs[i])); \
196 AddMapEntry(map, "flags", array); \
197 }
198
199 #if defined(CPU_FEATURES_ARCH_X86)
DEFINE_ADD_FLAGS(GetX86FeaturesEnumValue,GetX86FeaturesEnumName,X86Features,X86_LAST_)200 DEFINE_ADD_FLAGS(GetX86FeaturesEnumValue, GetX86FeaturesEnumName, X86Features,
201 X86_LAST_)
202 #elif defined(CPU_FEATURES_ARCH_ARM)
203 DEFINE_ADD_FLAGS(GetArmFeaturesEnumValue, GetArmFeaturesEnumName, ArmFeatures,
204 ARM_LAST_)
205 #elif defined(CPU_FEATURES_ARCH_AARCH64)
206 DEFINE_ADD_FLAGS(GetAarch64FeaturesEnumValue, GetAarch64FeaturesEnumName,
207 Aarch64Features, AARCH64_LAST_)
208 #elif defined(CPU_FEATURES_ARCH_MIPS)
209 DEFINE_ADD_FLAGS(GetMipsFeaturesEnumValue, GetMipsFeaturesEnumName,
210 MipsFeatures, MIPS_LAST_)
211 #elif defined(CPU_FEATURES_ARCH_PPC)
212 DEFINE_ADD_FLAGS(GetPPCFeaturesEnumValue, GetPPCFeaturesEnumName, PPCFeatures,
213 PPC_LAST_)
214 #elif defined(CPU_FEATURES_ARCH_S390X)
215 DEFINE_ADD_FLAGS(GetS390XFeaturesEnumValue, GetS390XFeaturesEnumName, S390XFeatures,
216 S390X_LAST_)
217 #elif defined(CPU_FEATURES_ARCH_RISCV)
218 DEFINE_ADD_FLAGS(GetRiscvFeaturesEnumValue, GetRiscvFeaturesEnumName, RiscvFeatures,
219 RISCV_LAST_)
220 #elif defined(CPU_FEATURES_ARCH_LOONGARCH)
221 DEFINE_ADD_FLAGS(GetLoongArchFeaturesEnumValue, GetLoongArchFeaturesEnumName, LoongArchFeatures,
222 LOONGARCH_LAST_)
223 #endif
224
225 // Prints a json string with characters escaping.
226 static void printJsonString(const char* str) {
227 putchar('"');
228 for (; str && *str; ++str) {
229 switch (*str) {
230 case '\"':
231 case '\\':
232 case '/':
233 case '\b':
234 case '\f':
235 case '\n':
236 case '\r':
237 case '\t':
238 putchar('\\');
239 }
240 putchar(*str);
241 }
242 putchar('"');
243 }
244
245 // Walks a Node and print it as json.
printJson(const Node * current)246 static void printJson(const Node* current) {
247 assert(current);
248 switch (current->type) {
249 case NT_INVALID:
250 break;
251 case NT_INT:
252 printf("%d", current->integer);
253 break;
254 case NT_STRING:
255 printJsonString(current->string);
256 break;
257 case NT_ARRAY:
258 putchar('[');
259 if (current->next) printJson(current->next);
260 putchar(']');
261 break;
262 case NT_MAP:
263 putchar('{');
264 if (current->next) printJson(current->next);
265 putchar('}');
266 break;
267 case NT_MAP_ENTRY:
268 printf("\"%s\":", current->string);
269 printJson(current->value);
270 if (current->next) {
271 putchar(',');
272 printJson(current->next);
273 }
274 break;
275 case NT_ARRAY_ELEMENT:
276 printJson(current->value);
277 if (current->next) {
278 putchar(',');
279 printJson(current->next);
280 }
281 break;
282 }
283 }
284
285 // Walks a Node and print it as text.
printTextField(const Node * current)286 static void printTextField(const Node* current) {
287 switch (current->type) {
288 case NT_INVALID:
289 break;
290 case NT_INT:
291 printf("%3d (0x%02X)", current->integer, current->integer);
292 break;
293 case NT_STRING:
294 fputs(current->string, stdout);
295 break;
296 case NT_ARRAY:
297 if (current->next) printTextField(current->next);
298 break;
299 case NT_MAP:
300 if (current->next) {
301 printf("{");
302 printJson(current->next);
303 printf("}");
304 }
305 break;
306 case NT_MAP_ENTRY:
307 printf("%-15s : ", current->string);
308 printTextField(current->value);
309 if (current->next) {
310 putchar('\n');
311 printTextField(current->next);
312 }
313 break;
314 case NT_ARRAY_ELEMENT:
315 printTextField(current->value);
316 if (current->next) {
317 putchar(',');
318 printTextField(current->next);
319 }
320 break;
321 }
322 }
323
printTextRoot(const Node * current)324 static void printTextRoot(const Node* current) {
325 if (current->type == NT_MAP && current->next) printTextField(current->next);
326 }
327
showUsage(const char * name)328 static void showUsage(const char* name) {
329 printf(
330 "\n"
331 "Usage: %s [options]\n"
332 " Options:\n"
333 " -h | --help Show help message.\n"
334 " -j | --json Format output as json instead of plain text.\n"
335 "\n",
336 name);
337 }
338
GetCacheTypeString(CacheType cache_type)339 static Node* GetCacheTypeString(CacheType cache_type) {
340 switch (cache_type) {
341 case CPU_FEATURE_CACHE_NULL:
342 return CreateConstantString("null");
343 case CPU_FEATURE_CACHE_DATA:
344 return CreateConstantString("data");
345 case CPU_FEATURE_CACHE_INSTRUCTION:
346 return CreateConstantString("instruction");
347 case CPU_FEATURE_CACHE_UNIFIED:
348 return CreateConstantString("unified");
349 case CPU_FEATURE_CACHE_TLB:
350 return CreateConstantString("tlb");
351 case CPU_FEATURE_CACHE_DTLB:
352 return CreateConstantString("dtlb");
353 case CPU_FEATURE_CACHE_STLB:
354 return CreateConstantString("stlb");
355 case CPU_FEATURE_CACHE_PREFETCH:
356 return CreateConstantString("prefetch");
357 }
358 CPU_FEATURES_UNREACHABLE();
359 }
360
AddCacheInfo(Node * root,const CacheInfo * cache_info)361 static void AddCacheInfo(Node* root, const CacheInfo* cache_info) {
362 Node* array = CreateArray();
363 for (int i = 0; i < cache_info->size; ++i) {
364 CacheLevelInfo info = cache_info->levels[i];
365 Node* map = CreateMap();
366 AddMapEntry(map, "level", CreateInt(info.level));
367 AddMapEntry(map, "cache_type", GetCacheTypeString(info.cache_type));
368 AddMapEntry(map, "cache_size", CreateInt(info.cache_size));
369 AddMapEntry(map, "ways", CreateInt(info.ways));
370 AddMapEntry(map, "line_size", CreateInt(info.line_size));
371 AddMapEntry(map, "tlb_entries", CreateInt(info.tlb_entries));
372 AddMapEntry(map, "partitioning", CreateInt(info.partitioning));
373 AddArrayElement(array, map);
374 }
375 AddMapEntry(root, "cache_info", array);
376 }
377
CreateTree(void)378 static Node* CreateTree(void) {
379 Node* root = CreateMap();
380 #if defined(CPU_FEATURES_ARCH_X86)
381 const X86Info info = GetX86Info();
382 const CacheInfo cache_info = GetX86CacheInfo();
383 AddMapEntry(root, "arch", CreateString("x86"));
384 AddMapEntry(root, "brand", CreateString(info.brand_string));
385 AddMapEntry(root, "family", CreateInt(info.family));
386 AddMapEntry(root, "model", CreateInt(info.model));
387 AddMapEntry(root, "stepping", CreateInt(info.stepping));
388 AddMapEntry(root, "uarch",
389 CreateString(
390 GetX86MicroarchitectureName(GetX86Microarchitecture(&info))));
391 AddFlags(root, &info.features);
392 AddCacheInfo(root, &cache_info);
393 #elif defined(CPU_FEATURES_ARCH_ARM)
394 const ArmInfo info = GetArmInfo();
395 AddMapEntry(root, "arch", CreateString("ARM"));
396 AddMapEntry(root, "implementer", CreateInt(info.implementer));
397 AddMapEntry(root, "architecture", CreateInt(info.architecture));
398 AddMapEntry(root, "variant", CreateInt(info.variant));
399 AddMapEntry(root, "part", CreateInt(info.part));
400 AddMapEntry(root, "revision", CreateInt(info.revision));
401 AddFlags(root, &info.features);
402 #elif defined(CPU_FEATURES_ARCH_AARCH64)
403 const Aarch64Info info = GetAarch64Info();
404 AddMapEntry(root, "arch", CreateString("aarch64"));
405 AddMapEntry(root, "implementer", CreateInt(info.implementer));
406 AddMapEntry(root, "variant", CreateInt(info.variant));
407 AddMapEntry(root, "part", CreateInt(info.part));
408 AddMapEntry(root, "revision", CreateInt(info.revision));
409 AddFlags(root, &info.features);
410 #elif defined(CPU_FEATURES_ARCH_MIPS)
411 const MipsInfo info = GetMipsInfo();
412 AddMapEntry(root, "arch", CreateString("mips"));
413 AddFlags(root, &info.features);
414 #elif defined(CPU_FEATURES_ARCH_PPC)
415 const PPCInfo info = GetPPCInfo();
416 const PPCPlatformStrings strings = GetPPCPlatformStrings();
417 AddMapEntry(root, "arch", CreateString("ppc"));
418 AddMapEntry(root, "platform", CreateString(strings.platform));
419 AddMapEntry(root, "model", CreateString(strings.model));
420 AddMapEntry(root, "machine", CreateString(strings.machine));
421 AddMapEntry(root, "cpu", CreateString(strings.cpu));
422 AddMapEntry(root, "instruction", CreateString(strings.type.platform));
423 AddMapEntry(root, "microarchitecture",
424 CreateString(strings.type.base_platform));
425 AddFlags(root, &info.features);
426 #elif defined(CPU_FEATURES_ARCH_S390X)
427 const S390XInfo info = GetS390XInfo();
428 const S390XPlatformStrings strings = GetS390XPlatformStrings();
429 AddMapEntry(root, "arch", CreateString("s390x"));
430 AddMapEntry(root, "platform", CreateString("zSeries"));
431 AddMapEntry(root, "model", CreateString(strings.type.platform));
432 AddMapEntry(root, "# processors", CreateInt(strings.num_processors));
433 AddFlags(root, &info.features);
434 #elif defined(CPU_FEATURES_ARCH_RISCV)
435 const RiscvInfo info = GetRiscvInfo();
436 AddMapEntry(root, "arch", CreateString("risc-v"));
437 AddMapEntry(root, "vendor", CreateString(info.vendor));
438 AddMapEntry(root, "microarchitecture", CreateString(info.uarch));
439 AddFlags(root, &info.features);
440 #elif defined(CPU_FEATURES_ARCH_LOONGARCH)
441 const LoongArchInfo info = GetLoongArchInfo();
442 AddMapEntry(root, "arch", CreateString("loongarch"));
443 AddFlags(root, &info.features);
444 #endif
445 return root;
446 }
447
main(int argc,char ** argv)448 int main(int argc, char** argv) {
449 BA_Align();
450 const Node* const root = CreateTree();
451 bool outputJson = false;
452 int i = 1;
453 for (; i < argc; ++i) {
454 const char* arg = argv[i];
455 if (strcmp(arg, "-j") == 0 || strcmp(arg, "--json") == 0) {
456 outputJson = true;
457 } else {
458 showUsage(argv[0]);
459 if (strcmp(arg, "-h") == 0 || strcmp(arg, "--help") == 0)
460 return EXIT_SUCCESS;
461 return EXIT_FAILURE;
462 }
463 }
464 if (outputJson)
465 printJson(root);
466 else
467 printTextRoot(root);
468 putchar('\n');
469 return EXIT_SUCCESS;
470 }
471