ltrace.exp revision 3aac545b7947db4225a37408bc1c765d5e32ff7c
1# This file is part of ltrace.
2# Copyright (C) 2012, 2013 Petr Machata, Red Hat Inc.
3# Copyright (C) 2006 Yao Qi, IBM Corporation
4#
5# This program is free software; you can redistribute it and/or
6# modify it under the terms of the GNU General Public License as
7# published by the Free Software Foundation; either version 2 of the
8# License, or (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful, but
11# WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13# General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18# 02110-1301 USA
19
20# Generic ltrace test subroutines that should work for any target.  If these
21# need to be modified for any target, it can be done with a variable
22# or by passing arguments.
23
24source $objdir/env.exp
25
26if [info exists TOOL_EXECUTABLE] {
27	set LTRACE $TOOL_EXECUTABLE
28} else {
29	set LTRACE $objdir/../ltrace
30}
31
32if {[info exists VALGRIND] && ![string equal $VALGRIND {}]} {
33	verbose "Running under valgrind command: `$VALGRIND'"
34	set LTRACE "$VALGRIND $LTRACE"
35}
36
37set LTRACE_OPTIONS {}
38set LTRACE_ARGS {}
39set LTRACE_TEMP_FILES {}
40
41# ltrace_compile SOURCE DEST TYPE OPTIONS
42#
43# Compile PUT(program under test) by native compiler.   ltrace_compile runs
44# the right compiler, and TCL captures the output, and I evaluate the output.
45#
46# SOURCE is the name of program under test, with full directory.
47# DEST is the name of output of compilation, with full directory.
48# TYPE is an enum-like variable to affect the format or result of compiler
49#   output.  Values:
50#   executable   if output is an executable.
51#   object       if output is an object.
52# OPTIONS is option to compiler in this compilation.
53proc ltrace_compile {source dest type options} {
54    global LTRACE_TESTCASE_OPTIONS;
55
56    if {![string equal "object" $type]} {
57	# Add platform-specific options if a shared library was specified using
58	# "shlib=librarypath" in OPTIONS.
59	set new_options ""
60	set shlib_found 0
61
62	foreach opt $options {
63	    if [regexp {^shlib=(.*)} $opt dummy_var shlib_name] {
64		if [test_compiler_info "xlc*"] {
65		    # IBM xlc compiler doesn't accept shared library named other
66		    # than .so: use "-Wl," to bypass this
67		    lappend source "-Wl,$shlib_name"
68		} else {
69		    lappend source $shlib_name
70		}
71
72		if {$shlib_found == 0} {
73		    set shlib_found 1
74
75		    if { ([test_compiler_info "gcc-*"]&& ([istarget "powerpc*-*-aix*"]|| [istarget "rs6000*-*-aix*"] ))} {
76			lappend options "additional_flags=-L${objdir}/${subdir}"
77		    } elseif { [istarget "mips-sgi-irix*"] } {
78			lappend options "additional_flags=-rpath ${objdir}/${subdir}"
79		    }
80		}
81
82	    } else {
83		lappend new_options $opt
84	    }
85	}
86
87	#end of for loop
88	set options $new_options
89    }
90
91    # dump some information for debug purpose.
92    verbose "options are $options"
93    verbose "source is $source $dest $type $options"
94
95    # Wipe the DEST file, so that we don't end up running an obsolete
96    # version of the binary.
97    exec rm -f $dest
98
99    set result [target_compile $source $dest $type $options];
100    verbose "result is $result"
101    regsub "\[\r\n\]*$" "$result" "" result;
102    regsub "^\[\r\n\]*" "$result" "" result;
103    if { $result != "" && [lsearch $options quiet] == -1} {
104	clone_output "compile failed for ltrace test, $result"
105    }
106    return $result;
107}
108
109proc get_compiler_info {binfile args} {
110    # For compiler.c and compiler.cc
111    global srcdir
112
113    # I am going to play with the log to keep noise out.
114    global outdir
115    global tool
116
117    # These come from compiler.c or compiler.cc
118    global compiler_info
119
120    # Legacy global data symbols.
121    #global gcc_compiled
122
123    # Choose which file to preprocess.
124    set ifile "${srcdir}/lib/compiler.c"
125    if { [llength $args] > 0 && [lindex $args 0] == "c++" } {
126	    set ifile "${srcdir}/lib/compiler.cc"
127    }
128
129    # Run $ifile through the right preprocessor.
130    # Toggle ltrace.log to keep the compiler output out of the log.
131    #log_file
132    set cppout [ ltrace_compile "${ifile}" "" preprocess [list "$args" quiet] ]
133    #log_file -a "$outdir/$tool.log"
134
135    # Eval the output.
136    set unknown 0
137    foreach cppline [ split "$cppout" "\n" ] {
138	    if { [ regexp "^#" "$cppline" ] } {
139	      # line marker
140	    } elseif { [ regexp "^\[\n\r\t \]*$" "$cppline" ] } {
141	      # blank line
142	    } elseif { [ regexp "^\[\n\r\t \]*set\[\n\r\t \]" "$cppline" ] } {
143	    # eval this line
144	      verbose "get_compiler_info: $cppline" 2
145	      eval "$cppline"
146	  } else {
147	    # unknown line
148	    verbose "get_compiler_info: $cppline"
149	    set unknown 1
150	  }
151      }
152
153    # Reset to unknown compiler if any diagnostics happened.
154    if { $unknown } {
155	    set compiler_info "unknown"
156    }
157  return 0
158}
159
160proc test_compiler_info { {compiler ""} } {
161    global compiler_info
162
163     if [string match "" $compiler] {
164         if [info exists compiler_info] {
165	     verbose "compiler_info=$compiler_info"
166	     # if no arg, return the compiler_info string
167             return $compiler_info
168         } else {
169             perror "No compiler info found."
170         }
171     }
172
173    return [string match $compiler $compiler_info]
174}
175
176proc ltrace_compile_shlib {sources dest options} {
177    set obj_options $options
178    verbose "+++++++ [test_compiler_info]"
179    switch -glob [test_compiler_info] {
180	"xlc-*" {
181	    lappend obj_options "additional_flags=-qpic"
182	}
183	"gcc-*" {
184	    if { !([istarget "powerpc*-*-aix*"]
185		   || [istarget "rs6000*-*-aix*"]) } {
186                lappend obj_options "additional_flags=-fpic"
187	    }
188          }
189  "xlc++-*" {
190      lappend obj_options "additional_flags=-qpic"
191  }
192
193	default {
194	    fail "Bad compiler!"
195            }
196    }
197
198    if {![LtraceCompileObjects $sources $obj_options objects]} {
199	return -1
200    }
201
202    set link_options $options
203    if { [test_compiler_info "xlc-*"] || [test_compiler_info "xlc++-*"]} {
204	lappend link_options "additional_flags=-qmkshrobj"
205    } else {
206	lappend link_options "additional_flags=-shared"
207    }
208    if {[ltrace_compile "${objects}" "${dest}" executable $link_options] != ""} {
209	return -1
210    }
211
212    return
213}
214
215# WipeFiles --
216#
217#	Delete each file in the list.
218#
219# Arguments:
220#	files	List of files to delete.
221#
222# Results:
223#	Each of the files is deleted.  Files are deleted in reverse
224#	order, so that directories are emptied and can be deleted
225#	without using -force.  Returns nothing.
226
227proc WipeFiles {files} {
228    verbose "WipeFiles: $files\n"
229    foreach f [lreverse $files] {
230	file delete $f
231    }
232}
233
234# LtraceTmpDir --
235#
236#	Guess what directory to use for temporary files.
237#	This was adapted from http://wiki.tcl.tk/772
238#
239# Results:
240#	A temporary directory to use.  The current directory if no
241#	other seems to be available.
242
243proc LtraceTmpDir {} {
244    set tmpdir [pwd]
245
246    if {[file exists "/tmp"]} {
247	set tmpdir "/tmp"
248    }
249
250    catch {set tmpdir $::env(TMP)}
251    catch {set tmpdir $::env(TEMP)}
252    catch {set tmpdir $::env(TMPDIR)}
253
254    return $tmpdir
255}
256
257set LTRACE_TEMP_DIR [LtraceTmpDir]
258
259# LtraceTempFile --
260#
261#	Create a temporary file according to a pattern, and return its
262#	name.  This behaves similar to mktemp.  We don't use mktemp
263#	directly, because on older systems, mktemp requires that the
264#	array of X's be at the very end of the string, while ltrace
265#	temporary files need to have suffixes.
266#
267# Arguments:
268#	pat	Pattern to use.  See mktemp for description of its format.
269#
270# Results:
271#	Creates the temporary file and returns its name.  The name is
272#	also appended to LTRACE_TEMP_FILES.
273
274proc LtraceTempFile {pat} {
275    global LTRACE_TEMP_FILES
276    global LTRACE_TEMP_DIR
277
278    set letters "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
279    set numLetters [string length $letters]
280
281    if {![regexp -indices {(X{3,})} $pat m]} {
282	send_error -- "Pattern $pat contains insufficient number of X's."
283	return {}
284    }
285
286    set start [lindex $m 0]
287    set end [lindex $m 1]
288    set len [expr {$end - $start + 1}]
289
290    for {set j 0} {$j < 10} {incr j} {
291
292	# First, generate a random name.
293
294	set randstr {}
295	for {set i 0} {$i < $len} {incr i} {
296	    set r [expr {int(rand() * $numLetters)}]
297	    append randstr [string index $letters $r]
298	}
299	set prefix [string range $pat 0 [expr {$start - 1}]]
300	set suffix [string range $pat [expr {$end + 1}] end]
301	set name [file join $LTRACE_TEMP_DIR "$prefix$randstr$suffix"]
302
303	# Now check that it's free.  This is of course racy, but this
304	# is a test suite, not anything used in actual production.
305
306	if {[file exists $name]} {
307	    continue
308	}
309
310	# We don't bother attempting to open the file.  Downstream
311	# code can do it itself.
312
313	lappend LTRACE_TEMP_FILES $name
314	return $name
315    }
316
317    send_error -- "Couldn't create a temporary file for pattern $pat."
318    return
319}
320
321# ltraceNamedSource --
322#
323#	Create a file named FILENAME, and prime it with TEXT.  If
324#	REMEMBERTEMP, add the file into LTRACE_TEMP_FILES, so that
325#	ltraceDone (or rather WipeFiles) erases it later.
326#
327# Arguments:
328#	filename	Name of the file to create.
329#
330#	text	Contents of the new file.
331#
332#	rememberTemp	Whether to add filename to LTRACE_TEMP_FILES.
333#
334# Results:
335#	Returns $filename, which now refers to a file with contents
336#	given by TEXT.
337
338proc ltraceNamedSource {filename text {rememberTemp 1}} {
339    global LTRACE_TEMP_FILES
340
341    set chan [open $filename w]
342    puts $chan $text
343    close $chan
344
345    if $rememberTemp {
346	lappend LTRACE_TEMP_FILES $filename
347    }
348
349    return $filename
350}
351
352# ltraceSource --
353#
354#	Create a temporary file with a given suffix and prime it with
355#	contents given in text.
356#
357# Arguments:
358#	suffix	Suffix of the temp file to be created.
359#
360#	text	Contents of the new file.
361#
362# Results:
363#	Returns file name of created file.
364
365proc ltraceSource {suffix text} {
366    return [ltraceNamedSource \
367		[LtraceTempFile "lt-XXXXXXXXXX.$suffix"] $text 0]
368}
369
370# ltraceDir --
371#
372#	Create a temporary directory.
373#
374# Arguments:
375#
376# Results:
377#	Returns name of created directory.
378
379proc ltraceDir {} {
380    set ret [LtraceTempFile "lt-XXXXXXXXXX.dir"]
381    file mkdir $ret
382    return $ret
383}
384
385# LtraceCompileObjects --
386#
387#	Compile each source file into an object file.  ltrace_compile
388#	is called to perform actual compilation.
389#
390# Arguments:
391#	sources	List of source files.
392#
393#	options	Options for ltrace_compile.
394#
395#	retName Variable where the resulting list of object names is
396#		to be placed.
397# Results:
398#	Returns true or false depending on whether there were any
399#	errors.  If it returns true, then variable referenced by
400#	retName contains list of object files, produced by compiling
401#	files in sources list.
402
403proc LtraceCompileObjects {sources options retName} {
404    global LTRACE_TEMP_FILES
405    upvar $retName ret
406    set ret {}
407
408    foreach source $sources {
409	set sourcebase [file tail $source]
410	set dest $source.o
411	lappend LTRACE_TEMP_FILES $dest
412	verbose "LtraceCompileObjects: $source -> $dest"
413	if {[ltrace_compile $source $dest object $options] != ""} {
414	    return false
415	}
416	lappend ret $dest
417    }
418
419    return true
420}
421
422# ltraceCompile --
423#
424#	This attempts to compile a binary from sources given in ARGS.
425#
426# Arguments:
427#	dest	A binary to be produced.  If this is called lib*.so, then
428#		the resulting binary will be a library, if *.pie, it
429#		will be a PIE, otherwise it will be an executable.  In
430#		theory this could also be *.o for "object" and *.i for
431#		"preprocess" for cases with one source file, but that
432#		is not supported at the moment.  The binary will be
433#		placed in $objdir/$subdir.
434#
435#	args	List of options and source files.
436#
437#		Options are arguments that start with a dash.  Options
438#		(sans the dash) are passed to ltrace_compile.
439#
440#		Source files named lib*.so are libraries.  Those are
441#		passed to ltrace_compile as options shlib=X.  Source
442#		files named *.o are objects.  The remaining source
443#		files are first compiled (by LtraceCompileObjects) and
444#		then together with other objects passed to
445#		ltrace_compile to produce resulting binary.
446#
447#		Any argument that is empty string prompts the function
448#		to fail.  This is done so that errors caused by
449#		ltraceSource (or similar) distribute naturally
450#		upwards.
451#
452# Results:
453#	This compiles given source files into a binary.  Full file name
454#	of that binary is returned.  Empty string is returned in case
455#	of a failure.
456
457proc ltraceCompile {dest args} {
458    global objdir
459    global subdir
460
461    get_compiler_info {} c
462    get_compiler_info {} c++
463
464    if {[string match "lib*.so" $dest]} {
465	set type "library"
466	set extraObjOptions "additional_flags=-fpic"
467	set extraOptions "additional_flags=-shared"
468    } elseif {[string match "*.pie" $dest]} {
469	set type "executable"
470	set extraObjOptions "additional_flags=-fpic"
471	set extraOptions "additional_flags=-pie"
472    } else {
473	set type "executable"
474	set extraObjOptions {}
475	set extraOptions {}
476    }
477
478    set options {}
479    set sources {}
480    set objects {}
481    foreach a $args {
482	if {[string match "-l*" $a]} {
483	    lappend options "shlib=$a"
484	} elseif {[string match "-?*" $a]} {
485	    lappend options [string range $a 1 end]
486	} elseif {[string match "*.so" $a]} {
487	    lappend options "shlib=$a"
488	} elseif {[string match "*.o" $a]} {
489	    lappend objects $a
490	} else {
491	    lappend sources $a
492	}
493    }
494
495    if {[string equal $dest {}]} {
496	set dest [LtraceTempFile "exe-XXXXXXXXXX"]
497    } elseif {[string equal $dest ".pie"]} {
498	set dest [LtraceTempFile "pie-XXXXXXXXXX"]
499    } else {
500	set dest $objdir/$subdir/$dest
501    }
502
503    verbose "ltraceCompile: dest $dest"
504    verbose "             : options $options"
505    verbose "             : sources $sources"
506    verbose "             : objects $objects"
507
508    if {![LtraceCompileObjects $sources \
509	      [concat $options $extraObjOptions] newObjects]} {
510	return {}
511    }
512    set objects [concat $objects $newObjects]
513
514    verbose "ltraceCompile: objects $objects"
515
516    if {[ltrace_compile $objects $dest $type \
517	     [concat $options $extraOptions]] != ""} {
518	return {}
519    }
520
521    return $dest
522}
523
524# ltraceRun --
525#
526#	Invoke command identified by LTRACE global variable with given
527#	ARGS.  A logfile redirection is automatically ordered by
528#	passing -o and a temporary file name.
529#
530# Arguments:
531#	args	Arguments to ltrace binary.
532#
533# Results:
534#	Returns name of logfile.  The "exec" command that it uses
535#	under the hood fails loudly if the process exits with a
536#	non-zero exit status, or uses stderr in any way.
537
538proc ltraceRun {args} {
539    global LTRACE
540    global objdir
541    global subdir
542
543    set LdPath [ld_library_path $objdir/$subdir]
544    set logfile [ltraceSource ltrace {}]
545
546    # Run ltrace.  expect will show an error if this doesn't exit with
547    # zero exit status (i.e. ltrace fails, valgrind finds errors,
548    # etc.).
549
550    set command "exec env LD_LIBRARY_PATH=$LdPath $LTRACE -o $logfile $args"
551    verbose $command
552    if {[catch {eval $command}] } {
553	fail "test case execution failed"
554	send_error -- $command
555	send_error -- $::errorInfo
556    }
557
558    return $logfile
559}
560
561# ltraceDone --
562#
563#	Wipes or dumps all temporary files after a test suite has
564#	finished.
565#
566# Results:
567#	Doesn't return anything.  Wipes all files gathered in
568#	LTRACE_TEMP_FILES.  If SAVE_TEMPS is defined and true, the
569#	temporary files are not wiped, but their names are dumped
570#	instead.  Contents of LTRACE_TEMP_FILES are deleted in any
571#	case.
572
573proc ltraceDone {} {
574    global SAVE_TEMPS
575    global LTRACE_TEMP_FILES
576
577    if {[info exists SAVE_TEMPS] && $SAVE_TEMPS} {
578	foreach tmp $LTRACE_TEMP_FILES {
579	    send_user "$tmp\n"
580	}
581    } else {
582	WipeFiles $LTRACE_TEMP_FILES
583    }
584
585    set LTRACE_TEMP_FILES {}
586    return
587}
588
589# Grep --
590#
591#	Return number of lines in a given file, matching a given
592#	regular expression.
593#
594# Arguments:
595#	logfile	File to search through.
596#
597#	re	Regular expression to match.
598#
599# Results:
600#	Returns number of matching lines.
601
602proc Grep {logfile re} {
603    set count 0
604    set fp [open $logfile]
605    while {[gets $fp line] >= 0} {
606	if [regexp -- $re $line] {
607	    incr count
608	}
609    }
610    close $fp
611    return $count
612}
613
614# ltraceMatch1 --
615#
616#	Look for a pattern in a given logfile, comparing number of
617#	occurences of the pattern with expectation.
618#
619# Arguments:
620#	logfile	The name of file where to look for patterns.
621#
622#	pattern	Regular expression pattern to look for.
623#
624#	op	Operator to compare number of occurences.
625#
626#	expect	Second operand to op, the first being number of
627#		occurences of pattern.
628#
629# Results:
630#	Doesn't return anything, but calls fail or pass depending on
631#	whether the patterns matches expectation.
632
633proc ltraceMatch1 {logfile pattern {op ==} {expect 1}} {
634    set count [Grep $logfile $pattern]
635    set msgMain "$pattern appears in $logfile $count times"
636    set msgExpect ", expected $op $expect"
637
638    if {[eval expr $count $op $expect]} {
639	pass $msgMain
640    } else {
641	fail $msgMain$msgExpect
642    }
643    return
644}
645
646# ltraceMatch --
647#
648#	Look for series of patterns in a given logfile, comparing
649#	number of occurences of each pattern with expectations.
650#
651# Arguments:
652#	logfile	The name of file where to look for patterns.
653#
654#	patterns List of patterns to look for.  ltraceMatch1 is called
655#		on each of these in turn.
656#
657# Results:
658#
659#	Doesn't return anything, but calls fail or pass depending on
660#	whether each of the patterns holds.
661
662proc ltraceMatch {logfile patterns} {
663    foreach pat $patterns {
664	eval ltraceMatch1 [linsert $pat 0 $logfile]
665    }
666    return
667}
668
669# ltraceLibTest --
670#
671#	Generate a binary, a library (liblib.so) and a config file.
672#	Run the binary using ltraceRun, passing it -F to load the
673#	config file.
674#
675# Arguments:
676#	conf	Contents of ltrace config file.
677#
678#	cdecl	Contents of header file.
679#
680#	libcode	Contents of library implementation file.
681#
682#	maincode	Contents of function "main".
683#
684#	params	Additional parameters to pass to ltraceRun.
685#
686# Results:
687#
688#	Returns whatever ltraceRun returns.
689
690proc ltraceLibTest {conf cdecl libcode maincode {params ""}} {
691    set conffile [ltraceSource conf $conf]
692    set lib [ltraceCompile liblib.so [ltraceSource c [concat $cdecl $libcode]]]
693    set bin [ltraceCompile {} $lib \
694		 [ltraceSource c \
695		      [concat $cdecl "int main(void) {" $maincode "}"]]]
696
697    return [eval [concat "ltraceRun -F $conffile " $params "-- $bin"]]
698}
699
700#
701# ltrace_options OPTIONS_LIST
702# Pass ltrace commandline options.
703#
704proc ltrace_options { args } {
705
706	global LTRACE_OPTIONS
707	set LTRACE_OPTIONS $args
708}
709
710#
711# ltrace_args ARGS_LIST
712# Pass ltrace'd program its own commandline options.
713#
714proc ltrace_args { args } {
715
716	global LTRACE_ARGS
717	set LTRACE_ARGS $args
718}
719
720#
721# handle run-time library paths
722#
723proc ld_library_path { args } {
724
725	set ALL_LIBRARY_PATHS { }
726	if [info exists LD_LIBRARY_PATH] {
727		lappend ALL_LIBRARY_PATHS $LD_LIBRARY_PATH
728	}
729	global libelf_LD_LIBRARY_PATH
730	if {[string length $libelf_LD_LIBRARY_PATH] > 0} {
731		lappend ALL_LIBRARY_PATHS $libelf_LD_LIBRARY_PATH
732	}
733	global libunwind_LD_LIBRARY_PATH
734	if {[string length $libunwind_LD_LIBRARY_PATH] > 0} {
735		lappend ALL_LIBRARY_PATHS $libunwind_LD_LIBRARY_PATH
736	}
737	lappend ALL_LIBRARY_PATHS $args
738	join $ALL_LIBRARY_PATHS ":"
739}
740
741#
742# ltrace_runtest LD_LIBRARY_PATH BIN FILE
743# Trace the execution of BIN and return result.
744#
745# BIN is program-under-test.
746# LD_LIBRARY_PATH is the env for program-under-test to run.
747# FILE is to save the output from ltrace with default name $BIN.ltrace.
748# Retrun output from ltrace.
749#
750proc ltrace_runtest { args } {
751
752	global LTRACE
753	global LTRACE_OPTIONS
754	global LTRACE_ARGS
755
756	verbose "LTRACE = $LTRACE"
757
758	set LD_LIBRARY_PATH_ [ld_library_path [lindex $args 0]]
759	set BIN [lindex $args 1]
760
761	# specify the output file, the default one is $BIN.ltrace
762	if [llength $args]==3 then {
763		set file [lindex $args 2]
764	} else {
765		set file $BIN.ltrace
766	}
767
768	# Remove the file first.  If ltrace fails to overwrite it, we
769	# would be comparing output to an obsolete run.
770	exec rm -f $file
771
772	# append this option to LTRACE_OPTIONS.
773	lappend LTRACE_OPTIONS "-o"
774	lappend LTRACE_OPTIONS "$file"
775	verbose "LTRACE_OPTIONS = $LTRACE_OPTIONS"
776	set command "exec sh -c {export LD_LIBRARY_PATH=$LD_LIBRARY_PATH_; \
777		$LTRACE $LTRACE_OPTIONS $BIN $LTRACE_ARGS;exit}"
778	#ltrace the PUT.
779	if {[catch $command output]} {
780	    fail "test case execution failed"
781	    send_error -- $command
782	    send_error -- $::errorInfo
783	}
784
785	# return output from ltrace.
786	return $output
787}
788
789#
790# ltrace_verify_output FILE_TO_SEARCH PATTERN MAX_LINE
791# Verify the ltrace output by comparing the number of PATTERN in
792# FILE_TO_SEARCH with INSTANCE_NO.  Do not specify INSTANCE_NO if
793# instance number is ignored in this test.
794# Reutrn:
795#      0 = number of PATTERN in FILE_TO_SEARCH inqual to INSTANCE_NO.
796#      1 = number of PATTERN in FILE_TO_SEARCH qual to INSTANCE_NO.
797#
798proc ltrace_verify_output { file_to_search pattern {instance_no 0} {grep_command "grep"}} {
799
800	# compute the number of PATTERN in FILE_TO_SEARCH by grep and wc.
801	catch "exec sh -c {$grep_command \"$pattern\" $file_to_search | wc -l ;exit}" output
802	verbose "output = $output"
803
804	if [ regexp "syntax error" $output ] then {
805		fail "Invalid regular expression $pattern"
806        } elseif { $instance_no == 0 } then {
807		if { $output == 0 } then {
808			fail "Fail to find $pattern in $file_to_search"
809		} else {
810			pass "$pattern in $file_to_search"
811		}
812	} elseif { $output >= $instance_no } then {
813		pass "$pattern in $file_to_search for $output times"
814	} else {
815		fail "$pattern in $file_to_search for $output times, should be $instance_no"
816	}
817}
818