pjproject
pjproject copied to clipboard
Segfault in example python application from tutorial.
Describe the bug Using code from example application https://www.pjsip.org/docs/book-latest/html/intro_pjsua2.html#using-in-python-application leads to segfault.
To Reproduce Steps to reproduce the behavior:
- Compile python bindings for debian 10, use latest swig for python module generation
- Copy-paste example application (see link above)
- Run example application, assure having some sip server to accept sip registration
- observe segfault
Expected behavior Not segfaulting
Logs/Screenshots If applicable, add screenshots PJSIP logs with verbosity level 5, packet capture (e.g: using Wireshark), audio/video recording, screenshot, etc.
Desktop/Smartphone (please complete the following information):
- OS, Distribution & Version: Debian 10
- PJSIP
- version: master branch
- applied patch(es): none
-
configure
script params: as in tutorial -
config_site.h
contents: - related third party libraries & versions:
Additional context
If code in example app is modified like so
time.sleep(10)
to this:
while True: ep.libHandleEvents(10)
(i.e. replace time.sleep(10)
with loop on ep.libHandleEvents(10)
)
program seems to run as expected, not segfaulting.
I'm seeing what I think is the same problem. See also my Ruby wrapper Pull request #2690.
It appears to happen when the calling script is blocked by a system call. The simplest way to cause it is to call sleep(). As the above says, calling libHandleEvents() can work around that. But other reasons for blocking e.g. reading from standard input or a TCP socket can't be worked around so easily.
Here's a stack backtrace:
(gdb) bt
#0 0x00007ffff7a5aa4a in PyObject_Call () from /lib64/libpython2.7.so.1.0
#1 0x00007fffef6b55ed in SWIG_Python_NewShadowInstance (data=0x7c0a90, data=0x7c0a90, swig_this=0x7fffea04f510) at pjsua2_wrap.cpp:2511
#2 SWIG_Python_NewPointerObj (ptr=0x7fffe48e1c40, type=0x7fffefb69e40 <_swigt__p_pj__OnRegStateParam>, flags=flags@entry=0, self=0x0) at pjsua2_wrap.cpp:2664
#3 0x00007fffef75168a in SwigDirector_Account::onRegState (this=0x8081c0, prm=...) at pjsua2_wrap.cpp:9036
#4 0x00007fffef3a42f4 in pj::Endpoint::on_reg_state2 (acc_id=0, info=0x7fffe48e1d10) at ../src/pjsua2/endpoint.cpp:797
#5 0x00007fffeed7139b in regc_cb (param=0x7fffe48e1d80) at ../src/pjsua-lib/pjsua_acc.c:2419
#6 0x00007fffeeb4cfa1 in call_callback (regc=0x8344e8, status=0, st_code=200, reason=0x833590, rdata=0x832288, expiration=300, contact_cnt=1, contact=0x7fffe48e1e50, is_unreg=0) at ../src/pjsip-ua/sip_reg.c:792
#7 0x00007fffeeb4e165 in regc_tsx_callback (token=0x8344e8, event=0x7fffe48e20a0) at ../src/pjsip-ua/sip_reg.c:1374
#8 0x00007fffee703381 in mod_util_on_tsx_state (tsx=0x837368, event=0x7fffe48e20a0) at ../src/pjsip/sip_util_statefull.c:81
#9 0x00007fffee6ff47c in tsx_set_state (tsx=0x837368, state=PJSIP_TSX_STATE_COMPLETED, event_src_type=PJSIP_EVENT_RX_MSG, event_src=0x832288, flag=0) at ../src/pjsip/sip_transaction.c:1272
#10 0x00007fffee702958 in tsx_on_state_proceeding_uac (tsx=0x837368, event=0x7fffe48e21c0) at ../src/pjsip/sip_transaction.c:3016
#11 0x00007fffee701d72 in tsx_on_state_calling (tsx=0x837368, event=0x7fffe48e21c0) at ../src/pjsip/sip_transaction.c:2599
#12 0x00007fffee70061a in pjsip_tsx_recv_msg (tsx=0x837368, rdata=0x832288) at ../src/pjsip/sip_transaction.c:1832
#13 0x00007fffee6fe92b in mod_tsx_layer_on_rx_response (rdata=0x832288) at ../src/pjsip/sip_transaction.c:893
#14 0x00007fffee6e170d in pjsip_endpt_process_rx_data (endpt=0x6ba788, rdata=0x832288, p=0x7fffe48e2390, p_handled=0x7fffe48e238c) at ../src/pjsip/sip_endpoint.c:938
#15 0x00007fffee6e19d0 in endpt_on_rx_msg (endpt=0x6ba788, status=0, rdata=0x832288) at ../src/pjsip/sip_endpoint.c:1080
#16 0x00007fffee6ebff0 in pjsip_tpmgr_receive_packet (mgr=0x6f0818, rdata=0x832288) at ../src/pjsip/sip_transport.c:2182
#17 0x00007fffee6ee2f6 in udp_on_read_complete (key=0x7f27b0, op_key=0x8322a0, bytes_read=451) at ../src/pjsip/sip_transport_udp.c:189
#18 0x00007fffec8eb0ae in ioqueue_dispatch_read_event (ioqueue=0x7f1cc0, h=0x7f27b0) at ../src/pj/ioqueue_common_abs.c:605
#19 0x00007fffec8ed38a in pj_ioqueue_poll (ioqueue=0x7f1cc0, timeout=0x7fffe48e2e30) at ../src/pj/ioqueue_select.c:1069
#20 0x00007fffee6e1321 in pjsip_endpt_handle_events2 (endpt=0x6ba788, max_timeout=0x7fffe48e2e80, p_count=0x7fffe48e2e98) at ../src/pjsip/sip_endpoint.c:745
#21 0x00007fffeed862f2 in pjsua_handle_events (msec_timeout=10) at ../src/pjsua-lib/pjsua_core.c:2156
#22 0x00007fffeed83404 in worker_thread (arg=0x0) at ../src/pjsua-lib/pjsua_core.c:786
#23 0x00007fffec8ee335 in thread_main (param=0x82d5c8) at ../src/pj/os_core_unix.c:541
#24 0x00007ffff77fadd5 in start_thread () from /lib64/libpthread.so.0
#25 0x00007ffff6e1aead in clone () from /lib64/libc.so.6
(gdb)
Getting the same error. Did anyone find the solution, or at least any other alternative...?
sorry, no
Getting the same error. Did anyone find the solution, or at least any other alternative...?
You really can work around this by using:
while True:
ep.libHandleEvents(10)
It has worked well for my needs, when you also set pjsip to one threaded mode and, for example, programming your logic around sip call state changes.
Sorry for waking up a zombie discussion. @pbalash0v could you please elaborate more on your solution? I have come across the same issue, but am unable to create any more complex scenarios with the workaround you have suggested. Thank you.
#!/usr/bin/env python3
import pjsua2 as pj
import sys, os, logging
class Account(pj.Account):
def __init__(self):
super().__init__()
def onRegState(self, prm):
logging.debug("***OnRegState: " + prm.reason)
def main():
# Create and initialize the library
ep_cfg = pj.EpConfig()
ep_cfg.uaConfig.threadCnt = 0
ep_cfg.uaConfig.mainThreadOnly = True
ep_cfg.logConfig.level = 5
ep_cfg.logConfig.consoleLevel = 5
ep = pj.Endpoint()
ep.libCreate()
ep.libInit(ep_cfg)
ep.audDevManager().setNullDev()
# Create SIP transport
sipTpConfig = pj.TransportConfig()
ep.transportCreate(pj.PJSIP_TRANSPORT_UDP, sipTpConfig)
# Start the library
ep.libStart()
acfg = pj.AccountConfig()
acfg.idUri = ....
acfg.regConfig.registrarUri = ....
cred = pj.AuthCredInfo("digest", "*", ...., 0, ....)
acfg.sipConfig.authCreds.append(cred)
# Create the account
acc = Account()
acc.create(acfg)
while true:
ep.libHandleEvents(10)
# Destroy the library
ep.libDestroy()
if __name__ == "__main__":
main()
@jrozhon , above is a rough sketch of an overall idea. Fill in the ....
with your specifics.
Thank you for sharing your code. Basically, I have the same. But I need to make it work in a more advanced manner. Such as making/accepting/putting on hold a call and so on. So, this is not something you use, actually. Is it?
#!/usr/bin/env python3
import sys, os, logging
import pjsua2 as pj
class Call(pj.Call):
def __init__(self, acc, call_id = pj.PJSUA_INVALID_ID):
pj.Call.__init__(self, acc, call_id)
self.call_state = None
def onCallState(self, prm):
"""
typedef enum pjsip_inv_state
{
PJSIP_INV_STATE_NULL, /**< Before INVITE is sent or received */
PJSIP_INV_STATE_CALLING, /**< After INVITE is sent */
PJSIP_INV_STATE_INCOMING, /**< After INVITE is received. */
PJSIP_INV_STATE_EARLY, /**< After response with To tag. */
PJSIP_INV_STATE_CONNECTING, /**< After 2xx is sent/received. */
PJSIP_INV_STATE_CONFIRMED, /**< After ACK is sent/received. */
PJSIP_INV_STATE_DISCONNECTED, /**< Session is terminated. */
} pjsip_inv_state;
"""
ci = self.getInfo()
logging.debug("Call state: {}".format(ci.state))
logging.debug("Call stateText: {}".format(ci.stateText))
# save call state to some class var
self.call_state = ci.state
class Account(pj.Account):
def __init__(self):
super().__init__()
def main():
# Create and initialize the library
ep_cfg = pj.EpConfig()
ep_cfg.uaConfig.threadCnt = 0
ep_cfg.uaConfig.mainThreadOnly = True
ep_cfg.logConfig.level = 5
ep_cfg.logConfig.consoleLevel = 5
ep = pj.Endpoint()
ep.libCreate()
ep.libInit(ep_cfg)
ep.audDevManager().setNullDev()
# Create SIP transport. Error handling sample is shown
sipTpConfig = pj.TransportConfig()
ep.transportCreate(pj.PJSIP_TRANSPORT_UDP, sipTpConfig)
# Start the library
ep.libStart()
acfg = pj.AccountConfig()
acfg.idUri = ...caller_uri...
cred = pj.AuthCredInfo("digest", "*", ...., 0, ....)
acfg.sipConfig.authCreds.append(cred)
# Create the account
acc = Account()
acc.create(acfg)
call = Call(acc)
call_param = pj.CallOpParam()
call_param.opt.audioCount = 1
call_param.opt.videoCount = 0
call.makeCall(....callee_uri...., call_param)
while not call.done:
ep.libHandleEvents(10)
# check your saved call_state here
if not call.call_state:
continue
if call.call_state == pj.PJSIP_INV_STATE_CONFIRMED:
call.done = True
# Destroy the library
ep.libDestroy()
if __name__ == "__main__":
main()
@jrozhon , this is a sketch what is supposedly should try outgoing call to some uri and hangup on callee's answer. Please bear in mind that I'm not using pjsip python wrapper for any serious UA/Proxy/B2B whatever, mostly tinkering.
@pbalash0v Thank you! Will test it. It is strange as I have tried something similar but segfaulted immediately after libHandleEvents()
@jrozhon For all other advanced stuff
like making/accepting/putting on hold a call and so on
you have to consult underlying C++ headers anyway (often even lower C-API). The python API wrapper is literally one-to-one adaptation.
@pbalash0v yes, I have figured it out already, but wasn't able to get past segfault part :-).
@jrozhon And one more thing - generally I've noticed when dealing with C<->python callback based bindings care should be taken of python object lifetimes etc. It often can bite really unexpectedly. Could be a segfault reason, I guess.
@pbalash0v Thanks for everything!
I don't think that this should be marked as completed. It is a bug. It can be worked around in come circumstances. But I still don't have a solution when the process blocks for some other reason (e.g. TCP socket read) - see above.