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 CPU_FEATURES_UNREACHABLE();
344 }
345
AddCacheInfo(Node * root,const CacheInfo * cache_info)346 static void AddCacheInfo(Node* root, const CacheInfo* cache_info) {
347 Node* array = CreateArray();
348 for (int i = 0; i < cache_info->size; ++i) {
349 CacheLevelInfo info = cache_info->levels[i];
350 Node* map = CreateMap();
351 AddMapEntry(map, "level", CreateInt(info.level));
352 AddMapEntry(map, "cache_type", GetCacheTypeString(info.cache_type));
353 AddMapEntry(map, "cache_size", CreateInt(info.cache_size));
354 AddMapEntry(map, "ways", CreateInt(info.ways));
355 AddMapEntry(map, "line_size", CreateInt(info.line_size));
356 AddMapEntry(map, "tlb_entries", CreateInt(info.tlb_entries));
357 AddMapEntry(map, "partitioning", CreateInt(info.partitioning));
358 AddArrayElement(array, map);
359 }
360 AddMapEntry(root, "cache_info", array);
361 }
362
CreateTree()363 static Node* CreateTree() {
364 Node* root = CreateMap();
365 #if defined(CPU_FEATURES_ARCH_X86)
366 char brand_string[49];
367 const X86Info info = GetX86Info();
368 const CacheInfo cache_info = GetX86CacheInfo();
369 FillX86BrandString(brand_string);
370 AddMapEntry(root, "arch", CreateString("x86"));
371 AddMapEntry(root, "brand", CreateString(brand_string));
372 AddMapEntry(root, "family", CreateInt(info.family));
373 AddMapEntry(root, "model", CreateInt(info.model));
374 AddMapEntry(root, "stepping", CreateInt(info.stepping));
375 AddMapEntry(root, "uarch",
376 CreateString(
377 GetX86MicroarchitectureName(GetX86Microarchitecture(&info))));
378 AddFlags(root, &info.features);
379 AddCacheInfo(root, &cache_info);
380 #elif defined(CPU_FEATURES_ARCH_ARM)
381 const ArmInfo info = GetArmInfo();
382 AddMapEntry(root, "arch", CreateString("ARM"));
383 AddMapEntry(root, "implementer", CreateInt(info.implementer));
384 AddMapEntry(root, "architecture", CreateInt(info.architecture));
385 AddMapEntry(root, "variant", CreateInt(info.variant));
386 AddMapEntry(root, "part", CreateInt(info.part));
387 AddMapEntry(root, "revision", CreateInt(info.revision));
388 AddFlags(root, &info.features);
389 #elif defined(CPU_FEATURES_ARCH_AARCH64)
390 const Aarch64Info info = GetAarch64Info();
391 AddMapEntry(root, "arch", CreateString("aarch64"));
392 AddMapEntry(root, "implementer", CreateInt(info.implementer));
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_MIPS)
398 const MipsInfo info = GetMipsInfo();
399 AddMapEntry(root, "arch", CreateString("mips"));
400 AddFlags(root, &info.features);
401 #elif defined(CPU_FEATURES_ARCH_PPC)
402 const PPCInfo info = GetPPCInfo();
403 const PPCPlatformStrings strings = GetPPCPlatformStrings();
404 AddMapEntry(root, "arch", CreateString("ppc"));
405 AddMapEntry(root, "platform", CreateString(strings.platform));
406 AddMapEntry(root, "model", CreateString(strings.model));
407 AddMapEntry(root, "machine", CreateString(strings.machine));
408 AddMapEntry(root, "cpu", CreateString(strings.cpu));
409 AddMapEntry(root, "instruction", CreateString(strings.type.platform));
410 AddMapEntry(root, "microarchitecture",
411 CreateString(strings.type.base_platform));
412 AddFlags(root, &info.features);
413 #endif
414 return root;
415 }
416
main(int argc,char ** argv)417 int main(int argc, char** argv) {
418 BA_Align();
419 const Node* const root = CreateTree();
420 bool outputJson = false;
421 int i = 1;
422 for (; i < argc; ++i) {
423 const char* arg = argv[i];
424 if (strcmp(arg, "-j") == 0 || strcmp(arg, "--json") == 0) {
425 outputJson = true;
426 } else {
427 showUsage(argv[0]);
428 if (strcmp(arg, "-h") == 0 || strcmp(arg, "--help") == 0)
429 return EXIT_SUCCESS;
430 return EXIT_FAILURE;
431 }
432 }
433 if (outputJson)
434 printJson(root);
435 else
436 printTextRoot(root);
437 putchar('\n');
438 return EXIT_SUCCESS;
439 }
440