FREESWITCH系列之内存泄漏排查总结

心路历程

1.懵逼状态

在接到这个任务的时候,对FreeSwitch处于一问三不知的情况,不了解FreeSwitch的业务,不了解流程,更要不要说代码了。但是还是硬着头皮上了,原本以为是个短期接触战,没想到被自己搞成了持久战。回过来头看,一开始对任务的难度还是低估了,没有做好持久战的准备。

2.无头苍蝇状态

在接到任务后,在没有先去了解业务的情况下,就开始了各种尝试。

工具1:valgrind,跑了一段时间后,除了输出一堆无用的垃圾外,没什么用

工具2:fmpool,开源的工具,使用该工具后,性能下降明显,需要跑较长时间,才能输出一些有用的信息

工具3:gperftool,google开源的工具,使用该工具,同样性能也下降了,也需要跑较长时间,但是输出的信息很有用。可以明显的看到泄漏路径。

但是拿到这个内存泄漏图后,不了解底层代码的情况,根本看不懂。更不要说如何修改了。

3.走了弯路

在选择内存泄漏排查工具的过程中,还是走了一些弯路。一度怀疑是内存空洞造成的内存泄漏。然后看明面上的freeswitch日志,以为是视频通话场景下,分配了大量的jitter_buffer造成的内存空洞,然后导致了泄漏。在这个方向上,浪费了很多时间。

唯一的收获是了解了apr库内存池的使用,以及排除了session和channel层内存泄漏的可能性。

4.走进sofia-sip底层

后面XX告诉我84环境freeswitch有内存泄漏的情况,用上了gperftool后,输出的内存泄漏路径都在sofia-sip库。但是底层代码没人熟悉,只能沉下来心来,去啃这块硬骨头了。

5.抓注重点

在梳理内存泄漏路径的过程中,发现sofia-sip库的内存分配是基于Home-base的内存分配机制。什么意思?就是sofia-sip库的内存分配,会先分配一个home的内存,然后需要再次分配内存的时候,通过这个home来分配,释放的时候也有home来释放。所以只要抓注su_home_new和su_home_deinit,就抓注了内向泄漏的重点了。

6.日志改造

原来的日志比较简单,后面打开了很多调试级别的日志,以及在su_alloc.c加上日志调试,就能清晰的看到一个的流程所有日志。通过大量的阅读日志和代码,终于定位到是两个场景触发的内存泄漏。

一个是无效注册请求包。无效注册请求就是在userinfo表没有记录的用户的请求,原来的流程是没有做这个判断,然后会回复401。后面是改成了,不回复401就完事了。这样导致了分配的handle,soa_session,message的内存都没有被释放。

一个是无效INVITE包。无效INVITE包也是在userinfo表没有记录的用户请求。原来的流程是没有做这个判断,然后会回复407。后面是改成了,将IP加入黑名单,后面就不会收到这个IP的包。但是在测试环境中并没有加入黑名单的动作,所以出现了大量的内存泄漏。

泄漏修改点

原代码:判断用户非法后没有给响应,导致申请的nua_handle,message,soa_session的Home内存没有被释放

	if (regtype == REG_REGISTER) {
		if(switch_akcs_check_valid_user(de->sip->sip_to->a_url->url_user) > 0)
		{	
			nua_respond(nh, SIP_401_UNAUTHORIZED, TAG_IF(msg, NUTAG_WITH_THIS_MSG(msg)), SIPTAG_WWW_AUTHENTICATE_STR(auth_str), TAG_END());
		}
	} else if (regtype == REG_INVITE) {
		
		if(switch_akcs_check_valid_user(de->sip->sip_from->a_url->url_user) > 0)
		{
			nua_respond(nh, SIP_407_PROXY_AUTH_REQUIRED, 
						TAG_IF(msg, NUTAG_WITH_THIS_MSG(msg)), 
						SIPTAG_PROXY_AUTHENTICATE_STR(auth_str), TAG_END());	
		}
		else if(switch_akcs_check_valid_user(de->sip->sip_to->a_url->url_user) <= 0)
		{
			//拉入黑名单
			char network_ip[80] = {0};
			//int network_port = 0;

			
			//sofia_glue_get_addr(de->data->e_msg, network_ip, sizeof(network_ip), &network_port);
			//added by chenyc, 2019-12-06 加入业务限制记录中
			//AddBussiness(network_ip);
			if(de->sip->sip_via)
			{
				sip_via_t *via = NULL;
				for (via = de->sip->sip_via; via->v_next; via = via->v_next)
				{
					//switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "via ip:%s port:%s\n",via->v_host, via->v_port);
				}
				switch_copy_string(network_ip, via->v_host, sizeof(network_ip));
		
				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid user:%s,from ip:%s\n",de->sip->sip_to->a_url->url_user,network_ip);
				switch_akcs_add_bussiness(network_ip);
			}
		}
	}

修复方法:

if (regtype == REG_REGISTER) {
        if (switch_akcs_check_valid_user(de->sip->sip_to->a_url->url_user) > 0) {
            nua_respond(nh, SIP_401_UNAUTHORIZED, TAG_IF(msg, NUTAG_WITH_THIS_MSG(msg)), SIPTAG_WWW_AUTHENTICATE_STR(auth_str), TAG_END());
        } else {
			nua_respond(nh, SIP_403_FORBIDDEN, TAG_IF(msg, NUTAG_WITH_THIS_MSG(msg)), TAG_END());
		}
    } else if (regtype == REG_INVITE) {

        if (switch_akcs_check_valid_user(de->sip->sip_from->a_url->url_user) > 0) {
            nua_respond(nh, SIP_407_PROXY_AUTH_REQUIRED,
                        TAG_IF(msg, NUTAG_WITH_THIS_MSG(msg)),
                        SIPTAG_PROXY_AUTHENTICATE_STR(auth_str), TAG_END());
        } else if (switch_akcs_check_valid_user(de->sip->sip_to->a_url->url_user) <= 0) {
            //拉入黑名单
            char network_ip[80] = {0};
            //int network_port = 0;


            //sofia_glue_get_addr(de->data->e_msg, network_ip, sizeof(network_ip), &network_port);
            //added by chenyc, 2019-12-06 加入业务限制记录中
            //AddBussiness(network_ip);
            if (de->sip->sip_via) {
                sip_via_t *via = NULL;
                for (via = de->sip->sip_via; via->v_next; via = via->v_next) {
                    //switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "via ip:%s port:%s\n",via->v_host, via->v_port);
                }
                switch_copy_string(network_ip, via->v_host, sizeof(network_ip));

                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid user:%s,from ip:%s\n", de->sip->sip_to->a_url->url_user, network_ip);
                switch_akcs_add_bussiness(network_ip);
            }
            nua_respond(nh, SIP_407_PROXY_AUTH_REQUIRED,
                        TAG_IF(msg, NUTAG_WITH_THIS_MSG(msg)),
                        SIPTAG_PROXY_AUTHENTICATE_STR(auth_str), TAG_END());
        }
    }

总结

从不知所措到最终攻克这个难题,坚持很重要。每个人都会有畏难的心态,但是要学会去克服,释放自己的压力。怎么克服呢?

1、把困难当作历练提升的契机。像这次内存泄漏定位,就是很好的了解freeswitch的机会,借着这次机会,提升了对sofia-sip的底层的理解。

2、把自己的压力讲出来。

3、关注过程更甚于关注结果。如果目标比较远,需要时间累积。一直望著遥远的目标没有帮助,只是增加现在不想开始的藉口。改为关注自己每一次迈进的步伐,只要方向正确,比什么都没有开始好。

你可能感兴趣的:(FreeSwitch)