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