/* SPDX-License-Identifier: MIT */ /* Copyright (c) 2013-2024 The SRS Authors */ #ifdef __linux__ #define _GNU_SOURCE #endif #include #include #include #include #ifdef __linux__ #include #include #include void* parse_symbol_offset(char* frame) { char* p = NULL; char* p_symbol = NULL; int nn_symbol = 0; char* p_offset = NULL; int nn_offset = 0; // Read symbol and offset, for example: // /tools/backtrace(foo+0x1820) [0x555555555820] for (p = frame; *p; p++) { if (*p == '(') { p_symbol = p + 1; } else if (*p == '+') { if (p_symbol) nn_symbol = p - p_symbol; p_offset = p + 1; } else if (*p == ')') { if (p_offset) nn_offset = p - p_offset; } } if (!nn_symbol && !nn_offset) { return NULL; } // Convert offset(0x1820) to pointer, such as 0x1820. char tmp[128]; if (!nn_offset || nn_offset >= sizeof(tmp)) { return NULL; } int r0 = EOF; void* offset = NULL; tmp[nn_offset] = 0; if ((r0 = sscanf(strncpy(tmp, p_offset, nn_offset), "%p", &offset)) == EOF) { return NULL; } // Covert symbol(foo) to offset, such as 0x2fba. if (!nn_symbol || nn_symbol >= sizeof(tmp)) { return offset; } void* object_file; if ((object_file = dlopen(NULL, RTLD_LAZY)) == NULL) { return offset; } void* address; tmp[nn_symbol] = 0; if ((address = dlsym(object_file, strncpy(tmp, p_symbol, nn_symbol))) == NULL) { dlclose(object_file); return offset; } Dl_info symbol_info; if ((r0 = dladdr(address, &symbol_info)) == 0) { dlclose(object_file); return offset; } dlclose(object_file); return symbol_info.dli_saddr - symbol_info.dli_fbase + offset; } char* addr2line_format(void* addr, char* symbol, char* buffer, int nn_buffer) { char cmd[512] = {0}; int r0 = snprintf(cmd, sizeof(cmd), "addr2line -C -p -s -f -a -e %s %p", "backtrace", addr - 1); if (r0 < 0 || r0 >= sizeof(cmd)) return symbol; FILE* fp = popen(cmd, "r"); if (!fp) return symbol; char* p = fgets(buffer, nn_buffer, fp); pclose(fp); if (p == NULL) return symbol; if ((r0 = strlen(p)) == 0) return symbol; // Trait the last newline if exists. if (p[r0 - 1] == '\n') p[r0 - 1] = '\0'; // Find symbol not match by addr2line, like // 0x0000000000021c87: ?? ??:0 // 0x0000000000002ffa: _start at ??:? for (p = buffer; p < buffer + r0 - 1; p++) { if (p[0] == '?' && p[1] == '?') return symbol; } return buffer; } #endif #ifdef __linux__ void bar2() { void* addresses[64]; int nn_addresses = backtrace(addresses, sizeof(addresses) / sizeof(void*)); printf("\naddresses:\n"); for (int i = 0; i < nn_addresses; i++) { printf("%p\n", addresses[i]); } char** symbols = backtrace_symbols(addresses, nn_addresses); printf("\nsymbols:\n"); for (int i = 0; i < nn_addresses; i++) { printf("%s\n", symbols[i]); } char buffer[128]; printf("\nframes:\n"); for (int i = 0; i < nn_addresses; i++) { void* frame = parse_symbol_offset(symbols[i]); char* fmt = addr2line_format(frame, symbols[i], buffer, sizeof(buffer)); int parsed = (fmt == buffer); printf("%p %d %s\n", frame, parsed, fmt); } free(symbols); printf("bar2 OK\n"); return; } #endif int always_use_builtin = 0; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wframe-address" void bar() { // Each item in the array pointed to by buffer is of type void *, and is the return address from the corresponding // stack frame. void* addresses[64]; int nn_addresses = backtrace(addresses, sizeof(addresses) / sizeof(void*)); if (!nn_addresses || always_use_builtin) { printf("Try to get return addresses by __builtin_return_address\n"); void* p = NULL; nn_addresses = 0; if ((p = __builtin_return_address(0)) != NULL) { addresses[nn_addresses++] = p; if ((p = __builtin_return_address(1)) != NULL) { addresses[nn_addresses++] = p; if ((p = __builtin_return_address(2)) != NULL) { addresses[nn_addresses++] = p; if ((p = __builtin_return_address(3)) != NULL) { addresses[nn_addresses++] = p; if ((p = __builtin_return_address(4)) != NULL) { addresses[nn_addresses++] = p; if ((p = __builtin_return_address(5)) != NULL) { addresses[nn_addresses++] = p; } } } } } } } char** symbols = backtrace_symbols(addresses, nn_addresses); printf("nn_addresses=%d, symbols=%p, symbols[0]=%p\n", nn_addresses, symbols, symbols[0]); printf("\naddresses:\n"); for (int i = 0; i < nn_addresses; i++) { printf("%p\n", addresses[i]); } printf("\nsymbols:\n"); for (int i = 0; i < nn_addresses; i++) { printf("%s\n", symbols[i]); } free(symbols); printf("bar OK\n"); return; } #pragma GCC diagnostic pop void foo() { bar(); #ifdef __linux__ bar2(); #endif printf("foo OK\n"); return; } void* start(void* arg) { foo(); printf("coroutine OK\n"); return NULL; } int main(int argc, char** argv) { if (argc > 1) { always_use_builtin = 1; } st_init(); st_thread_create(start, NULL, 0, 0); st_thread_exit(NULL); printf("main done\n"); return 0; }