LCOV - code coverage report
Current view: top level - aws-c-common/source/posix - system_info.c (source / functions) Hit Total Coverage
Test: all_fuzz.info Lines: 0 316 0.0 %
Date: 2021-04-23 16:28:21 Functions: 0 15 0.0 %

          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 */

Generated by: LCOV version 1.13