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