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