// // Copyright (c) 2013-2025 The SRS Authors // // SPDX-License-Identifier: MIT // #include #include #include #include #include #include #include #include #include #include using namespace std; const int maxLogBuf = 4 * 1024 * 1024; #if defined(SRS_BACKTRACE) && defined(__linux) #include #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 >= (int)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 >= (int)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 (char *)symbol_info.dli_saddr - (char *)symbol_info.dli_fbase + (char *)offset; } extern const char *_srs_binary; 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", _srs_binary, (void *)((char *)addr - 1)); if (r0 < 0 || r0 >= (int)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 int srs_parse_asan_backtrace_symbols(char *symbol, char *out_buf) { #if defined(SRS_BACKTRACE) && defined(__linux) void *frame = parse_symbol_offset(symbol); if (!frame) { return ERROR_BACKTRACE_PARSE_OFFSET; } char *fmt = addr2line_format(frame, symbol, out_buf, sizeof(out_buf)); if (fmt != out_buf) { return ERROR_BACKTRACE_ADDR2LINE; } return ERROR_SUCCESS; #endif return ERROR_BACKTRACE_PARSE_NOT_SUPPORT; } #ifdef SRS_SANITIZER_LOG void asan_report_callback(const char *str) { static char buf[256]; // No error code for assert failed. errno = 0; std::vector asan_logs = srs_strings_split(string(str), "\n"); size_t log_count = asan_logs.size(); for (size_t i = 0; i < log_count; i++) { std::string log = asan_logs[i]; if (!srs_strings_starts_with(srs_strings_trim_start(log, " "), "#")) { srs_error("%s", log.c_str()); continue; } buf[0] = 0; int r0 = srs_parse_asan_backtrace_symbols((char *)log.c_str(), buf); if (r0 != ERROR_SUCCESS) { srs_error("%s, r0=%d", log.c_str(), r0); } else { srs_error("%s, %s", log.c_str(), buf); } } } #endif #ifdef SRS_SANITIZER // This function return the default options for asan, before main() is called, // see https://github.com/google/sanitizers/wiki/AddressSanitizerFlags#run-time-flags // // Disable halt on errors by halt_on_error, only print messages, note that it still quit for fatal errors, // see https://github.com/google/sanitizers/wiki/AddressSanitizerFlags // // Disable the memory leaking detect for daemon by detect_leaks, // see https://github.com/google/sanitizers/wiki/SanitizerCommonFlags // // Also disable alloc_dealloc_mismatch for gdb. extern "C" const char *__asan_default_options() { return "halt_on_error=0:detect_leaks=0:alloc_dealloc_mismatch=0"; } #endif bool srs_is_system_control_error(srs_error_t err) { int error_code = srs_error_code(err); return error_code == ERROR_CONTROL_RTMP_CLOSE || error_code == ERROR_CONTROL_REPUBLISH || error_code == ERROR_CONTROL_REDIRECT; } bool srs_is_client_gracefully_close(srs_error_t err) { int error_code = srs_error_code(err); return error_code == ERROR_SOCKET_READ || error_code == ERROR_SOCKET_READ_FULLY || error_code == ERROR_SOCKET_WRITE // For RTMP || error_code == ERROR_SRT_IO // For SRT ; } bool srs_is_server_gracefully_close(srs_error_t err) { int code = srs_error_code(err); return code == ERROR_HTTP_STREAM_EOF; } SrsCplxError::SrsCplxError() { code_ = ERROR_SUCCESS; wrapped_ = NULL; rerrno_ = line_ = 0; } SrsCplxError::~SrsCplxError() { srs_freep(wrapped_); } std::string SrsCplxError::description() { if (desc_.empty()) { stringstream ss; ss << "code=" << code_; string code_str = srs_error_code_str(this); if (!code_str.empty()) ss << "(" << code_str << ")"; string code_longstr = srs_error_code_strlong(this); if (!code_longstr.empty()) ss << "(" << code_longstr << ")"; SrsCplxError *next = this; while (next) { ss << " : " << next->msg_; next = next->wrapped_; } ss << endl; next = this; while (next) { ss << "thread [" << getpid() << "][" << next->cid_.c_str() << "]: " << next->func_ << "() [" << next->file_ << ":" << next->line_ << "]" << "[errno=" << next->rerrno_ << "]"; next = next->wrapped_; if (next) { ss << endl; } } desc_ = ss.str(); } return desc_; } std::string SrsCplxError::summary() { if (summary_.empty()) { stringstream ss; ss << "code=" << code_; string code_str = srs_error_code_str(this); if (!code_str.empty()) ss << "(" << code_str << ")"; SrsCplxError *next = this; while (next) { ss << " : " << next->msg_; next = next->wrapped_; } summary_ = ss.str(); } return summary_; } SrsCplxError *SrsCplxError::create(const char *func, const char *file, int line, int code, const char *fmt, ...) { int rerrno = (int)errno; va_list ap; va_start(ap, fmt); static char *buffer = new char[maxLogBuf]; int r0 = vsnprintf(buffer, maxLogBuf, fmt, ap); va_end(ap); SrsCplxError *err = new SrsCplxError(); err->func_ = func; err->file_ = file; err->line_ = line; err->code_ = code; err->rerrno_ = rerrno; if (r0 > 0 && r0 < maxLogBuf) { err->msg_ = string(buffer, r0); } err->wrapped_ = NULL; if (_srs_context) { err->cid_ = _srs_context->get_id(); } return err; } SrsCplxError *SrsCplxError::wrap(const char *func, const char *file, int line, SrsCplxError *v, const char *fmt, ...) { int rerrno = (int)errno; va_list ap; va_start(ap, fmt); static char *buffer = new char[maxLogBuf]; int r0 = vsnprintf(buffer, maxLogBuf, fmt, ap); va_end(ap); // Ensure buffer is null-terminated even if vsnprintf failed or truncated. if (r0 < 0) { buffer[0] = '\0'; } else if (r0 >= maxLogBuf) { buffer[maxLogBuf - 1] = '\0'; } SrsCplxError *err = new SrsCplxError(); err->func_ = func; err->file_ = file; err->line_ = line; err->code_ = srs_error_code(v); err->rerrno_ = rerrno; err->msg_ = string(buffer); err->wrapped_ = v; if (_srs_context) { err->cid_ = _srs_context->get_id(); } return err; } SrsCplxError *SrsCplxError::transform(const char *func, const char *file, int line, int code, SrsCplxError *v, const char *fmt, ...) { va_list ap; va_start(ap, fmt); static char *buffer = new char[maxLogBuf]; int r0 = vsnprintf(buffer, maxLogBuf, fmt, ap); va_end(ap); // Ensure buffer is null-terminated even if vsnprintf failed or truncated. if (r0 < 0) { buffer[0] = '\0'; } else if (r0 >= maxLogBuf) { buffer[maxLogBuf - 1] = '\0'; } // Wrap the error with additional context showing the code transformation. SrsCplxError *err = NULL; err = wrap(func, file, line, v, "code transformed (%d => %d): %s", srs_error_code(v), code, buffer); err->code_ = code; return err; } SrsCplxError *SrsCplxError::success() { return NULL; } SrsCplxError *SrsCplxError::copy(SrsCplxError *from) { if (from == srs_success) { return srs_success; } SrsCplxError *err = new SrsCplxError(); err->code_ = from->code_; err->wrapped_ = srs_error_copy(from->wrapped_); err->msg_ = from->msg_; err->func_ = from->func_; err->file_ = from->file_; err->line_ = from->line_; err->cid_ = from->cid_; err->rerrno_ = from->rerrno_; err->desc_ = from->desc_; return err; } string SrsCplxError::description(SrsCplxError *err) { return err ? err->description() : "Success"; } string SrsCplxError::summary(SrsCplxError *err) { return err ? err->summary() : "Success"; } int SrsCplxError::error_code(SrsCplxError *err) { return err ? err->code_ : ERROR_SUCCESS; } #define SRS_STRERRNO_GEN(n, v, m, s) {(SrsErrorCode)v, m, s}, static struct { SrsErrorCode code; const char *name; const char *descripton; } _srs_strerror_tab[] = { #ifndef _WIN32 {ERROR_SUCCESS, "Success", "Success"}, #endif SRS_ERRNO_MAP_SYSTEM(SRS_STRERRNO_GEN) SRS_ERRNO_MAP_RTMP(SRS_STRERRNO_GEN) SRS_ERRNO_MAP_APP(SRS_STRERRNO_GEN) SRS_ERRNO_MAP_HTTP(SRS_STRERRNO_GEN) SRS_ERRNO_MAP_RTC(SRS_STRERRNO_GEN) SRS_ERRNO_MAP_SRT(SRS_STRERRNO_GEN) SRS_ERRNO_MAP_USER(SRS_STRERRNO_GEN)}; #undef SRS_STRERRNO_GEN std::string SrsCplxError::error_code_str(SrsCplxError *err) { static string not_found = ""; static std::map error_map; // Build map if empty. if (error_map.empty()) { for (int i = 0; i < (int)(sizeof(_srs_strerror_tab) / sizeof(_srs_strerror_tab[0])); i++) { SrsErrorCode code = _srs_strerror_tab[i].code; error_map[code] = _srs_strerror_tab[i].name; } } std::map::iterator it = error_map.find((SrsErrorCode)srs_error_code(err)); if (it == error_map.end()) { return not_found; } return it->second; } std::string SrsCplxError::error_code_strlong(SrsCplxError *err) { static string not_found = ""; static std::map error_map; // Build map if empty. if (error_map.empty()) { for (int i = 0; i < (int)(sizeof(_srs_strerror_tab) / sizeof(_srs_strerror_tab[0])); i++) { SrsErrorCode code = _srs_strerror_tab[i].code; error_map[code] = _srs_strerror_tab[i].descripton; } } std::map::iterator it = error_map.find((SrsErrorCode)srs_error_code(err)); if (it == error_map.end()) { return not_found; } return it->second; } void SrsCplxError::srs_assert(bool expression) { #if defined(SRS_BACKTRACE) && defined(__linux) if (!expression) { void *addresses[64]; int nn_addresses = backtrace(addresses, sizeof(addresses) / sizeof(void *)); char **symbols = backtrace_symbols(addresses, nn_addresses); // No error code for assert failed. errno = 0; char buffer[128]; srs_error("backtrace %d frames of %s %s", nn_addresses, _srs_binary, RTMP_SIG_SRS_SERVER); 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); srs_error("#%d %p %d %s", i, frame, parsed, fmt); } free(symbols); } #endif assert(expression); }