阅读Sofia-SIP源码 - su模块 - su_debug.h/su_module_debug.h

现在知道去哪找介绍文档的文字了。本头文件的主要作用就是定义SU模块的调试宏。

/**@ingroup su_log
 * @file sofia-sip/su_debug.h
 * @brief SU debugging macros
 *
 * The logging levels and macros to use are defined as follows:
 *  - SU_DEBUG_0()  fatal errors, panic
 *  - SU_DEBUG_1()  critical errors, minimal progress at subsystem level
 *  - SU_DEBUG_2()  non-critical errors
 *  - SU_DEBUG_3()  warnings, progress messages
 *  - SU_DEBUG_5()  signaling protocol actions (incoming packets, etc.)
 *  - SU_DEBUG_7()  media protocol actions (incoming packets, etc.)
 *  - SU_DEBUG_9()  entering/exiting functions, very verbatim progress
 *
 * These macros are used to log with module-specific levels. The SU_LOG
 * macro is redefined with a pointer to a module-specific #su_log_t
 * structure, e.g., "iptsec_debug.h".
 *
 * @author Pekka Pessi <Pekka.Pessi@nokia.com>
 *
 * @date Created: Tue Feb  8 10:06:33 2000 ppessi
 *
 * @sa @ref debug_logs, su_llog(), su_vllog(), #su_log_t,
 */
从文字可以看出来,将会定义SU_DEBUG_0至SU_DEBUG_9这十个宏。还会用一个指向su_log_t结构体的变量重新再定义SU_LOG宏。所谓的重新定义SU_LOG宏的意思是,su模块是个基础模块,其他功能模块将使用它提供的基础服务来实现高层功能。例如,其他功能模块都会使用su模块提供的日志服务。因此其他功能模块一般都会再建立一个xxx_debug.h头文件,在这个头文件内会申明一个su_log_t结构体变量,然后再用这个新的su_log_t结构体变量重新定义SU_LOG宏。具体可参考iptsec_debug.h头文件。由于其他功能模块采用链接su模块代码的方式,因此所有的功能模块都会包含一份自身的SU_DEBUG_x和SU_LOG宏。

其实这也验证了之前在分析su_log.c/h文件时的疑惑,那时就觉得这只是日志框架代码,并没有任何实际使用的代码。经过这里的介绍内容,可以获知一些如何使用su模块日志服务的规则。


#ifndef SU_DEBUG_MAX
/** The maximum debugging level. */
#define SU_DEBUG_MAX 9
#endif

#define SU_LOG_LEVEL \
((SU_LOG != NULL && SU_LOG->log_init) == 0 ? 9 : \
((SU_LOG != NULL && SU_LOG->log_init > 1) ? \
  SU_LOG->log_level : su_log_default->log_level))

上面前一段是重新定义SU_DEBUG_MAX宏,为数字9。后面那段是定义SU_LOG_LEVEL宏的值。SU_LOG也是一个宏,它会在各个功能模块的xxx_debug.h头文件中重新定义。可以查看iptsec_debug.c/h。

SOFIA_BEGIN_DECLS

/** Common log for client and server components. */
SOFIAPUBVAR su_log_t iptsec_log[];

SOFIA_END_DECLS

#define SU_LOG (iptsec_log)

#include <sofia-sip/su_debug.h>
su_log_t iptsec_log[] = { SU_LOG_INIT("iptsec", "IPTSEC_DEBUG", SU_DEBUG) };
我们正在分析的头文件su_debug.h在定义了SU_LOG后被包含。也就是说在特定功能模块的debug.h头文件内会首先定义模块特定的一个su_log_t结构体变量,并将SU_LOG重定义成SU_LOG。如此一来,su_debug.h中SU_LOG_LEVEL的值将取决于iptset_log,或者说取决于特定模块的su_log_t结构体变量。换种说法是,每个功能模块(例如iptsec模块)都有自己特定的SU_LOG_LEVEL宏,这个值取决于自身模块的su_log_t结构体变量(iptsec模块内是iptset_log变量)。

因此,su_debug.h文件中SU_LOG_LEVEL中出现的SU_LOG指向的就是特定模块的su_log_t结构体变量。如果SU_LOG是个空指针,或者SU_LOG不是个空指针但log_init为假即未初始化,那么SU_LOG_LEVEL就是9。如果SU_LOG不是个空指针且log_init大于1(log_init为2说明log_level值由log_env决定),那么SU_LOG_LEVEL的值取SU_LOG全局变量的log_level属性值。如果SU_LOG不是个空指针且log_init小于2,那么SU_LOG_LEVEL的值取su_log_default全局变量的log_level属性值。简单的说就是,如果SU_LOG全局变量为空指针或还未初始化,那么日志等级就是9;如果已被初始化那么就采用SU_LOG全局变量的log_level属性值或者su_log_default全局变量的log_level属性值。从先后顺序看,首先会采用环境变量指定的日志等级。


有了上述SU_LOG_LEVLE的分析经验。我们知道SU_LOG会在某个特定模块内重新定义:iptsec模块内是iptset_log变量。下面这段的意思是,如果某个模块未定义自己特定的su_log_t结构体变量,那么就使用su_log文件中定义的su_log_default变量。

#if SU_DEBUG_MAX >= 0
#ifndef SU_LOG
#define SU_LOG       (su_log_default)
#else
SOFIAPUBVAR su_log_t SU_LOG[];
#endif

研究到这,突然让我感觉到了面向对象编程范式里的继承、接口等概念。su_log文件内出现就是面向对象里的基类这么一类对象。像su_log_t结构体。su_debug文件内出现的则是所谓面向对象的接口概念。su_debug文件内定义了一套接口。每个具体的功能模块要使用su模块提供的日志输出功能,则会实现这套接口。su_debug文件使用了宏这个技术来达到接口的目的。su_debug文件内所有的su_log_t结构体变量都用SU_LOG宏来代替。在su_debug文件内它只是一个虚拟的东西,而且su_debug文件不会被单独使用,它肯定会像iptsec_debug中那样出现。在某个特定的模块内重定义了SU_LOG宏后再来包括su_debug文件。如此一来,每个特定模块都有了自己一套按照接口来实现的接口类。通过这个案例,我们也知道了c语言下如何实现c++语言下的继承以及接口等概念的技巧。

接下来是定义SU_DEBUG_DEF宏。它的作用是生成各类日志等级水平的内联函数:su_debug_0至su_debug_9。

#define SU_DEBUG_DEF(level) \
  su_inline void su_debug_##level(char const *fmt, ...) \
    __attribute__ ((__format__ (printf, 1, 2))); \
  su_inline void su_debug_##level(char const *fmt, ...) \
    { va_list ap; va_start(ap, fmt); su_vllog(SU_LOG, level, fmt, ap); va_end(ap); }
然后会像下面这样使用。
SU_DEBUG_DEF(0)
/** Log messages at level 0.
 *
 * Fatal errors and panic messages should be logged at level 0.
 *
 * @sa su_llog(), su_vllog(), #su_log_t, @ref debug_logs
 */
#define SU_DEBUG_0(x) (SU_LOG_LEVEL >= 0 ? (su_debug_0 x) : (void)0)

/** Log C library errors. */
#define SU_LERROR(s) (su_llog(SU_LOG, 1, "%s: %s\n", (s), strerror(errno)))
/** Log socket errors. */
#define SU_LSERROR(s) \
  (su_llog(SU_LOG, 1, "%s: %s\n", (s), su_strerror(su_errno())))
#else
#define SU_DEBUG_0(x) ((void)0)
#define SU_LERROR(s)  ((void)0)
#define SU_LSERROR(s) ((void)0)
#endif

如果日志等级满足大于等于0,那么SU_DEBUG_0将是su_debug_0函数,否则SU_DEBUG_0将退化成0(数字零)最终宏替换后成为一条“0;”这样的无意义语句。同时,连带着定义另两个宏:SU_LERROR和SU_LSERROR。一个用来记录C库的错误,一个用来记录socket错误。如果日志等级小于0,那么这三个宏都将退化成0(数字零)。文件中其他部分的内容都是生成1至9日志等级的宏(SU_DEBUG_X)。


我们在回头看看这个宏定义。它是在编译期确定最多生成哪几类日志等级输出函数(su_debug_x)。

#ifndef SU_DEBUG_MAX
/** The maximum debugging level. */
#define SU_DEBUG_MAX 9
#endif
而SU_DEBUG_LEVEL将决定编译期生成哪几类对应实际输出函数的SU_DEBUG_X宏。否则SU_DEBUG_X宏将对应无意义的“0;”这样的语句。


分析su_debug.h头文件让我们对su模块内的基础日志输出服务有了更深一层的认识。同时,也清楚了其他模块内如何使用这基础服务。查看su_module_debug.h头文件会发现,其他su模块自身也以同样的方式在使用这个基础服务框架。su模块内将使用su_log文件内定义的su_log_global变量记录日志。这里同样是重定义了SU_LOG后,再包含su_debug.h头文件。

** Debugging log for @b su module. */
SOFIAPUBVAR su_log_t su_log_global[];
#define SU_LOG (su_log_global)
#include <sofia-sip/su_debug.h>


但还有一个疑问:因为每个特定功能模块都会重定义SU_LOG宏,以及再包含su_debug.h头文件,那么可以认定每个特定功能模块都有一套自己的SU_DEBUG_X宏(因为宏里使用了SU_LOG宏)。如果是这样的话,那同时使用两个或两个以上的功能模块时是否会产生SU_DEBUG_X宏重定义的冲突?如果不想造成冲突,那么在某个特定功能模块提供出的公开API接口头文件中不应该包括特定功能模块的xx_debug.h头文件,因为这头文件就是用来重新定义SU_DEBUG_X宏的。更进一步猜测,所有其他功能模块内的代码都只会在.c文件中包括自身模块的xx_debug.h头文件来获得日志输出接口。





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值