system_info.c 15 KB

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