1#!/bin/bash
2#
3# This Source Code Form is subject to the terms of the Mozilla Public
4# License, v. 2.0. If a copy of the MPL was not distributed with this
5# file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7########################################################################
8#
9# mozilla/security/nss/tests/memleak/memleak.sh
10#
11# Script to test memory leaks in NSS
12#
13# needs to work on Solaris and Linux platforms, on others just print a message
14# that OS is not supported
15#
16# special strings
17# ---------------
18#   FIXME ... known problems, search for this string
19#   NOTE .... unexpected behavior
20#
21########################################################################
22
23############################# memleak_init #############################
24# local shell function to initialize this script
25########################################################################
26memleak_init()
27{
28	if [ -z "${INIT_SOURCED}" -o "${INIT_SOURCED}" != "TRUE" ]; then
29		cd ../common
30		. ./init.sh
31	fi
32
33	if [ ! -r ${CERT_LOG_FILE} ]; then
34		cd ${QADIR}/cert
35		. ./cert.sh
36	fi
37
38	SCRIPTNAME="memleak.sh"
39	if [ -z "${CLEANUP}" ] ; then
40		CLEANUP="${SCRIPTNAME}"
41	fi
42
43	OLD_LIBRARY_PATH=${LD_LIBRARY_PATH}
44	TMP_LIBDIR="${HOSTDIR}/tmp"
45	TMP_STACKS="${HOSTDIR}/stacks"
46	TMP_SORTED="${HOSTDIR}/sorted"
47	TMP_COUNT="${HOSTDIR}/count"
48	DBXOUT="${HOSTDIR}/dbxout"
49	DBXERR="${HOSTDIR}/dbxerr"
50	DBXCMD="${HOSTDIR}/dbxcmd"
51
52	PORT=${PORT:-8443}
53
54	MODE_LIST="NORMAL BYPASS FIPS"
55
56	SERVER_DB="${HOSTDIR}/server_memleak"
57	CLIENT_DB="${HOSTDIR}/client_memleak"
58	cp -r ${HOSTDIR}/server ${SERVER_DB}
59	cp -r ${HOSTDIR}/client ${CLIENT_DB}
60
61	LOGDIR="${HOSTDIR}/memleak_logs"
62	mkdir -p ${LOGDIR}
63
64	FOUNDLEAKS="${LOGDIR}/foundleaks"
65
66	REQUEST_FILE="${QADIR}/memleak/sslreq.dat"
67	IGNORED_STACKS="${QADIR}/memleak/ignored"
68
69	gline=`echo ${OBJDIR} | grep "_64_"`
70	if [ -n "${gline}" ] ; then
71		BIT_NAME="64"
72	else
73		BIT_NAME="32"
74	fi
75
76	case "${OS_NAME}" in
77	"SunOS")
78		DBX=`which dbx`
79		AWK=nawk
80
81		if [ $? -eq 0 ] ; then
82			echo "${SCRIPTNAME}: DBX found: ${DBX}"
83		else
84			echo "${SCRIPTNAME}: DBX not found, skipping memory leak checking."
85			exit 0
86		fi
87
88		PROC_ARCH=`uname -p`
89
90		if [ "${PROC_ARCH}" = "sparc" ] ; then
91			if [ "${BIT_NAME}" = "64" ] ; then
92				FREEBL_DEFAULT="libfreebl_64fpu_3"
93				FREEBL_LIST="${FREEBL_DEFAULT} libfreebl_64int_3"
94			else
95				FREEBL_DEFAULT="libfreebl_32fpu_3"
96				FREEBL_LIST="${FREEBL_DEFAULT} libfreebl_32int64_3"
97			fi
98		else
99			if [ "${BIT_NAME}" = "64" ] ; then
100				echo "${SCRIPTNAME}: OS not supported for memory leak checking."
101				exit 0
102			fi
103
104			FREEBL_DEFAULT="libfreebl_3"
105			FREEBL_LIST="${FREEBL_DEFAULT}"
106		fi
107
108		RUN_COMMAND_DBG="run_command_dbx"
109		PARSE_LOGFILE="parse_logfile_dbx"
110		;;
111	"Linux")
112		VALGRIND=`which valgrind`
113		AWK=awk
114
115		if [ $? -eq 0 ] ; then
116			echo "${SCRIPTNAME}: Valgrind found: ${VALGRIND}"
117		else
118			echo "${SCRIPTNAME}: Valgrind not found, skipping memory leak checking."
119			exit 0
120		fi
121
122		FREEBL_DEFAULT="libfreebl_3"
123		FREEBL_LIST="${FREEBL_DEFAULT}"
124
125		RUN_COMMAND_DBG="run_command_valgrind"
126		PARSE_LOGFILE="parse_logfile_valgrind"
127		;;
128	*)
129		echo "${SCRIPTNAME}: OS not supported for memory leak checking."
130		exit 0
131		;;
132	esac
133
134	if [ "${BUILD_OPT}" = "1" ] ; then
135		OPT="OPT"
136	else
137		OPT="DBG"
138	fi
139
140	NSS_DISABLE_UNLOAD="1"
141	export NSS_DISABLE_UNLOAD
142
143	SELFSERV_ATTR="-D -p ${PORT} -d ${SERVER_DB} -n ${HOSTADDR} -e ${HOSTADDR}-ec -w nss -c :C001:C002:C003:C004:C005:C006:C007:C008:C009:C00A:C00B:C00C:C00D:C00E:C00F:C010:C011:C012:C013:C014cdefgijklmnvyz -t 5 -V ssl3:tls1.2"
144	TSTCLNT_ATTR="-p ${PORT} -h ${HOSTADDR} -c j -f -d ${CLIENT_DB} -w nss -o"
145	STRSCLNT_ATTR="-q -p ${PORT} -d ${CLIENT_DB} -w nss -c 1000 -n TestUser ${HOSTADDR}"
146
147	tbytes=0
148	tblocks=0
149	truns=0
150
151	MEMLEAK_DBG=1
152	export MEMLEAK_DBG
153}
154
155########################### memleak_cleanup ############################
156# local shell function to clean up after this script
157########################################################################
158memleak_cleanup()
159{
160	unset MEMLEAK_DBG
161	unset NSS_DISABLE_UNLOAD
162
163	. ${QADIR}/common/cleanup.sh
164}
165
166############################ set_test_mode #############################
167# local shell function to set testing mode for server and for client
168########################################################################
169set_test_mode()
170{
171	if [ "${server_mode}" = "BYPASS" ] ; then
172		echo "${SCRIPTNAME}: BYPASS is ON"
173		SERVER_OPTION="-B -s"
174		CLIENT_OPTION=""
175	elif [ "${client_mode}" = "BYPASS" ] ; then
176		echo "${SCRIPTNAME}: BYPASS is ON"
177		SERVER_OPTION=""
178		CLIENT_OPTION="-B -s"
179	else
180		echo "${SCRIPTNAME}: BYPASS is OFF"
181		SERVER_OPTION=""
182		CLIENT_OPTION=""
183	fi
184
185	if [ "${server_mode}" = "FIPS" ] ; then
186		${BINDIR}/modutil -dbdir ${SERVER_DB} -fips true -force
187		${BINDIR}/modutil -dbdir ${SERVER_DB} -list
188		${BINDIR}/modutil -dbdir ${CLIENT_DB} -fips false -force
189		${BINDIR}/modutil -dbdir ${CLIENT_DB} -list
190
191		echo "${SCRIPTNAME}: FIPS is ON"
192		cipher_list="c d e i j k n v y z"
193	elif [ "${client_mode}" = "FIPS" ] ; then
194
195		${BINDIR}/modutil -dbdir ${SERVER_DB} -fips false -force
196		${BINDIR}/modutil -dbdir ${SERVER_DB} -list
197		${BINDIR}/modutil -dbdir ${CLIENT_DB} -fips true -force
198		${BINDIR}/modutil -dbdir ${CLIENT_DB} -list
199
200		echo "${SCRIPTNAME}: FIPS is ON"
201		cipher_list="c d e i j k n v y z"
202	else
203		${BINDIR}/modutil -dbdir ${SERVER_DB} -fips false -force
204		${BINDIR}/modutil -dbdir ${SERVER_DB} -list
205		${BINDIR}/modutil -dbdir ${CLIENT_DB} -fips false -force
206		${BINDIR}/modutil -dbdir ${CLIENT_DB} -list
207
208		echo "${SCRIPTNAME}: FIPS is OFF"
209		# ciphers l and m removed, see bug 1136095
210		cipher_list=":C001 :C002 :C003 :C004 :C005 :C006 :C007 :C008 :C009 :C00A :C010 :C011 :C012 :C013 :C014 c d e f g i j k n v y z"
211	fi
212}
213
214############################## set_freebl ##############################
215# local shell function to set freebl - sets temporary path for libraries
216########################################################################
217set_freebl()
218{
219	if [ "${freebl}" = "${FREEBL_DEFAULT}" ] ; then
220		LD_LIBRARY_PATH="${OLD_LIBRARY_PATH}"
221		export LD_LIBRARY_PATH
222	else
223		if [ -d "${TMP_LIBDIR}" ] ; then
224			rm -rf ${TMP_LIBDIR}
225		fi
226
227		mkdir ${TMP_LIBDIR}
228		[ $? -ne 0 ] && html_failed "Create temp directory" && return 1
229
230		cp ${DIST}/${OBJDIR}/lib/*.so ${DIST}/${OBJDIR}/lib/*.chk ${TMP_LIBDIR}
231		[ $? -ne 0 ] && html_failed "Copy libraries to temp directory" && return 1
232
233		echo "${SCRIPTNAME}: Using ${freebl} instead of ${FREEBL_DEFAULT}"
234
235		mv ${TMP_LIBDIR}/${FREEBL_DEFAULT}.so ${TMP_LIBDIR}/${FREEBL_DEFAULT}.so.orig
236		[ $? -ne 0 ] && html_failed "Move ${FREEBL_DEFAULT}.so -> ${FREEBL_DEFAULT}.so.orig" && return 1
237
238		cp ${TMP_LIBDIR}/${freebl}.so ${TMP_LIBDIR}/${FREEBL_DEFAULT}.so
239		[ $? -ne 0 ] && html_failed "Copy ${freebl}.so -> ${FREEBL_DEFAULT}.so" && return 1
240
241		mv ${TMP_LIBDIR}/${FREEBL_DEFAULT}.chk ${TMP_LIBDIR}/${FREEBL_DEFAULT}.chk.orig
242		[ $? -ne 0 ] && html_failed "Move ${FREEBL_DEFAULT}.chk -> ${FREEBL_DEFAULT}.chk.orig" && return 1
243
244		cp ${TMP_LIBDIR}/${freebl}.chk ${TMP_LIBDIR}/${FREEBL_DEFAULT}.chk
245		[ $? -ne 0 ] && html_failed "Copy ${freebl}.chk to temp directory" && return 1
246
247		echo "ls -l ${TMP_LIBDIR}"
248		ls -l ${TMP_LIBDIR}
249
250		LD_LIBRARY_PATH="${TMP_LIBDIR}"
251		export LD_LIBRARY_PATH
252	fi
253
254	return 0
255}
256
257############################# clear_freebl #############################
258# local shell function to set default library path and clear temporary
259# directory for libraries created by function set_freebl
260########################################################################
261clear_freebl()
262{
263	LD_LIBRARY_PATH="${OLD_LIBRARY_PATH}"
264	export LD_LIBRARY_PATH
265
266	if [ -d "${TMP_LIBDIR}" ] ; then
267		rm -rf ${TMP_LIBDIR}
268	fi
269}
270
271############################ run_command_dbx ###########################
272# local shell function to run command under dbx tool
273########################################################################
274run_command_dbx()
275{
276	COMMAND=$1
277	shift
278	ATTR=$*
279
280	COMMAND=`which ${COMMAND}`
281
282	echo "dbxenv follow_fork_mode parent" > ${DBXCMD}
283	echo "dbxenv rtc_mel_at_exit verbose" >> ${DBXCMD}
284	echo "dbxenv rtc_biu_at_exit verbose" >> ${DBXCMD}
285	echo "check -memuse -match 16 -frames 16" >> ${DBXCMD}
286	echo "run ${ATTR}" >> ${DBXCMD}
287
288	export NSS_DISABLE_ARENA_FREE_LIST=1
289
290	echo "${SCRIPTNAME}: -------- Running ${COMMAND} under DBX:"
291	echo "${DBX} ${COMMAND}"
292	echo "${SCRIPTNAME}: -------- DBX commands:"
293	cat ${DBXCMD}
294
295	( ${DBX} ${COMMAND} < ${DBXCMD} > ${DBXOUT} 2> ${DBXERR} )
296	grep -v Reading ${DBXOUT} 1>&2
297	cat ${DBXERR}
298
299	unset NSS_DISABLE_ARENA_FREE_LIST
300
301	grep "exit code is" ${DBXOUT}
302	grep "exit code is 0" ${DBXOUT} > /dev/null
303	return $?
304}
305
306######################### run_command_valgrind #########################
307# local shell function to run command under valgrind tool
308########################################################################
309run_command_valgrind()
310{
311	COMMAND=$1
312	shift
313	ATTR=$*
314
315	export NSS_DISABLE_ARENA_FREE_LIST=1
316
317	echo "${SCRIPTNAME}: -------- Running ${COMMAND} under Valgrind:"
318	echo "${VALGRIND} --tool=memcheck --leak-check=yes --show-reachable=yes --partial-loads-ok=yes --leak-resolution=high --num-callers=50 ${COMMAND} ${ATTR}"
319	echo "Running: ${COMMAND} ${ATTR}" 1>&2
320	${VALGRIND} --tool=memcheck --leak-check=yes --show-reachable=yes --partial-loads-ok=yes --leak-resolution=high --num-callers=50 ${COMMAND} ${ATTR} 1>&2
321	ret=$?
322	echo "==0=="
323
324	unset NSS_DISABLE_ARENA_FREE_LIST
325
326	return $ret
327}
328
329############################# run_selfserv #############################
330# local shell function to start selfserv
331########################################################################
332run_selfserv()
333{
334	echo "PATH=${PATH}"
335	echo "LD_LIBRARY_PATH=${LD_LIBRARY_PATH}"
336	echo "${SCRIPTNAME}: -------- Running selfserv:"
337	echo "selfserv ${SELFSERV_ATTR}"
338	${BINDIR}/selfserv ${SELFSERV_ATTR}
339	ret=$?
340	if [ $ret -ne 0 ]; then
341		html_failed "${LOGNAME}: Selfserv"
342		echo "${SCRIPTNAME} ${LOGNAME}: " \
343			"Selfserv produced a returncode of ${ret} - FAILED"
344	fi
345}
346
347########################### run_selfserv_dbg ###########################
348# local shell function to start selfserv under debug tool
349########################################################################
350run_selfserv_dbg()
351{
352	echo "PATH=${PATH}"
353	echo "LD_LIBRARY_PATH=${LD_LIBRARY_PATH}"
354	${RUN_COMMAND_DBG} ${BINDIR}/selfserv ${SERVER_OPTION} ${SELFSERV_ATTR}
355	ret=$?
356	if [ $ret -ne 0 ]; then
357		html_failed "${LOGNAME}: Selfserv"
358		echo "${SCRIPTNAME} ${LOGNAME}: " \
359			"Selfserv produced a returncode of ${ret} - FAILED"
360	fi
361}
362
363############################# run_strsclnt #############################
364# local shell function to run strsclnt for all ciphers and send stop
365# command to selfserv over tstclnt
366########################################################################
367run_strsclnt()
368{
369	for cipher in ${cipher_list}; do
370		VMIN="ssl3"
371		VMAX="tls1.2"
372		case "${cipher}" in
373		f|g)
374			# TLS 1.1 disallows export cipher suites.
375			VMAX="tls1.0"
376			;;
377		esac
378		ATTR="${STRSCLNT_ATTR} -C ${cipher} -V ${VMIN}:${VMAX}"
379		echo "${SCRIPTNAME}: -------- Trying cipher ${cipher}:"
380		echo "strsclnt ${ATTR}"
381		${BINDIR}/strsclnt ${ATTR}
382		ret=$?
383		if [ $ret -ne 0 ]; then
384			html_failed "${LOGNAME}: Strsclnt with cipher ${cipher}"
385			echo "${SCRIPTNAME} ${LOGNAME}: " \
386				"Strsclnt produced a returncode of ${ret} - FAILED"
387		fi
388	done
389
390	ATTR="${TSTCLNT_ATTR} -V ssl3:tls1.2"
391	echo "${SCRIPTNAME}: -------- Stopping server:"
392	echo "tstclnt ${ATTR} < ${REQUEST_FILE}"
393	${BINDIR}/tstclnt ${ATTR} < ${REQUEST_FILE}
394	ret=$?
395	if [ $ret -ne 0 ]; then
396		html_failed "${LOGNAME}: Tstclnt"
397		echo "${SCRIPTNAME} ${LOGNAME}: " \
398			"Tstclnt produced a returncode of ${ret} - FAILED"
399	fi
400
401	sleep 20
402	kill $(jobs -p) 2> /dev/null
403}
404
405########################### run_strsclnt_dbg ###########################
406# local shell function to run strsclnt under debug tool for all ciphers
407# and send stop command to selfserv over tstclnt
408########################################################################
409run_strsclnt_dbg()
410{
411	for cipher in ${cipher_list}; do
412		VMIN="ssl3"
413		VMAX="tls1.2"
414		case "${cipher}" in
415		f|g)
416			# TLS 1.1 disallows export cipher suites.
417			VMAX="tls1.0"
418			;;
419		esac
420		ATTR="${STRSCLNT_ATTR} -C ${cipher} -V ${VMIN}:${VMAX}"
421		${RUN_COMMAND_DBG} ${BINDIR}/strsclnt ${CLIENT_OPTION} ${ATTR}
422		ret=$?
423		if [ $ret -ne 0 ]; then
424			html_failed "${LOGNAME}: Strsclnt with cipher ${cipher}"
425			echo "${SCRIPTNAME} ${LOGNAME}: " \
426				"Strsclnt produced a returncode of ${ret} - FAILED"
427		fi
428	done
429
430	ATTR="${TSTCLNT_ATTR} -V ssl3:tls1.2"
431	echo "${SCRIPTNAME}: -------- Stopping server:"
432	echo "tstclnt ${ATTR} < ${REQUEST_FILE}"
433	${BINDIR}/tstclnt ${ATTR} < ${REQUEST_FILE}
434	ret=$?
435	if [ $ret -ne 0 ]; then
436		html_failed "${LOGNAME}: Tstclnt"
437		echo "${SCRIPTNAME} ${LOGNAME}: " \
438			"Tstclnt produced a returncode of ${ret} - FAILED"
439	fi
440
441	kill $(jobs -p) 2> /dev/null
442}
443
444stat_clear()
445{
446	stat_minbytes=9999999
447	stat_maxbytes=0
448	stat_minblocks=9999999
449	stat_maxblocks=0
450	stat_bytes=0
451	stat_blocks=0
452	stat_runs=0
453}
454
455stat_add()
456{
457	read hash lbytes bytes_str lblocks blocks_str in_str lruns runs_str \
458		minbytes minbytes_str maxbytes maxbytes_str minblocks \
459		minblocks_str maxblocks maxblocks_str rest < ${TMP_COUNT}
460	rm ${TMP_COUNT}
461
462	tbytes=`expr ${tbytes} + ${lbytes}`
463	tblocks=`expr ${tblocks} + ${lblocks}`
464	truns=`expr ${truns} + ${lruns}`
465
466	if [ ${stat_minbytes} -gt ${minbytes} ]; then
467		stat_minbytes=${minbytes}
468	fi
469
470	if [ ${stat_maxbytes} -lt ${maxbytes} ]; then
471		stat_maxbytes=${maxbytes}
472	fi
473
474	if [ ${stat_minblocks} -gt ${minblocks} ]; then
475		stat_minblocks=${minblocks}
476	fi
477
478	if [ ${stat_maxblocks} -lt ${maxblocks} ]; then
479		stat_maxblocks=${maxblocks}
480	fi
481
482	stat_bytes=`expr ${stat_bytes} + ${lbytes}`
483	stat_blocks=`expr ${stat_blocks} + ${lblocks}`
484	stat_runs=`expr ${stat_runs} + ${lruns}`
485}
486
487stat_print()
488{
489	if [ ${stat_runs} -gt 0 ]; then
490		stat_avgbytes=`expr "${stat_bytes}" / "${stat_runs}"`
491		stat_avgblocks=`expr "${stat_blocks}" / "${stat_runs}"`
492
493		echo
494		echo "$1 statistics:"
495		echo "Leaked bytes: ${stat_minbytes} min, ${stat_avgbytes} avg, ${stat_maxbytes} max"
496		echo "Leaked blocks: ${stat_minblocks} min, ${stat_avgblocks} avg, ${stat_maxblocks} max"
497		echo "Total runs: ${stat_runs}"
498		echo
499	fi
500}
501
502########################## run_ciphers_server ##########################
503# local shell function to test server part of code (selfserv)
504########################################################################
505run_ciphers_server()
506{
507	html_head "Memory leak checking - server"
508
509	stat_clear
510
511	client_mode="NORMAL"
512	for server_mode in ${MODE_LIST}; do
513		set_test_mode
514
515		for freebl in ${FREEBL_LIST}; do
516			set_freebl || continue
517
518			LOGNAME=server-${BIT_NAME}-${freebl}-${server_mode}
519			LOGFILE=${LOGDIR}/${LOGNAME}.log
520			echo "Running ${LOGNAME}"
521
522			(
523			    run_selfserv_dbg 2>> ${LOGFILE} &
524			    sleep 5
525			    run_strsclnt
526			)
527
528			sleep 20
529			clear_freebl
530
531			log_parse
532			ret=$?
533
534			html_msg ${ret} 0 "${LOGNAME}" "produced a returncode of $ret, expected is 0"
535		done
536	done
537
538	stat_print "Selfserv"
539
540	html "</TABLE><BR>"
541}
542
543########################## run_ciphers_client ##########################
544# local shell function to test client part of code (strsclnt)
545########################################################################
546run_ciphers_client()
547{
548	html_head "Memory leak checking - client"
549
550	stat_clear
551
552	server_mode="NORMAL"
553	for client_mode in ${MODE_LIST}; do
554		set_test_mode
555
556		for freebl in ${FREEBL_LIST}; do
557			set_freebl || continue
558
559			LOGNAME=client-${BIT_NAME}-${freebl}-${client_mode}
560			LOGFILE=${LOGDIR}/${LOGNAME}.log
561			echo "Running ${LOGNAME}"
562
563			(
564			    run_selfserv &
565			    sleep 5
566			    run_strsclnt_dbg 2>> ${LOGFILE}
567			)
568
569			sleep 20
570			clear_freebl
571
572			log_parse
573			ret=$?
574			html_msg ${ret} 0 "${LOGNAME}" "produced a returncode of $ret, expected is 0"
575		done
576	done
577
578	stat_print "Strsclnt"
579
580	html "</TABLE><BR>"
581}
582
583########################## parse_logfile_dbx ###########################
584# local shell function to parse and process logs from dbx
585########################################################################
586parse_logfile_dbx()
587{
588	${AWK} '
589	BEGIN {
590		in_mel = 0
591		mel_line = 0
592		bytes = 0
593		lbytes = 0
594		minbytes = 9999999
595		maxbytes = 0
596		blocks = 0
597		lblocks = 0
598		minblocks = 9999999
599		maxblocks = 0
600		runs = 0
601		stack_string = ""
602		bin_name = ""
603	}
604	/Memory Leak \(mel\):/ ||
605	/Possible memory leak -- address in block \(aib\):/ ||
606	/Block in use \(biu\):/ {
607		in_mel = 1
608		stack_string = ""
609		next
610	}
611	in_mel == 1 && /^$/ {
612		print bin_name stack_string
613		in_mel = 0
614		mel_line = 0
615		next
616	}
617	in_mel == 1 {
618		mel_line += 1
619	}
620	/Found leaked block of size/ {
621		bytes += $6
622		blocks += 1
623		next
624	}
625	/Found .* leaked blocks/ {
626		bytes += $8
627		blocks += $2
628		next
629	}
630	/Found block of size/ {
631		bytes += $5
632		blocks += 1
633		next
634	}
635	/Found .* blocks totaling/ {
636		bytes += $5
637		blocks += $2
638		next
639	}
640	mel_line > 2 {
641		gsub(/\(\)/, "")
642		new_line = $2
643		stack_string = "/" new_line stack_string
644		next
645	}
646	/^Running: / {
647		bin_name = $2
648		next
649	}
650	/execution completed/ {
651		runs += 1
652		lbytes += bytes
653		minbytes = (minbytes < bytes) ? minbytes : bytes
654		maxbytes = (maxbytes > bytes) ? maxbytes : bytes
655		bytes = 0
656		lblocks += blocks
657		minblocks = (minblocks < blocks) ? minblocks : blocks
658		maxblocks = (maxblocks > blocks) ? maxblocks : blocks
659		blocks = 0
660		next
661	}
662	END {
663		print "# " lbytes " bytes " lblocks " blocks in " runs " runs " \
664		minbytes " minbytes " maxbytes " maxbytes " minblocks " minblocks " \
665		maxblocks " maxblocks " > "/dev/stderr"
666	}' 2> ${TMP_COUNT}
667
668	stat_add
669}
670
671######################## parse_logfile_valgrind ########################
672# local shell function to parse and process logs from valgrind
673########################################################################
674parse_logfile_valgrind()
675{
676	${AWK} '
677	BEGIN {
678		in_mel = 0
679		in_sum = 0
680		bytes = 0
681		lbytes = 0
682		minbytes = 9999999
683		maxbytes = 0
684		blocks = 0
685		lblocks = 0
686		minblocks = 9999999
687		maxblocks = 0
688		runs = 0
689		stack_string = ""
690		bin_name = ""
691	}
692	!/==[0-9]*==/ {
693		if ( $1 == "Running:" )
694			bin_name = $2
695			bin_nf = split(bin_name, bin_fields, "/")
696			bin_name = bin_fields[bin_nf]
697		next
698	}
699	/blocks are/ {
700		in_mel = 1
701		stack_string = ""
702		next
703	}
704	/LEAK SUMMARY/ {
705		in_sum = 1
706		next
707	}
708	/^==[0-9]*== *$/ {
709		if (in_mel)
710			print bin_name stack_string
711		if (in_sum) {
712			runs += 1
713			lbytes += bytes
714			minbytes = (minbytes < bytes) ? minbytes : bytes
715			maxbytes = (maxbytes > bytes) ? maxbytes : bytes
716			bytes = 0
717			lblocks += blocks
718			minblocks = (minblocks < blocks) ? minblocks : blocks
719			maxblocks = (maxblocks > blocks) ? maxblocks : blocks
720			blocks = 0
721		}
722		in_sum = 0
723		in_mel = 0
724		next
725	}
726	in_mel == 1 {
727		new_line = $4
728		if ( new_line == "(within")
729			new_line = "*"
730		stack_string = "/" new_line stack_string
731	}
732	in_sum == 1 {
733		for (i = 2; i <= NF; i++) {
734			if ($i == "bytes") {
735				str = $(i - 1)
736				gsub(",", "", str)
737				bytes += str
738			}
739			if ($i == "blocks.") {
740				str = $(i - 1)
741				gsub(",", "", str)
742				blocks += str
743			}
744		}
745	}
746	END {
747		print "# " lbytes " bytes " lblocks " blocks in " runs " runs " \
748		minbytes " minbytes " maxbytes " maxbytes " minblocks " minblocks " \
749		maxblocks " maxblocks " > "/dev/stderr"
750	}' 2> ${TMP_COUNT}
751
752	stat_add
753}
754
755############################# check_ignored ############################
756# local shell function to check all stacks if they are not ignored
757########################################################################
758check_ignored()
759{
760	${AWK} -F/ '
761	BEGIN {
762		ignore = "'${IGNORED_STACKS}'"
763		# read in the ignore file
764		BUGNUM = ""
765		count = 0
766		new = 0
767		while ((getline line < ignore) > 0)  {
768			if (line ~ "^#[0-9]+") {
769				BUGNUM = line
770			} else if (line ~ "^#") {
771				continue
772			} else if (line == "") {
773				continue
774			} else {
775				bugnum_array[count] = BUGNUM
776				# Create a regular expression for the ignored stack:
777				# replace * with % so we can later replace them with regular expressions
778				# without messing up everything (the regular expressions contain *)
779				gsub("\\*", "%", line)
780				# replace %% with .*
781				gsub("%%", ".*", line)
782				# replace % with [^/]*
783				gsub("%", "[^/]*", line)
784				# add ^ at the beginning
785				# add $ at the end
786				line_array[count] = "^" line "$"
787				count++
788			}
789		}
790	}
791	{
792		match_found = 0
793		# Look for matching ignored stack
794		for (i = 0; i < count; i++) {
795			if ($0 ~ line_array[i]) {
796				# found a match
797				match_found = 1
798				bug_found = bugnum_array[i]
799				break
800			}
801		}
802		# Process result
803		if (match_found == 1 ) {
804				if (bug_found != "") {
805					print "IGNORED STACK (" bug_found "): " $0
806				} else {
807					print "IGNORED STACK: " $0
808				}
809		} else {
810				print "NEW STACK: " $0
811				new = 1
812		}
813	}
814	END {
815		exit new
816	}'
817	ret=$?
818	return $ret
819}
820
821############################### parse_log ##############################
822# local shell function to parse log file
823########################################################################
824log_parse()
825{
826	${PARSE_LOGFILE} < ${LOGFILE} > ${TMP_STACKS}
827	echo "${SCRIPTNAME}: Processing log ${LOGNAME}:" > ${TMP_SORTED}
828	cat ${TMP_STACKS} | sort -u | check_ignored >> ${TMP_SORTED}
829	ret=$?
830	echo >> ${TMP_SORTED}
831
832	cat ${TMP_SORTED} | tee -a ${FOUNDLEAKS}
833	rm ${TMP_STACKS} ${TMP_SORTED}
834
835	return ${ret}
836}
837
838############################## cnt_total ###############################
839# local shell function to count total leaked bytes
840########################################################################
841cnt_total()
842{
843	echo ""
844	echo "TinderboxPrint:${OPT} Lk bytes: ${tbytes}"
845	echo "TinderboxPrint:${OPT} Lk blocks: ${tblocks}"
846	echo "TinderboxPrint:${OPT} # of runs: ${truns}"
847	echo ""
848}
849
850############################### run_ocsp ###############################
851# local shell function to run ocsp tests
852########################################################################
853run_ocsp()
854{
855	stat_clear
856
857	cd ${QADIR}/iopr
858	. ./ocsp_iopr.sh
859	ocsp_iopr_run
860
861	stat_print "Ocspclnt"
862}
863
864############################## run_chains ##############################
865# local shell function to run PKIX certificate chains tests
866########################################################################
867run_chains()
868{
869    stat_clear
870
871    LOGNAME="chains"
872    LOGFILE=${LOGDIR}/chains.log
873
874    . ${QADIR}/chains/chains.sh
875
876    stat_print "Chains"
877}
878
879############################## run_chains ##############################
880# local shell function to run memory leak tests
881#
882# NSS_MEMLEAK_TESTS - list of tests to run, if not defined before,
883# then is redefined to default list
884########################################################################
885memleak_run_tests()
886{
887    nss_memleak_tests="ssl_server ssl_client chains ocsp"
888    NSS_MEMLEAK_TESTS="${NSS_MEMLEAK_TESTS:-$nss_memleak_tests}"
889
890    for MEMLEAK_TEST in ${NSS_MEMLEAK_TESTS}
891    do
892        case "${MEMLEAK_TEST}" in
893        "ssl_server")
894            run_ciphers_server
895            ;;
896        "ssl_client")
897            run_ciphers_client
898            ;;
899        "chains")
900            run_chains
901            ;;
902        "ocsp")
903            run_ocsp
904            ;;
905        esac
906    done
907}
908
909################################# main #################################
910
911memleak_init
912memleak_run_tests
913cnt_total
914memleak_cleanup
915
916