• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include <stdbool.h>
2 #include <stdint.h>
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
6 
7 #if !defined(__ANDROID__)
8 /*
9  * sched.h is only used for CPU_SETSIZE constant.
10  * Android NDK headers before platform 21 do have this constant in sched.h
11  */
12 #include <sched.h>
13 #endif
14 
15 #include <cpuinfo/log.h>
16 #include <linux/api.h>
17 
18 #define STRINGIFY(token) #token
19 
20 #define KERNEL_MAX_FILENAME "/sys/devices/system/cpu/kernel_max"
21 #define KERNEL_MAX_FILESIZE 32
22 #define FREQUENCY_FILENAME_SIZE \
23 	(sizeof("/sys/devices/system/cpu/cpu" STRINGIFY(UINT32_MAX) "/cpufreq/cpuinfo_max_freq"))
24 #define CUR_FREQUENCY_FILENAME_FORMAT "/sys/devices/system/cpu/cpu%" PRIu32 "/cpufreq/cpuinfo_cur_freq"
25 #define MAX_FREQUENCY_FILENAME_FORMAT "/sys/devices/system/cpu/cpu%" PRIu32 "/cpufreq/cpuinfo_max_freq"
26 #define MIN_FREQUENCY_FILENAME_FORMAT "/sys/devices/system/cpu/cpu%" PRIu32 "/cpufreq/cpuinfo_min_freq"
27 #define FREQUENCY_FILESIZE 32
28 #define PACKAGE_ID_FILENAME_SIZE \
29 	(sizeof("/sys/devices/system/cpu/cpu" STRINGIFY(UINT32_MAX) "/topology/physical_package_id"))
30 #define PACKAGE_ID_FILENAME_FORMAT "/sys/devices/system/cpu/cpu%" PRIu32 "/topology/physical_package_id"
31 #define PACKAGE_ID_FILESIZE 32
32 #define CORE_ID_FILENAME_SIZE (sizeof("/sys/devices/system/cpu/cpu" STRINGIFY(UINT32_MAX) "/topology/core_id"))
33 #define CORE_ID_FILENAME_FORMAT "/sys/devices/system/cpu/cpu%" PRIu32 "/topology/core_id"
34 #define CORE_ID_FILESIZE 32
35 
36 #define CORE_CPUS_FILENAME_SIZE (sizeof("/sys/devices/system/cpu/cpu" STRINGIFY(UINT32_MAX) "/topology/core_cpus_list"))
37 #define CORE_CPUS_FILENAME_FORMAT "/sys/devices/system/cpu/cpu%" PRIu32 "/topology/core_cpus_list"
38 #define CORE_SIBLINGS_FILENAME_SIZE \
39 	(sizeof("/sys/devices/system/cpu/cpu" STRINGIFY(UINT32_MAX) "/topology/core_siblings_list"))
40 #define CORE_SIBLINGS_FILENAME_FORMAT "/sys/devices/system/cpu/cpu%" PRIu32 "/topology/core_siblings_list"
41 #define CLUSTER_CPUS_FILENAME_SIZE \
42 	(sizeof("/sys/devices/system/cpu/cpu" STRINGIFY(UINT32_MAX) "/topology/cluster_cpus_list"))
43 #define CLUSTER_CPUS_FILENAME_FORMAT "/sys/devices/system/cpu/cpu%" PRIu32 "/topology/cluster_cpus_list"
44 #define PACKAGE_CPUS_FILENAME_SIZE \
45 	(sizeof("/sys/devices/system/cpu/cpu" STRINGIFY(UINT32_MAX) "/topology/package_cpus_list"))
46 #define PACKAGE_CPUS_FILENAME_FORMAT "/sys/devices/system/cpu/cpu%" PRIu32 "/topology/package_cpus_list"
47 #define THREAD_SIBLINGS_FILENAME_SIZE \
48 	(sizeof("/sys/devices/system/cpu/cpu" STRINGIFY(UINT32_MAX) "/topology/thread_siblings_list"))
49 #define THREAD_SIBLINGS_FILENAME_FORMAT "/sys/devices/system/cpu/cpu%" PRIu32 "/topology/thread_siblings_list"
50 
51 #define POSSIBLE_CPULIST_FILENAME "/sys/devices/system/cpu/possible"
52 #define PRESENT_CPULIST_FILENAME "/sys/devices/system/cpu/present"
53 
parse_number(const char * start,const char * end,uint32_t number_ptr[restrict static1])54 inline static const char* parse_number(const char* start, const char* end, uint32_t number_ptr[restrict static 1]) {
55 	uint32_t number = 0;
56 	const char* parsed = start;
57 	for (; parsed != end; parsed++) {
58 		const uint32_t digit = (uint32_t)(uint8_t)(*parsed) - (uint32_t)'0';
59 		if (digit >= 10) {
60 			break;
61 		}
62 		number = number * UINT32_C(10) + digit;
63 	}
64 	*number_ptr = number;
65 	return parsed;
66 }
67 
68 /* Locale-independent */
is_whitespace(char c)69 inline static bool is_whitespace(char c) {
70 	switch (c) {
71 		case ' ':
72 		case '\t':
73 		case '\n':
74 		case '\r':
75 			return true;
76 		default:
77 			return false;
78 	}
79 }
80 
81 #if defined(__ANDROID__) && !defined(CPU_SETSIZE)
82 /*
83  * Android NDK headers before platform 21 do not define CPU_SETSIZE,
84  * so we hard-code its value, as defined in platform 21 headers
85  */
86 #if defined(__LP64__)
87 static const uint32_t default_max_processors_count = 1024;
88 #else
89 static const uint32_t default_max_processors_count = 32;
90 #endif
91 #else
92 static const uint32_t default_max_processors_count = CPU_SETSIZE;
93 #endif
94 
uint32_parser(const char * filename,const char * text_start,const char * text_end,void * context)95 static bool uint32_parser(const char* filename, const char* text_start, const char* text_end, void* context) {
96 	if (text_start == text_end) {
97 		cpuinfo_log_error("failed to parse file %s: file is empty", KERNEL_MAX_FILENAME);
98 		return false;
99 	}
100 
101 	uint32_t kernel_max = 0;
102 	const char* parsed_end = parse_number(text_start, text_end, &kernel_max);
103 	if (parsed_end == text_start) {
104 		cpuinfo_log_error(
105 			"failed to parse file %s: \"%.*s\" is not an unsigned number",
106 			filename,
107 			(int)(text_end - text_start),
108 			text_start);
109 		return false;
110 	} else {
111 		for (const char* char_ptr = parsed_end; char_ptr != text_end; char_ptr++) {
112 			if (!is_whitespace(*char_ptr)) {
113 				cpuinfo_log_warning(
114 					"non-whitespace characters \"%.*s\" following number in file %s are ignored",
115 					(int)(text_end - char_ptr),
116 					char_ptr,
117 					filename);
118 				break;
119 			}
120 		}
121 	}
122 
123 	uint32_t* kernel_max_ptr = (uint32_t*)context;
124 	*kernel_max_ptr = kernel_max;
125 	return true;
126 }
127 
cpuinfo_linux_get_max_processors_count(void)128 uint32_t cpuinfo_linux_get_max_processors_count(void) {
129 	uint32_t kernel_max;
130 	if (cpuinfo_linux_parse_small_file(KERNEL_MAX_FILENAME, KERNEL_MAX_FILESIZE, uint32_parser, &kernel_max)) {
131 		cpuinfo_log_debug("parsed kernel_max value of %" PRIu32 " from %s", kernel_max, KERNEL_MAX_FILENAME);
132 
133 		if (kernel_max >= default_max_processors_count) {
134 			cpuinfo_log_warning(
135 				"kernel_max value of %" PRIu32
136 				" parsed from %s exceeds platform-default limit %" PRIu32,
137 				kernel_max,
138 				KERNEL_MAX_FILENAME,
139 				default_max_processors_count - 1);
140 		}
141 
142 		return kernel_max + 1;
143 	} else {
144 		cpuinfo_log_warning(
145 			"using platform-default max processors count = %" PRIu32, default_max_processors_count);
146 		return default_max_processors_count;
147 	}
148 }
149 
cpuinfo_linux_get_processor_cur_frequency(uint32_t processor)150 uint32_t cpuinfo_linux_get_processor_cur_frequency(uint32_t processor) {
151 	char cur_frequency_filename[FREQUENCY_FILENAME_SIZE];
152 	const int chars_formatted =
153 		snprintf(cur_frequency_filename, FREQUENCY_FILENAME_SIZE, CUR_FREQUENCY_FILENAME_FORMAT, processor);
154 	if ((unsigned int)chars_formatted >= FREQUENCY_FILENAME_SIZE) {
155 		cpuinfo_log_warning("failed to format filename for current frequency of processor %" PRIu32, processor);
156 		return 0;
157 	}
158 
159 	uint32_t cur_frequency;
160 	if (cpuinfo_linux_parse_small_file(cur_frequency_filename, FREQUENCY_FILESIZE, uint32_parser, &cur_frequency)) {
161 		cpuinfo_log_debug(
162 			"parsed currrent frequency value of %" PRIu32 " KHz for logical processor %" PRIu32 " from %s",
163 			cur_frequency,
164 			processor,
165 			cur_frequency_filename);
166 		return cur_frequency;
167 	} else {
168 		cpuinfo_log_warning(
169 			"failed to parse current frequency for processor %" PRIu32 " from %s",
170 			processor,
171 			cur_frequency_filename);
172 		return 0;
173 	}
174 }
175 
cpuinfo_linux_get_processor_max_frequency(uint32_t processor)176 uint32_t cpuinfo_linux_get_processor_max_frequency(uint32_t processor) {
177 	char max_frequency_filename[FREQUENCY_FILENAME_SIZE];
178 	const int chars_formatted =
179 		snprintf(max_frequency_filename, FREQUENCY_FILENAME_SIZE, MAX_FREQUENCY_FILENAME_FORMAT, processor);
180 	if ((unsigned int)chars_formatted >= FREQUENCY_FILENAME_SIZE) {
181 		cpuinfo_log_warning("failed to format filename for max frequency of processor %" PRIu32, processor);
182 		return 0;
183 	}
184 
185 	uint32_t max_frequency;
186 	if (cpuinfo_linux_parse_small_file(max_frequency_filename, FREQUENCY_FILESIZE, uint32_parser, &max_frequency)) {
187 		cpuinfo_log_debug(
188 			"parsed max frequency value of %" PRIu32 " KHz for logical processor %" PRIu32 " from %s",
189 			max_frequency,
190 			processor,
191 			max_frequency_filename);
192 		return max_frequency;
193 	} else {
194 		cpuinfo_log_warning(
195 			"failed to parse max frequency for processor %" PRIu32 " from %s",
196 			processor,
197 			max_frequency_filename);
198 		return 0;
199 	}
200 }
201 
cpuinfo_linux_get_processor_min_frequency(uint32_t processor)202 uint32_t cpuinfo_linux_get_processor_min_frequency(uint32_t processor) {
203 	char min_frequency_filename[FREQUENCY_FILENAME_SIZE];
204 	const int chars_formatted =
205 		snprintf(min_frequency_filename, FREQUENCY_FILENAME_SIZE, MIN_FREQUENCY_FILENAME_FORMAT, processor);
206 	if ((unsigned int)chars_formatted >= FREQUENCY_FILENAME_SIZE) {
207 		cpuinfo_log_warning("failed to format filename for min frequency of processor %" PRIu32, processor);
208 		return 0;
209 	}
210 
211 	uint32_t min_frequency;
212 	if (cpuinfo_linux_parse_small_file(min_frequency_filename, FREQUENCY_FILESIZE, uint32_parser, &min_frequency)) {
213 		cpuinfo_log_debug(
214 			"parsed min frequency value of %" PRIu32 " KHz for logical processor %" PRIu32 " from %s",
215 			min_frequency,
216 			processor,
217 			min_frequency_filename);
218 		return min_frequency;
219 	} else {
220 		/*
221 		 * This error is less severe than parsing max frequency, because
222 		 * min frequency is only useful for clustering, while max
223 		 * frequency is also needed for peak FLOPS calculation.
224 		 */
225 		cpuinfo_log_info(
226 			"failed to parse min frequency for processor %" PRIu32 " from %s",
227 			processor,
228 			min_frequency_filename);
229 		return 0;
230 	}
231 }
232 
cpuinfo_linux_get_processor_core_id(uint32_t processor,uint32_t core_id_ptr[restrict static1])233 bool cpuinfo_linux_get_processor_core_id(uint32_t processor, uint32_t core_id_ptr[restrict static 1]) {
234 	char core_id_filename[PACKAGE_ID_FILENAME_SIZE];
235 	const int chars_formatted =
236 		snprintf(core_id_filename, CORE_ID_FILENAME_SIZE, CORE_ID_FILENAME_FORMAT, processor);
237 	if ((unsigned int)chars_formatted >= CORE_ID_FILENAME_SIZE) {
238 		cpuinfo_log_warning("failed to format filename for core id of processor %" PRIu32, processor);
239 		return 0;
240 	}
241 
242 	uint32_t core_id;
243 	if (cpuinfo_linux_parse_small_file(core_id_filename, CORE_ID_FILESIZE, uint32_parser, &core_id)) {
244 		cpuinfo_log_debug(
245 			"parsed core id value of %" PRIu32 " for logical processor %" PRIu32 " from %s",
246 			core_id,
247 			processor,
248 			core_id_filename);
249 		*core_id_ptr = core_id;
250 		return true;
251 	} else {
252 		cpuinfo_log_info(
253 			"failed to parse core id for processor %" PRIu32 " from %s", processor, core_id_filename);
254 		return false;
255 	}
256 }
257 
cpuinfo_linux_get_processor_package_id(uint32_t processor,uint32_t package_id_ptr[restrict static1])258 bool cpuinfo_linux_get_processor_package_id(uint32_t processor, uint32_t package_id_ptr[restrict static 1]) {
259 	char package_id_filename[PACKAGE_ID_FILENAME_SIZE];
260 	const int chars_formatted =
261 		snprintf(package_id_filename, PACKAGE_ID_FILENAME_SIZE, PACKAGE_ID_FILENAME_FORMAT, processor);
262 	if ((unsigned int)chars_formatted >= PACKAGE_ID_FILENAME_SIZE) {
263 		cpuinfo_log_warning("failed to format filename for package id of processor %" PRIu32, processor);
264 		return 0;
265 	}
266 
267 	uint32_t package_id;
268 	if (cpuinfo_linux_parse_small_file(package_id_filename, PACKAGE_ID_FILESIZE, uint32_parser, &package_id)) {
269 		cpuinfo_log_debug(
270 			"parsed package id value of %" PRIu32 " for logical processor %" PRIu32 " from %s",
271 			package_id,
272 			processor,
273 			package_id_filename);
274 		*package_id_ptr = package_id;
275 		return true;
276 	} else {
277 		cpuinfo_log_info(
278 			"failed to parse package id for processor %" PRIu32 " from %s", processor, package_id_filename);
279 		return false;
280 	}
281 }
282 
max_processor_number_parser(uint32_t processor_list_start,uint32_t processor_list_end,void * context)283 static bool max_processor_number_parser(uint32_t processor_list_start, uint32_t processor_list_end, void* context) {
284 	uint32_t* processor_number_ptr = (uint32_t*)context;
285 	const uint32_t processor_list_last = processor_list_end - 1;
286 	if (*processor_number_ptr < processor_list_last) {
287 		*processor_number_ptr = processor_list_last;
288 	}
289 	return true;
290 }
291 
cpuinfo_linux_get_max_possible_processor(uint32_t max_processors_count)292 uint32_t cpuinfo_linux_get_max_possible_processor(uint32_t max_processors_count) {
293 	uint32_t max_possible_processor = 0;
294 	if (!cpuinfo_linux_parse_cpulist(
295 		    POSSIBLE_CPULIST_FILENAME, max_processor_number_parser, &max_possible_processor)) {
296 #if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64
297 		cpuinfo_log_error("failed to parse the list of possible processors in %s", POSSIBLE_CPULIST_FILENAME);
298 #else
299 		cpuinfo_log_warning("failed to parse the list of possible processors in %s", POSSIBLE_CPULIST_FILENAME);
300 #endif
301 		return UINT32_MAX;
302 	}
303 	if (max_possible_processor >= max_processors_count) {
304 		cpuinfo_log_warning(
305 			"maximum possible processor number %" PRIu32 " exceeds system limit %" PRIu32
306 			": truncating to the latter",
307 			max_possible_processor,
308 			max_processors_count - 1);
309 		max_possible_processor = max_processors_count - 1;
310 	}
311 	return max_possible_processor;
312 }
313 
cpuinfo_linux_get_max_present_processor(uint32_t max_processors_count)314 uint32_t cpuinfo_linux_get_max_present_processor(uint32_t max_processors_count) {
315 	uint32_t max_present_processor = 0;
316 	if (!cpuinfo_linux_parse_cpulist(
317 		    PRESENT_CPULIST_FILENAME, max_processor_number_parser, &max_present_processor)) {
318 #if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64
319 		cpuinfo_log_error("failed to parse the list of present processors in %s", PRESENT_CPULIST_FILENAME);
320 #else
321 		cpuinfo_log_warning("failed to parse the list of present processors in %s", PRESENT_CPULIST_FILENAME);
322 #endif
323 		return UINT32_MAX;
324 	}
325 	if (max_present_processor >= max_processors_count) {
326 		cpuinfo_log_warning(
327 			"maximum present processor number %" PRIu32 " exceeds system limit %" PRIu32
328 			": truncating to the latter",
329 			max_present_processor,
330 			max_processors_count - 1);
331 		max_present_processor = max_processors_count - 1;
332 	}
333 	return max_present_processor;
334 }
335 
336 struct detect_processors_context {
337 	uint32_t max_processors_count;
338 	uint32_t* processor0_flags;
339 	uint32_t processor_struct_size;
340 	uint32_t detected_flag;
341 };
342 
detect_processor_parser(uint32_t processor_list_start,uint32_t processor_list_end,void * context)343 static bool detect_processor_parser(uint32_t processor_list_start, uint32_t processor_list_end, void* context) {
344 	const uint32_t max_processors_count = ((struct detect_processors_context*)context)->max_processors_count;
345 	const uint32_t* processor0_flags = ((struct detect_processors_context*)context)->processor0_flags;
346 	const uint32_t processor_struct_size = ((struct detect_processors_context*)context)->processor_struct_size;
347 	const uint32_t detected_flag = ((struct detect_processors_context*)context)->detected_flag;
348 
349 	for (uint32_t processor = processor_list_start; processor < processor_list_end; processor++) {
350 		if (processor >= max_processors_count) {
351 			break;
352 		}
353 		*((uint32_t*)((uintptr_t)processor0_flags + processor_struct_size * processor)) |= detected_flag;
354 	}
355 	return true;
356 }
357 
cpuinfo_linux_detect_possible_processors(uint32_t max_processors_count,uint32_t * processor0_flags,uint32_t processor_struct_size,uint32_t possible_flag)358 bool cpuinfo_linux_detect_possible_processors(
359 	uint32_t max_processors_count,
360 	uint32_t* processor0_flags,
361 	uint32_t processor_struct_size,
362 	uint32_t possible_flag) {
363 	struct detect_processors_context context = {
364 		.max_processors_count = max_processors_count,
365 		.processor0_flags = processor0_flags,
366 		.processor_struct_size = processor_struct_size,
367 		.detected_flag = possible_flag,
368 	};
369 	if (cpuinfo_linux_parse_cpulist(POSSIBLE_CPULIST_FILENAME, detect_processor_parser, &context)) {
370 		return true;
371 	} else {
372 		cpuinfo_log_warning("failed to parse the list of possible processors in %s", POSSIBLE_CPULIST_FILENAME);
373 		return false;
374 	}
375 }
376 
cpuinfo_linux_detect_present_processors(uint32_t max_processors_count,uint32_t * processor0_flags,uint32_t processor_struct_size,uint32_t present_flag)377 bool cpuinfo_linux_detect_present_processors(
378 	uint32_t max_processors_count,
379 	uint32_t* processor0_flags,
380 	uint32_t processor_struct_size,
381 	uint32_t present_flag) {
382 	struct detect_processors_context context = {
383 		.max_processors_count = max_processors_count,
384 		.processor0_flags = processor0_flags,
385 		.processor_struct_size = processor_struct_size,
386 		.detected_flag = present_flag,
387 	};
388 	if (cpuinfo_linux_parse_cpulist(PRESENT_CPULIST_FILENAME, detect_processor_parser, &context)) {
389 		return true;
390 	} else {
391 		cpuinfo_log_warning("failed to parse the list of present processors in %s", PRESENT_CPULIST_FILENAME);
392 		return false;
393 	}
394 }
395 
396 struct siblings_context {
397 	const char* group_name;
398 	uint32_t max_processors_count;
399 	uint32_t processor;
400 	cpuinfo_siblings_callback callback;
401 	void* callback_context;
402 };
403 
siblings_parser(uint32_t sibling_list_start,uint32_t sibling_list_end,struct siblings_context * context)404 static bool siblings_parser(uint32_t sibling_list_start, uint32_t sibling_list_end, struct siblings_context* context) {
405 	const char* group_name = context->group_name;
406 	const uint32_t max_processors_count = context->max_processors_count;
407 	const uint32_t processor = context->processor;
408 
409 	if (sibling_list_end > max_processors_count) {
410 		cpuinfo_log_warning(
411 			"ignore %s siblings %" PRIu32 "-%" PRIu32 " of processor %" PRIu32,
412 			group_name,
413 			max_processors_count,
414 			sibling_list_end - 1,
415 			processor);
416 		sibling_list_end = max_processors_count;
417 	}
418 
419 	return context->callback(processor, sibling_list_start, sibling_list_end, context->callback_context);
420 }
421 
cpuinfo_linux_detect_core_cpus(uint32_t max_processors_count,uint32_t processor,cpuinfo_siblings_callback callback,void * context)422 bool cpuinfo_linux_detect_core_cpus(
423 	uint32_t max_processors_count,
424 	uint32_t processor,
425 	cpuinfo_siblings_callback callback,
426 	void* context) {
427 	char core_cpus_filename[CORE_CPUS_FILENAME_SIZE];
428 	const int chars_formatted =
429 		snprintf(core_cpus_filename, CORE_CPUS_FILENAME_SIZE, CORE_CPUS_FILENAME_FORMAT, processor);
430 	if ((unsigned int)chars_formatted >= CORE_CPUS_FILENAME_SIZE) {
431 		cpuinfo_log_warning("failed to format filename for core cpus of processor %" PRIu32, processor);
432 		return false;
433 	}
434 
435 	struct siblings_context siblings_context = {
436 		.group_name = "cpus",
437 		.max_processors_count = max_processors_count,
438 		.processor = processor,
439 		.callback = callback,
440 		.callback_context = context,
441 	};
442 	if (cpuinfo_linux_parse_cpulist(
443 		    core_cpus_filename, (cpuinfo_cpulist_callback)siblings_parser, &siblings_context)) {
444 		return true;
445 	} else {
446 		cpuinfo_log_info(
447 			"failed to parse the list of core cpus for processor %" PRIu32 " from %s",
448 			processor,
449 			core_cpus_filename);
450 		return false;
451 	}
452 }
453 
cpuinfo_linux_detect_core_siblings(uint32_t max_processors_count,uint32_t processor,cpuinfo_siblings_callback callback,void * context)454 bool cpuinfo_linux_detect_core_siblings(
455 	uint32_t max_processors_count,
456 	uint32_t processor,
457 	cpuinfo_siblings_callback callback,
458 	void* context) {
459 	char core_siblings_filename[CORE_SIBLINGS_FILENAME_SIZE];
460 	const int chars_formatted =
461 		snprintf(core_siblings_filename, CORE_SIBLINGS_FILENAME_SIZE, CORE_SIBLINGS_FILENAME_FORMAT, processor);
462 	if ((unsigned int)chars_formatted >= CORE_SIBLINGS_FILENAME_SIZE) {
463 		cpuinfo_log_warning("failed to format filename for core siblings of processor %" PRIu32, processor);
464 		return false;
465 	}
466 
467 	struct siblings_context siblings_context = {
468 		.group_name = "package",
469 		.max_processors_count = max_processors_count,
470 		.processor = processor,
471 		.callback = callback,
472 		.callback_context = context,
473 	};
474 	if (cpuinfo_linux_parse_cpulist(
475 		    core_siblings_filename, (cpuinfo_cpulist_callback)siblings_parser, &siblings_context)) {
476 		return true;
477 	} else {
478 		cpuinfo_log_info(
479 			"failed to parse the list of core siblings for processor %" PRIu32 " from %s",
480 			processor,
481 			core_siblings_filename);
482 		return false;
483 	}
484 }
485 
cpuinfo_linux_detect_thread_siblings(uint32_t max_processors_count,uint32_t processor,cpuinfo_siblings_callback callback,void * context)486 bool cpuinfo_linux_detect_thread_siblings(
487 	uint32_t max_processors_count,
488 	uint32_t processor,
489 	cpuinfo_siblings_callback callback,
490 	void* context) {
491 	char thread_siblings_filename[THREAD_SIBLINGS_FILENAME_SIZE];
492 	const int chars_formatted = snprintf(
493 		thread_siblings_filename, THREAD_SIBLINGS_FILENAME_SIZE, THREAD_SIBLINGS_FILENAME_FORMAT, processor);
494 	if ((unsigned int)chars_formatted >= THREAD_SIBLINGS_FILENAME_SIZE) {
495 		cpuinfo_log_warning("failed to format filename for thread siblings of processor %" PRIu32, processor);
496 		return false;
497 	}
498 
499 	struct siblings_context siblings_context = {
500 		.group_name = "core",
501 		.max_processors_count = max_processors_count,
502 		.processor = processor,
503 		.callback = callback,
504 		.callback_context = context,
505 	};
506 	if (cpuinfo_linux_parse_cpulist(
507 		    thread_siblings_filename, (cpuinfo_cpulist_callback)siblings_parser, &siblings_context)) {
508 		return true;
509 	} else {
510 		cpuinfo_log_info(
511 			"failed to parse the list of thread siblings for processor %" PRIu32 " from %s",
512 			processor,
513 			thread_siblings_filename);
514 		return false;
515 	}
516 }
517 
cpuinfo_linux_detect_cluster_cpus(uint32_t max_processors_count,uint32_t processor,cpuinfo_siblings_callback callback,void * context)518 bool cpuinfo_linux_detect_cluster_cpus(
519 	uint32_t max_processors_count,
520 	uint32_t processor,
521 	cpuinfo_siblings_callback callback,
522 	void* context) {
523 	char cluster_cpus_filename[CLUSTER_CPUS_FILENAME_SIZE];
524 	const int chars_formatted =
525 		snprintf(cluster_cpus_filename, CLUSTER_CPUS_FILENAME_SIZE, CLUSTER_CPUS_FILENAME_FORMAT, processor);
526 	if ((unsigned int)chars_formatted >= CLUSTER_CPUS_FILENAME_SIZE) {
527 		cpuinfo_log_warning("failed to format filename for cluster cpus of processor %" PRIu32, processor);
528 		return false;
529 	}
530 
531 	struct siblings_context siblings_context = {
532 		.group_name = "cluster",
533 		.max_processors_count = max_processors_count,
534 		.processor = processor,
535 		.callback = callback,
536 		.callback_context = context,
537 	};
538 	if (cpuinfo_linux_parse_cpulist(
539 		    cluster_cpus_filename, (cpuinfo_cpulist_callback)siblings_parser, &siblings_context)) {
540 		return true;
541 	} else {
542 		cpuinfo_log_info(
543 			"failed to parse the list of cluster cpus for processor %" PRIu32 " from %s",
544 			processor,
545 			cluster_cpus_filename);
546 		return false;
547 	}
548 }
549 
cpuinfo_linux_detect_package_cpus(uint32_t max_processors_count,uint32_t processor,cpuinfo_siblings_callback callback,void * context)550 bool cpuinfo_linux_detect_package_cpus(
551 	uint32_t max_processors_count,
552 	uint32_t processor,
553 	cpuinfo_siblings_callback callback,
554 	void* context) {
555 	char package_cpus_filename[PACKAGE_CPUS_FILENAME_SIZE];
556 	const int chars_formatted =
557 		snprintf(package_cpus_filename, PACKAGE_CPUS_FILENAME_SIZE, PACKAGE_CPUS_FILENAME_FORMAT, processor);
558 	if ((unsigned int)chars_formatted >= PACKAGE_CPUS_FILENAME_SIZE) {
559 		cpuinfo_log_warning("failed to format filename for package cpus of processor %" PRIu32, processor);
560 		return false;
561 	}
562 
563 	struct siblings_context siblings_context = {
564 		.group_name = "package",
565 		.max_processors_count = max_processors_count,
566 		.processor = processor,
567 		.callback = callback,
568 		.callback_context = context,
569 	};
570 	if (cpuinfo_linux_parse_cpulist(
571 		    package_cpus_filename, (cpuinfo_cpulist_callback)siblings_parser, &siblings_context)) {
572 		return true;
573 	} else {
574 		cpuinfo_log_info(
575 			"failed to parse the list of package cpus for processor %" PRIu32 " from %s",
576 			processor,
577 			package_cpus_filename);
578 		return false;
579 	}
580 }
581