如何最快找出复杂代码运行时的函数调用流程
概述
最近在使用freeswitch测试发送DTMF码的时候,碰到一个函数调用流程的问题。
fs本身的模块较多,注册回调函数也比较多且复杂,有时候看到一条日志,却不知道流程的发起端在哪里。
通常我们在梳理代码调用流程的时候有几种方法。
1, 梳理源代码流程,代码太复杂的时候效果差。
2, 在调用函数中打印日志,代码太复杂的时候效果差。
3, 通过backtrace函数追踪当前堆栈,改动多,细节多。
4, gdb调试,命令复杂难记。
今天突然想到一个偷懒的办法,比上面几种方法都要快速简单。
环境
centos7
freeswitch 1.6.19
gcc version 4.8.5
起源
fs的日志如下,简单来看,在dialplan中调用了sleep之后,rtp流程开始发送DTMF。
EXECUTE sofia/external/1001@10.55.55.138:5080 sleep(5000)
2022-06-17 14:42:54.890047 [DEBUG] switch_rtp.c:5237 Send start packet for [1] ts=160 dur=160/160/2000 seq=8238 lw=160
但是rtp的调用过程隔的太远,源代码看了好久也没看出来所以然,主要是菜。
修改源代码
首先找到“switch_rtp.c:5237”的位置,修改源代码,增加一行断言判定。
switch_assert(FALSE);
重新对fs编译安装。
测试
启动fs,并执行测试命令,日志如下。
freeswitch@localhost.localdomain> originate {originator_codec=PCMA,origination_caller_id_number=0755110}sofia/external/sip:1001@10.55.55.138:5080 1001 XML ext_test
…
EXECUTE sofia/external/1001@10.55.55.138:5080 sleep(5000)
freeswitch: src/switch_rtp.c:5236: do_2833: Assertion `0' failed.
Aborted
哈,和设想的一样,fs程序coredump了
core堆栈
在bin目录下找到core文件
-rw-------. 1 root root 71778304 Jun 17 15:24 core.14650
使用gdb打开core文件,并显示堆栈。
sudo gdb freeswitch core.14650
…
(gdb) bt
#0 0x00007f0b9e371387 in raise () from /lib64/libc.so.6
#1 0x00007f0b9e372a78 in abort () from /lib64/libc.so.6
#2 0x00007f0b9e36a1a6 in __assert_fail_base () from /lib64/libc.so.6
#3 0x00007f0b9e36a252 in __assert_fail () from /lib64/libc.so.6
#4 0x00007f0ba0e8be2a in do_2833 (rtp_session=rtp_session@entry=0x7f0b1c012d98) at src/switch_rtp.c:5236
#5 0x00007f0ba0e8d4fb in rtp_common_read (rtp_session=rtp_session@entry=0x7f0b1c012d98, payload_type=payload_type@entry=0x7f0b34040aa4 "", pmapP=pmapP@entry=0x7f0b34040ac8, flags=flags@entry=0x7f0b34040ab8, io_flags=io_flags@entry=0) at src/switch_rtp.c:7321
#6 0x00007f0ba0e8e59f in switch_rtp_zerocopy_read_frame (rtp_session=0x7f0b1c012d98, frame=frame@entry=0x7f0b34040a60, io_flags=io_flags@entry=0) at src/switch_rtp.c:7617
#7 0x00007f0ba0e41990 in switch_core_media_read_frame (session=session@entry=0x7f0b3402f3b8, frame=frame@entry=0x7f0b482795d8, flags=flags@entry=0, stream_id=stream_id@entry=0, type=type@entry=SWITCH_MEDIA_TYPE_AUDIO) at src/switch_core_media.c:2219
#8 0x00007f0b984f1298 in sofia_read_frame (session=0x7f0b3402f3b8, frame=0x7f0b482795d8, flags=0, stream_id=0) at mod_sofia.c:1044
#9 0x00007f0ba0e2a5d6 in switch_core_session_read_frame (session=session@entry=0x7f0b3402f3b8, frame=frame@entry=0x7f0b482795d8, flags=flags@entry=0, stream_id=stream_id@entry=0) at src/switch_core_io.c:188
#10 0x00007f0ba0ec426d in switch_ivr_sleep (session=session@entry=0x7f0b3402f3b8, ms=ms@entry=5000, sync=sync@entry=SWITCH_TRUE, args=args@entry=0x7f0b48279850) at src/switch_ivr.c:294
#11 0x00007f0b7273d5c4 in sleep_function (session=0x7f0b3402f3b8, data=<optimized out>) at mod_dptools.c:2288
#12 0x00007f0ba0e2472b in switch_core_session_exec (session=session@entry=0x7f0b3402f3b8, application_interface=application_interface@entry=0x1ccfcd8, arg=arg@entry=0x7f0b34053240 "5000") at src/switch_core_session.c:2802
#13 0x00007f0ba0e24cb9 in switch_core_session_execute_application_get_flags (session=session@entry=0x7f0b3402f3b8, app=0x7f0b34053238 "sleep", arg=0x7f0b34053240 "5000", flags=flags@entry=0x0) at src/switch_core_session.c:2672
#14 0x00007f0ba0e28ae4 in switch_core_standard_on_execute (session=0x7f0b3402f3b8) at src/switch_core_state_machine.c:353
#15 switch_core_session_run (session=0x7f0b3402f3b8) at src/switch_core_state_machine.c:650
#16 0x00007f0ba0e21f7e in switch_core_session_thread (thread=<optimized out>, obj=0x7f0b3402f3b8) at src/switch_core_session.c:1648
#17 0x00007f0ba0e1dc73 in switch_core_session_thread_pool_worker (thread=0x7f0b3404f340, obj=<optimized out>) at src/switch_core_session.c:1711
#18 0x00007f0ba10d8b10 in dummy_worker (opaque=0x7f0b3404f340) at threadproc/unix/thread.c:151
#19 0x00007f0b9ede5ea5 in start_thread () from /lib64/libpthread.so.0
#20 0x00007f0b9e439b0d in clone () from /lib64/libc.so.6
(gdb)
啧啧,清晰的函数调用流程,一览无余。
总结
fs的模块比较多,代码调用链大都比较长,涉及到媒体方面流程更是复杂难以跟踪。
剑走偏锋,独辟蹊径。
空空如常
求真得真