ltrace.exp revision 0c4f66d02980f16989ab9a70e01066df78cb48f8
1# This file is part of ltrace.
2# Copyright (C) 2006 Yao Qi, IBM Corporation
3#
4# This program is free software; you can redistribute it and/or
5# modify it under the terms of the GNU General Public License as
6# published by the Free Software Foundation; either version 2 of the
7# License, or (at your option) any later version.
8#
9# This program is distributed in the hope that it will be useful, but
10# WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12# General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License
15# along with this program; if not, write to the Free Software
16# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
17# 02110-1301 USA
18
19# Generic ltrace test subroutines that should work for any target.  If these
20# need to be modified for any target, it can be done with a variable
21# or by passing arguments.
22
23source $objdir/env.exp
24
25if [info exists TOOL_EXECUTABLE] {
26	set LTRACE $TOOL_EXECUTABLE
27} else {
28	set LTRACE $objdir/../ltrace
29}
30
31if {[info exists VALGRIND] && ![string equal $VALGRIND {}]} {
32	verbose "Running under valgrind command: `$VALGRIND'"
33	set LTRACE "$VALGRIND $LTRACE"
34}
35
36set LTRACE_OPTIONS "";
37set LTRACE_ARGS "";
38
39# ltrace_compile SOURCE DEST TYPE OPTIONS
40#
41# Compile PUT(program under test) by native compiler.   ltrace_compile runs
42# the right compiler, and TCL captures the output, and I evaluate the output.
43#
44# SOURCE is the name of program under test, with full directory.
45# DEST is the name of output of compilation, with full directory.
46# TYPE is an enum-like variable to affect the format or result of compiler
47#   output.  Values:
48#   executable   if output is an executable.
49#   object       if output is an object.
50# OPTIONS is option to compiler in this compilation.
51proc ltrace_compile {source dest type options} {
52    global LTRACE_TESTCASE_OPTIONS;
53
54    if {![string equal "object" $type]} {
55	# Add platform-specific options if a shared library was specified using
56	# "shlib=librarypath" in OPTIONS.
57	set new_options ""
58	set shlib_found 0
59
60	foreach opt $options {
61	    if [regexp {^shlib=(.*)} $opt dummy_var shlib_name] {
62		if [test_compiler_info "xlc*"] {
63		    # IBM xlc compiler doesn't accept shared library named other
64		    # than .so: use "-Wl," to bypass this
65		    lappend source "-Wl,$shlib_name"
66		} else {
67		    lappend source $shlib_name
68		}
69
70		if {$shlib_found == 0} {
71		    set shlib_found 1
72
73		    if { ([test_compiler_info "gcc-*"]&& ([istarget "powerpc*-*-aix*"]|| [istarget "rs6000*-*-aix*"] ))} {
74			lappend options "additional_flags=-L${objdir}/${subdir}"
75		    } elseif { [istarget "mips-sgi-irix*"] } {
76			lappend options "additional_flags=-rpath ${objdir}/${subdir}"
77		    }
78		}
79
80	    } else {
81		lappend new_options $opt
82	    }
83	}
84
85	#end of for loop
86	set options $new_options
87    }
88
89    # dump some information for debug purpose.
90    verbose "options are $options"
91    verbose "source is $source $dest $type $options"
92
93    # Wipe the DEST file, so that we don't end up running an obsolete
94    # version of the binary.
95    exec rm -f $dest
96
97    set result [target_compile $source $dest $type $options];
98    verbose "result is $result"
99    regsub "\[\r\n\]*$" "$result" "" result;
100    regsub "^\[\r\n\]*" "$result" "" result;
101    if { $result != "" && [lsearch $options quiet] == -1} {
102	clone_output "compile failed for ltrace test, $result"
103    }
104    return $result;
105}
106
107proc get_compiler_info {binfile args} {
108    # For compiler.c and compiler.cc
109    global srcdir
110
111    # I am going to play with the log to keep noise out.
112    global outdir
113    global tool
114
115    # These come from compiler.c or compiler.cc
116    global compiler_info
117
118    # Legacy global data symbols.
119    #global gcc_compiled
120
121    # Choose which file to preprocess.
122    set ifile "${srcdir}/lib/compiler.c"
123    if { [llength $args] > 0 && [lindex $args 0] == "c++" } {
124	    set ifile "${srcdir}/lib/compiler.cc"
125    }
126
127    # Run $ifile through the right preprocessor.
128    # Toggle ltrace.log to keep the compiler output out of the log.
129    #log_file
130    set cppout [ ltrace_compile "${ifile}" "" preprocess [list "$args" quiet] ]
131    #log_file -a "$outdir/$tool.log"
132
133    # Eval the output.
134    set unknown 0
135    foreach cppline [ split "$cppout" "\n" ] {
136	    if { [ regexp "^#" "$cppline" ] } {
137	      # line marker
138	    } elseif { [ regexp "^\[\n\r\t \]*$" "$cppline" ] } {
139	      # blank line
140	    } elseif { [ regexp "^\[\n\r\t \]*set\[\n\r\t \]" "$cppline" ] } {
141	    # eval this line
142	      verbose "get_compiler_info: $cppline" 2
143	      eval "$cppline"
144	  } else {
145	    # unknown line
146	    verbose "get_compiler_info: $cppline"
147	    set unknown 1
148	  }
149      }
150
151    # Reset to unknown compiler if any diagnostics happened.
152    if { $unknown } {
153	    set compiler_info "unknown"
154    }
155  return 0
156}
157
158proc test_compiler_info { {compiler ""} } {
159    global compiler_info
160
161     if [string match "" $compiler] {
162         if [info exists compiler_info] {
163	     verbose "compiler_info=$compiler_info"
164	     # if no arg, return the compiler_info string
165             return $compiler_info
166         } else {
167             perror "No compiler info found."
168         }
169     }
170
171    return [string match $compiler $compiler_info]
172}
173
174proc ltrace_compile_shlib {sources dest options} {
175    set obj_options $options
176    verbose "+++++++ [test_compiler_info]"
177    switch -glob [test_compiler_info] {
178	"xlc-*" {
179	    lappend obj_options "additional_flags=-qpic"
180	}
181	"gcc-*" {
182	    if { !([istarget "powerpc*-*-aix*"]
183		   || [istarget "rs6000*-*-aix*"]) } {
184                lappend obj_options "additional_flags=-fpic"
185	    }
186          }
187  "xlc++-*" {
188      lappend obj_options "additional_flags=-qpic"
189  }
190
191	default {
192	    fail "Bad compiler!"
193            }
194    }
195
196    if {![LtraceCompileObjects $sources $obj_options objects]} {
197	return -1
198    }
199
200    set link_options $options
201    if { [test_compiler_info "xlc-*"] || [test_compiler_info "xlc++-*"]} {
202	lappend link_options "additional_flags=-qmkshrobj"
203    } else {
204	lappend link_options "additional_flags=-shared"
205    }
206    if {[ltrace_compile "${objects}" "${dest}" executable $link_options] != ""} {
207	return -1
208    }
209}
210
211# LtraceCompileObjects --
212#
213#	Compile each source file into an object file.  ltrace_compile
214#	is called to perform actual compilation.
215#
216# Arguments:
217#	sources	List of source files.
218#
219#	options	Options for ltrace_compile.
220#
221#	retName Variable where the resulting list of object names is
222#		to be placed.
223# Results:
224#	Returns true or false depending on whether there were any
225#	errors.  If it returns true, then variable referenced by
226#	retName contains list of object files, produced by compiling
227#	files in sources list.
228
229proc LtraceCompileObjects {sources options retName} {
230    upvar $retName ret
231    set ret {}
232
233    foreach source $sources {
234	set sourcebase [file tail $source]
235	set dest $source.o
236	verbose "LtraceCompileObjects: $source -> $dest"
237	if {[ltrace_compile $source $dest object $options] != ""} {
238	    return false
239	}
240	lappend ret $dest
241    }
242
243    return true
244}
245
246#
247# ltrace_options OPTIONS_LIST
248# Pass ltrace commandline options.
249#
250proc ltrace_options { args } {
251
252	global LTRACE_OPTIONS
253	set LTRACE_OPTIONS $args
254}
255
256#
257# ltrace_args ARGS_LIST
258# Pass ltrace'd program its own commandline options.
259#
260proc ltrace_args { args } {
261
262	global LTRACE_ARGS
263	set LTRACE_ARGS $args
264}
265
266#
267# handle run-time library paths
268#
269proc ld_library_path { args } {
270
271	set ALL_LIBRARY_PATHS { }
272	if [info exists LD_LIBRARY_PATH] {
273		lappend ALL_LIBRARY_PATHS $LD_LIBRARY_PATH
274	}
275	global libelf_LD_LIBRARY_PATH
276	if {[string length $libelf_LD_LIBRARY_PATH] > 0} {
277		lappend ALL_LIBRARY_PATHS $libelf_LD_LIBRARY_PATH
278	}
279	global libunwind_LD_LIBRARY_PATH
280	if {[string length $libunwind_LD_LIBRARY_PATH] > 0} {
281		lappend ALL_LIBRARY_PATHS $libunwind_LD_LIBRARY_PATH
282	}
283	lappend ALL_LIBRARY_PATHS $args
284	join $ALL_LIBRARY_PATHS ":"
285}
286
287#
288# ltrace_runtest LD_LIBRARY_PATH BIN FILE
289# Trace the execution of BIN and return result.
290#
291# BIN is program-under-test.
292# LD_LIBRARY_PATH is the env for program-under-test to run.
293# FILE is to save the output from ltrace with default name $BIN.ltrace.
294# Retrun output from ltrace.
295#
296proc ltrace_runtest { args } {
297
298	global LTRACE
299	global LTRACE_OPTIONS
300	global LTRACE_ARGS
301
302	verbose "LTRACE = $LTRACE"
303
304	set LD_LIBRARY_PATH_ [ld_library_path [lindex $args 0]]
305	set BIN [lindex $args 1]
306
307	# specify the output file, the default one is $BIN.ltrace
308	if [llength $args]==3 then {
309		set file [lindex $args 2]
310	} else {
311		set file $BIN.ltrace
312	}
313
314	# Remove the file first.  If ltrace fails to overwrite it, we
315	# would be comparing output to an obsolete run.
316	exec rm -f $file
317
318	# append this option to LTRACE_OPTIONS.
319	lappend LTRACE_OPTIONS "-o"
320	lappend LTRACE_OPTIONS "$file"
321	verbose "LTRACE_OPTIONS = $LTRACE_OPTIONS"
322	#ltrace the PUT.
323	catch "exec sh -c {export LD_LIBRARY_PATH=$LD_LIBRARY_PATH_; $LTRACE $LTRACE_OPTIONS $BIN $LTRACE_ARGS;exit}" output
324
325	# return output from ltrace.
326	return $output
327}
328
329#
330# ltrace_saveoutput OUTPUT FILE
331# Save OUTPUT from ltrace to file FILE.
332# OUTPUT is output from ltrace or return value of ltrace_runtest.
333# FILE is file save output.
334#
335proc ltrace_saveoutput { args } {
336
337	set output [lindex $args 0]
338	set file [lindex $args 1]
339
340	set fd [open $file w]
341     	puts $fd $output
342	close $fd
343}
344
345
346#
347# ltrace_verify_output FILE_TO_SEARCH PATTERN MAX_LINE
348# Verify the ltrace output by comparing the number of PATTERN in
349# FILE_TO_SEARCH with INSTANCE_NO.  Do not specify INSTANCE_NO if
350# instance number is ignored in this test.
351# Reutrn:
352#      0 = number of PATTERN in FILE_TO_SEARCH inqual to INSTANCE_NO.
353#      1 = number of PATTERN in FILE_TO_SEARCH qual to INSTANCE_NO.
354#
355proc ltrace_verify_output { file_to_search pattern {instance_no 0} {grep_command "grep"}} {
356
357	# compute the number of PATTERN in FILE_TO_SEARCH by grep and wc.
358	catch "exec sh -c {$grep_command \"$pattern\" $file_to_search | wc -l ;exit}" output
359	verbose "output = $output"
360
361	if [ regexp "syntax error" $output ] then {
362		fail "Invalid regular expression $pattern"
363        } elseif { $instance_no == 0 } then {
364		if { $output == 0 } then {
365			fail "Fail to find $pattern in $file_to_search"
366		} else {
367			pass "$pattern in $file_to_search"
368		}
369	} elseif { $output >= $instance_no } then {
370		pass "$pattern in $file_to_search for $output times"
371	} else {
372		fail "$pattern in $file_to_search for $output times, should be $instance_no"
373	}
374}
375