• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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