xref: /aosp_15_r20/external/cronet/third_party/cpu_features/src/src/utils/list_cpu_features.c (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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