• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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