pjlib系列之错误errno

pjlib提供了一套统一的错误码,都是正数,并且屏蔽各系统的错误码。pjlib的错误在文件errno.h errno.c compat/errno.h os_core_unix.c。

错误码范围分段


/**
 * PJ_ERRNO_START is where PJLIB specific error values start.
 */
#define PJ_ERRNO_START		20000

/**
 * PJ_ERRNO_SPACE_SIZE is the maximum number of errors in one of 
 * the error/status range below.
 */
#define PJ_ERRNO_SPACE_SIZE	50000

/**
 * PJ_ERRNO_START_STATUS is where PJLIB specific status codes start.
 * Effectively the error in this class would be 70000 - 119000.
 */
#define PJ_ERRNO_START_STATUS	(PJ_ERRNO_START + PJ_ERRNO_SPACE_SIZE)

/**
 * PJ_ERRNO_START_SYS converts platform specific error codes into
 * pj_status_t values.
 * Effectively the error in this class would be 120000 - 169000.
 */
#define PJ_ERRNO_START_SYS	(PJ_ERRNO_START_STATUS + PJ_ERRNO_SPACE_SIZE)

/**
 * PJ_ERRNO_START_USER are reserved for applications that use error
 * codes along with PJLIB codes.
 * Effectively the error in this class would be 170000 - 219000.
 */
#define PJ_ERRNO_START_USER	(PJ_ERRNO_START_SYS + PJ_ERRNO_SPACE_SIZE)


/*
 * Below are list of error spaces that have been taken so far:
 *  - PJSIP_ERRNO_START		(PJ_ERRNO_START_USER)
 *  - PJMEDIA_ERRNO_START	(PJ_ERRNO_START_USER + PJ_ERRNO_SPACE_SIZE)
 *  - PJSIP_SIMPLE_ERRNO_START	(PJ_ERRNO_START_USER + PJ_ERRNO_SPACE_SIZE*2)
 *  - PJLIB_UTIL_ERRNO_START	(PJ_ERRNO_START_USER + PJ_ERRNO_SPACE_SIZE*3)
 *  - PJNATH_ERRNO_START	(PJ_ERRNO_START_USER + PJ_ERRNO_SPACE_SIZE*4)
 *  - PJMEDIA_AUDIODEV_ERRNO_START (PJ_ERRNO_START_USER + PJ_ERRNO_SPACE_SIZE*5)
 *  - PJ_SSL_ERRNO_START	   (PJ_ERRNO_START_USER + PJ_ERRNO_SPACE_SIZE*6)
 *  - PJMEDIA_VIDEODEV_ERRNO_START (PJ_ERRNO_START_USER + PJ_ERRNO_SPACE_SIZE*7)
 */

每个区间50000个,第一个区间START开头的没有定义,第二个区间STATUS是pjlib自定义的错误码,第三个区间SYS是操作系统映射的错误码,第四个区间保留给应用扩展,其中PJSIP PJMEDIA等模块在上面扩展。

映射操作系统错误码

映射原理很简单,就是操作系统错误码先转成正数(有的系统错误码是负数),再加上PJ_ERRNO_START_SYS。

/**
 * @hideinitializer
 * Fold a platform specific error into an pj_status_t code.
 *
 * @param e	The platform os error code.
 * @return	pj_status_t
 * @warning	Macro implementation; the syserr argument may be evaluated
 *		multiple times.
 */
#if PJ_NATIVE_ERR_POSITIVE
#   define PJ_STATUS_FROM_OS(e) (e == 0 ? PJ_SUCCESS : e + PJ_ERRNO_START_SYS)
#else
#   define PJ_STATUS_FROM_OS(e) (e == 0 ? PJ_SUCCESS : PJ_ERRNO_START_SYS - e)
#endif

/**
 * @hideinitializer
 * Fold an pj_status_t code back to the native platform defined error.
 *
 * @param e	The pj_status_t folded platform os error code.
 * @return	pj_os_err_type
 * @warning	macro implementation; the statcode argument may be evaluated
 *		multiple times.  If the statcode was not created by 
 *		pj_get_os_error or PJ_STATUS_FROM_OS, the results are undefined.
 */
#if PJ_NATIVE_ERR_POSITIVE
#   define PJ_STATUS_TO_OS(e) (e == 0 ? PJ_SUCCESS : e - PJ_ERRNO_START_SYS)
#else
#   define PJ_STATUS_TO_OS(e) (e == 0 ? PJ_SUCCESS : PJ_ERRNO_START_SYS - e)
#endif

获取和设置系统操作码,对于Linux来说,就是errno变量,在os_error_unix.c

PJ_DEF(pj_status_t) pj_get_os_error(void)
{
    return PJ_STATUS_FROM_OS(errno);
}

PJ_DEF(void) pj_set_os_error(pj_status_t code)
{
    errno = PJ_STATUS_TO_OS(code);
}

PJ_DEF(pj_status_t) pj_get_netos_error(void)
{
    return PJ_STATUS_FROM_OS(errno);
}

PJ_DEF(void) pj_set_netos_error(pj_status_t code)
{
    errno = PJ_STATUS_TO_OS(code);
}

错误码转错误字符串

错误码提示的信息过于单调,需要转成更容易分析的字符串,通过pj_strerror可以把操作码转成字符串。操作码在转字符串的过程中,分为3类,如果是pjlib区间的错误码,则使用pjlib自己实现的映射表pjlib_error,如果是操作系统相关的错误码platform_strerror,则调用系统相关的函数,如果是应用扩展的错误码,则需要先注册pj_register_strerror回调函数,应用在回调函数构造错误码对应的字符串。

PJ_DEF(pj_str_t) pj_strerror( pj_status_t statcode, 
			      char *buf, pj_size_t bufsize )
{
    int len = -1;
    pj_str_t errstr;

    pj_assert(buf && bufsize);

    if (statcode == PJ_SUCCESS) {
	len = pj_ansi_snprintf( buf, bufsize, "Success");

    } else if (statcode < PJ_ERRNO_START + PJ_ERRNO_SPACE_SIZE) {
        len = pj_ansi_snprintf( buf, bufsize, "Unknown error %d", statcode);

    } else if (statcode < PJ_ERRNO_START_STATUS + PJ_ERRNO_SPACE_SIZE) {
        len = pjlib_error(statcode, buf, bufsize);

    } else if (statcode < PJ_ERRNO_START_SYS + PJ_ERRNO_SPACE_SIZE) {
        len = platform_strerror(PJ_STATUS_TO_OS(statcode), buf, bufsize);

    } else {
	unsigned i;

	/* Find user handler to get the error message. */
	for (i=0; i<err_msg_hnd_cnt; ++i) {
	    if (IN_RANGE(statcode, err_msg_hnd[i].begin, err_msg_hnd[i].end)) {
		return (*err_msg_hnd[i].strerror)(statcode, buf, bufsize);
	    }
	}
...
}

pjlib_error就是查找全局数组映射,platform_strerror在os_error_unix.c,其本质调用strerror,应用扩展,需先注册到数组

/* Error message handler. */
static unsigned err_msg_hnd_cnt;
static struct err_msg_hnd
{
    pj_status_t	    begin;
    pj_status_t	    end;
    pj_str_t	  (*strerror)(pj_status_t, char*, pj_size_t);

} err_msg_hnd[PJLIB_MAX_ERR_MSG_HANDLER];

错误输出到日志

错误最终要保存到日志,可以用宏PJ_PERROR或函数pj_perror,前者可以在编译链接的时候就优化,比如你的日志等级只有1,则比较低的等级在编译时为空,而使用pj_perror则会进行函数调用,但是函数内部实现为空。相比之下,宏PJ_PERROR更高效,看一下代码注释的描述。

 *   - #PJ_PERROR() macro: use this macro similar to PJ_LOG to format
 *      an error message and display them to the log
 *
 *   - #pj_perror(): this function is similar to PJ_PERROR() but unlike
 *      #PJ_PERROR(), this function will always be included in the
 *      link process. Due to this reason, prefer to use #PJ_PERROR()
 *      if the application is concerned about the executable size.
 *

不管是宏PJ_PERROR还是函数pj_perror,最终都调用pj_perror_imp,里面先调用pj_strerror把错误码转成字符串,再调用invoke_log输出到日志,最终调用的是pj_log。

static void invoke_log(const char *sender, int level, const char *format, ...)
{
    va_list arg;
    va_start(arg, format);
    pj_log(sender, level, format, arg);
    va_end(arg);
}

static void pj_perror_imp(int log_level, const char *sender, 
			  pj_status_t status,
		          const char *title_fmt, va_list marker)
{
    char titlebuf[PJ_PERROR_TITLE_BUF_SIZE];
    char errmsg[PJ_ERR_MSG_SIZE];
    int len;

    /* Build the title */
    len = pj_ansi_vsnprintf(titlebuf, sizeof(titlebuf), title_fmt, marker);
    if (len < 0 || len >= (int)sizeof(titlebuf))
	pj_ansi_strcpy(titlebuf, "Error");

    /* Get the error */
    pj_strerror(status, errmsg, sizeof(errmsg));

    /* Send to log */
    invoke_log(sender, log_level, "%s: %s", titlebuf, errmsg);
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值