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