• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include <stdbool.h>
2 #include <stdint.h>
3 #include <stdlib.h>
4 #include <stdio.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 <linux/api.h>
16 #include <cpuinfo/log.h>
17 
18 
19 #define STRINGIFY(token) #token
20 
21 #define KERNEL_MAX_FILENAME "/sys/devices/system/cpu/kernel_max"
22 #define KERNEL_MAX_FILESIZE 32
23 #define FREQUENCY_FILENAME_SIZE (sizeof("/sys/devices/system/cpu/cpu" STRINGIFY(UINT32_MAX) "/cpufreq/cpuinfo_max_freq"))
24 #define MAX_FREQUENCY_FILENAME_FORMAT "/sys/devices/system/cpu/cpu%" PRIu32 "/cpufreq/cpuinfo_max_freq"
25 #define MIN_FREQUENCY_FILENAME_FORMAT "/sys/devices/system/cpu/cpu%" PRIu32 "/cpufreq/cpuinfo_min_freq"
26 #define FREQUENCY_FILESIZE 32
27 #define PACKAGE_ID_FILENAME_SIZE (sizeof("/sys/devices/system/cpu/cpu" STRINGIFY(UINT32_MAX) "/topology/physical_package_id"))
28 #define PACKAGE_ID_FILENAME_FORMAT "/sys/devices/system/cpu/cpu%" PRIu32 "/topology/physical_package_id"
29 #define PACKAGE_ID_FILESIZE 32
30 #define CORE_ID_FILENAME_SIZE (sizeof("/sys/devices/system/cpu/cpu" STRINGIFY(UINT32_MAX) "/topology/core_id"))
31 #define CORE_ID_FILENAME_FORMAT "/sys/devices/system/cpu/cpu%" PRIu32 "/topology/core_id"
32 #define CORE_ID_FILESIZE 32
33 
34 #define CORE_SIBLINGS_FILENAME_SIZE (sizeof("/sys/devices/system/cpu/cpu" STRINGIFY(UINT32_MAX) "/topology/core_siblings_list"))
35 #define CORE_SIBLINGS_FILENAME_FORMAT "/sys/devices/system/cpu/cpu%" PRIu32 "/topology/core_siblings_list"
36 #define THREAD_SIBLINGS_FILENAME_SIZE (sizeof("/sys/devices/system/cpu/cpu" STRINGIFY(UINT32_MAX) "/topology/thread_siblings_list"))
37 #define THREAD_SIBLINGS_FILENAME_FORMAT "/sys/devices/system/cpu/cpu%" PRIu32 "/topology/thread_siblings_list"
38 
39 #define POSSIBLE_CPULIST_FILENAME "/sys/devices/system/cpu/possible"
40 #define PRESENT_CPULIST_FILENAME "/sys/devices/system/cpu/present"
41 
42 
parse_number(const char * start,const char * end,uint32_t number_ptr[restrict static1])43 inline static const char* parse_number(const char* start, const char* end, uint32_t number_ptr[restrict static 1]) {
44 	uint32_t number = 0;
45 	const char* parsed = start;
46 	for (; parsed != end; parsed++) {
47 		const uint32_t digit = (uint32_t) (uint8_t) (*parsed) - (uint32_t) '0';
48 		if (digit >= 10) {
49 			break;
50 		}
51 		number = number * UINT32_C(10) + digit;
52 	}
53 	*number_ptr = number;
54 	return parsed;
55 }
56 
57 /* Locale-independent */
is_whitespace(char c)58 inline static bool is_whitespace(char c) {
59 	switch (c) {
60 		case ' ':
61 		case '\t':
62 		case '\n':
63 		case '\r':
64 			return true;
65 		default:
66 			return false;
67 	}
68 }
69 
70 #if defined(__ANDROID__) && !defined(CPU_SETSIZE)
71 	/*
72 	 * Android NDK headers before platform 21 do not define CPU_SETSIZE,
73 	 * so we hard-code its value, as defined in platform 21 headers
74 	 */
75 	#if defined(__LP64__)
76 		static const uint32_t default_max_processors_count = 1024;
77 	#else
78 		static const uint32_t default_max_processors_count = 32;
79 	#endif
80 #else
81 	static const uint32_t default_max_processors_count = CPU_SETSIZE;
82 #endif
83 
uint32_parser(const char * text_start,const char * text_end,void * context)84 static bool uint32_parser(const char* text_start, const char* text_end, void* context) {
85 	if (text_start == text_end) {
86 		cpuinfo_log_error("failed to parse file %s: file is empty", KERNEL_MAX_FILENAME);
87 		return false;
88 	}
89 
90 	uint32_t kernel_max = 0;
91 	const char* parsed_end = parse_number(text_start, text_end, &kernel_max);
92 	if (parsed_end == text_start) {
93 		cpuinfo_log_error("failed to parse file %s: \"%.*s\" is not an unsigned number",
94 			KERNEL_MAX_FILENAME, (int) (text_end - text_start), text_start);
95 		return false;
96 	} else {
97 		for (const char* char_ptr = parsed_end; char_ptr != text_end; char_ptr++) {
98 			if (!is_whitespace(*char_ptr)) {
99 				cpuinfo_log_warning("non-whitespace characters \"%.*s\" following number in file %s are ignored",
100 					(int) (text_end - char_ptr), char_ptr, KERNEL_MAX_FILENAME);
101 				break;
102 			}
103 		}
104 	}
105 
106 	uint32_t* kernel_max_ptr = (uint32_t*) context;
107 	*kernel_max_ptr = kernel_max;
108 	return true;
109 }
110 
cpuinfo_linux_get_max_processors_count(void)111 uint32_t cpuinfo_linux_get_max_processors_count(void) {
112 	uint32_t kernel_max;
113 	if (cpuinfo_linux_parse_small_file(KERNEL_MAX_FILENAME, KERNEL_MAX_FILESIZE, uint32_parser, &kernel_max)) {
114 		cpuinfo_log_debug("parsed kernel_max value of %"PRIu32" from %s", kernel_max, KERNEL_MAX_FILENAME);
115 
116 		if (kernel_max >= default_max_processors_count) {
117 			cpuinfo_log_warning("kernel_max value of %"PRIu32" parsed from %s exceeds platform-default limit %"PRIu32,
118 				kernel_max, KERNEL_MAX_FILENAME, default_max_processors_count - 1);
119 		}
120 
121 		return kernel_max + 1;
122 	} else {
123 		cpuinfo_log_warning("using platform-default max processors count = %"PRIu32, default_max_processors_count);
124 		return default_max_processors_count;
125 	}
126 }
127 
cpuinfo_linux_get_processor_max_frequency(uint32_t processor)128 uint32_t cpuinfo_linux_get_processor_max_frequency(uint32_t processor) {
129 	char max_frequency_filename[FREQUENCY_FILENAME_SIZE];
130 	const int chars_formatted = snprintf(
131 		max_frequency_filename, FREQUENCY_FILENAME_SIZE, MAX_FREQUENCY_FILENAME_FORMAT, processor);
132 	if ((unsigned int) chars_formatted >= FREQUENCY_FILENAME_SIZE) {
133 		cpuinfo_log_warning("failed to format filename for max frequency of processor %"PRIu32, processor);
134 		return 0;
135 	}
136 
137 	uint32_t max_frequency;
138 	if (cpuinfo_linux_parse_small_file(max_frequency_filename, FREQUENCY_FILESIZE, uint32_parser, &max_frequency)) {
139 		cpuinfo_log_debug("parsed max frequency value of %"PRIu32" KHz for logical processor %"PRIu32" from %s",
140 			max_frequency, processor, max_frequency_filename);
141 		return max_frequency;
142 	} else {
143 		cpuinfo_log_warning("failed to parse max frequency for processor %"PRIu32" from %s",
144 			processor, max_frequency_filename);
145 		return 0;
146 	}
147 }
148 
cpuinfo_linux_get_processor_min_frequency(uint32_t processor)149 uint32_t cpuinfo_linux_get_processor_min_frequency(uint32_t processor) {
150 	char min_frequency_filename[FREQUENCY_FILENAME_SIZE];
151 	const int chars_formatted = snprintf(
152 		min_frequency_filename, FREQUENCY_FILENAME_SIZE, MIN_FREQUENCY_FILENAME_FORMAT, processor);
153 	if ((unsigned int) chars_formatted >= FREQUENCY_FILENAME_SIZE) {
154 		cpuinfo_log_warning("failed to format filename for min frequency of processor %"PRIu32, processor);
155 		return 0;
156 	}
157 
158 	uint32_t min_frequency;
159 	if (cpuinfo_linux_parse_small_file(min_frequency_filename, FREQUENCY_FILESIZE, uint32_parser, &min_frequency)) {
160 		cpuinfo_log_debug("parsed min frequency value of %"PRIu32" KHz for logical processor %"PRIu32" from %s",
161 			min_frequency, processor, min_frequency_filename);
162 		return min_frequency;
163 	} else {
164 		/*
165 		 * This error is less severe than parsing max frequency, because min frequency is only useful for clustering,
166 		 * while max frequency is also needed for peak FLOPS calculation.
167 		 */
168 		cpuinfo_log_info("failed to parse min frequency for processor %"PRIu32" from %s",
169 			processor, min_frequency_filename);
170 		return 0;
171 	}
172 }
173 
cpuinfo_linux_get_processor_core_id(uint32_t processor,uint32_t core_id_ptr[restrict static1])174 bool cpuinfo_linux_get_processor_core_id(uint32_t processor, uint32_t core_id_ptr[restrict static 1]) {
175 	char core_id_filename[PACKAGE_ID_FILENAME_SIZE];
176 	const int chars_formatted = snprintf(
177 		core_id_filename, CORE_ID_FILENAME_SIZE, CORE_ID_FILENAME_FORMAT, processor);
178 	if ((unsigned int) chars_formatted >= CORE_ID_FILENAME_SIZE) {
179 		cpuinfo_log_warning("failed to format filename for core id of processor %"PRIu32, processor);
180 		return 0;
181 	}
182 
183 	uint32_t core_id;
184 	if (cpuinfo_linux_parse_small_file(core_id_filename, CORE_ID_FILESIZE, uint32_parser, &core_id)) {
185 		cpuinfo_log_debug("parsed core id value of %"PRIu32" for logical processor %"PRIu32" from %s",
186 			core_id, processor, core_id_filename);
187 		*core_id_ptr = core_id;
188 		return true;
189 	} else {
190 		cpuinfo_log_info("failed to parse core id for processor %"PRIu32" from %s",
191 			processor, core_id_filename);
192 		return false;
193 	}
194 }
195 
cpuinfo_linux_get_processor_package_id(uint32_t processor,uint32_t package_id_ptr[restrict static1])196 bool cpuinfo_linux_get_processor_package_id(uint32_t processor, uint32_t package_id_ptr[restrict static 1]) {
197 	char package_id_filename[PACKAGE_ID_FILENAME_SIZE];
198 	const int chars_formatted = snprintf(
199 		package_id_filename, PACKAGE_ID_FILENAME_SIZE, PACKAGE_ID_FILENAME_FORMAT, processor);
200 	if ((unsigned int) chars_formatted >= PACKAGE_ID_FILENAME_SIZE) {
201 		cpuinfo_log_warning("failed to format filename for package id of processor %"PRIu32, processor);
202 		return 0;
203 	}
204 
205 	uint32_t package_id;
206 	if (cpuinfo_linux_parse_small_file(package_id_filename, PACKAGE_ID_FILESIZE, uint32_parser, &package_id)) {
207 		cpuinfo_log_debug("parsed package id value of %"PRIu32" for logical processor %"PRIu32" from %s",
208 			package_id, processor, package_id_filename);
209 		*package_id_ptr = package_id;
210 		return true;
211 	} else {
212 		cpuinfo_log_info("failed to parse package id for processor %"PRIu32" from %s",
213 			processor, package_id_filename);
214 		return false;
215 	}
216 }
217 
max_processor_number_parser(uint32_t processor_list_start,uint32_t processor_list_end,void * context)218 static bool max_processor_number_parser(uint32_t processor_list_start, uint32_t processor_list_end, void* context) {
219 	uint32_t* processor_number_ptr = (uint32_t*) context;
220 	const uint32_t processor_list_last = processor_list_end - 1;
221 	if (*processor_number_ptr < processor_list_last) {
222 		*processor_number_ptr = processor_list_last;
223 	}
224 	return true;
225 }
226 
cpuinfo_linux_get_max_possible_processor(uint32_t max_processors_count)227 uint32_t cpuinfo_linux_get_max_possible_processor(uint32_t max_processors_count) {
228 	uint32_t max_possible_processor = 0;
229 	if (!cpuinfo_linux_parse_cpulist(POSSIBLE_CPULIST_FILENAME, max_processor_number_parser, &max_possible_processor)) {
230 		#if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64
231 			cpuinfo_log_error("failed to parse the list of possible processors in %s", POSSIBLE_CPULIST_FILENAME);
232 		#else
233 			cpuinfo_log_warning("failed to parse the list of possible processors in %s", POSSIBLE_CPULIST_FILENAME);
234 		#endif
235 		return UINT32_MAX;
236 	}
237 	if (max_possible_processor >= max_processors_count) {
238 		cpuinfo_log_warning(
239 			"maximum possible processor number %"PRIu32" exceeds system limit %"PRIu32": truncating to the latter",
240 			max_possible_processor, max_processors_count - 1);
241 		max_possible_processor = max_processors_count - 1;
242 	}
243 	return max_possible_processor;
244 }
245 
cpuinfo_linux_get_max_present_processor(uint32_t max_processors_count)246 uint32_t cpuinfo_linux_get_max_present_processor(uint32_t max_processors_count) {
247 	uint32_t max_present_processor = 0;
248 	if (!cpuinfo_linux_parse_cpulist(PRESENT_CPULIST_FILENAME, max_processor_number_parser, &max_present_processor)) {
249 		#if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64
250 			cpuinfo_log_error("failed to parse the list of present processors in %s", PRESENT_CPULIST_FILENAME);
251 		#else
252 			cpuinfo_log_warning("failed to parse the list of present processors in %s", PRESENT_CPULIST_FILENAME);
253 		#endif
254 		return UINT32_MAX;
255 	}
256 	if (max_present_processor >= max_processors_count) {
257 		cpuinfo_log_warning(
258 			"maximum present processor number %"PRIu32" exceeds system limit %"PRIu32": truncating to the latter",
259 			max_present_processor, max_processors_count - 1);
260 		max_present_processor = max_processors_count - 1;
261 	}
262 	return max_present_processor;
263 }
264 
265 struct detect_processors_context {
266 	uint32_t max_processors_count;
267 	uint32_t* processor0_flags;
268 	uint32_t processor_struct_size;
269 	uint32_t detected_flag;
270 };
271 
detect_processor_parser(uint32_t processor_list_start,uint32_t processor_list_end,void * context)272 static bool detect_processor_parser(uint32_t processor_list_start, uint32_t processor_list_end, void* context) {
273 	const uint32_t max_processors_count   = ((struct detect_processors_context*) context)->max_processors_count;
274 	const uint32_t* processor0_flags      = ((struct detect_processors_context*) context)->processor0_flags;
275 	const uint32_t processor_struct_size  = ((struct detect_processors_context*) context)->processor_struct_size;
276 	const uint32_t detected_flag          = ((struct detect_processors_context*) context)->detected_flag;
277 
278 	for (uint32_t processor = processor_list_start; processor < processor_list_end; processor++) {
279 		if (processor >= max_processors_count) {
280 			break;
281 		}
282 		*((uint32_t*) ((uintptr_t) processor0_flags + processor_struct_size * processor)) |= detected_flag;
283 	}
284 	return true;
285 }
286 
cpuinfo_linux_detect_possible_processors(uint32_t max_processors_count,uint32_t * processor0_flags,uint32_t processor_struct_size,uint32_t possible_flag)287 bool cpuinfo_linux_detect_possible_processors(uint32_t max_processors_count,
288 	uint32_t* processor0_flags, uint32_t processor_struct_size, uint32_t possible_flag)
289 {
290 	struct detect_processors_context context = {
291 		.max_processors_count = max_processors_count,
292 		.processor0_flags = processor0_flags,
293 		.processor_struct_size = processor_struct_size,
294 		.detected_flag = possible_flag,
295 	};
296 	if (cpuinfo_linux_parse_cpulist(POSSIBLE_CPULIST_FILENAME, detect_processor_parser, &context)) {
297 		return true;
298 	} else {
299 		cpuinfo_log_warning("failed to parse the list of possible processors in %s", POSSIBLE_CPULIST_FILENAME);
300 		return false;
301 	}
302 }
303 
cpuinfo_linux_detect_present_processors(uint32_t max_processors_count,uint32_t * processor0_flags,uint32_t processor_struct_size,uint32_t present_flag)304 bool cpuinfo_linux_detect_present_processors(uint32_t max_processors_count,
305 	uint32_t* processor0_flags, uint32_t processor_struct_size, uint32_t present_flag)
306 {
307 	struct detect_processors_context context = {
308 		.max_processors_count = max_processors_count,
309 		.processor0_flags = processor0_flags,
310 		.processor_struct_size = processor_struct_size,
311 		.detected_flag = present_flag,
312 	};
313 	if (cpuinfo_linux_parse_cpulist(PRESENT_CPULIST_FILENAME, detect_processor_parser, &context)) {
314 		return true;
315 	} else {
316 		cpuinfo_log_warning("failed to parse the list of present processors in %s", PRESENT_CPULIST_FILENAME);
317 		return false;
318 	}
319 }
320 
321 struct siblings_context {
322 	const char* group_name;
323 	uint32_t max_processors_count;
324 	uint32_t processor;
325 	cpuinfo_siblings_callback callback;
326 	void* callback_context;
327 };
328 
siblings_parser(uint32_t sibling_list_start,uint32_t sibling_list_end,struct siblings_context * context)329 static bool siblings_parser(uint32_t sibling_list_start, uint32_t sibling_list_end, struct siblings_context* context) {
330 	const char* group_name                   = context->group_name;
331 	const uint32_t max_processors_count      = context->max_processors_count;
332 	const uint32_t processor                 = context->processor;
333 
334 	if (sibling_list_end > max_processors_count) {
335 		cpuinfo_log_warning("ignore %s siblings %"PRIu32"-%"PRIu32" of processor %"PRIu32,
336 			group_name, max_processors_count, sibling_list_end - 1, processor);
337 		sibling_list_end = max_processors_count;
338 	}
339 
340 	return context->callback(processor, sibling_list_start, sibling_list_end, context->callback_context);
341 }
342 
cpuinfo_linux_detect_core_siblings(uint32_t max_processors_count,uint32_t processor,cpuinfo_siblings_callback callback,void * context)343 bool cpuinfo_linux_detect_core_siblings(
344 	uint32_t max_processors_count,
345 	uint32_t processor,
346 	cpuinfo_siblings_callback callback,
347 	void* context)
348 {
349 	char core_siblings_filename[CORE_SIBLINGS_FILENAME_SIZE];
350 	const int chars_formatted = snprintf(
351 		core_siblings_filename, CORE_SIBLINGS_FILENAME_SIZE, CORE_SIBLINGS_FILENAME_FORMAT, processor);
352 	if ((unsigned int) chars_formatted >= CORE_SIBLINGS_FILENAME_SIZE) {
353 		cpuinfo_log_warning("failed to format filename for core siblings of processor %"PRIu32, processor);
354 		return false;
355 	}
356 
357 	struct siblings_context siblings_context = {
358 		.group_name = "package",
359 		.max_processors_count = max_processors_count,
360 		.processor = processor,
361 		.callback = callback,
362 		.callback_context = context,
363 	};
364 	if (cpuinfo_linux_parse_cpulist(core_siblings_filename,
365 		(cpuinfo_cpulist_callback) siblings_parser, &siblings_context))
366 	{
367 		return true;
368 	} else {
369 		cpuinfo_log_info("failed to parse the list of core siblings for processor %"PRIu32" from %s",
370 			processor, core_siblings_filename);
371 		return false;
372 	}
373 }
374 
cpuinfo_linux_detect_thread_siblings(uint32_t max_processors_count,uint32_t processor,cpuinfo_siblings_callback callback,void * context)375 bool cpuinfo_linux_detect_thread_siblings(
376 	uint32_t max_processors_count,
377 	uint32_t processor,
378 	cpuinfo_siblings_callback callback,
379 	void* context)
380 {
381 	char thread_siblings_filename[THREAD_SIBLINGS_FILENAME_SIZE];
382 	const int chars_formatted = snprintf(
383 		thread_siblings_filename, THREAD_SIBLINGS_FILENAME_SIZE, THREAD_SIBLINGS_FILENAME_FORMAT, processor);
384 	if ((unsigned int) chars_formatted >= THREAD_SIBLINGS_FILENAME_SIZE) {
385 		cpuinfo_log_warning("failed to format filename for thread siblings of processor %"PRIu32, processor);
386 		return false;
387 	}
388 
389 	struct siblings_context siblings_context = {
390 		.group_name = "core",
391 		.max_processors_count = max_processors_count,
392 		.processor = processor,
393 		.callback = callback,
394 		.callback_context = context,
395 	};
396 	if (cpuinfo_linux_parse_cpulist(thread_siblings_filename,
397 		(cpuinfo_cpulist_callback) siblings_parser, &siblings_context))
398 	{
399 		return true;
400 	} else {
401 		cpuinfo_log_info("failed to parse the list of thread siblings for processor %"PRIu32" from %s",
402 			processor, thread_siblings_filename);
403 		return false;
404 	}
405 }
406 
407