概述
freeswitch是一款开源的VOIP软交换平台,功能强大。
在使用fs进行呼叫业务的过程中,我们最常见到的日志就是呼叫通道的启动信息,日志如下
2022-03-03 14:14:30.028832 [NOTICE] switch_channel.c:1133 New Channel sofia/internal/1001@192.168.0.152 [d70acc45-294c-4787-8dce-ff46d2cd96a1]
这行日志表示一个新的通话channel初始化完成。
如果我们希望知道channel是如何初始化和启动的,该如何去梳理代码流程呢,有以下几种方式。
1, 查看源码。但是fs的源码比较复杂,调用层次多,第三方库也多。
2, 打印跟踪日志。常规操作,最常见也最简单的方式。
3, 堆栈打印。使用系统库函数打印堆栈来查看调用流程。
今天我们介绍第3种方式,通过堆栈来查看channel的初始化流程。
环境
centos:CentOS release 7.0 (Final)或以上版本
freeswitch:v1.8.7
GCC:4.8.5
代码修改
修改src\switch_channel.c文件,代码如下,其中print_stack函数用来打印函数堆栈信息。对于backtrace的介绍在网上有很多,这里就不详细说明了。
#include <execinfo.h>
static void print_stack()
{
void *stacktrace[99];
char **symbols;
int size;
int i;
size = backtrace(stacktrace, 99);
symbols = backtrace_symbols(stacktrace, size);
if (!symbols) {
return;
}
for (i = 0; i < size; i++) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "STACK: %s\n", symbols[i]);
}
free(symbols);
}
SWITCH_DECLARE(switch_status_t) switch_channel_set_name(switch_channel_t *channel, const char *name)
{
const char *old = NULL;
switch_assert(channel != NULL);
if (!zstr(channel->name)) {
old = channel->name;
}
channel->name = NULL;
if (name) {
char *uuid = switch_core_session_get_uuid(channel->session);
channel->name = switch_core_session_strdup(channel->session, name);
switch_channel_set_variable(channel, SWITCH_CHANNEL_NAME_VARIABLE, name);
if (old) {
switch_log_printf(SWITCH_CHANNEL_CHANNEL_LOG(channel), SWITCH_LOG_NOTICE, "Rename Channel %s->%s [%s]\n", old, name, uuid);
} else {
switch_log_printf(SWITCH_CHANNEL_CHANNEL_LOG(channel), SWITCH_LOG_NOTICE, "New Channel %s [%s]\n", name, uuid);
print_stack();
}
}
return SWITCH_STATUS_SUCCESS;
}
对freeswitch的代码重新编译安装,并启动fs进程。
呼叫测试
1001号码通过SIP注册到FS服务器,并发起INVITE呼叫,呼叫不需要通过,仅仅关注主叫方A路的通道初始化过程。
------------------------------------------------------------------------
INVITE sip:1002@192.168.0.152 SIP/2.0
Via: SIP/2.0/UDP 10.9.0.70:25969;branch=z9hG4bK-d87543-010e715d272bcd27-1--d87543-;rport
Max-Forwards: 70
Contact: <sip:1001@10.9.0.70:25969>
To: "1002"<sip:1002@192.168.0.152>
From: "1001"<sip:1001@192.168.0.152>;tag=9b6ae45a
Call-ID: NDJjYzY2NDMwZmQ1ZWE5YmY3ZjE3MWNiNGIwNmQ5YWQ.
CSeq: 1 INVITE
Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY, MESSAGE, SUBSCRIBE, INFO
Content-Type: application/sdp
User-Agent: eyeBeam release 1004p stamp 31962
Content-Length: 478
v=0
o=- 2 2 IN IP4 10.9.0.70
s=CounterPath eyeBeam 1.5
c=IN IP4 10.9.0.70
t=0 0
m=audio 41980 RTP/AVP 0 98 18 101
a=alt:1 4 : 2cib73mX anCjScUO 10.9.0.70 41980
a=fmtp:18 annexb=no
a=fmtp:101 0-15
a=rtpmap:98 iLBC/8000
a=rtpmap:101 telephone-event/8000
a=sendrecv
a=x-rtp-session-id:F73CAA782C7D401EBE0ACC803E495B81
------------------------------------------------------------------------
switch_channel.c:1133 New Channel sofia/internal/1001@192.168.0.152 [d70acc45-294c-4787-8dce-ff46d2cd96a1]
switch_channel.c:1112 STACK: /usr/local/freeswitch/lib/libfreeswitch.so.1(+0x5f14d) [0x7fc46acb314d]
switch_channel.c:1112 STACK: /usr/local/freeswitch/lib/libfreeswitch.so.1(switch_channel_set_name+0x129) [0x7fc46acb9b59]
switch_channel.c:1112 STACK: /usr/local/freeswitch/mod/mod_sofia.so(+0x89a7c) [0x7fc4612bea7c]
switch_channel.c:1112 STACK: /usr/local/freeswitch/mod/mod_sofia.so(+0x8a25e) [0x7fc4612bf25e]
switch_channel.c:1112 STACK: /usr/local/freeswitch/mod/mod_sofia.so(+0x5c3b7) [0x7fc4612913b7]
switch_channel.c:1112 STACK: /usr/local/freeswitch/mod/mod_sofia.so(+0x11dfc1) [0x7fc461352fc1]
switch_channel.c:1112 STACK: /usr/local/freeswitch/mod/mod_sofia.so(+0x151e52) [0x7fc461386e52]
switch_channel.c:1112 STACK: /usr/local/freeswitch/mod/mod_sofia.so(+0x1524a1) [0x7fc4613874a1]
switch_channel.c:1112 STACK: /usr/local/freeswitch/mod/mod_sofia.so(+0x61ea4) [0x7fc461296ea4]
switch_channel.c:1112 STACK: /usr/local/freeswitch/lib/libfreeswitch.so.1(+0x35bbd0) [0x7fc46afafbd0]
switch_channel.c:1112 STACK: /lib64/libpthread.so.0(+0x7ea5) [0x7fc468893ea5]
switch_channel.c:1112 STACK: /lib64/libc.so.6(clone+0x6d) [0x7fc467ee7b0d]
在上面的日志打印中,我们可以看到“STACK”的打印,就是函数调用堆栈的信息。
但是打印信息中,我们只能看到模块的名字和地址信息,看不到具体的函数名。
比如,在mod_sofia.so模块中,就有7次调用,无法看到函数名。
在尝试了对gcc编译参数增加“-ggdb”和“-rdynamic”之后,函数名的问题仍然无法解决。
后面通过“addr2line”工具,间接的查找到函数名。
函数名查看
通过“addr2line”工具,使用模块名和偏移地址,查找函数名。
addr2line 0x5f14d -e /usr/local/freeswitch/lib/libfreeswitch.so.1 -f
print_stack
/root/zr/freeswitch-1.8.7/src/switch_channel.c:1107
switch_channel.c:1112 STACK: /usr/local/freeswitch/lib/libfreeswitch.so.1(switch_channel_set_name+0x129) [0x7fc46acb9b59]
addr2line 0x89a7c -e /usr/local/freeswitch/mod/mod_sofia.so -f
sofia_glue_set_name
/root/zr/freeswitch-1.8.7/src/mod/endpoints/mod_sofia/sofia_glue.c:70
addr2line 0x8a25e -e /usr/local/freeswitch/mod/mod_sofia.so -f
sofia_glue_attach_private
/root/zr/freeswitch-1.8.7/src/mod/endpoints/mod_sofia/sofia_glue.c:208
addr2line 0x5c3b7 -e /usr/local/freeswitch/mod/mod_sofia.so -f
sofia_event_callback
/root/zr/freeswitch-1.8.7/src/mod/endpoints/mod_sofia/sofia.c:2578
addr2line 0x11dfc1 -e /usr/local/freeswitch/mod/mod_sofia.so -f
su_msg_is_non_null
/root/zr/freeswitch-1.8.7/libs/sofia-sip/libsofia-sip-ua/nua/../../libsofia-sip-ua/su/sofia-sip/su_wait.h:565 (discriminator 5)
addr2line 0x151e52 -e /usr/local/freeswitch/mod/mod_sofia.so -f
su_base_port_execute_msgs
/root/zr/freeswitch-1.8.7/libs/sofia-sip/libsofia-sip-ua/su/su_base_port.c:283
addr2line 0x1524a1 -e /usr/local/freeswitch/mod/mod_sofia.so -f
su_base_port_step
/root/zr/freeswitch-1.8.7/libs/sofia-sip/libsofia-sip-ua/su/su_base_port.c:473
addr2line 0x61ea4 -e /usr/local/freeswitch/mod/mod_sofia.so -f
sofia_profile_thread_run
/root/zr/freeswitch-1.8.7/src/mod/endpoints/mod_sofia/sofia.c:3392
addr2line 0x35bbd0 -e /usr/local/freeswitch/lib/libfreeswitch.so.1 -f
dummy_worker
/root/zr/freeswitch-1.8.7/libs/apr/threadproc/unix/thread.c:152
addr2line 0x7ea5 -e /lib64/libpthread.so.0 -f
start_thread
pthread_create.c:?
channel初始化流程
从上面可以看出一通新的呼叫channel的初始化过程如下。
sofia_profile_thread_run->su_base_port_step->su_base_port_execute_msgs->su_msg_is_non_null->sofia_event_callback->sofia_glue_attach_private->sofia_glue_set_name->switch_channel_set_name
sofia_profile_thread_run,mod_sofia模块,启动profile端口监听。
su_base_port_step,sofia_sip库,端口收到消息。
su_base_port_execute_msgs,sofia_sip库,分发消息。
sofia_event_callback,mod_sofia模块,sip消息回调函数。
sofia_glue_attach_private,mod_sofia模块,初始化session和channel的参数设置。
sofia_glue_set_name,mod_sofia模块,设置channel名称。
switch_channel_set_name,freeswitch核心,设置channel名称。
到底为止,我们就回到了文章最开始的地方,日志打印“New Channel”的地方。
总结
我们使用了系统库函数backtrace和工具addr2line,得到了函数调用的流程。
在fs这种复杂的代码架构中,单纯使用backtrace无法解决全部问题。
查看代码流程多了一种不同的方式,对于工作中解决问题有帮助。
空空如常
求真得真