ltrace.exp revision 59d2fb2b7c4eb4dab5ad5f08b10cfdc4fa8f27cb
1# This file is part of ltrace. 2# Copyright (C) 2012 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# Results: 222# Each of the files is deleted. Returns nothing. 223 224proc WipeFiles {files} { 225 verbose "WipeFiles: $files\n" 226 foreach f $files { 227 file delete $f 228 } 229} 230 231# LtraceTempFile -- 232# 233# Create a temporary file according to a pattern, and return its 234# name. 235# 236# Arguments: 237# pat Pattern to use. See mktemp for description of its format. 238# 239# Results: 240# Creates the temporary file and returns its name. The name is 241# also appended to LTRACE_TEMP_FILES. 242 243proc LtraceTempFile {pat} { 244 global LTRACE_TEMP_FILES 245 set ret [exec mktemp --tmpdir $pat] 246 lappend LTRACE_TEMP_FILES $ret 247 return $ret 248} 249 250# ltraceSource -- 251# 252# Create a temporary file with a given suffix and prime it with 253# contents given in text. 254# 255# Arguments: 256# suffix Suffix of the temp file to be created. 257# 258# text Contents of the new file. 259# 260# Results: 261# Returns file name of created file. 262 263proc ltraceSource {suffix text} { 264 set ret [LtraceTempFile "lt-XXXXXXXXXX.$suffix"] 265 266 set chan [open $ret w] 267 puts -nonewline $chan $text 268 close $chan 269 270 return $ret 271} 272 273# LtraceCompileObjects -- 274# 275# Compile each source file into an object file. ltrace_compile 276# is called to perform actual compilation. 277# 278# Arguments: 279# sources List of source files. 280# 281# options Options for ltrace_compile. 282# 283# retName Variable where the resulting list of object names is 284# to be placed. 285# Results: 286# Returns true or false depending on whether there were any 287# errors. If it returns true, then variable referenced by 288# retName contains list of object files, produced by compiling 289# files in sources list. 290 291proc LtraceCompileObjects {sources options retName} { 292 global LTRACE_TEMP_FILES 293 upvar $retName ret 294 set ret {} 295 296 foreach source $sources { 297 set sourcebase [file tail $source] 298 set dest $source.o 299 lappend LTRACE_TEMP_FILES $dest 300 verbose "LtraceCompileObjects: $source -> $dest" 301 if {[ltrace_compile $source $dest object $options] != ""} { 302 return false 303 } 304 lappend ret $dest 305 } 306 307 return true 308} 309 310# ltraceCompile -- 311# 312# This attempts to compile a binary from sources given in ARGS. 313# 314# Arguments: 315# dest A binary to be produced. If this is called lib*.so, then 316# the resulting binary will be a library, if *.pie, it 317# will be a PIE, otherwise it will be an executable. In 318# theory this could also be *.o for "object" and *.i for 319# "preprocess" for cases with one source file, but that 320# is not supported at the moment. The binary will be 321# placed in $objdir/$subdir. 322# 323# args List of options and source files. 324# 325# Options are arguments that start with a dash. Options 326# (sans the dash) are passed to ltrace_compile. 327# 328# Source files named lib*.so are libraries. Those are 329# passed to ltrace_compile as options shlib=X. Source 330# files named *.o are objects. The remaining source 331# files are first compiled (by LtraceCompileObjects) and 332# then together with other objects passed to 333# ltrace_compile to produce resulting binary. 334# 335# Any argument that is empty string prompts the function 336# to fail. This is done so that errors caused by 337# ltraceSource (or similar) distribute naturally 338# upwards. 339# 340# Results: 341# This compiles given source files into a binary. Full file name 342# of that binary is returned. Empty string is returned in case 343# of a failure. 344 345proc ltraceCompile {dest args} { 346 global objdir 347 global subdir 348 349 get_compiler_info {} c 350 get_compiler_info {} c++ 351 352 if {[string match "lib*.so" $dest]} { 353 set type "library" 354 set extraObjOptions "additional_flags=-fpic" 355 set extraOptions "additional_flags=-shared" 356 } elseif {[string match "*.pie" $dest]} { 357 set type "executable" 358 set extraObjOptions "additional_flags=-fpic" 359 set extraOptions "additional_flags=-pie" 360 } else { 361 set type "executable" 362 set extraObjOptions {} 363 set extraOptions {} 364 } 365 366 set options {} 367 set sources {} 368 set objects {} 369 foreach a $args { 370 if {[string match "-?*" $a]} { 371 lappend options [string range $a 1 end] 372 } elseif {[string match "*.so" $a]} { 373 lappend options "shlib=$a" 374 } elseif {[string match "*.o" $a]} { 375 lappend objects $a 376 } else { 377 lappend sources $a 378 } 379 } 380 381 if {[string equal $dest {}]} { 382 set dest [LtraceTempFile "exe-XXXXXXXXXX"] 383 } elseif {[string equal $dest ".pie"]} { 384 set dest [LtraceTempFile "pie-XXXXXXXXXX"] 385 } else { 386 set dest $objdir/$subdir/$dest 387 } 388 389 verbose "ltraceCompile: dest $dest" 390 verbose " : options $options" 391 verbose " : sources $sources" 392 verbose " : objects $objects" 393 394 if {![LtraceCompileObjects $sources \ 395 [concat $options $extraObjOptions] newObjects]} { 396 return {} 397 } 398 set objects [concat $objects $newObjects] 399 400 verbose "ltraceCompile: objects $objects" 401 402 if {[ltrace_compile $objects $dest $type \ 403 [concat $options $extraOptions]] != ""} { 404 return {} 405 } 406 407 return $dest 408} 409 410# ltraceRun -- 411# 412# Invoke command identified by LTRACE global variable with given 413# ARGS. A logfile redirection is automatically ordered by 414# passing -o and a temporary file name. 415# 416# Arguments: 417# args Arguments to ltrace binary. 418# 419# Results: 420# Returns name of logfile. The "exec" command that it uses 421# under the hood fails loudly if the process exits with a 422# non-zero exit status, or uses stderr in any way. 423 424proc ltraceRun {args} { 425 global LTRACE 426 global objdir 427 global subdir 428 429 set LdPath [ld_library_path $objdir/$subdir] 430 set logfile [ltraceSource ltrace {}] 431 432 # Run ltrace. expect will show an error if this doesn't exit with 433 # zero exit status (i.e. ltrace fails, valgrind finds errors, 434 # etc.). 435 436 verbose "env LD_LIBRARY_PATH=$LdPath $LTRACE -o $logfile $args" 437 eval "exec env LD_LIBRARY_PATH=$LdPath $LTRACE -o $logfile $args" 438 439 return $logfile 440} 441 442# ltraceDone -- 443# 444# Wipes or dumps all temporary files after a test suite has 445# finished. 446# 447# Results: 448# Doesn't return anything. Wipes all files gathered in 449# LTRACE_TEMP_FILES. If SAVE_TEMPS is defined and true, the 450# temporary files are not wiped, but their names are dumped 451# instead. Contents of LTRACE_TEMP_FILES are deleted in any 452# case. 453 454proc ltraceDone {} { 455 global SAVE_TEMPS 456 global LTRACE_TEMP_FILES 457 458 if {[info exists SAVE_TEMPS] && $SAVE_TEMPS} { 459 foreach tmp $LTRACE_TEMP_FILES { 460 send_user "$tmp\n" 461 } 462 } else { 463 WipeFiles $LTRACE_TEMP_FILES 464 } 465 466 set LTRACE_TEMP_FILES {} 467 return 468} 469 470# Grep -- 471# 472# Return number of lines in a given file, matching a given 473# regular expression. 474# 475# Arguments: 476# logfile File to search through. 477# 478# re Regular expression to match. 479# 480# Results: 481# Returns number of matching lines. 482 483proc Grep {logfile re} { 484 set count 0 485 set fp [open $logfile] 486 while {[gets $fp line] >= 0} { 487 if [regexp -- $re $line] { 488 incr count 489 } 490 } 491 close $fp 492 return $count 493} 494 495# ltraceMatch1 -- 496# 497# Look for a pattern in a given logfile, comparing number of 498# occurences of the pattern with expectation. 499# 500# Arguments: 501# logfile The name of file where to look for patterns. 502# 503# pattern Regular expression pattern to look for. 504# 505# op Operator to compare number of occurences. 506# 507# expect Second operand to op, the first being number of 508# occurences of pattern. 509# 510# Results: 511# Doesn't return anything, but calls fail or pass depending on 512# whether the patterns matches expectation. 513 514proc ltraceMatch1 {logfile pattern {op ==} {expect 1}} { 515 set count [Grep $logfile $pattern] 516 set msgMain "$pattern appears in $logfile $count times" 517 set msgExpect ", expected $op $expect" 518 519 if {[eval expr $count $op $expect]} { 520 pass $msgMain 521 } else { 522 fail $msgMain$msgExpect 523 } 524 return 525} 526 527# ltraceMatch -- 528# 529# Look for series of patterns in a given logfile, comparing 530# number of occurences of each pattern with expectations. 531# 532# Arguments: 533# logfile The name of file where to look for patterns. 534# 535# patterns List of patterns to look for. ltraceMatch1 is called 536# on each of these in turn. 537# 538# Results: 539# 540# Doesn't return anything, but calls fail or pass depending on 541# whether each of the patterns holds. 542 543proc ltraceMatch {logfile patterns} { 544 foreach pat $patterns { 545 eval ltraceMatch1 [linsert $pat 0 $logfile] 546 } 547 return 548} 549 550# 551# ltrace_options OPTIONS_LIST 552# Pass ltrace commandline options. 553# 554proc ltrace_options { args } { 555 556 global LTRACE_OPTIONS 557 set LTRACE_OPTIONS $args 558} 559 560# 561# ltrace_args ARGS_LIST 562# Pass ltrace'd program its own commandline options. 563# 564proc ltrace_args { args } { 565 566 global LTRACE_ARGS 567 set LTRACE_ARGS $args 568} 569 570# 571# handle run-time library paths 572# 573proc ld_library_path { args } { 574 575 set ALL_LIBRARY_PATHS { } 576 if [info exists LD_LIBRARY_PATH] { 577 lappend ALL_LIBRARY_PATHS $LD_LIBRARY_PATH 578 } 579 global libelf_LD_LIBRARY_PATH 580 if {[string length $libelf_LD_LIBRARY_PATH] > 0} { 581 lappend ALL_LIBRARY_PATHS $libelf_LD_LIBRARY_PATH 582 } 583 global libunwind_LD_LIBRARY_PATH 584 if {[string length $libunwind_LD_LIBRARY_PATH] > 0} { 585 lappend ALL_LIBRARY_PATHS $libunwind_LD_LIBRARY_PATH 586 } 587 lappend ALL_LIBRARY_PATHS $args 588 join $ALL_LIBRARY_PATHS ":" 589} 590 591# 592# ltrace_runtest LD_LIBRARY_PATH BIN FILE 593# Trace the execution of BIN and return result. 594# 595# BIN is program-under-test. 596# LD_LIBRARY_PATH is the env for program-under-test to run. 597# FILE is to save the output from ltrace with default name $BIN.ltrace. 598# Retrun output from ltrace. 599# 600proc ltrace_runtest { args } { 601 602 global LTRACE 603 global LTRACE_OPTIONS 604 global LTRACE_ARGS 605 606 verbose "LTRACE = $LTRACE" 607 608 set LD_LIBRARY_PATH_ [ld_library_path [lindex $args 0]] 609 set BIN [lindex $args 1] 610 611 # specify the output file, the default one is $BIN.ltrace 612 if [llength $args]==3 then { 613 set file [lindex $args 2] 614 } else { 615 set file $BIN.ltrace 616 } 617 618 # Remove the file first. If ltrace fails to overwrite it, we 619 # would be comparing output to an obsolete run. 620 exec rm -f $file 621 622 # append this option to LTRACE_OPTIONS. 623 lappend LTRACE_OPTIONS "-o" 624 lappend LTRACE_OPTIONS "$file" 625 verbose "LTRACE_OPTIONS = $LTRACE_OPTIONS" 626 #ltrace the PUT. 627 catch "exec sh -c {export LD_LIBRARY_PATH=$LD_LIBRARY_PATH_; $LTRACE $LTRACE_OPTIONS $BIN $LTRACE_ARGS;exit}" output 628 629 # return output from ltrace. 630 return $output 631} 632 633# 634# ltrace_verify_output FILE_TO_SEARCH PATTERN MAX_LINE 635# Verify the ltrace output by comparing the number of PATTERN in 636# FILE_TO_SEARCH with INSTANCE_NO. Do not specify INSTANCE_NO if 637# instance number is ignored in this test. 638# Reutrn: 639# 0 = number of PATTERN in FILE_TO_SEARCH inqual to INSTANCE_NO. 640# 1 = number of PATTERN in FILE_TO_SEARCH qual to INSTANCE_NO. 641# 642proc ltrace_verify_output { file_to_search pattern {instance_no 0} {grep_command "grep"}} { 643 644 # compute the number of PATTERN in FILE_TO_SEARCH by grep and wc. 645 catch "exec sh -c {$grep_command \"$pattern\" $file_to_search | wc -l ;exit}" output 646 verbose "output = $output" 647 648 if [ regexp "syntax error" $output ] then { 649 fail "Invalid regular expression $pattern" 650 } elseif { $instance_no == 0 } then { 651 if { $output == 0 } then { 652 fail "Fail to find $pattern in $file_to_search" 653 } else { 654 pass "$pattern in $file_to_search" 655 } 656 } elseif { $output >= $instance_no } then { 657 pass "$pattern in $file_to_search for $output times" 658 } else { 659 fail "$pattern in $file_to_search for $output times, should be $instance_no" 660 } 661} 662