load_lib site.exp load_lib "compile_flags.exp" # Since 'server_ns_cleanup' is called in shutdown_server, we need to # include server_ns.exp here. load_lib "server_ns.exp" proc installtest_p {} { global TOOL_OPTIONS if {[info exists TOOL_OPTIONS] && [string match "*install*" $TOOL_OPTIONS]} { return 1 } else { return 0 } } proc grep_kallsyms { pattern } { if {! [catch {exec grep -q "$pattern" "/proc/kallsyms"} dummy]} { return 1 } return 0 } # Test for kernel built-in utrace (CONFIG_UTRACE). # Only original rhel5/6-era utrace need apply. proc utrace_orig_p {} { # We want utrace_attach (rhel5) or utrace_attach_task (rhel6), but don't # get confused by the private module version of any active stap module. return [expr [grep_kallsyms "T utrace_attach"] || [grep_kallsyms "T .utrace_attach_task"]] } # Test for tracepoint-based utrace, or rather the in-kernel # facilities which enable our emulation of utrace. proc utrace_emu_p {} { # Check for the set of 5 tracepoints we need and task_work_add() # (see runtime/autoconf-utrace-via-tracepoints.c for details). return [expr [grep_kallsyms task_work_add] \ && [grep_kallsyms tracepoint_sched_process_fork] \ && [grep_kallsyms tracepoint_sched_process_exec] \ && [grep_kallsyms tracepoint_sched_process_exit] \ && [grep_kallsyms tracepoint_sys_enter] \ && [grep_kallsyms tracepoint_sys_exit] \ ] } # Test for utrace - any flavor will do... proc utrace_p {} { return [expr [utrace_orig_p] || [utrace_emu_p] ] } proc classic_uprobes_p {} { # If this is a utrace kernel, then we can use our version of uprobes. # No need to build it now, stap will handle that itself. # # Although ia64 has classical utrace, uprobes hasn't been # ported there (PR7081). return [expr [utrace_orig_p] && ! [istarget ia64-*-*] ] } proc inode_uprobes_p {} { # inode-uprobes (or unlikely compiled-in classical uprobes?) # # Note we're looking for " uprobe_register" to avoid finding # 'kallsyms_uprobe_register' from a loaded systemtap module. return [expr ([grep_kallsyms register_uprobe] \ || [grep_kallsyms " uprobe_register"]) \ && ! [classic_uprobes_p] \ ] } proc uprobes_p {} { return [expr [classic_uprobes_p] || [inode_uprobes_p] ] } proc classic_uretprobes_p {} { # Classic uprobes always has uretprobes return [classic_uprobes_p] } proc inode_uretprobes_p {} { # inode-uprobes, now with return probes! Looking for any mention of # uretprobe, notably arch_uretprobe_hijack_return_addr return [expr [inode_uprobes_p] && [grep_kallsyms uretprobe] ] } proc uretprobes_p {} { return [expr [classic_uretprobes_p] || [inode_uretprobes_p] ] } proc plt_probes_p {} { # .plt probes need uprobes and a supported arch (x86 and arm) return [expr [uprobes_p] \ && [regexp "^(x86_64|i.86|arm.*|aarch64)$" $::tcl_platform(machine)] ] } proc perf_probes_p {} { # perf probes need and exported perf_event_create_kernel_counter return [grep_kallsyms perf_event_create_kernel_counter] } proc hwbkpt_probes_p {} { global have_hw_breakpoint_p return $have_hw_breakpoint_p } proc readline_p {} { global systemtap_readline_p return $systemtap_readline_p } # Callee probes require GCC v4.7 and stap compiled # with elfutils 0.153 proc callee_probes_p {} { global GCC_Version ELF_Version return [expr [strverscmp $GCC_Version 4.7] >= 0 && \ [strverscmp $ELF_Version 0.153] >= 0] } proc dyninst_p {} { global systemtap_dyninst_p return $systemtap_dyninst_p } proc nss_p {} { global systemtap_nss_p return $systemtap_nss_p } proc java_p {} { global systemtap_java_p return $systemtap_java_p } proc python2_p {} { global systemtap_python2_p return $systemtap_python2_p } proc python3_p {} { global systemtap_python3_p return $systemtap_python3_p } proc bootprobing_p {} { global Dracut_Version return [expr [strverscmp $Dracut_Version 025] >= 0 && \ [file exists "/sbin/new-kernel-pkg"]] } proc module_refresh_p {} { return [min_kernel_vers_p 2.6.29] } proc hrtimer_p {} { return [min_kernel_vers_p 2.6.17] } proc kprobes_disabling_p {} { return [min_kernel_vers_p 2.6.30] } # Returns 1 if kernel vers >= @min, 0 otherwise. The @min parameter must be in # the format x.y.z (e.g. 2.6.18). proc min_kernel_vers_p {min} { # Check proper format if {![regexp {^[0-9]+(\.[0-9]+)*$} $min]} { error "$min is not a valid version number" } set uname [exec uname -r] if {![regexp {^([0-9]+\.[0-9]+\.[0-9]+).*} $uname dummy cur]} { error "can't parse version number from uname -r" } return [expr [strverscmp $cur $min] >= 0] } proc bpf_p {} { # configure.ac checks for , so we will too. return [file exists "/usr/include/linux/bpf.h"] } proc get_runtime_list {} { # Always return the default runtime. set runtime_list [list ""] if {[dyninst_p]} { lappend runtime_list "dyninst" } return $runtime_list } proc print_systemtap_version {} { set version [exec /bin/uname -r] set location "/boot/vmlinux-$version" if {! [file exists $location]} { # try the debuginfo location set location "/usr/lib/debug/lib/modules/$version/vmlinux" if {! [file exists $location]} { set location "" } } print "kernel location: $location" print "kernel version: $version" set location [exec /usr/bin/which stap] regexp {version [^)]*} [exec stap -V 2>@ stdout] version print "systemtap location: $location" print "systemtap version: $version" set location [exec /usr/bin/which gcc] set version [exec gcc --version | head -1] print "gcc location: $location" print "gcc version: $version" } # Check for the optional 'stapusr', 'stapsys', and 'stapdev' users # (and make sure they belong to the correct groups). Check for the # optional 'stap-server' user. proc systemtap_check_users {} { global systemtap_stapusr_user_exists global systemtap_stapsys_user_exists global systemtap_stapdev_user_exists global systemtap_stap_server_user_exists # The optional 'stapusr', 'stapsys', and 'stapdev' users are used # in server testing. If they don't exist, those tests won't get # run. # If we have the optional 'stapusr' user, does he belong to the # 'stapusr' group (and not the 'stapdev' or 'stapsys' groups)? set systemtap_stapusr_user_exists 0 if {! [catch {exec /usr/bin/id -nG stapusr} user_info]} { if {[regexp {.*stapusr.*} "$user_info"] && ! [regexp {.*stapsys.*} "$user_info"] && ! [regexp {.*stapdev.*} "$user_info"]} { set systemtap_stapusr_user_exists 1 } } # If we have the optional 'stapsys' user, does he belong to the # 'stapusr' and 'stapsys' groups (and not the 'stapdev' group)? set systemtap_stapsys_user_exists 0 if {! [catch {exec /usr/bin/id -nG stapsys} user_info]} { if {[regexp {.*stapusr.*} "$user_info"] && [regexp {.*stapsys.*} "$user_info"] && ! [regexp {.*stapdev.*} "$user_info"]} { set systemtap_stapsys_user_exists 1 } } # If we have the optional 'stapdev' user, does he belong to the # 'stapusr' and 'stapdev' groups (it doesn't really matter about # the 'stapsys' group)? set systemtap_stapdev_user_exists 0 if {! [catch {exec /usr/bin/id -nG stapdev} user_info]} { if {[regexp {.*stapusr.*} "$user_info"] && [regexp {.*stapdev.*} "$user_info"]} { set systemtap_stapdev_user_exists 1 } } # The optional 'stap-server' user is used to run the http # server. If it doesn't exist, we can't run the http server. set systemtap_stap_server_user_exists 0 if {! [catch {exec /usr/bin/id stap-server} user_info]} { set systemtap_stap_server_user_exists 1 } } proc setup_systemtap_environment {} { global srcdir env server_pid systemtap_dyninst_p global systemtap_nss_p systemtap_java_p global systemtap_python2_p systemtap_python3_p global have_hw_breakpoint_p systemtap_readline_p global kill_needs_doubledash # need an absolute SRCDIR for the top-level src/ tree # XXX: or, we could change nearby uses of ${SRCDIR}/testsuite to ${SRCDIR} set env(SRCDIR) [fullpath $srcdir/..] # pretend to be a dumb terminal so that coloring is always turned off # otherwise, we will have problems with expect set env(TERM) dumb # Use a local systemtap directory and cache. Add user name so # make check and sudo make check don't clobber each other. set env(SYSTEMTAP_DIR) [pwd]/.systemtap-[exec whoami] exec mkdir -p $env(SYSTEMTAP_DIR) # Start with fresh server certificates exec rm -fr $env(SYSTEMTAP_DIR)/ssl # Remove the rc file exec rm -f $env(SYSTEMTAP_DIR)/rc # All hail the prophet lockdep set chan [open $env(SYSTEMTAP_DIR)/rc w] # Set the --rlimit-cpu just slightly under the 15 minutes, so that following # two independent timeout mechanisms do not coincide. puts $chan "--rlimit-cpu=850" puts $chan "-E 'probe timer.s(900){error(\"probe timeout after 15 minutes\")}'" close $chan # Zap any previous uprobes, if any catch { exec /sbin/rmmod uprobes } # No compile-server started yet. set server_pid 0 # If the environment variable ARCH exists, this can throw off our # custom module building (when set to the wrong value). To be # safe, remove it. if [info exists env(ARCH)] { verbose -log "clearing env ARCH (was $env(ARCH))" unset env(ARCH) } # PATH, SYSTEMTAP_TAPSET, SYSTEMTAP_RUNTIME, LD_LIBRARY_PATH are already set. foreach var {PATH STAP SRCDIR SYSTEMTAP_TAPSET SYSTEMTAP_RUNTIME SYSTEMTAP_DIR LD_LIBRARY_PATH} { if [info exists env($var)] { verbose -log "env $var = $env($var)" } } # Remember if this very version of systemtap compiled with dyninst support if {! [catch {exec sh -c "stap -V 2>&1 | grep -q DYNINST"} dummy]} { set systemtap_dyninst_p 1 } else { set systemtap_dyninst_p 0 } # Remember if this selfsame version of systemtap compiled with nss support if {! [catch {exec sh -c "stap -V 2>&1 | grep -q NSS"} dummy]} { set systemtap_nss_p 1 } else { set systemtap_nss_p 0 } # Remember if this selfsame version of systemtap compiled with java support if {! [catch {exec sh -c "stap -V 2>&1 | grep -q JAVA"} dummy]} { set systemtap_java_p 1 } else { set systemtap_java_p 0 } # Remember if this selfsame version of systemtap compiled with # python2 support if {! [catch {exec sh -c "stap -V 2>&1 | grep -q PYTHON2"} dummy]} { set systemtap_python2_p 1 } else { set systemtap_python2_p 0 } # Remember if this selfsame version of systemtap compiled with # python3 support if {! [catch {exec sh -c "stap -V 2>&1 | grep -q PYTHON3"} dummy]} { set systemtap_python3_p 1 } else { set systemtap_python3_p 0 } # Remember if this selfsame version of systemtap supports HW breakpoints if {! [catch {exec stap -l {kernel.data(0x123).rw}}]} { set have_hw_breakpoint_p 1 } else { set have_hw_breakpoint_p 0 } # Remember if this selfsame version of systemtap compiled with # readline support if {! [catch {exec sh -c "stap -V 2>&1 | grep -q READLINE"} dummy]} { set systemtap_readline_p 1 } else { set systemtap_readline_p 0 } # There are some kill executables that had issues with -SIG -PID, thinking # that the -PID was an option (procps-ng v3.3.2 and v3.3.3) and thus require # a -- before -PID. So check for those, otherwise, just assume that no -- is # needed. if {![catch {exec kill --version 2>@1} killver]} { if {[string match "*procps-ng 3.3.\[23\]" $killver]} { set kill_needs_doubledash 1 } else { # any other procps versions support the standard convention # and anything not procps (e.g. util-linux) supports both set kill_needs_doubledash 0 } } else { # kill doesn't support --version (e.g. busybox), just assume standard # convention set kill_needs_doubledash 0 } systemtap_check_users arch_compile_init return 1 } proc shutdown_server {} { global server_pid server_logfile if { $server_pid != 0 } then { verbose -log "Stopping the systemtap server with PID==$server_pid" catch {exec stap-stop-server $server_pid} set server_pid 0 # Collect then delete the temporary server log file set output [exec cat $server_logfile] verbose -log $output catch {exec rm -f $server_logfile} } foreach module [glob -nocomplain [pwd]/stap_*.ko] { exec /bin/rm -f $module } foreach sig [glob -nocomplain [pwd]/stap_*.ko.sgn] { exec /bin/rm -f $sig } # Make sure that stap can no longer find the server. set waited 0 set use_server --use-server set res 0 set listRes 1 while { $listRes > 0 && $waited < 30 } { catch exec sleep 1 incr waited set res [catch { exec stap --list-servers=online,trusted,compatible >& stap-list-servers.out } seen_servers] set listRes [catch { exec grep "^ host" stap-list-servers.out } looksee] } if { $listRes > 0 } { verbose -log "shutdown_server: warning - stap can still find a server" } else { verbose -log "shutdown_server: no servers (after $waited tries)" } # Cleanup the custom server namespace (if needed). server_ns_cleanup } # Set up the environment so that tests will be performed using the systemtap # client and server. proc setup_server { args } { global srcdir env installed_stap use_server server_spec avahi_ok_p global server_logfile # Create a temporary server log file. Note that we'll delete it # when shutting the server down. if {[catch {exec mktemp} server_logfile]} { verbose -log "Failed to create temporary server log file: $server_logfile" exit 1 } # Start the server if {! [start_server $args]} then { return 0 } # Make sure that stap can find the server. # # Why is the server immediately trusted as an SSL peer without # running "stap --trust-servers=ssl ..."? The server is always # trusted by the user that started it. Technically, the # certificates get saved under ~/.systemtap/ssl (of the user that # started the server). set waited 0 set use_server --use-server set res 0 set listRes 1 while { $listRes != 0 && $waited < 15 } { catch exec sleep 1 incr waited set res [catch { exec stap --list-servers=online,trusted,compatible >& stap-list-servers.out } seen_servers] set listRes [catch { exec grep "^ host" stap-list-servers.out } looksee] } verbose -log "stap --list-servers returned: res==$res" verbose -log $seen_servers[exec cat stap-list-servers.out] verbose -log "grep for servers returned: res==$res\n$looksee" # Try to discover the port the server is listening on from the server log. # We're matching a line of the form: # "Thu Jul 25 11:58:11 2013: Using network address :37804" set res [catch { exec /bin/cat $server_logfile | awk "/Using network address/ {print \$9}" } server_address] send_log "server_address=='$server_address'\n" if {$res != 0} then { verbose -log "Unable to discover the address used by the systemtap server" shutdown_server return 0 } set res [regsub ".*:(\[0-9\]\[0-9\]*)\$" $server_address {\1} server_port] send_log "server_port=='$server_port'\n" if {$res != 1} then { verbose -log "Unable to discover the port used by the systemtap server" shutdown_server return 0 } set server_spec [info hostname]:$server_port send_log "server_spec=='$server_spec'\n" set avahi_ok_p 1 if {$listRes != 0} then { verbose -log "Unable to automatically find the systemtap server -- check firewall settings for mDNS" set avahi_ok_p 0 verbose -log "Client/Server tests will be run by contacting the server directly as $server_spec" # Make sure stap can contact the server directly set use_server --use-server=$server_spec set res [catch {exec stap $use_server -p2 -e {probe begin {exit()}}} looksee] if {$res != 0} then { verbose -log "Unable to contact the server at $server_spec directly" shutdown_server return 0 } } return 1 } proc start_server { options } { global srcdir env server_pid installed_stap server_logfile if {! [nss_p]} { return 0 } # Server management scripts and data are installed if this is an # install test, otherwise there is some setup to do. # Make sure the server management scripts and tools are on the $PATH. if {! [installtest_p]} then { set env(PATH) "$srcdir/..:[pwd]/..:$env(PATH)" set installed_stap "[pwd]/../stap" set env(SYSTEMTAP_SERVER_SCRIPTS) "$srcdir/.." } else { set env(PATH) "$env(PKGLIBDIR):$env(PATH)" set installed_stap "$env(SYSTEMTAP_PATH)/stap" set env(SYSTEMTAP_SERVER_SCRIPTS) $env(PKGLIBDIR) } # Try to start the server. set status 0 if {[catch {eval {exec env STAP_PR11197_OVERRIDE=1 \ env SYSTEMTAP_STAP=[exec which stap] stap-start-server \ --log=$server_logfile} $options} server_pid]} { if {[lindex $::errorCode 0] eq "CHILDSTATUS"} { set status [lindex $::errorCode 2] } } verbose -log "output: $server_pid" if { "$server_pid" == "" || $status != 0 } then { verbose -log "Cannot start a systemtap server" set server_pid 0 return 0 } else { verbose -log "Started a systemtap server as PID==$server_pid" } return 1 } proc cleanup {} { # Stop the stap server, if we started it. shutdown_server } proc normalize_arch { arch } { if {$arch == "ppc64"} then {return "powerpc"} if {$arch == "s390x"} then {return "s390"} if {$arch == "i686"} then {return "i386"} if {$arch == "armv5tel"} then {return "arm"} if {$arch == "armv7l"} then {return "arm"} if {$arch == "armv7lh"} then {return "arm"} if {$arch == "aarch64"} then {return "arm64"} if {$arch == "ppc64le"} then {return "powerpc"} if {$arch == "mips64"} then {return "mips"} return $arch } proc fullpath { path } { if {[string index $path 0] != "/"} then { # relative paths are anchored to the current directory return [pwd]/$path } else { return $path } } proc get_system_info {} { global Host Snapshot Distro DistroID DistroVer GCC_Version GCC_FullVersion ELF_Version env SElinux Dracut_Version set Host [exec /bin/uname -a] if [file exists ../SNAPSHOT] { set Snapshot [exec /bin/cat ../SNAPSHOT] } elseif [file exists $env(SRCDIR)/../SNAPSHOT] { set Snapshot [exec /bin/cat $env(SRCDIR)/../SNAPSHOT] } else { regexp {version [^)]*} [exec stap -V 2>@ stdout] version set Snapshot $version } set Distro "Linux" if {[file exists /usr/bin/lsb_release] \ && ! [catch {exec /usr/bin/lsb_release -d} dummy]} { # this produces one line of this format: # Distribution:\tSTRING set Distro [lrange $dummy 1 end] } else { foreach f {/etc/fedora-release /etc/enterprise-release /etc/redhat-release /etc/suse-release /etc/debian_version} { if [file exists $f] then {set Distro [exec /bin/cat $f]; break } } } # http://0pointer.de/blog/projects/os-release.html # TODO: For distros not having /etc/os-release, we can set DistroID and DistroVer # based on parsing the above Distro string if needed. set DistroID "unknown" set DistroVer "unknown" if {[file exists /etc/os-release]} { set DistroID [exec bash -c {source /etc/os-release; echo $ID}] set DistroVer [exec bash -c {source /etc/os-release; echo $VERSION_ID}] } # Easily parsable version first major minor patch level set n [exec echo "__GNUC__ __GNUC_MINOR__ __GNUC_PATCHLEVEL__" | cpp -P] set n [string map {" " "."} $n] set n [string map {"\n" ""} $n] set GCC_Version "$n" # Plus full version between square brackets for GCC_FullVersion set full [exec gcc --version | head -1] set GCC_FullVersion "$n \[$full\]" # Parse elfutils version stap was compiled with regexp {version [^/]*/([^,/]*)} [exec stap -V 2>@ stdout] all ELF_Version # selinux status if [file exists /usr/sbin/getenforce] { set SElinux [exec /usr/sbin/getenforce] } else { set SElinux "unknown" } # Parse dracut version regexp {\d[^\-]*} [exec sh -c "dracut --help 2>&1 | grep Version || echo 0"] Dracut_Version } proc environment_sanity_test {} { # PR11798: die if kernel-devel is not sufficient to build any modules if {[catch {exec stap -p4 -e {probe begin {exit()}} 2>@ stdout} result]} { puts "\n\n\n**** failed systemtap kernel-devel smoke test:\n" puts $result # puts "****\n" # puts $options puts "\n**** aborting testing.\n" cleanup exit 1 } # PR11798: die also if kernel-debuginfo is not available # NB: if one introduced a [kernel_debuginfo_p] proc like the # ones for uprobes/utrace above, and sprinkled it throughout, # then this wouldn't have to be a failing condition. # Note the --skip-badvars -w, we just care there is some debuginfo, # it is allowed have bad var location descriptors (which will cause # some tests to fail of course). Just test -p2, kernel-devel smoke # test above does a full module build, we don't need another one. if {[catch {exec stap --skip-badvars -w -p2 -e {probe kernel.function("do_exit") {println ($code)}} 2>@ stdout} result]} { puts "\n\n\n**** failed systemtap kernel-debuginfo smoke test:\n" puts $result # puts "****\n" # puts $options puts "\n**** aborting testing.\n" cleanup exit 1 } # Create a tempory directory. if {[catch {exec mktemp -d} tmpdir]} { verbose -log "Failed to create temporary directory: $tmpdir" exit 1 } set curdir [pwd] cd ${tmpdir} # Make more likely that all development packages for the supported ABIs (-m64 -m32/-m31) # are installed by building a quick hello.c and hello.cxx program with both. set source "hello.c" set hello_file [open $source "w"] puts $hello_file "#include " puts $hello_file "int main () { printf(\"Hello World!\"); return 0; }" close $hello_file for {set i 0} {$i < [arch_compile_flags]} {incr i} { set flags "additional_flags=-g compiler=gcc [arch_compile_flag $i]" set exe "hello-[arch_compile_flag_name $i]" set result [target_compile $source $exe executable $flags] if { $result != "" } { puts "\n\n\n**** failed gcc [arch_compile_flag_name $i] smoke test:\n" puts $result puts "Please install libgcc and glibc development packages for [arch_compile_flag_name $i]\n" exit 1 } } set source "hello.cxx" set hello_file [open $source "w"] puts $hello_file "#include " puts $hello_file "using namespace std;" puts $hello_file "int main () { cout << \"Hello World!\" << endl; return 0; }" close $hello_file for {set i 0} {$i < [arch_compile_flags]} {incr i} { set flags "additional_flags=-g compiler=g++ [arch_compile_flag $i]" set exe "hello-[arch_compile_flag_name $i]" set result [target_compile $source $exe executable $flags] if { $result != "" } { puts "\n\n\n**** failed g++ [arch_compile_flag_name $i] smoke test:\n" puts $result puts "Please install libstdc++-devel package for [arch_compile_flag_name $i]\n" exit 1 } } cd ${curdir} catch {exec rm -rf $tmpdir} } if {! [setup_systemtap_environment]} then { cleanup exit 1 } print_systemtap_version get_system_info if { ! [info exists env(STAP_PARALLEL)] } { environment_sanity_test } proc systemtap_init {args} { global env srcdir artifactdir if {[info exists env(STAP_PARALLEL)] } { regsub {\.exp$} $args {} args regsub ${srcdir}/ $args {} args set artifactdir "$env(SYSTEMTAP_DIR)/../artifacts/$args" if {[catch {exec mkdir -p $artifactdir} err_msg]} { verbose -log "Failed to create artifacts directory: $err_msg" exit 1 } # Switch to the artifacts directory before the testcase starts cd $artifactdir } } proc systemtap_finish {args} { global env srcdir if {[info exists env(STAP_PARALLEL)] } { # Return from the artifacts directory after the testcase finishes cd $srcdir } } proc systemtap_version {} {} proc stap_run_batch {filename args} { if {[string length $filename]} { # Many of our test cases use "#! stap ...". Since these lack # /full/paths, they are not really executable. (We can't have # /full/paths because the choice of systemtap interpreter is set # at "make check" time.) # So we cheat. If the file begins with "#! stap", we will spawn # stap manually here (relying on $PATH). Otherwise, we presume # the file properly executable. set file [open $filename r] set firstbits [gets $file] close $file if {[regexp -line {\#! stap (.*)} $firstbits -> stap_args]} { verbose -log "spawn1 stap $stap_args $filename $args" # Make sure we don't accidentially add an extra empty argument. if {$args == ""} { eval spawn stap $stap_args $filename } else { eval spawn stap $stap_args $filename $args } } else { verbose -log "spawn2 $filename $args" # Make sure we don't accidentially add an extra empty argument. if {$args == ""} { spawn $filename } else { spawn $filename $args } } } else { verbose -log "spawn3 stap $args" eval spawn stap $args } expect { -timeout -1 -re {[^\r\n]*\r\n} { exp_continue } eof { } } catch { close } set results [wait] verbose -log "wait results: $results" if {[llength $results] >= 5} { # Unexpected output. stap must have crashed return -1 } else { return [lindex $results 3] } } proc as_root { command } { set effective_uid [exec /usr/bin/id -u] if {$effective_uid != 0} { set command "sudo $command" } verbose -log "as_root $command" set res [catch {eval exec $command} value] verbose -log "OUT $value" verbose -log "RC $res" return $res } proc as_non_root { command } { set effective_uid [exec /usr/bin/id -u] if {$effective_uid == 0} { # If logname fails (which it can if we're not in a login # shell) or if we're root, use user 'nobody'. # # Note that user 'nobody' can't use systemtap to load kernel # modules, since he isn't a member of the stapusr/stapdev # groups. But, 'nobody' can use systemtap to compile a kernel # module. set logname "root" if {[catch {exec /usr/bin/logname} logname] || $logname == "root"} { set logname "nobody" } # User 'nobody' doesn't have a home directory, which means we # can't create ~nobody/.systemtap. So, temporarily override # SYSTEMTAP_DIR. if {$logname == "nobody"} { set stap_dir "/tmp/.systemtap-nobody" set command "su -s /bin/sh $logname -c \"SYSTEMTAP_DIR=$stap_dir $command\"" } else { set command "su -s /bin/sh $logname -c \"$command\"" } } verbose -log "as_non_root $command" set res [catch {eval exec $command} value] if {$effective_uid == 0 && $logname == "nobody"} { # If we created a SYSTEMTAP_DIR for user 'nobody' above, we # can now remove it. catch {exec rm -rf $stap_dir} } verbose -log "OUT $value" verbose -log "RC $res" return $res } proc sdt_includes {} { global srcdir env # The wrapper sys/sdt.h for testing STAP_SDT_V[12] is here. set dirs [list $srcdir] if {[installtest_p]} { # Use the installed . lappend dirs $env(SYSTEMTAP_INCLUDES) } else { # Find in the source tree. lappend dirs $srcdir/../includes # The uninstalled, configured sdt-config.h has to be found here. lappend dirs ../includes/sys } set flags "" foreach dir $dirs { set flags "$flags additional_flags=-isystem${dir}" } return $flags } proc stripped_p { EXE } { if { [catch {eval exec "file $EXE | grep -q \"not stripped\""} dummy] } { return 1 } return 0 } proc prelink_p {} { global prelink_bin set prelink_bin "/usr/sbin/prelink" return [file exists "$prelink_bin"] } # Run prelink via a temp file, so it can succeed even when running from an nfs # $HOME, for instance. Otherwise, prelink complains that it can't restore context. proc prelink { FILE ADDR } { global prelink_bin if {![prelink_p]} { verbose -log "Prelink is not found on this system" return 0 } if {[catch {exec mktemp} tmpfile]} { verbose -log "Failed to create prelink temporary file: $tmpfile" return 0 } if {[catch {exec cp -a "$FILE" "$tmpfile"} result]} { verbose -log "Failed to copy file for prelink: $result" catch {exec rm -rf "$tmpfile"} return 0 } set prelink_cmd [concat "$prelink_bin" -vfNR -r "$ADDR" "$tmpfile"] send_log "Executing: $prelink_cmd\n" if {[catch {eval exec $prelink_cmd} result]} { verbose -log "Prelink failed: $result" catch {exec rm -rf "$tmpfile"} return 0 } if {[catch {exec mv "$tmpfile" "$FILE"} result]} { verbose -log "Failed to move prelinked file back: $result" catch {exec rm -rf "$tmpfile"} return 0 } return 1 } # Send signal SIG to PID. If KILL_TIMEOUT > 0, waits KILL_TIMEOUT # seconds before also sending a SIGKILL signal to PID. SIG can be with # or without leading '-'. If AS_ROOT isn't 0, send the signal as root. proc kill { SIG PID {KILL_TIMEOUT 0} {AS_ROOT 0} } { global kill_needs_doubledash if {$kill_needs_doubledash} { set PID "-- $PID" } if {![string match -* $SIG]} { set SIG "-$SIG" } if {$AS_ROOT != 0} { as_root [list kill $SIG $PID] } else { verbose -log "Executing: kill $SIG $PID" if {[catch {eval exec kill $SIG $PID} killout]} { verbose -log "kill: $killout" } } if {$KILL_TIMEOUT > 0} { while {$KILL_TIMEOUT > 0} { if {![file isdirectory /proc/$PID]} { return } sleep 1 incr KILL_TIMEOUT -1 } if {$AS_ROOT != 0} { as_root [list kill -KILL $PID] } else { verbose -log "Executing: kill -KILL $PID" if {[catch {eval exec kill -KILL $PID} killout]} { verbose -log "kill: $killout" } } } } # Compares vers1 to vers2. Returns an integer less than, equal to, or greater # than zero if vers1 is before, equal, or after vers2. Similar to the GNU # extension strverscmp(3), but restricted to simple M.N... version numbers. proc strverscmp { vers1 vers2 } { # Check that both versions are valid M.N... format if {![regexp {^[0-9]+(\.[0-9]+)*$} $vers1]} { error "$vers1 is not a valid version number" } if {![regexp {^[0-9]+(\.[0-9]+)*$} $vers2]} { error "$vers2 is not a valid version number" } while {1} { # Get the next M for each as well as following N... regexp {^([0-9]+)(\.(.*))?} $vers1 dummy subvers1 dummy nextvers1 regexp {^([0-9]+)(\.(.*))?} $vers2 dummy subvers2 dummy nextvers2 # Compare M if {$subvers1 < $subvers2} { return -1 } if {$subvers1 > $subvers2} { return 1 } # The M parts are equal # Check if one (or both) of them don't have a next subversion if {![string length $nextvers1] \ && ![string length $nextvers2]} { return 0 } if {![string length $nextvers1] \ && [string length $nextvers2]} { return -1 } if {[string length $nextvers1] \ && ![string length $nextvers2]} { return 1 } # They both have a subversion following, let's re-iterate # Sanity check that we're making progress and not looping forever if {[string length $nextvers1] >= [string length $vers1]} \ { error "$vers1 not converging" } if {[string length $nextvers2] >= [string length $vers2]} \ { error "$vers2 not converging" } set vers1 $nextvers1 set vers2 $nextvers2 } } # The original DejaGnu setup_kfail()'s target triplet e.g. ('s390x-ibm-linux-gnu', # or 'x86_64-pc-linux-gnu', or even 'rs6000*-*-aix*') as implemented in dejagnu's # framework.exp, doesn't offer fine enough granularity. proc _setup_kfail { bugid {arches "-"} {distroids "-"} {maxkernelver "-"} {maxdistrover "-"} } { global DistroID DistroVer if { "$arches" != "-" } { if {[string first ,$::tcl_platform(machine), ,$arches,] == -1} { return 0 } } if { "$distroids" != "-" } { if {[string first ,$DistroID, ,$distroids,] == -1} { return 0 } } if { "$maxkernelver" != "-" } { if { [min_kernel_vers_p $maxkernelver] <= 0 } { return 0 } } if { "$maxdistrover" != "-" } { if { [strverscmp $DistroVer $maxdistrover] > 0 } { return 0 } } setup_kfail $bugid *-*-* }