diff options
author | David Smith <dsmith@redhat.com> | 2009-01-20 15:59:47 -0600 |
---|---|---|
committer | David Smith <dsmith@redhat.com> | 2009-01-20 15:59:47 -0600 |
commit | f53975c8047cad5fb37e59196c48daaa99613207 (patch) | |
tree | 938ef8a8cd7232267a0403f61742e54f4b13e32d /scripts | |
parent | 316ac9052cb44a69729357f74a331b66f15d12f0 (diff) | |
download | systemtap-steved-f53975c8047cad5fb37e59196c48daaa99613207.tar.gz systemtap-steved-f53975c8047cad5fb37e59196c48daaa99613207.tar.xz systemtap-steved-f53975c8047cad5fb37e59196c48daaa99613207.zip |
Adding kprobes test scripts.
Diffstat (limited to 'scripts')
-rw-r--r-- | scripts/kprobes_test/.gitignore | 2 | ||||
-rw-r--r-- | scripts/kprobes_test/README | 57 | ||||
-rw-r--r-- | scripts/kprobes_test/config_opts.py | 32 | ||||
-rw-r--r-- | scripts/kprobes_test/default.cfg | 16 | ||||
-rwxr-xr-x | scripts/kprobes_test/gen_code.py | 80 | ||||
-rwxr-xr-x | scripts/kprobes_test/is_probed.py | 55 | ||||
-rw-r--r-- | scripts/kprobes_test/kprobe_module.c | 65 | ||||
-rwxr-xr-x | scripts/kprobes_test/monitor_system.py | 82 | ||||
-rwxr-xr-x | scripts/kprobes_test/readelf.py | 76 | ||||
-rwxr-xr-x | scripts/kprobes_test/run_module.py | 121 | ||||
-rwxr-xr-x | scripts/kprobes_test/whitelist.exp | 162 | ||||
-rwxr-xr-x | scripts/kprobes_test/whitelist_lib.exp | 175 |
12 files changed, 923 insertions, 0 deletions
diff --git a/scripts/kprobes_test/.gitignore b/scripts/kprobes_test/.gitignore new file mode 100644 index 00000000..2f836aac --- /dev/null +++ b/scripts/kprobes_test/.gitignore @@ -0,0 +1,2 @@ +*~ +*.pyc diff --git a/scripts/kprobes_test/README b/scripts/kprobes_test/README new file mode 100644 index 00000000..c2c0fcb9 --- /dev/null +++ b/scripts/kprobes_test/README @@ -0,0 +1,57 @@ +Here are the original instructions for 'gen_whitelist', which tested +systemtap probe safety. This modified code tests the underlying +kprobes themselves. + +To start a kprobes test run: + runtest whitelist.exp + +---- +Here is an implementation of generating whitelist for safe-mode probes +based on the discussion in former thread +(http://sourceware.org/ml/systemtap/2006-q3/msg00574.html). + +Its main idea is: + 1) Fetch a group of probe points from probes.pending, probe them and + run some workloads(e.g. runltp) parallely meanwhile. + + 2) If the probe test ends without crash, those actually triggered + probe points are moved into probes.passed and those untriggered + are into probes.untriggered; + If the probe test crashes the system, it will be resumed + automatically after system reboot. Those probe points which have + been triggered are also moved into probes.passed, but those + untriggered ones are moved into probes.failed. + + 3) Repeat the above until probes.pending becomes empty, then: + Normally, probes.pending is reinitialized from probes.failed and + probes.untriggered, then start the next iteration; + But if max running level (now 3) is reached, or probes.pending, + probes.failed and probes.untriggered are all empty, stop the + whole test. + +To be able to resume after a crash, this test will append its startup +codes to the end of /etc/rc.d/rc.local at the beginning and remove +them at the end of the test. + +The gen_tapset_all.sh is used to generate a "probes.all" file based +on current tapset of systemtap. + +It is suggested to use a script in a remote server to restart the +test machine automatically in case it is crashed by the probe test. + +Instructions: + 0) Please jump to 6) directly if you use all default settings + 1) Define your list of probe points in a file named "probes.all"; + otherwise, it will be automatically extracted from + stap -p2 -e 'probe kernel.function("*"){} + 2) Define your own workloads by changing the "benchs" list variable; + otherwise, several LTP testcases will be used + 3) Define how may times you want the test to be iterated by changing + MAX_RUNNING_LEVEL variable; otherwise, 3 is the default + 4) Define the group size for different iteration level by changing + proper_current_size() function; otherwise, they will be set in + a decreasing order based on the length of probes.all + 5) Remove /stp_genwhitelist_running if you want to start a test from + the beginning after a system crash + 6) Start a test: + runtest whitelist.exp diff --git a/scripts/kprobes_test/config_opts.py b/scripts/kprobes_test/config_opts.py new file mode 100644 index 00000000..6163db50 --- /dev/null +++ b/scripts/kprobes_test/config_opts.py @@ -0,0 +1,32 @@ +# Copyright (C) 2008 Red Hat Inc. +# +# This file is part of systemtap, and is free software. You can +# redistribute it and/or modify it under the terms of the GNU General +# Public License (GPL); either version 2, or (at your option) any +# later version. + +import os +import sys + +# Here we set up the default config options. These can be overridden +# by the config file. +config_opts = dict() + +# Various file names +config_opts['probes_result'] = 'probe.out' +config_opts['probes_all'] = 'probes.all' +config_opts['probes_pending'] = 'probes.pending' +config_opts['probes_current'] = 'probes.current' +config_opts['probes_passed'] = 'probes.passed' +config_opts['probes_failed'] = 'probes.failed' +config_opts['probes_untriggered'] = 'probes.untriggered' +config_opts['probes_unregistered'] = 'probes.unregistered' + +# Read in the config file +print "Reading config file..." +cfg = os.path.join(os.getcwd(), 'default.cfg') +if os.path.exists(cfg): + execfile(cfg) +else: + print >>sys.stderr, ("Could not find required config file: %s" % cfg) + sys.exit(1) diff --git a/scripts/kprobes_test/default.cfg b/scripts/kprobes_test/default.cfg new file mode 100644 index 00000000..c1c8acdb --- /dev/null +++ b/scripts/kprobes_test/default.cfg @@ -0,0 +1,16 @@ +# -*-python-*- + +# Commands to run to generate a system load +config_opts['load_cmds'] = [ + [ '/usr/local/ltp/runltp', '-t', '600s', '-f', 'syscalls', ], + [ '/usr/local/ltp/runltp', '-t', '600s', '-f', 'nfs', ], + [ '/usr/local/ltp/runltp', '-t', '600s', '-f', 'rpc', ], + [ '/usr/local/ltp/runltp', '-t', '600s', '-f', 'ipc', ], + [ '/usr/local/ltp/runltp', '-t', '600s', '-f', 'dio', ], + [ '/usr/local/ltp/runltp', '-t', '600s', '-f', 'fs', ], + [ '/usr/local/ltp/runltp', '-t', '600s', '-f', 'mm', ], + [ '/usr/local/ltp/runltp', '-t', '600s', '-f', 'tcp_cmds', ], + # [ '/usr/local/ltp/runltp', '-t', '600s', '-f', 'ltp-aio-stress.part1', ], + [ '/usr/local/ltp/runltp', '-t', '600s', '-f', 'lvm.part1' ], + ] + diff --git a/scripts/kprobes_test/gen_code.py b/scripts/kprobes_test/gen_code.py new file mode 100755 index 00000000..1361f98e --- /dev/null +++ b/scripts/kprobes_test/gen_code.py @@ -0,0 +1,80 @@ +#!/usr/bin/python + +# Copyright (C) 2008 Red Hat Inc. +# +# This file is part of systemtap, and is free software. You can +# redistribute it and/or modify it under the terms of the GNU General +# Public License (GPL); either version 2, or (at your option) any +# later version. + +import os +import sys +from config_opts import config_opts + +def gen_files(dir, subset): + f = open('%s/kprobe_defs.h' % dir, 'w') + + # Output the array of kp_data structs + print >>f, "static struct kp_data kp_data[] = {" + i = 0 + while i < len(subset): + print >>f, ("\t{ .kp={ .symbol_name=\"%s\", .pre_handler=&handler_pre, }, .use_count=ATOMIC_INIT(0) }," + % (subset[i])) + i += 1 + print >>f, "};" + print >>f + f.close() + + # Generate the Makefile + f = open('Makefile', 'w') + print >>f, """ +EXTRA_CFLAGS := +EXTRA_CFLAGS += -freorder-blocks +EXTRA_CFLAGS += -Wno-unused -Werror +obj-m := kprobe_module.o""" + f.close() + +def run_make_cmd(cmd): + # Before running make, fix up the environment a bit. Clean out a + # few variables that /lib/modules/${KVER}/build/Makefile uses. + os.unsetenv("ARCH") + os.unsetenv("KBUILD_EXTMOD") + os.unsetenv("CROSS_COMPILE") + os.unsetenv("KBUILD_IMAGE") + os.unsetenv("KCONFIG_CONFIG") + os.unsetenv("INSTALL_PATH"); + + print "Running", cmd + return os.system(cmd) + +def gen_module(): + f = open(config_opts['probes_current']) + probes = f.readlines() + f.close() + if len(probes) == 0: + print >>sys.stderr, ("Error: no probe points in %s" + % config_opts['probes_current']) + return -1 + + # Cleanup each probe by stripping whitespace + i = 0 + while i < len(probes): + probes[i] = probes[i].rstrip() + i += 1 + + # Generate necessary files + gen_files(os.getcwd(), probes) + + # Try to build the module - add "V=1" at the end for more verbosity + os.system('rm -f ./kprobe_module.ko') + (sysname, nodename, release, version, machine) = os.uname() + cmd = ("make -C \"/lib/modules/%s/build\" M=\"%s\" modules >build.log 2>&1" + % (release, os.getcwd())) + rc = run_make_cmd(cmd) + if os.WEXITSTATUS(rc) != 0: + print >>sys.stderr, "Error: Make failed, see build.log for details" + return -1 + return 0 + +rc = gen_module() +sys.exit(rc) diff --git a/scripts/kprobes_test/is_probed.py b/scripts/kprobes_test/is_probed.py new file mode 100755 index 00000000..192abd88 --- /dev/null +++ b/scripts/kprobes_test/is_probed.py @@ -0,0 +1,55 @@ +#!/usr/bin/python + +# Copyright (C) 2008 Red Hat Inc. +# +# This file is part of systemtap, and is free software. You can +# redistribute it and/or modify it under the terms of the GNU General +# Public License (GPL); either version 2, or (at your option) any +# later version. + +import re +import sys +import os +from config_opts import config_opts + +# Parse the output file, looking for probe points +pp_re = re.compile(": (-?\d+) (\S+)$") +f = open(config_opts['probes_result'], 'r') +pp = dict() +line = f.readline() +while line: + match = pp_re.search(line) + if match: + pp[match.group(2)] = int(match.group(1)) + line = f.readline() +f.close() + +if len(pp.keys()) == 0: + print >>sys.stderr, "No data found?" + sys.exit(1) + +# Parse the list of probe points. +f = open(config_opts['probes_current'], 'r') +passed = open(config_opts['probes_passed'], 'a') +failed = open(config_opts['probes_failed'], 'a') +untriggered = open(config_opts['probes_untriggered'], 'a') +unregistered = open(config_opts['probes_unregistered'], 'a') +line = f.readline().strip() +while line: + if pp.has_key(line): + if pp[line] > 0: + passed.write(line + '\n') + elif pp[line] == 0: + untriggered.write(line + '\n') + elif pp[line] == -1: + unregistered.write(line + '\n') + else: + failed.write(line + '\n') + line = f.readline().strip() +f.close() +passed.close() +failed.close() +untriggered.close() +unregistered.close() + +sys.exit(0) diff --git a/scripts/kprobes_test/kprobe_module.c b/scripts/kprobes_test/kprobe_module.c new file mode 100644 index 00000000..0ba6c1db --- /dev/null +++ b/scripts/kprobes_test/kprobe_module.c @@ -0,0 +1,65 @@ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/kprobes.h> + +struct kp_data { + struct kprobe kp; + atomic_t use_count; +}; + +static int handler_pre(struct kprobe *p, struct pt_regs *regs); + +#include "kprobe_defs.h" + + +/* kprobe pre_handler: called just before the probed instruction is executed */ +static int handler_pre(struct kprobe *p, struct pt_regs *regs) +{ + struct kp_data *k = container_of(p, struct kp_data, kp); + atomic_inc(&k->use_count); + return 0; +} + +static int __init kprobe_init(void) +{ + int ret; + int probes_registered = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(kp_data); i++) { + ret = register_kprobe(&kp_data[i].kp); + if (ret != 0) + atomic_set(&kp_data[i].use_count, -1); + else + probes_registered++; + } + if (probes_registered == 0) { + for (i = 0; i < ARRAY_SIZE(kp_data); i++) { + printk(KERN_INFO "-1 %s\n", kp_data[i].kp.symbol_name); + } + printk(KERN_INFO "kprobe_module unloaded\n"); + return ret; + } + printk(KERN_INFO "Planted kprobes\n"); + return 0; +} + +static void __exit kprobe_exit(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(kp_data); i++) { + if (atomic_read(&kp_data[i].use_count) != -1) + unregister_kprobe(&kp_data[i].kp); + } + printk(KERN_INFO "kprobes unregistered\n"); + for (i = 0; i < ARRAY_SIZE(kp_data); i++) { + printk(KERN_INFO "%d %s\n", atomic_read(&kp_data[i].use_count), + kp_data[i].kp.symbol_name); + } + printk(KERN_INFO "kprobe_module unloaded\n"); +} + +module_init(kprobe_init) +module_exit(kprobe_exit) +MODULE_LICENSE("GPL"); diff --git a/scripts/kprobes_test/monitor_system.py b/scripts/kprobes_test/monitor_system.py new file mode 100755 index 00000000..59d49e7d --- /dev/null +++ b/scripts/kprobes_test/monitor_system.py @@ -0,0 +1,82 @@ +#!/usr/bin/python + +# Copyright (C) 2008 Red Hat Inc. +# +# This file is part of systemtap, and is free software. You can +# redistribute it and/or modify it under the terms of the GNU General +# Public License (GPL); either version 2, or (at your option) any +# later version. + +# This script monitors a remote system that is running the kprobes +# test. If several consecutive 'ping's fail, the system is rebooted. +# +# This script takes as an argument a config filename, whose contents +# should look like the following: +# +# config_opts['system_name'] = "SYSTEM_NAME" +# config_opts['restart_cmds'] = [ +# 'CMD1', +# 'CMD2', +# ] +# +# As an example, here is a config file used when monitoring a kvm +# instance: +# +# config_opts['system_name'] = "dhcp-148" +# config_opts['restart_cmds'] = [ +# 'sudo virsh destroy kvm-rawhide-64-1', +# 'sudo virsh start kvm-rawhide-64-1', +# ] + +import sys +import os +import time + +if len(sys.argv) != 2: + print >>sys.stderr, "Usage: %s config_file" % sys.argv[0] + sys.exit(1) +cfg = sys.argv[1] + +# Read in the config file +if not os.path.exists(cfg): + print >>sys.stderr, ("Could not find required config file: %s" % cfg) + sys.exit(1) + +print "Reading config file %s..." % cfg +config_opts = dict() +execfile(cfg) +if not config_opts.has_key('system_name'): + print >>sys.stderr, "Missing required config opt 'system_name'" + sys.exit(1) +if not config_opts.has_key('restart_cmds'): + print >>sys.stderr, "Missing required config opt 'restart_cmds'" + sys.exit(1) + +errors = 0 +while 1: + rc = os.system("ping -c 1 %s" % config_opts['system_name']) + # If ping worked, system is still up and running. Wait a minute + # and try again. + if os.WEXITSTATUS(rc) == 0: + time.sleep(60) + errors = 0 + + # If the ping failed, increase the error count. If we've got 3 + # consecutive errors, assume the machine has crashed and restart + # it. + else: + errors += 1 + if errors < 3: + time.sleep(30) + else: + print >>sys.stderr, "Restarting %s..." % config_opts['system_name'] + # Run each restart command + + for cmd in config_opts['restart_cmds']: + print >>sys.stderr, "Running '%s'..." % cmd + os.system(cmd) + # Sleep for 5 minutes to give the system a chance to boot + print >>sys.stderr, "Sleeping for 5 minutes..." + time.sleep(5 * 60) + errors = 0 + diff --git a/scripts/kprobes_test/readelf.py b/scripts/kprobes_test/readelf.py new file mode 100755 index 00000000..d031625b --- /dev/null +++ b/scripts/kprobes_test/readelf.py @@ -0,0 +1,76 @@ +#!/usr/bin/python + +# Copyright (C) 2008 Red Hat Inc. +# +# This file is part of systemtap, and is free software. You can +# redistribute it and/or modify it under the terms of the GNU General +# Public License (GPL); either version 2, or (at your option) any +# later version. + +import re +import sys +import os +#import pickle +import subprocess +from config_opts import config_opts + +# Read the output of eu-readelf on vmlinux +(sysname, nodename, release, version, machine) = os.uname() +cmd = "eu-readelf --symbols /usr/lib/debug/lib/modules/%s/vmlinux" % release +print "Running", cmd +p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE) +lines = p.stdout.readlines() +p.wait() +if p.returncode != 0: + print >>sys.stderr, "Error: eu-readelf failed." + sys.exit(p.returncode) + +# Parse the output +kprobes_text_start = 0 +kprobes_text_end = 0 +syms = dict() +func_re = re.compile("^\s*\d+:\s+([0-9a-f]+)\s+\d+\s+FUNC\s+\S+\s+\S+\s+\d+\s+(\S+)$") +notype_re = re.compile("^\s*\d+:\s+([0-9a-f]+)\s+\d+\s+NOTYPE\s+\S+\s+\S+\s+\d+\s+(\S+)$") +for line in lines: + match = notype_re.match(line) + if match: + addr = match.group(1) + name = match.group(2) + if name == "__kprobes_text_start": + kprobes_text_start = long(addr, 16) + elif name == "__kprobes_text_end": + kprobes_text_end = long(addr, 16) + continue + + match = func_re.match(line) + if match: + addr = match.group(1) + name = match.group(2) + syms[name] = long(addr, 16) + +# Now we've parsed everything. Now we need to go back and remove all +# symbols between '__kprobes_text_start' and '__kprobes_text_end', +# since they are already protected from kprobes. We couldn't do this +# in the loop above, since we might encounter symbols that need to be +# removed before we found the start/end of the kprobes section. +if kprobes_text_start == 0 or kprobes_text_end == 0: + print "Error - didn't find kprobes_test_start(%d) or kprobes_text_end(%d)" \ + % (kprobes_text_start, kprobes_text_end) + sys.exit(1) + +for name in syms.keys(): + if syms[name] >= kprobes_text_start and syms[name] < kprobes_text_end: + print "deleting", name + del syms[name] + +## Save data +#f = open('%s.syms' % (release), 'w') +#p = pickle.Pickler(f) +#p.dump(syms) +#f.close() + +# Write the data out in text format +f = open(config_opts['probes_all'], 'w') +for name in syms.keys(): + print >>f, name +f.close() diff --git a/scripts/kprobes_test/run_module.py b/scripts/kprobes_test/run_module.py new file mode 100755 index 00000000..74465d94 --- /dev/null +++ b/scripts/kprobes_test/run_module.py @@ -0,0 +1,121 @@ +#!/usr/bin/python + +# Copyright (C) 2008 Red Hat Inc. +# +# This file is part of systemtap, and is free software. You can +# redistribute it and/or modify it under the terms of the GNU General +# Public License (GPL); either version 2, or (at your option) any +# later version. + +import sys +import os +import os.path +import time +import select +from config_opts import config_opts + +def run_module(): + # Find the current size of /var/log/messages + logfile = '/var/log/messages' + print >>sys.stderr, "Getting size of %s..." % logfile + + start_pos = os.path.getsize(logfile) + l = open(logfile, 'r') + l.seek(start_pos) + + # Insert the module + print >>sys.stderr, "Inserting module..." + rc = os.system("/sbin/insmod %s" % os.path.join(os.getcwd(), + 'kprobe_module.ko')) + if os.WEXITSTATUS(rc) != 0: + # This is actually semi-OK, which is why there is no error + # message here, only a notice. This might mean that every + # probe tried cannot be registered. + print >>sys.stderr, "Notice: insmod failed" + else: + # Run the load generate commands (if any). Note we ignore the + # return values, since we don't really care if the commands + # succeed or not. + if config_opts.has_key('load_cmds'): + num_waits = 0 + + # Redirect the output to 'run_module.log' by modifying + # what stdout points to. + old_stdout = os.dup(sys.stdout.fileno()) + fd = os.open('run_module.log', os.O_CREAT | os.O_WRONLY) + os.dup2(fd, sys.stdout.fileno()) + + # Run the commands. + for cmd in config_opts['load_cmds']: + pid = os.spawnvp(os.P_NOWAIT, cmd[0], cmd) + num_waits += 1 + while num_waits > 0: + (pid, status) = os.waitpid(-1, 0) + num_waits -= 1 + + # Restore old value of stdout. + os.close(fd) + os.dup2(old_stdout, sys.stdout.fileno()) + + # Remove the module + print >>sys.stderr, "Removing module..." + rc = os.system("/sbin/rmmod kprobe_module") + if os.WEXITSTATUS(rc) != 0: + print >>sys.stderr, "Error: rmmod failed" + return -1 + + # Now we have to wait until everything is flushed to the logfile + f = open(config_opts['probes_result'], 'w') + while 1: + # Find the ending size of /var/log/messages + end_pos = os.path.getsize(logfile) + + if end_pos < start_pos: + # The log files have been rotated. Read any leftover data, + # then reopen file. + data = l.read() + if data: + f.write(data) + + # See if we can find 'kprobe_module unloaded' in the + # data we just read. + if data.find('kprobe_module unloaded') != -1: + break + + l.close() + l = open(logfile, 'r') + start_pos = 0 + continue + + # Try to wait until data is available + while 1: + try: + input, output, exc = select.select([l.fileno()], [], [], 60) + break + except select.error, err: + if err[0] != EINTR: + raise + + # Get the new stuff logged to /var/log/messages + data = l.read(end_pos - start_pos + 1) + if not data: + # ignore EOF + time.sleep(2) + continue + + # Write results data + f.write(data) + + # See if we can find 'kprobe_module unloaded' in the data we + # just read. + if data.find('kprobe_module unloaded') == -1: + start_pos = end_pos + else: + break + + l.close() + f.close() + return 0 + +rc = run_module() +sys.exit(rc) diff --git a/scripts/kprobes_test/whitelist.exp b/scripts/kprobes_test/whitelist.exp new file mode 100755 index 00000000..37c1502c --- /dev/null +++ b/scripts/kprobes_test/whitelist.exp @@ -0,0 +1,162 @@ +# Test kernel functions for kprobe safety. +# +# Copyright (C) 2006 IBM Corp. +# Copyright (C) 2008 Red Hat Inc. +# +# This file is part of systemtap, and is free software. You can +# redistribute it and/or modify it under the terms of the GNU General +# Public License (GPL); either version 2, or (at your option) any +# later version. +# +# Based on software written by Gui Jian <guijian@cn.ibm.com> +# + +load_lib "whitelist_lib.exp" + +set MAX_RUNNING_LEVEL 3 + +set GENKPROBES_RUNNING "/genkprobes_running" +set PROBES_RESULT "probe.out" +set PROBES_ALL "probes.all" +set PROBES_PENDING "probes.pending" +set PROBES_CURRENT "probes.current" +set PROBES_PASSED "probes.passed" +set PROBES_FAILED "probes.failed" +set PROBES_UNTRIGGERED "probes.untriggered" +set PROBES_UNREGISTERED "probes.unregistered" + +proc proper_current_size {level inputfile} { + set totalsize [get_linesize $inputfile] + switch $level { + {1} {set currentsize [expr $totalsize/9] + # Use a max size of 1000 + if {$currentsize > 1000} { + set currentsize 1000 + } + # Use a min size of 50 + if {$currentsize < 50} { + set currentsize 50 + } + } + {2} {set currentsize [expr $totalsize/49] + # Use a max size of 400 + if {$currentsize > 400} { + set currentsize 400 + } + # Use a min size of 10 + if {$currentsize < 10} { + set currentsize 10 + } + } + {3} {set currentsize 100} + {4} {set currentsize 10} + default { + puts "Reached unexpected iteration level: $level" + set currentsize $totalsize + } + } + if {$currentsize <= 0} { + set currentsize 5 + } + return $currentsize +} + +# +# Main routine of the whole test +# +if {[get_running_level] == 0} { + # Not started yet, start the whole test from the scratch + # Append the startup code to /etc/init.d/rc.local if not yet + register_service + # Check whether probes.all is empty or not given + if {[get_linesize $PROBES_ALL] == 0} { + init_probes_all + } + # Set current_running_level as 1 to indicate a new test started + init_running_level + # Initialize intermediate files based on probe.all + exec rm -f $PROBES_PENDING $PROBES_CURRENT + exec rm -f $PROBES_PASSED $PROBES_UNTRIGGERED $PROBES_FAILED $PROBES_UNREGISTERED + file copy $PROBES_ALL $PROBES_PENDING + exec touch $PROBES_PASSED $PROBES_UNTRIGGERED $PROBES_FAILED $PROBES_UNREGISTERED + sync_discs + puts "Start a fresh stp_genwhitelist test." +} else { + # Maybe started already, so do some cleaning if necessary + garbage_collect $ALREADY_CRASHED + puts "Recovered from last maybe crashed probe test." +} + +set current_size_const [proper_current_size [get_running_level] $PROBES_ALL] +puts "current_size_const is initialized as $current_size_const" + +while {1} { + puts "Current size of probes.pending is [get_linesize $PROBES_PENDING]" + if {[get_linesize $PROBES_PENDING] == 0} { + # Check whether we need the next iteration or not + global MAX_RUNNING_LEVEL + # incr running_level for the start of a new iteration + set old_running_level [get_running_level] + incr_running_level + puts "Running level increased to [get_running_level]" + if {[get_running_level] > $MAX_RUNNING_LEVEL} { + puts "Reached max iteration limit: [get_running_level]" + break + } else { + exec cp $PROBES_PASSED $PROBES_PASSED-pass$old_running_level + exec cp $PROBES_FAILED $PROBES_FAILED-pass$old_running_level + exec cp $PROBES_UNTRIGGERED $PROBES_UNTRIGGERED-pass$old_running_level + + puts "Current running level is [get_running_level]" + exec rm -f $PROBES_PENDING + if {[get_linesize $PROBES_FAILED] > 0} { + # Append probes.failed to probes.pending + exec cat $PROBES_FAILED >> $PROBES_PENDING + file delete $PROBES_FAILED + exec touch $PROBES_FAILED + puts "Append $PROBES_FAILED to $PROBES_PENDING, now size=[get_linesize $PROBES_PENDING]" + } + if {[get_linesize $PROBES_UNTRIGGERED] > 0} { + # Append probes.untriggered to probes.pending + exec cat $PROBES_UNTRIGGERED >> $PROBES_PENDING + file delete $PROBES_UNTRIGGERED + exec touch $PROBES_UNTRIGGERED + puts "Append $PROBES_UNTRIGGERED to $PROBES_PENDING, now size=[get_linesize $PROBES_PENDING]" + } + if {[get_linesize $PROBES_PENDING] == 0} { + # No more pending probe points + puts "No more iterations needed. Stopped." + break + } + # set new value of current_size_const for new iteration level + set current_size_const [proper_current_size [get_running_level] $PROBES_ALL] + puts "current_size_const is set as $current_size_const now" + continue + } + } + # Now, non-empty probes.pending should be ready + # Generate probes.current + exec rm -f $PROBES_CURRENT + exec head -n $current_size_const $PROBES_PENDING > $PROBES_CURRENT + exec tail -n+[expr $current_size_const+1] $PROBES_PENDING > /tmp/whitelist_tmpfile + exec mv /tmp/whitelist_tmpfile $PROBES_PENDING + sync_discs + + set d [exec date] + puts "Ready to do current probe test at $d" + # Do actual probe test + do_current_test + set d [exec date] + puts "Completed one probe test successfully at $d" + + # No crash fortunately, so do some cleaning to prepare for next test + garbage_collect $NO_CRASH +} + +# Congratulations for arriving here +# Remove all temporary files and unregister myself +puts "Now removing all temporary files, unregistering the service and exit." +exec rm -f $PROBES_PENDING $PROBES_CURRENT $GENKPROBES_RUNNING +exec rm -f $PROBES_RESULT /tmp/whitelist_tmpfile +unregister_service +exit 0 diff --git a/scripts/kprobes_test/whitelist_lib.exp b/scripts/kprobes_test/whitelist_lib.exp new file mode 100755 index 00000000..122c320b --- /dev/null +++ b/scripts/kprobes_test/whitelist_lib.exp @@ -0,0 +1,175 @@ +# Defines the supporting procedures used by whitelist.exp +# +# Copyright (C) 2006 IBM Corp. +# Copyright (C) 2008 Red Hat Inc. +# +# This file is part of systemtap, and is free software. You can +# redistribute it and/or modify it under the terms of the GNU General +# Public License (GPL); either version 2, or (at your option) any +# later version. +# +# Based on software written by Gui Jian <guijian@cn.ibm.com> +# +set NO_CRASH 0 +set ALREADY_CRASHED 1 + +set startup_line_in_RCLOCAL "cd $env(PWD) && runtest whitelist.exp&" +set RCLOCAL "/etc/rc.d/rc.local" + +set GROUP_NUM 1 + +proc do_current_test {} { + global GROUP_NUM + set testname "L[get_running_level]_G$GROUP_NUM" + set GROUP_NUM [expr $GROUP_NUM + 1] + + # Generate the C code + if {[catch {exec ./gen_code.py} results]} { + puts results + if {[lindex $::errorCode 0] eq "CHILDSTATUS"} { + set status [lindex $::errorCode 2] + } else { + # Some kind of unexpected failure + set status -1 + } + + if {$status != 0} { + return; + } + } + wait + set module_path "[pwd]/kprobe_module.ko" + whitelist_run $testname $module_path +} + +proc get_linesize {filename} { + if [file readable $filename] { + scan [exec wc -l $filename] "%d" lines + if {[info exists lines] && $lines > 0} { + return $lines + } + } + return 0 +} + +proc get_running_level {} { + global GENKPROBES_RUNNING + if [file readable $GENKPROBES_RUNNING] { + scan [exec cat $GENKPROBES_RUNNING] "%d" current_running_level + if {[info exists current_running_level] && $current_running_level > 0} { + return $current_running_level + } + } + return 0 +} + +proc sync_discs {} { + exec sync + exec sync + exec sync + exec sleep 5 + return +} + +# Notice throughout garbage_collect we make sure the discs are synced +# before we return. Otherwise, if the changes don't get flushed to +# disk and the system crashes, we'll end up in an infinite loop of +# crashes. +proc garbage_collect {{already_crashed $NO_CRASH}} { + global PROBES_RESULT + global PROBES_CURRENT + global PROBES_PASSED + global PROBES_FAILED + global PROBES_UNTRIGGERED + + if {[get_linesize $PROBES_CURRENT] == 0} { + puts "Empty $PROBES_CURRENT, returning" + exec rm -f $PROBES_CURRENT $PROBES_RESULT + sync_discs + return + } + if {[get_linesize $PROBES_RESULT] == 0} { + puts "Empty $PROBES_RESULT" + if {$already_crashed} { + exec cat $PROBES_CURRENT >> $PROBES_FAILED + } else { + exec cat $PROBES_CURRENT >> $PROBES_UNTRIGGERED + } + exec rm -f $PROBES_CURRENT $PROBES_RESULT + sync_discs + return + } + + # both probes.current and probe.out are non-empty + if [catch {open $PROBES_CURRENT r} Infile] { + puts "Failed to open $PROBES_CURRENT" + exec rm -f $PROBES_CURRENT $PROBES_RESULT + sync_discs + return + } + + puts "Running ./is_probed.py" + exec ./is_probed.py + + catch {close $Infile} + exec rm -f $PROBES_CURRENT $PROBES_RESULT + sync_discs + return +} + +proc incr_running_level {} { + global GENKPROBES_RUNNING + set newlevel [expr [get_running_level]+1] + if { $newlevel > 0 } { + exec echo $newlevel > $GENKPROBES_RUNNING + } else { + exec echo 0 > $GENKPROBES_RUNNING + } + return +} + +proc init_probes_all {} { + exec ./readelf.py +} + +proc init_running_level {} { + global GENKPROBES_RUNNING + exec echo 1 > $GENKPROBES_RUNNING +} + +proc register_service {} { + global startup_line_in_RCLOCAL + global RCLOCAL + exec sed -i -n -e "/runtest whitelist.exp/!p" $RCLOCAL + exec echo $startup_line_in_RCLOCAL >> $RCLOCAL +} + +proc unregister_service {} { + global RCLOCAL + exec sed -i -n -e "/runtest whitelist.exp/!p" $RCLOCAL +} + +proc whitelist_run { TEST_NAME MODULE } { + if {[info procs installtest_p] != "" && ![installtest_p]} { + untested $TEST_NAME; + return; + } + + set status 0 + if {[catch {exec ./run_module.py} results]} { + puts $results + if {[lindex $::errorCode 0] eq "CHILDSTATUS"} { + set status [lindex $::errorCode 2] + } else { + # Some kind of unexpected failure + set status -1 + } + } + if {$status != 0} { + fail $TEST_NAME; + return; + } + pass "$TEST_NAME end successfully"; + + wait +} |