1#!/usr/bin/env python 2# Copyright (c) Sasha Goldshtein, 2017 3# Licensed under the Apache License, Version 2.0 (the "License") 4 5import distutils.version 6import subprocess 7import os 8import re 9from unittest import main, skipUnless, TestCase 10 11TOOLS_DIR = "../../tools/" 12 13def 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") 25class 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 @skipUnless(kernel_version_ge(4,8), "requires kernel >= 4.8") 266 def test_shmsnoop(self): 267 self.run_with_int("shmsnoop.py") 268 269 @skipUnless(kernel_version_ge(4,8), "requires kernel >= 4.8") 270 def test_sofdsnoop(self): 271 self.run_with_int("sofdsnoop.py") 272 273 def test_slabratetop(self): 274 self.run_with_duration("slabratetop.py 1 1") 275 276 @skipUnless(kernel_version_ge(4,7), "requires kernel >= 4.7") 277 def test_softirqs(self): 278 self.run_with_duration("softirqs.py 1 1") 279 pass 280 281 @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4") 282 def test_solisten(self): 283 self.run_with_int("solisten.py") 284 285 @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4") 286 def test_sslsniff(self): 287 self.run_with_int("sslsniff.py") 288 289 @skipUnless(kernel_version_ge(4,6), "requires kernel >= 4.6") 290 def test_stackcount(self): 291 self.run_with_int("stackcount.py __kmalloc -i 1") 292 293 @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4") 294 def test_statsnoop(self): 295 self.run_with_int("statsnoop.py") 296 297 @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4") 298 def test_syncsnoop(self): 299 self.run_with_int("syncsnoop.py") 300 301 @skipUnless(kernel_version_ge(4,7), "requires kernel >= 4.7") 302 def test_syscount(self): 303 self.run_with_int("syscount.py -i 1") 304 305 @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4") 306 def test_tcpaccept(self): 307 self.run_with_int("tcpaccept.py") 308 309 @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4") 310 def test_tcpconnect(self): 311 self.run_with_int("tcpconnect.py") 312 313 @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4") 314 def test_tcpconnlat(self): 315 self.run_with_int("tcpconnlat.py") 316 317 @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4") 318 def test_tcplife(self): 319 self.run_with_int("tcplife.py") 320 321 @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4") 322 def test_tcpretrans(self): 323 self.run_with_int("tcpretrans.py") 324 325 @skipUnless(kernel_version_ge(4, 7), "requires kernel >= 4.7") 326 def test_tcpdrop(self): 327 self.run_with_int("tcpdrop.py") 328 329 def test_tcptop(self): 330 self.run_with_duration("tcptop.py 1 1") 331 332 def test_tplist(self): 333 self.run_with_duration("tplist.py -p %d" % os.getpid()) 334 335 @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4") 336 def test_trace(self): 337 self.run_with_int("trace.py do_sys_open") 338 339 @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4") 340 def test_ttysnoop(self): 341 self.run_with_int("ttysnoop.py /dev/console") 342 343 @skipUnless(kernel_version_ge(4,7), "requires kernel >= 4.7") 344 def test_ucalls(self): 345 self.run_with_int("lib/ucalls.py -l none -S %d" % os.getpid()) 346 347 @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4") 348 def test_uflow(self): 349 # The Python installed on the Ubuntu buildbot doesn't have USDT 350 # probes, so we can't run uflow. 351 # self.run_with_int("pythonflow.py %d" % os.getpid()) 352 pass 353 354 @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4") 355 def test_ugc(self): 356 # This requires a runtime that has GC probes to be installed. 357 # Python has them, but only in very recent versions. Skip. 358 pass 359 360 @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4") 361 def test_uobjnew(self): 362 self.run_with_int("cobjnew.sh %d" % os.getpid()) 363 364 @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4") 365 def test_ustat(self): 366 self.run_with_duration("lib/ustat.py 1 1") 367 368 @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4") 369 def test_uthreads(self): 370 self.run_with_int("lib/uthreads.py %d" % os.getpid()) 371 372 def test_vfscount(self): 373 self.run_with_int("vfscount.py", timeout=15, kill_timeout=15) 374 375 def test_vfsstat(self): 376 self.run_with_duration("vfsstat.py 1 1") 377 378 @skipUnless(kernel_version_ge(4,6), "requires kernel >= 4.6") 379 def test_wakeuptime(self): 380 self.run_with_duration("wakeuptime.py 1") 381 382 def test_xfsdist(self): 383 # Doesn't work on build bot because xfs functions not present in the 384 # kernel image. 385 # self.run_with_duration("xfsdist.py 1 1") 386 pass 387 388 @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4") 389 def test_xfsslower(self): 390 # Doesn't work on build bot because xfs functions not present in the 391 # kernel image. 392 # self.run_with_int("xfsslower.py") 393 pass 394 395 def test_zfsdist(self): 396 # Fails to attach the probe if zfs is not installed. 397 pass 398 399 @skipUnless(kernel_version_ge(4,4), "requires kernel >= 4.4") 400 def test_zfsslower(self): 401 # Fails to attach the probe if zfs is not installed. 402 pass 403 404if __name__ == "__main__": 405 main() 406