Line data Source code
1 : /**
2 : * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 : * SPDX-License-Identifier: Apache-2.0.
4 : */
5 :
6 : #include <aws/common/system_info.h>
7 :
8 : #include <aws/common/byte_buf.h>
9 : #include <aws/common/logging.h>
10 : #include <aws/common/platform.h>
11 : #include <aws/common/private/dlloads.h>
12 :
13 : #if defined(__FreeBSD__) || defined(__NetBSD__)
14 : # define __BSD_VISIBLE 1
15 : #endif
16 :
17 : #include <unistd.h>
18 :
19 : #if defined(HAVE_SYSCONF)
20 0 : size_t aws_system_info_processor_count(void) {
21 0 : long nprocs = sysconf(_SC_NPROCESSORS_ONLN);
22 0 : if (AWS_LIKELY(nprocs >= 0)) {
23 0 : return (size_t)nprocs;
24 0 : }
25 0 :
26 0 : AWS_FATAL_POSTCONDITION(nprocs >= 0);
27 0 : return 0;
28 0 : }
29 : #else
30 : size_t aws_system_info_processor_count(void) {
31 : # if defined(AWS_NUM_CPU_CORES)
32 : AWS_FATAL_PRECONDITION(AWS_NUM_CPU_CORES > 0);
33 : return AWS_NUM_CPU_CORES;
34 : # else
35 : return 1;
36 : # endif
37 : }
38 : #endif
39 :
40 : #include <ctype.h>
41 : #include <fcntl.h>
42 :
43 0 : uint16_t aws_get_cpu_group_count(void) {
44 0 : if (g_numa_num_configured_nodes_ptr) {
45 0 : return (uint16_t)g_numa_num_configured_nodes_ptr();
46 0 : }
47 0 :
48 0 : return 1u;
49 0 : }
50 :
51 0 : size_t aws_get_cpu_count_for_group(uint16_t group_idx) {
52 0 : if (g_numa_node_of_cpu_ptr) {
53 0 : size_t total_cpus = aws_system_info_processor_count();
54 0 :
55 0 : uint16_t cpu_count = 0;
56 0 : for (size_t i = 0; i < total_cpus; ++i) {
57 0 : if (group_idx == g_numa_node_of_cpu_ptr((int)i)) {
58 0 : cpu_count++;
59 0 : }
60 0 : }
61 0 : return cpu_count;
62 0 : }
63 0 :
64 0 : return aws_system_info_processor_count();
65 0 : }
66 :
67 0 : void aws_get_cpu_ids_for_group(uint16_t group_idx, struct aws_cpu_info *cpu_ids_array, size_t cpu_ids_array_length) {
68 0 : AWS_PRECONDITION(cpu_ids_array);
69 0 :
70 0 : if (!cpu_ids_array_length) {
71 0 : return;
72 0 : }
73 0 :
74 0 : /* go ahead and initialize everything. */
75 0 : for (size_t i = 0; i < cpu_ids_array_length; ++i) {
76 0 : cpu_ids_array[i].cpu_id = -1;
77 0 : cpu_ids_array[i].suspected_hyper_thread = false;
78 0 : }
79 0 :
80 0 : if (g_numa_node_of_cpu_ptr) {
81 0 : size_t total_cpus = aws_system_info_processor_count();
82 0 : size_t current_array_idx = 0;
83 0 : for (size_t i = 0; i < total_cpus && current_array_idx < cpu_ids_array_length; ++i) {
84 0 : if ((int)group_idx == g_numa_node_of_cpu_ptr((int)i)) {
85 0 : cpu_ids_array[current_array_idx].cpu_id = (int32_t)i;
86 0 :
87 0 : /* looking for an index jump is a more reliable way to find these. If they're in the group and then
88 0 : * the index jumps, say from 17 to 36, we're most-likely in hyper-thread land. Also, inside a node,
89 0 : * once we find the first hyper-thread, the remaining cores are also likely hyper threads. */
90 0 : if (current_array_idx > 0 && (cpu_ids_array[current_array_idx - 1].suspected_hyper_thread ||
91 0 : cpu_ids_array[current_array_idx - 1].cpu_id < ((int)i - 1))) {
92 0 : cpu_ids_array[current_array_idx].suspected_hyper_thread = true;
93 0 : }
94 0 : current_array_idx += 1;
95 0 : }
96 0 : }
97 0 :
98 0 : return;
99 0 : }
100 0 :
101 0 : /* a crude hint, but hyper-threads are numbered as the second half of the cpu id listing. The assumption if you
102 0 : * hit here is that this is just listing all cpus on the system. */
103 0 : size_t hyper_thread_hint = cpu_ids_array_length / 2 - 1;
104 0 :
105 0 : for (size_t i = 0; i < cpu_ids_array_length; ++i) {
106 0 : cpu_ids_array[i].cpu_id = (int32_t)i;
107 0 : cpu_ids_array[i].suspected_hyper_thread = i > hyper_thread_hint;
108 0 : }
109 0 : }
110 :
111 0 : bool aws_is_debugger_present(void) {
112 0 : /* Open the status file */
113 0 : const int status_fd = open("/proc/self/status", O_RDONLY);
114 0 : if (status_fd == -1) {
115 0 : return false;
116 0 : }
117 0 :
118 0 : /* Read its contents */
119 0 : char buf[4096];
120 0 : const ssize_t num_read = read(status_fd, buf, sizeof(buf) - 1);
121 0 : close(status_fd);
122 0 : if (num_read <= 0) {
123 0 : return false;
124 0 : }
125 0 : buf[num_read] = '\0';
126 0 :
127 0 : /* Search for the TracerPid field, which will indicate the debugger process */
128 0 : const char tracerPidString[] = "TracerPid:";
129 0 : const char *tracer_pid = strstr(buf, tracerPidString);
130 0 : if (!tracer_pid) {
131 0 : return false;
132 0 : }
133 0 :
134 0 : /* If it's not 0, then there's a debugger */
135 0 : for (const char *cur = tracer_pid + sizeof(tracerPidString) - 1; cur <= buf + num_read; ++cur) {
136 0 : if (!aws_isspace(*cur)) {
137 0 : return aws_isdigit(*cur) && *cur != '0';
138 0 : }
139 0 : }
140 0 :
141 0 : return false;
142 0 : }
143 :
144 : #include <signal.h>
145 :
146 : #ifndef __has_builtin
147 : # define __has_builtin(x) 0
148 : #endif
149 :
150 0 : void aws_debug_break(void) {
151 0 : #ifdef DEBUG_BUILD
152 0 : if (aws_is_debugger_present()) {
153 0 : # if __has_builtin(__builtin_debugtrap)
154 0 : __builtin_debugtrap();
155 : # else
156 : raise(SIGTRAP);
157 : # endif
158 : }
159 0 : #endif /* DEBUG_BUILD */
160 0 : }
161 :
162 : #if defined(AWS_HAVE_EXECINFO)
163 : # include <execinfo.h>
164 : # include <limits.h>
165 :
166 0 : # define AWS_BACKTRACE_DEPTH 128
167 :
168 : struct aws_stack_frame_info {
169 : char exe[PATH_MAX];
170 : char addr[32];
171 : char base[32]; /* base addr for dylib/exe */
172 : char function[128];
173 : };
174 :
175 : /* Ensure only safe characters in a path buffer in case someone tries to
176 : rename the exe and trigger shell execution via the sub commands used to
177 : resolve symbols */
178 0 : char *s_whitelist_chars(char *path) {
179 0 : char *cur = path;
180 0 : while (*cur) {
181 0 : bool whitelisted = aws_isalnum(*cur) || aws_isspace(*cur) || *cur == '/' || *cur == '_' || *cur == '.' ||
182 0 : (cur > path && *cur == '-');
183 0 : if (!whitelisted) {
184 0 : *cur = '_';
185 0 : }
186 0 : ++cur;
187 0 : }
188 0 : return path;
189 0 : }
190 :
191 : # if defined(__APPLE__)
192 : # include <ctype.h>
193 : # include <dlfcn.h>
194 : # include <mach-o/dyld.h>
195 : static char s_exe_path[PATH_MAX];
196 : static const char *s_get_executable_path() {
197 : static const char *s_exe = NULL;
198 : if (AWS_LIKELY(s_exe)) {
199 : return s_exe;
200 : }
201 : uint32_t len = sizeof(s_exe_path);
202 : if (!_NSGetExecutablePath(s_exe_path, &len)) {
203 : s_exe = s_exe_path;
204 : }
205 : return s_exe;
206 : }
207 : int s_parse_symbol(const char *symbol, void *addr, struct aws_stack_frame_info *frame) {
208 : /* symbols look like: <frame_idx> <exe-or-shared-lib> <addr> <function> + <offset>
209 : */
210 : const char *current_exe = s_get_executable_path();
211 : /* parse exe/shared lib */
212 : const char *exe_start = strstr(symbol, " ");
213 : while (aws_isspace(*exe_start)) {
214 : ++exe_start;
215 : }
216 : const char *exe_end = strstr(exe_start, " ");
217 : strncpy(frame->exe, exe_start, exe_end - exe_start);
218 : /* executables get basename'd, so restore the path */
219 : if (strstr(current_exe, frame->exe)) {
220 : strncpy(frame->exe, current_exe, strlen(current_exe));
221 : }
222 : s_whitelist_chars(frame->exe);
223 :
224 : /* parse addr */
225 : const char *addr_start = strstr(exe_end, "0x");
226 : const char *addr_end = strstr(addr_start, " ");
227 : strncpy(frame->addr, addr_start, addr_end - addr_start);
228 :
229 : /* parse function */
230 : const char *function_start = strstr(addr_end, " ") + 1;
231 : const char *function_end = strstr(function_start, " ");
232 : /* truncate function name if needed */
233 : size_t function_len = function_end - function_start;
234 : if (function_len >= (sizeof(frame->function) - 1)) {
235 : function_len = sizeof(frame->function) - 1;
236 : }
237 : strncpy(frame->function, function_start, function_end - function_start);
238 :
239 : /* find base addr for library/exe */
240 : Dl_info addr_info;
241 : dladdr(addr, &addr_info);
242 : snprintf(frame->base, sizeof(frame->base), "0x%p", addr_info.dli_fbase);
243 :
244 : return AWS_OP_SUCCESS;
245 : }
246 :
247 : void s_resolve_cmd(char *cmd, size_t len, struct aws_stack_frame_info *frame) {
248 : snprintf(cmd, len, "atos -o %s -l %s %s", frame->exe, frame->base, frame->addr);
249 : }
250 : # else
251 0 : int s_parse_symbol(const char *symbol, void *addr, struct aws_stack_frame_info *frame) {
252 0 : /* symbols look like: <exe-or-shared-lib>(<function>+<addr>) [0x<addr>]
253 0 : * or: <exe-or-shared-lib> [0x<addr>]
254 0 : * or: [0x<addr>]
255 0 : */
256 0 : (void)addr;
257 0 : const char *open_paren = strstr(symbol, "(");
258 0 : const char *close_paren = strstr(symbol, ")");
259 0 : const char *exe_end = open_paren;
260 0 : /* there may not be a function in parens, or parens at all */
261 0 : if (open_paren == NULL || close_paren == NULL) {
262 0 : exe_end = strstr(symbol, "[");
263 0 : if (!exe_end) {
264 0 : return AWS_OP_ERR;
265 0 : }
266 0 : /* if exe_end == symbol, there's no exe */
267 0 : if (exe_end != symbol) {
268 0 : exe_end -= 1;
269 0 : }
270 0 : }
271 0 :
272 0 : ptrdiff_t exe_len = exe_end - symbol;
273 0 : if (exe_len > 0) {
274 0 : strncpy(frame->exe, symbol, exe_len);
275 0 : }
276 0 : s_whitelist_chars(frame->exe);
277 0 :
278 0 : long function_len = (open_paren && close_paren) ? close_paren - open_paren - 1 : 0;
279 0 : if (function_len > 0) { /* dynamic symbol was found */
280 0 : /* there might be (<function>+<addr>) or just (<function>) */
281 0 : const char *function_start = open_paren + 1;
282 0 : const char *plus = strstr(function_start, "+");
283 0 : const char *function_end = (plus) ? plus : close_paren;
284 0 : if (function_end > function_start) {
285 0 : function_len = function_end - function_start;
286 0 : strncpy(frame->function, function_start, function_len);
287 0 : } else if (plus) {
288 0 : long addr_len = close_paren - plus - 1;
289 0 : strncpy(frame->addr, plus + 1, addr_len);
290 0 : }
291 0 : }
292 0 : if (frame->addr[0] == 0) {
293 0 : /* use the address in []'s, since it's all we have */
294 0 : const char *addr_start = strstr(exe_end, "[") + 1;
295 0 : char *addr_end = strstr(addr_start, "]");
296 0 : if (!addr_end) {
297 0 : return AWS_OP_ERR;
298 0 : }
299 0 : strncpy(frame->addr, addr_start, addr_end - addr_start);
300 0 : }
301 0 :
302 0 : return AWS_OP_SUCCESS;
303 0 : }
304 0 : void s_resolve_cmd(char *cmd, size_t len, struct aws_stack_frame_info *frame) {
305 0 : snprintf(cmd, len, "addr2line -afips -e %s %s", frame->exe, frame->addr);
306 0 : }
307 : # endif
308 :
309 0 : size_t aws_backtrace(void **stack_frames, size_t num_frames) {
310 0 : return backtrace(stack_frames, (int)aws_min_size(num_frames, INT_MAX));
311 0 : }
312 :
313 0 : char **aws_backtrace_symbols(void *const *stack_frames, size_t stack_depth) {
314 0 : return backtrace_symbols(stack_frames, (int)aws_min_size(stack_depth, INT_MAX));
315 0 : }
316 :
317 0 : char **aws_backtrace_addr2line(void *const *stack_frames, size_t stack_depth) {
318 0 : char **symbols = aws_backtrace_symbols(stack_frames, stack_depth);
319 0 : AWS_FATAL_ASSERT(symbols);
320 0 : struct aws_byte_buf lines;
321 0 : aws_byte_buf_init(&lines, aws_default_allocator(), stack_depth * 256);
322 0 :
323 0 : /* insert pointers for each stack entry */
324 0 : memset(lines.buffer, 0, stack_depth * sizeof(void *));
325 0 : lines.len += stack_depth * sizeof(void *);
326 0 :
327 0 : /* symbols look like: <exe-or-shared-lib>(<function>+<addr>) [0x<addr>]
328 0 : * or: <exe-or-shared-lib> [0x<addr>]
329 0 : * start at 1 to skip the current frame (this function) */
330 0 : for (size_t frame_idx = 0; frame_idx < stack_depth; ++frame_idx) {
331 0 : struct aws_stack_frame_info frame;
332 0 : AWS_ZERO_STRUCT(frame);
333 0 : const char *symbol = symbols[frame_idx];
334 0 : if (s_parse_symbol(symbol, stack_frames[frame_idx], &frame)) {
335 0 : goto parse_failed;
336 0 : }
337 0 :
338 0 : /* TODO: Emulate libunwind */
339 0 : char cmd[sizeof(struct aws_stack_frame_info)] = {0};
340 0 : s_resolve_cmd(cmd, sizeof(cmd), &frame);
341 0 : FILE *out = popen(cmd, "r");
342 0 : if (!out) {
343 0 : goto parse_failed;
344 0 : }
345 0 : char output[1024];
346 0 : if (fgets(output, sizeof(output), out)) {
347 0 : /* if addr2line or atos don't know what to do with an address, they just echo it */
348 0 : /* if there are spaces in the output, then they resolved something */
349 0 : if (strstr(output, " ")) {
350 0 : symbol = output;
351 0 : }
352 0 : }
353 0 : pclose(out);
354 0 :
355 0 : parse_failed:
356 0 : /* record the pointer to where the symbol will be */
357 0 : *((char **)&lines.buffer[frame_idx * sizeof(void *)]) = (char *)lines.buffer + lines.len;
358 0 : struct aws_byte_cursor line_cursor = aws_byte_cursor_from_c_str(symbol);
359 0 : line_cursor.len += 1; /* strings must be null terminated, make sure we copy the null */
360 0 : aws_byte_buf_append_dynamic(&lines, &line_cursor);
361 0 : }
362 0 : free(symbols);
363 0 : return (char **)lines.buffer; /* caller is responsible for freeing */
364 0 : }
365 :
366 0 : void aws_backtrace_print(FILE *fp, void *call_site_data) {
367 0 : siginfo_t *siginfo = call_site_data;
368 0 : if (siginfo) {
369 0 : fprintf(fp, "Signal received: %d, errno: %d\n", siginfo->si_signo, siginfo->si_errno);
370 0 : if (siginfo->si_signo == SIGSEGV) {
371 0 : fprintf(fp, " SIGSEGV @ 0x%p\n", siginfo->si_addr);
372 0 : }
373 0 : }
374 0 :
375 0 : void *stack_frames[AWS_BACKTRACE_DEPTH];
376 0 : size_t stack_depth = aws_backtrace(stack_frames, AWS_BACKTRACE_DEPTH);
377 0 : char **symbols = aws_backtrace_symbols(stack_frames, stack_depth);
378 0 : if (symbols == NULL) {
379 0 : fprintf(fp, "Unable to decode backtrace via backtrace_symbols\n");
380 0 : return;
381 0 : }
382 0 :
383 0 : fprintf(fp, "################################################################################\n");
384 0 : fprintf(fp, "Resolved stacktrace:\n");
385 0 : fprintf(fp, "################################################################################\n");
386 0 : /* symbols look like: <exe-or-shared-lib>(<function>+<addr>) [0x<addr>]
387 0 : * or: <exe-or-shared-lib> [0x<addr>]
388 0 : * or: [0x<addr>]
389 0 : * start at 1 to skip the current frame (this function) */
390 0 : for (size_t frame_idx = 1; frame_idx < stack_depth; ++frame_idx) {
391 0 : struct aws_stack_frame_info frame;
392 0 : AWS_ZERO_STRUCT(frame);
393 0 : const char *symbol = symbols[frame_idx];
394 0 : if (s_parse_symbol(symbol, stack_frames[frame_idx], &frame)) {
395 0 : goto parse_failed;
396 0 : }
397 0 :
398 0 : /* TODO: Emulate libunwind */
399 0 : char cmd[sizeof(struct aws_stack_frame_info)] = {0};
400 0 : s_resolve_cmd(cmd, sizeof(cmd), &frame);
401 0 : FILE *out = popen(cmd, "r");
402 0 : if (!out) {
403 0 : goto parse_failed;
404 0 : }
405 0 : char output[1024];
406 0 : if (fgets(output, sizeof(output), out)) {
407 0 : /* if addr2line or atos don't know what to do with an address, they just echo it */
408 0 : /* if there are spaces in the output, then they resolved something */
409 0 : if (strstr(output, " ")) {
410 0 : symbol = output;
411 0 : }
412 0 : }
413 0 : pclose(out);
414 0 :
415 0 : parse_failed:
416 0 : fprintf(fp, "%s%s", symbol, (symbol == symbols[frame_idx]) ? "\n" : "");
417 0 : }
418 0 :
419 0 : fprintf(fp, "################################################################################\n");
420 0 : fprintf(fp, "Raw stacktrace:\n");
421 0 : fprintf(fp, "################################################################################\n");
422 0 : for (size_t frame_idx = 1; frame_idx < stack_depth; ++frame_idx) {
423 0 : const char *symbol = symbols[frame_idx];
424 0 : fprintf(fp, "%s\n", symbol);
425 0 : }
426 0 : fflush(fp);
427 0 :
428 0 : free(symbols);
429 0 : }
430 :
431 : #else
432 : void aws_backtrace_print(FILE *fp, void *call_site_data) {
433 : (void)call_site_data;
434 : fprintf(fp, "No call stack information available\n");
435 : }
436 :
437 : size_t aws_backtrace(void **stack_frames, size_t size) {
438 : (void)stack_frames;
439 : (void)size;
440 : return 0;
441 : }
442 :
443 : char **aws_backtrace_symbols(void *const *stack_frames, size_t stack_depth) {
444 : (void)stack_frames;
445 : (void)stack_depth;
446 : return NULL;
447 : }
448 :
449 : char **aws_backtrace_addr2line(void *const *stack_frames, size_t stack_depth) {
450 : (void)stack_frames;
451 : (void)stack_depth;
452 : return NULL;
453 : }
454 : #endif /* AWS_HAVE_EXECINFO */
455 :
456 0 : void aws_backtrace_log() {
457 0 : void *stack_frames[1024];
458 0 : size_t num_frames = aws_backtrace(stack_frames, 1024);
459 0 : if (!num_frames) {
460 0 : return;
461 0 : }
462 0 : char **symbols = aws_backtrace_addr2line(stack_frames, num_frames);
463 0 : for (size_t line = 0; line < num_frames; ++line) {
464 0 : const char *symbol = symbols[line];
465 0 : AWS_LOGF_TRACE(AWS_LS_COMMON_GENERAL, "%s", symbol);
466 0 : }
467 0 : free(symbols);
468 0 : }
469 :
470 : #if defined(AWS_OS_APPLE)
471 : enum aws_platform_os aws_get_platform_build_os(void) {
472 : return AWS_PLATFORM_OS_MAC;
473 : }
474 : #else
475 0 : enum aws_platform_os aws_get_platform_build_os(void) {
476 0 : return AWS_PLATFORM_OS_UNIX;
477 0 : }
478 : #endif /* AWS_OS_APPLE */
|