pjproject icon indicating copy to clipboard operation
pjproject copied to clipboard

Segfault in example python application from tutorial.

Open pbalash0v opened this issue 3 years ago • 4 comments

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:

  1. Compile python bindings for debian 10, use latest swig for python module generation
  2. Copy-paste example application (see link above)
  3. Run example application, assure having some sip server to accept sip registration
  4. 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.

pbalash0v avatar Apr 04 '21 20:04 pbalash0v

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)

gfim avatar Apr 22 '21 23:04 gfim

Getting the same error. Did anyone find the solution, or at least any other alternative...?

haricane8133 avatar May 08 '22 10:05 haricane8133

sorry, no

gfim avatar Jun 23 '22 04:06 gfim

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.

pbalash0v avatar Jun 24 '22 13:06 pbalash0v

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.

jrozhon avatar Jan 07 '23 14:01 jrozhon

#!/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.

pbalash0v avatar Jan 11 '23 18:01 pbalash0v

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?

jrozhon avatar Jan 11 '23 18:01 jrozhon

#!/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 avatar Jan 11 '23 19:01 pbalash0v

@pbalash0v Thank you! Will test it. It is strange as I have tried something similar but segfaulted immediately after libHandleEvents()

jrozhon avatar Jan 11 '23 19:01 jrozhon

@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 avatar Jan 11 '23 19:01 pbalash0v

@pbalash0v yes, I have figured it out already, but wasn't able to get past segfault part :-).

jrozhon avatar Jan 11 '23 19:01 jrozhon

@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 avatar Jan 11 '23 19:01 pbalash0v

@pbalash0v Thanks for everything!

jrozhon avatar Jan 11 '23 19:01 jrozhon

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.

gfim avatar Mar 16 '23 03:03 gfim