SRS开源代码框架,错误类(SrsCplxError)的使用

本章内容解读SRS开源代码框架,无二次开发,以学习交流为目的。

SRS是国人开发的流媒体服务器,C++语言开发,本章使用版本:https://github.com/ossrs/srs/tree/5.0release

目录

    • SRS错误处理模块
    • 源码
    • 调用示例

SRS错误处理模块

SRS定义了一个功能强大的错误类(SrsCplxError),可自定义错误码、错误描述、详细信息、摘要,错误发生的文件名、函数名、行号;可建立连接多个错误类的单链表,形成一个错误栈;为上述功能提供了接口宏。
此外,还自定义了断言宏(srs_assert),在crash时使用backtrace打印回溯调用栈,方便排查bug。

源码

srs_kernel_error.hpp

#ifndef SRS_KERNEL_ERROR_HPP
#define SRS_KERNEL_ERROR_HPP

#include 
#define srs_min(a, b) (((a) < (b))? (a) : (b))
#define srs_max(a, b) (((a) < (b))? (b) : (a))
#define srs_freep(p) \
    delete p; \
    p = NULL; \
    (void)0
// Please use the freepa(T[]) to free an array, otherwise the behavior is undefined.
#define srs_freepa(pa) \
    delete[] pa; \
    pa = NULL; \
    (void)0

/**************************************************/
/* The system error. */
#define SRS_ERRNO_MAP_SYSTEM(XX) \
    XX(ERROR_SOCKET_CREATE                 , 1000, "SocketCreate", "Create socket fd failed") \
    XX(ERROR_SYSTEM_FILE_SETVBUF           , 1096, "FileSetVBuf", "Failed to set file vbuf") \

/**************************************************/
/* RTMP protocol error. */
#define SRS_ERRNO_MAP_RTMP(XX) \
    XX(ERROR_RTMP_PLAIN_REQUIRED           , 2000, "RtmpPlainRequired", "RTMP handshake requires plain text") \
    XX(ERROR_CONTROL_REPUBLISH             , 2999, "RtmpRepublish", "RTMP stream is republished")

// For human readable error generation. Generate integer error code.
#define SRS_ERRNO_GEN(n, v, m, s) n = v,
enum SrsErrorCode {
#ifndef _WIN32
    ERROR_SUCCESS = 0,
#endif
    SRS_ERRNO_MAP_SYSTEM(SRS_ERRNO_GEN)
    SRS_ERRNO_MAP_RTMP(SRS_ERRNO_GEN)
};
#undef SRS_ERRNO_GEN

// The complex error carries code, message, callstack and instant variables,
// which is more strong and easy to locate problem by log,
// 错误类包含代码、消息、调用堆栈和即时变量,更强大,更容易通过日志定位问题,
// please @read https://github.com/ossrs/srs/issues/913
class SrsCplxError
{
private:
    int code;
    SrsCplxError* wrapped;
    std::string msg;
    
    std::string func;
    std::string file;
    int line;
    
    /*SrsContextId*/std::string cid;//上下文ID改为std::string ,简化依赖
    int rerrno;
    
    std::string desc;//详细描述
    std::string _summary;//摘要
private:
    SrsCplxError();
public:
    virtual ~SrsCplxError();
private:
    virtual std::string description();
    virtual std::string summary();
public:
    static SrsCplxError* create(const char* func, const char* file, int line, int code, const char* fmt, ...);
    static SrsCplxError* wrap(const char* func, const char* file, int line, SrsCplxError* err, const char* fmt, ...);
    static SrsCplxError* success();
    static SrsCplxError* copy(SrsCplxError* from);
    static std::string description(SrsCplxError* err);
    static std::string summary(SrsCplxError* err);
    static int error_code(SrsCplxError* err);
    static std::string error_code_str(SrsCplxError* err);
    static std::string error_code_longstr(SrsCplxError* err);
public:
    static void srs_assert(bool expression);
};
typedef SrsCplxError* srs_error_t;

// Error helpers, should use these functions to new or wrap an error.
#define srs_success 0 // SrsCplxError::success()
#define srs_error_new(ret, fmt, ...) SrsCplxError::create(__FUNCTION__, __FILE__, __LINE__, ret, fmt, ##__VA_ARGS__)
#define srs_error_wrap(err, fmt, ...) SrsCplxError::wrap(__FUNCTION__, __FILE__, __LINE__, err, fmt, ##__VA_ARGS__)
#define srs_error_copy(err) SrsCplxError::copy(err)
#define srs_error_desc(err) SrsCplxError::description(err)
#define srs_error_summary(err) SrsCplxError::summary(err)
#define srs_error_code(err) SrsCplxError::error_code(err)
#define srs_error_code_str(err) SrsCplxError::error_code_str(err)
#define srs_error_code_longstr(err) SrsCplxError::error_code_longstr(err)
#define srs_error_reset(err) srs_freep(err); err = srs_success

#ifndef srs_assert
#define srs_assert(expression) SrsCplxError::srs_assert(expression)
#endif

#endif

srs_kernel_error.cpp

#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
using namespace std;

const int maxLogBuf = 4 * 1024 * 1024;

#define SRS_BACKTRACE   //程序崩溃时,打印回溯信息

#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 = "srs_error";

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*/-1;
    }

    char* fmt = addr2line_format(frame, symbol, out_buf, sizeof(out_buf));
    if (fmt != out_buf) {
        return /*ERROR_BACKTRACE_ADDR2LINE*/-2;
    }

    return ERROR_SUCCESS;
#endif
    return /*ERROR_BACKTRACE_PARSE_NOT_SUPPORT*/-3;
}

#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<std::string> asan_logs = srs_string_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_string_starts_with(srs_string_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

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_longstr(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;
    err->cid = "create";

    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);
    
    SrsCplxError* err = new SrsCplxError();
    
    err->func = func;
    err->file = file;
    err->line = line;
    if (v) {
        err->code = v->code;
    }
    err->rerrno = rerrno;
    if (r0 > 0 && r0 < maxLogBuf) {
        err->msg = string(buffer, r0);
    }
    err->wrapped = v;
    err->cid = "wrap";

    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)
};
#undef SRS_STRERRNO_GEN

std::string SrsCplxError::error_code_str(SrsCplxError* err)
{
    static string not_found = "";
    static std::map<SrsErrorCode, string> 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<SrsErrorCode, string>::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_longstr(SrsCplxError* err)
{
    static string not_found = "";
    static std::map<SrsErrorCode, string> 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<SrsErrorCode, string>::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)//crash时打印回溯信息
    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*/printf("backtrace %d frames of %s %s\n", 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*/printf("#%d %p %d %s\n", i, frame, parsed, fmt);
        }

        free(symbols);
    }
#endif

    assert(expression);
}

调用示例

#include "srs_kernel_error.hpp"

    srs_error_t err = srs_error_new(ERROR_SOCKET_CREATE, "name=%s,age=%d","chw",18);
    printf("srs_error_desc=%s\n",srs_error_desc(err).c_str());
    printf("srs_error_summary=%s\n",srs_error_summary(err).c_str());
    printf("srs_error_code_str=%s\n",srs_error_code_str(err).c_str());
    printf("srs_error_code_longstr=%s\n",srs_error_code_longstr(err).c_str());

    srs_error_t err2 = srs_error_wrap(err,"add err!");
    printf("srs_error_desc2=%s\n",srs_error_desc(err2).c_str());

    printf("\nsrs_assert:\n");
    srs_assert(0);

打印

srs_error_desc=code=1000(SocketCreate)(Create socket fd failed) : name=chw,age=18
thread [13422][create]: MainWindow() [../srs_error/mainwindow.cpp:13][errno=2]
srs_error_summary=code=1000(SocketCreate) : name=chw,age=18
srs_error_code_str=SocketCreate
srs_error_code_longstr=Create socket fd failed
srs_error_desc2=code=1000(SocketCreate)(Create socket fd failed) : add err! : name=chw,age=18
thread [13422][wrap]: MainWindow() [../srs_error/mainwindow.cpp:19][errno=2]
thread [13422][create]: MainWindow() [../srs_error/mainwindow.cpp:13][errno=2]

srs_assert:
backtrace 5 frames of srs_error RTMP_SIG_SRS_SERVER
#0 0x72a4 1 0x00000000000072a3: SrsCplxError::srs_assert(bool) 于 srs_kernel_error.cpp:388
#1 0x4e7d 1 0x0000000000004e7c: MainWindow::MainWindow(QWidget*) 于 mainwindow.cpp:23
#2 0x4b86 1 0x0000000000004b85: main 于 main.cpp:8
#3 0x28565 0 /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xd5) [0x7f457d79c565]
#4 0x4a7e 0 /home/chw/TempCode/srstest/srs_error/build-srs_error-Desktop_Qt_5_14_2_GCC_64bit-Debug/bin/srs_error(+0x4a7e) [0x5608701c4a7e]
srs_error: ../srs_error/srs_kernel_error.cpp:407static void SrsCplxError::srs_assert(bool): 假设 ‘expression’ 失败。
15:35:30: 程序异常结束。

你可能感兴趣的:(C/C++,c++)