Home | History | Annotate | Download | only in python
      1 #!/usr/bin/env python
      2 # Copyright (c) Sasha Goldshtein, 2017
      3 # Licensed under the Apache License, Version 2.0 (the "License")
      4 
      5 import distutils.version
      6 import subprocess
      7 import os
      8 import re
      9 from unittest import main, skipUnless, TestCase
     10 
     11 TOOLS_DIR = "../../tools/"
     12 
     13 def kernel_version_ge(major, minor):
     14     # True if running kernel is >= X.Y
     15     version = distutils.version.LooseVersion(os.uname()[2]).version
     16     if version[0] > major:
     17         return True
     18     if version[0] < major:
     19         return False
     20     if minor and version[1] < minor:
     21         return False
     22     return True
     23 
     24 @skipUnless(kernel_version_ge(4,1), "requires kernel >= 4.1")
     25 class SmokeTests(TestCase):
     26     # Use this for commands that have a built-in timeout, so they only need
     27     # to be killed in case of a hard hang.
     28     def run_with_duration(self, command, timeout=10):
     29         full_command = TOOLS_DIR + command
     30         self.assertEqual(0,     # clean exit
     31                 subprocess.call("timeout -s KILL %ds %s > /dev/null" %
     32                                 (timeout, full_command), shell=True))
     33 
     34     # Use this for commands that don't have a built-in timeout, so we have
     35     # to Ctrl-C out of them by sending SIGINT. If that still doesn't stop
     36     # them, send a kill signal 5 seconds later.
     37     def run_with_int(self, command, timeout=5, kill_timeout=5,
     38                      allow_early=False, kill=False):
     39         full_command = TOOLS_DIR + command
     40         signal = "KILL" if kill else "INT"
     41         rc = subprocess.call("timeout -s %s -k %ds %ds %s > /dev/null" %
     42                 (signal, kill_timeout, timeout, full_command), shell=True)
     43         # timeout returns 124 if the program did not terminate prematurely,
     44         # and returns 137 if we used KILL instead of INT. So there are three
     45         # sensible scenarios:
     46         #   1. The script is allowed to return early, and it did, with a
     47         #      success return code.
     48         #   2. The script timed out and was killed by the SIGINT signal.
     49         #   3. The script timed out and was killed by the SIGKILL signal, and
     50         #      this was what we asked for using kill=True.
     51         self.assertTrue((rc == 0 and allow_early) or rc == 124
     52                         or (rc == 137 and kill), "rc was %d" % rc)
     53 
     54     def kmod_loaded(self, mod):
     55         with open("/proc/modules", "r") as mods:
     56             reg = re.compile("^%s\s" % mod)
     57             for line in mods:
     58                 if reg.match(line):
     59                     return 1
     60                 return 0
     61 
     62     def setUp(self):
     63         pass
     64 
     65     def tearDown(self):
     66         pass
     67 
     68     def test_argdist(self):
     69         self.run_with_duration("argdist.py -v -C 'p::do_sys_open()' -n 1 -i 1")
     70 
     71     @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4")
     72     def test_bashreadline(self):
     73         self.run_with_int("bashreadline.py")
     74 
     75     def test_biolatency(self):
     76         self.run_with_duration("biolatency.py 1 1")
     77 
     78     @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4")
     79     def test_biosnoop(self):
     80         self.run_with_int("biosnoop.py")
     81 
     82     def test_biotop(self):
     83         self.run_with_duration("biotop.py 1 1")
     84 
     85     def test_bitesize(self):
     86         self.run_with_int("biotop.py")
     87 
     88     def test_bpflist(self):
     89         self.run_with_duration("bpflist.py")
     90 
     91     def test_btrfsdist(self):
     92         # Will attempt to do anything meaningful only when btrfs is installed.
     93         self.run_with_duration("btrfsdist.py 1 1")
     94 
     95     @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4")
     96     def test_btrfsslower(self):
     97         # Will attempt to do anything meaningful only when btrfs is installed.
     98         self.run_with_int("btrfsslower.py", allow_early=True)
     99 
    100     def test_cachestat(self):
    101         self.run_with_duration("cachestat.py 1 1")
    102 
    103     def test_cachetop(self):
    104         # TODO cachetop doesn't like to run without a terminal, disabled
    105         # for now.
    106         # self.run_with_int("cachetop.py 1")
    107         pass
    108 
    109     @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4")
    110     def test_capable(self):
    111         self.run_with_int("capable.py")
    112 
    113     def test_cpudist(self):
    114         self.run_with_duration("cpudist.py 1 1")
    115 
    116     @skipUnless(kernel_version_ge(4,9), "requires kernel >= 4.9")
    117     def test_cpuunclaimed(self):
    118         self.run_with_duration("cpuunclaimed.py 1 1")
    119 
    120     @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4")
    121     def test_dbslower(self):
    122         # Deliberately left empty -- dbslower requires an instance of either
    123         # MySQL or PostgreSQL to be running, or it fails to attach.
    124         pass
    125 
    126     @skipUnless(kernel_version_ge(4,3), "requires kernel >= 4.3")
    127     def test_dbstat(self):
    128         # Deliberately left empty -- dbstat requires an instance of either
    129         # MySQL or PostgreSQL to be running, or it fails to attach.
    130         pass
    131 
    132     @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4")
    133     def test_dcsnoop(self):
    134         self.run_with_int("dcsnoop.py")
    135 
    136     def test_dcstat(self):
    137         self.run_with_duration("dcstat.py 1 1")
    138 
    139     @skipUnless(kernel_version_ge(4,6), "requires kernel >= 4.6")
    140     def test_deadlock_detector(self):
    141         # TODO This tool requires a massive BPF stack traces table allocation,
    142         # which might fail the run or even trigger the oomkiller to kill some
    143         # other processes. Disabling for now.
    144         # self.run_with_int("deadlock_detector.py $(pgrep -n bash)", timeout=10)
    145         pass
    146 
    147     @skipUnless(kernel_version_ge(4,8), "requires kernel >= 4.8")
    148     def test_execsnoop(self):
    149         self.run_with_int("execsnoop.py")
    150 
    151     def test_ext4dist(self):
    152         self.run_with_duration("ext4dist.py 1 1")
    153 
    154     @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4")
    155     def test_ext4slower(self):
    156         self.run_with_int("ext4slower.py")
    157 
    158     @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4")
    159     def test_filelife(self):
    160         self.run_with_int("filelife.py")
    161 
    162     @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4")
    163     def test_fileslower(self):
    164         self.run_with_int("fileslower.py")
    165 
    166     def test_filetop(self):
    167         self.run_with_duration("filetop.py 1 1")
    168 
    169     def test_funccount(self):
    170         self.run_with_int("funccount.py __kmalloc -i 1")
    171 
    172     @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4")
    173     def test_funclatency(self):
    174         self.run_with_int("funclatency.py __kmalloc -i 1")
    175 
    176     @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4")
    177     def test_funcslower(self):
    178         self.run_with_int("funcslower.py __kmalloc")
    179 
    180     @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4")
    181     def test_gethostlatency(self):
    182         self.run_with_int("gethostlatency.py")
    183 
    184     def test_hardirqs(self):
    185         self.run_with_duration("hardirqs.py 1 1")
    186 
    187     @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4")
    188     def test_killsnoop(self):
    189         # Because killsnoop intercepts signals, if we send it a SIGINT we we
    190         # we likely catch it while it is handling the data packet from the
    191         # BPF program, and the exception from the SIGINT will be swallowed by
    192         # ctypes. Therefore, we use SIGKILL.
    193         # To reproduce the above issue, run killsnoop and in another shell run
    194         # `kill -s SIGINT $(pidof python)`. As a result, killsnoop will print
    195         # a traceback but will not exit.
    196         self.run_with_int("killsnoop.py", kill=True)
    197 
    198     @skipUnless(kernel_version_ge(4,9), "requires kernel >= 4.9")
    199     def test_llcstat(self):
    200         # Requires PMU, which is not available in virtual machines.
    201         pass
    202 
    203     @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4")
    204     def test_mdflush(self):
    205         self.run_with_int("mdflush.py")
    206 
    207     @skipUnless(kernel_version_ge(4,6), "requires kernel >= 4.6")
    208     def test_memleak(self):
    209         self.run_with_duration("memleak.py 1 1")
    210 
    211     @skipUnless(kernel_version_ge(4,8), "requires kernel >= 4.8")
    212     def test_mountsnoop(self):
    213         self.run_with_int("mountsnoop.py")
    214 
    215     @skipUnless(kernel_version_ge(4,3), "requires kernel >= 4.3")
    216     def test_mysqld_qslower(self):
    217         # Deliberately left empty -- mysqld_qslower requires an instance of
    218         # MySQL to be running, or it fails to attach.
    219         pass
    220 
    221     @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4")
    222     def test_nfsslower(self):
    223         if(self.kmod_loaded("nfs")):
    224             self.run_with_int("nfsslower.py")
    225         else:
    226             pass
    227 
    228     @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4")
    229     def test_nfsdist(self):
    230         if(self.kmod_loaded("nfs")):
    231             self.run_with_duration("nfsdist.py 1 1")
    232         else:
    233             pass
    234 
    235     @skipUnless(kernel_version_ge(4,6), "requires kernel >= 4.6")
    236     def test_offcputime(self):
    237         self.run_with_duration("offcputime.py 1")
    238 
    239     @skipUnless(kernel_version_ge(4,6), "requires kernel >= 4.6")
    240     def test_offwaketime(self):
    241         self.run_with_duration("offwaketime.py 1")
    242 
    243     @skipUnless(kernel_version_ge(4,9), "requires kernel >= 4.9")
    244     def test_oomkill(self):
    245         self.run_with_int("oomkill.py")
    246 
    247     @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4")
    248     def test_opensnoop(self):
    249         self.run_with_int("opensnoop.py")
    250 
    251     def test_pidpersec(self):
    252         self.run_with_int("pidpersec.py")
    253 
    254     @skipUnless(kernel_version_ge(4,9), "requires kernel >= 4.9")
    255     def test_profile(self):
    256         self.run_with_duration("profile.py 1")
    257 
    258     def test_runqlat(self):
    259         self.run_with_duration("runqlat.py 1 1")
    260 
    261     @skipUnless(kernel_version_ge(4,9), "requires kernel >= 4.9")
    262     def test_runqlen(self):
    263         self.run_with_duration("runqlen.py 1 1")
    264 
    265     def test_slabratetop(self):
    266         self.run_with_duration("slabratetop.py 1 1")
    267 
    268     @skipUnless(kernel_version_ge(4,7), "requires kernel >= 4.7")
    269     def test_softirqs(self):
    270         self.run_with_duration("softirqs.py 1 1")
    271         pass
    272 
    273     @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4")
    274     def test_solisten(self):
    275         self.run_with_int("solisten.py")
    276 
    277     @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4")
    278     def test_sslsniff(self):
    279         self.run_with_int("sslsniff.py")
    280 
    281     @skipUnless(kernel_version_ge(4,6), "requires kernel >= 4.6")
    282     def test_stackcount(self):
    283         self.run_with_int("stackcount.py __kmalloc -i 1")
    284 
    285     @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4")
    286     def test_statsnoop(self):
    287         self.run_with_int("statsnoop.py")
    288 
    289     @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4")
    290     def test_syncsnoop(self):
    291         self.run_with_int("syncsnoop.py")
    292 
    293     @skipUnless(kernel_version_ge(4,7), "requires kernel >= 4.7")
    294     def test_syscount(self):
    295         self.run_with_int("syscount.py -i 1")
    296 
    297     @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4")
    298     def test_tcpaccept(self):
    299         self.run_with_int("tcpaccept.py")
    300 
    301     @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4")
    302     def test_tcpconnect(self):
    303         self.run_with_int("tcpconnect.py")
    304 
    305     @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4")
    306     def test_tcpconnlat(self):
    307         self.run_with_int("tcpconnlat.py")
    308 
    309     @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4")
    310     def test_tcplife(self):
    311         self.run_with_int("tcplife.py")
    312 
    313     @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4")
    314     def test_tcpretrans(self):
    315         self.run_with_int("tcpretrans.py")
    316 
    317     @skipUnless(kernel_version_ge(4, 7), "requires kernel >= 4.7")
    318     def test_tcpdrop(self):
    319         self.run_with_int("tcpdrop.py")
    320 
    321     def test_tcptop(self):
    322         self.run_with_duration("tcptop.py 1 1")
    323 
    324     def test_tplist(self):
    325         self.run_with_duration("tplist.py -p %d" % os.getpid())
    326 
    327     @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4")
    328     def test_trace(self):
    329         self.run_with_int("trace.py do_sys_open")
    330 
    331     @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4")
    332     def test_ttysnoop(self):
    333         self.run_with_int("ttysnoop.py /dev/console")
    334 
    335     @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4")
    336     def test_ucalls(self):
    337         # This attaches a large number (300+) kprobes, which can be slow,
    338         # so use an increased timeout value.
    339         self.run_with_int("lib/ucalls.py -l none -S %d" % os.getpid(),
    340                           timeout=60, kill_timeout=60)
    341 
    342     @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4")
    343     def test_uflow(self):
    344         # The Python installed on the Ubuntu buildbot doesn't have USDT
    345         # probes, so we can't run uflow.
    346         # self.run_with_int("pythonflow.py %d" % os.getpid())
    347         pass
    348 
    349     @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4")
    350     def test_ugc(self):
    351         # This requires a runtime that has GC probes to be installed.
    352         # Python has them, but only in very recent versions. Skip.
    353         pass
    354 
    355     @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4")
    356     def test_uobjnew(self):
    357         self.run_with_int("cobjnew.sh %d" % os.getpid())
    358 
    359     @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4")
    360     def test_ustat(self):
    361         self.run_with_duration("lib/ustat.py 1 1")
    362 
    363     @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4")
    364     def test_uthreads(self):
    365         self.run_with_int("lib/uthreads.py %d" % os.getpid())
    366 
    367     def test_vfscount(self):
    368         self.run_with_int("vfscount.py", timeout=15, kill_timeout=15)
    369 
    370     def test_vfsstat(self):
    371         self.run_with_duration("vfsstat.py 1 1")
    372 
    373     @skipUnless(kernel_version_ge(4,6), "requires kernel >= 4.6")
    374     def test_wakeuptime(self):
    375         self.run_with_duration("wakeuptime.py 1")
    376 
    377     def test_xfsdist(self):
    378         # Doesn't work on build bot because xfs functions not present in the
    379         # kernel image.
    380         # self.run_with_duration("xfsdist.py 1 1")
    381         pass
    382 
    383     @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4")
    384     def test_xfsslower(self):
    385         # Doesn't work on build bot because xfs functions not present in the
    386         # kernel image.
    387         # self.run_with_int("xfsslower.py")
    388         pass
    389 
    390     def test_zfsdist(self):
    391         # Fails to attach the probe if zfs is not installed.
    392         pass
    393 
    394     @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4")
    395     def test_zfsslower(self):
    396         # Fails to attach the probe if zfs is not installed.
    397         pass
    398 
    399 if __name__ == "__main__":
    400     main()
    401