File: kprobes_test.py

package info (click to toggle)
systemtap 5.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 47,556 kB
  • sloc: cpp: 81,117; ansic: 54,933; xml: 49,795; exp: 43,595; sh: 11,526; python: 5,003; perl: 2,252; tcl: 1,312; makefile: 1,006; javascript: 149; lisp: 105; awk: 101; asm: 91; java: 70; sed: 16
file content (571 lines) | stat: -rwxr-xr-x 19,574 bytes parent folder | download | duplicates (9)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
#!/usr/bin/python

# Copyright (C) 2010 Red Hat Inc.
# 
# This file is part of systemtap, and is free software.  You can
# redistribute it and/or modify it under the terms of the GNU General
# Public License (GPL); either version 2, or (at your option) any
# later version.

import os
import pickle
import re
import sys
import time
from config_opts import config_opts
from gen_code import gen_module
from run_module import run_module

class BucketSet(object):
    def __init__(self, bucketA=list(), bucketA_result=0,
                 bucketB=list(), bucketB_result=0, buckets=list(),
                 passed=list(), failed=list(), untriggered=list(),
                 unregistered=list(), split=0): 
        # bucketA is the list of probes to test.
        self.bucketA = bucketA
        # bucketA_result tells us what state bucketA is in
        self.bucketA_result = bucketA_result
        # bucketB is the 2nd (optional) list of probes to test
        self.bucketB = bucketB
        # bucketB_result tells us what state bucketB is in
        self.bucketB_result = bucketB_result
        # buckets is the full list of probes to test (originally
        # filled in by reading config_opts['probes_all'])
        self.buckets = buckets
        # passed is the list of probe points that were sucessfully
        # registered and triggered (actually called)
        self.passed = passed
        # failed is the list of probe points that were sucessfully
        # registered but caused a crash.  The probe lists have been
        # bisected and singly eliminated.
        self.failed = failed
        # untriggered is the list of probe points that were sucessfully
        # registered and but never triggered (actually called)
        self.untriggered = untriggered
        # untriggered is the list of probe points that couldn't be
        # registered
        self.unregistered = unregistered
        # split tells us how to split a bucket.  0 means bisect
        # it, anything else is the number of times we've tried to do a
        # 1-by-1 split.
        self.split = split

    def bucketA_result_str(self):
        if self.bucketA_result == 0:
            return "untested"
        elif self.bucketA_result == 1:
            return "succeeded"
        elif self.bucketA_result == 2:
            return "failed"
        else:
            return "UNKNOWN"

    def bucketB_result_str(self):
        if self.bucketB_result == 0:
            return "untested"
        elif self.bucketB_result == 1:
            return "succeeded"
        elif self.bucketB_result == 2:
            return "failed"
        else:
            return "UNKNOWN"

probes = BucketSet()

# Install this script in config_opts['rclocal'] (typically
# /etc/rc.d/rc.local).  Returns true if the line wasn't already there
# (which means this is the 1st time the script has been run).
def register_script(install=True):
    rc = True
    regexp = re.compile("^.+/kprobes_test.py")
    f = open(config_opts['rclocal'], "r+")
    data = ""
    for line in f:
        if not regexp.match(line):
            data += line
        else:
            rc = False
    if install:
        data += "cd %s/ && ./kprobes_test.py &" % os.getcwd()
    f.seek(0, 0)
    f.write(data)
    f.truncate(f.tell())
    f.close()
    return rc

# Because it is very possible that we'll crash the system, we need to
# make sure our data files are written to the disk sucessfully before
# we run a module.  sync_disks() makes sure everything written is
# actually saved.
def sync_disks():
    os.system("sync; sync; sync")
    time.sleep(5)
    return

def read_probe_list():
    global probes

    # If the "pickled" file exists, read it in to recover our state
    if os.path.exists(config_opts['probes_db']):
        print "Reading state..."
        f = open(config_opts['probes_db'])
        p = pickle.Unpickler(f)
        probes = p.load()
        f.close()

    # if the 'probes_all' file exists, create the data from it
    elif os.path.exists(config_opts['probes_all']):
        # Read in the flat file
        f = open(config_opts['probes_all'])
        probe_lines = f.readlines()
        f.close()
        
        # Create the full bucket
        full_bucket = list()
        for line in probe_lines:
            full_bucket.append(line.rstrip())
        probes.buckets.append(full_bucket)

    # create the probes_all file?
    else:
        print >>sys.stderr, ("Could not find probes file")
        sys.exit(1)
    
def write_probe_list():
    global probes

    print "Writing state..."
    f = open(config_opts['probes_db'], 'w')
    p = pickle.Pickler(f)
    p.dump(probes)
    f.close()

def display_probe_list():
    global probes

    i = 0
    print "bucketA (%s) has %d entries" % \
        (probes.bucketA_result_str(), len(probes.bucketA))
    print "bucketB (%s) has %d entries" % \
        (probes.bucketB_result_str(), len(probes.bucketB))
    for bucket in probes.buckets:
        print "set %d has %d entries" % (i, len(bucket))
        i += 1
    print "passed set has %d entries" % len(probes.passed)
    total = 0
    for bucket in probes.failed:
        total += len(bucket)
    print "failed set has %d entries (in %d buckets)" % \
        (total, len(probes.failed))
    print "untriggered set has %d entries" % len(probes.untriggered)
    print "unregistered set has %d entries" % len(probes.unregistered)

def reset_buckets():
    global probes

    probes.bucketA = list()
    probes.bucketA_result = 0
    probes.bucketB = list()
    probes.bucketB_result = 0
    probes.split = 0

def grab_bucket():
    global probes

    reset_buckets()

    # Try to grab the 1st bucket from the list.
    if len(probes.buckets) > 0:
        bucket = probes.buckets[0]
        del probes.buckets[0]

        # if the bucket has more than 1000 probes, limit it to 1000
        if len(bucket) > 1000:
            probes.bucketA = bucket[0:1000]
            probes.bucketA_result = 0
            probes.bucketB_result = 0

            rest = bucket[1000:]
            probes.buckets.insert(0, rest)

        # otherwise just use the bucket
        else:
            probes.bucketA = bucket
        return True
    else:
        print "no buckets left"
        return False

def split_bucket(bucket):
    global probes

    if probes.split == 0:
        split = len(bucket) / 2
    else:
        split = 1
    bucketA = bucket[0:split]
    bucketB = bucket[split:]

    probes.bucketA = bucketA
    probes.bucketA_result = 0
    probes.bucketB = bucketB
    probes.bucketB_result = 0

def update_buckets(failed=True):
    global probes
    ret = True

    # if we don't have a current set, get one
    if len(probes.bucketA) == 0:
        ret = grab_bucket()

    # if bucketA is set, we've just finished up with it (or bucketB).
    elif len(probes.bucketA) > 0:
        if probes.bucketA_result == 0:
            if not failed:
                probes.bucketA_result = 1
            else:
                probes.bucketA_result = 2
            print "bucketA %s with %d probes..." % \
                (probes.bucketA_result_str(), len(probes.bucketA))
        else:
            if not failed:
                probes.bucketB_result = 1
            else:
                probes.bucketB_result = 2
            print "bucketB %s with %d probes..." % \
                (probes.bucketB_result_str(), len(probes.bucketB))

        # OK, we've got several cases here.
        # (1) Only bucketA was set.
        if len(probes.bucketB) == 0:
            print "case (1)..."
            # (1a) If bucketA passed, put it on the passed list and grab
            # the next bucket.
            if not failed:
                probes.passed.extend(probes.bucketA)
                probes.split = 0
                ret = grab_bucket()

            # (1b) If bucketA failed and is more than 1 probe, split it.
            elif len(probes.bucketA) > 1:
                split_bucket(probes.bucketA)

            # (1c) if bucketA failed and was only 1 probe, put it on
            # the failed list and grab the next bucket.
            else:
                probes.failed.append(probes.bucketA)
                ret = grab_bucket()

        # (2) Both bucketA and bucketB were set, but bucketB hasn't
        # been tested yet.
        elif probes.bucketB_result == 0:
            # Do nothing and let bucketB be tested.
            print "case (2)..."
            pass

        # (3) Both bucketA and bucketB have been tested.  Figure out
        # what to do next.
        else:
            print "case (3)..."

            # (3a) bucketA passed, bucketB failed.  Move bucketA to
            # the passed list and split bucketB.
            if probes.bucketA_result == 1 and probes.bucketB_result == 2:
                print "case (3a)..."
                probes.passed.extend(probes.bucketA)
                probes.split = 0

                # (3a1) If bucketB failed and is more than 1 probe,
                # split it.
                if len(probes.bucketB) > 1:
                    split_bucket(probes.bucketB)

                # (3a2) If bucketB failed and was only 1 probe, put it
                # on the failed list and grab the next bucket.
                else:
                    probes.failed.append(probes.bucketB)
                    ret = grab_bucket()

            # (3b) bucketA failed, bucketB passed.  Move bucketB to
            # the passed list and split bucketA.
            elif probes.bucketA_result == 2 and probes.bucketB_result == 1:
                print "case (3b)..."
                probes.passed.extend(probes.bucketB)
                probes.split = 0

                # (3b1) If bucketA failed and is more than 1 probe,
                # split it.
                if len(probes.bucketA) > 1:
                    split_bucket(probes.bucketA)

                # (3b2) If bucketA failed and was only 1 probe, put it
                # on the failed list and grab the next bucket.
                else:
                    probes.failed.append(probes.bucketA)
                    ret = grab_bucket()

            # (3c) Both buckets failed.
            elif probes.bucketA_result == 2 and probes.bucketB_result == 2:
                print "case (3c)..."
                # (3c1) bucketA and bucketB were both just 1 probe
                # each.  Put them both on the failed list and grab the
                # next bucket.
                if len(probes.bucketA) == 1 and len(probes.bucketB) == 1:
                    print "case (3c1)..."
                    probes.failed.append(probes.bucketA)
                    probes.failed.append(probes.bucketB)
                    ret = grab_bucket()

                # (3c2) bucketA was just 1 probe, but bucketB was more
                # than 1 probe
                elif len(probes.bucketA) == 1 and len(probes.bucketB) > 1:
                    print "case (3c2)..."
                    probes.failed.append(probes.bucketA)
                    split_bucket(probes.bucketB)
                # (3c3) bucketA was more than 1 probe, but bucketB was
                # just 1 probe
                elif len(probes.bucketA) > 1 and len(probes.bucketB) == 1:
                    print "case (3c3)..."
                    probes.failed.append(probes.bucketB)
                    split_bucket(probes.bucketA)
                # (3c4) Both buckets were more than 1 probe.  In this
                # case, split bucketA and put bucketB back on the main
                # list.  This will cause bucketB to get tested twice.
                else:
                    print "case (3c4)..."
                    probes.buckets.insert(0, probes.bucketB)
                    split_bucket(probes.bucketA)

            # (3d) Both buckets passed.  This sounds good, but this
            # means that together bucketA and bucketB failed, but
            # separately they passed.  So, the combination of the sets
            # is the problem.
            else:
                print "case (3d)..."
                # (3d1) If both buckets are just 1 probe, combine the
                # buckets into one and put it on the failed list.
                # Then grab the next bucket.
                if len(probes.bucketA) == 1 and len(probes.bucketB) == 1:
                    bucket = probes.bucketA + probes.bucketB
                    probes.failed.append(bucket)
                    ret = grab_bucket()

                # (3d2) Combine both buckets here, and try eliminating
                # the probes 1-by-1.  We'll go back to bisecting if we
                # can remove 1 probe from this list or the list is
                # indivisible (case 3d3a).
                elif probes.split == 0:
                    probes.split = 1
                    bucket = probes.bucketA + probes.bucketB
                    split_bucket(bucket)
                # (3d3) We're eliminating the probes 1-by-1 and the
                # last attempt failed.  Reverse the order and try
                # again.
                else:
                    probes.split += 1
                    print "case (3d3): bucketA(%d) bucketB(%d)" % (len(probes.bucketA), len(probes.bucketB))
                    bucket = probes.bucketB + probes.bucketA
                    print "case (3d3): combined bucket(%d)" % (len(bucket))
                    if len(bucket) > 500:
                        print >>sys.stderr, "Error: bucket grew?"
                        return -1
                    
                    # (3d3a) If we've tried every probe singly, and
                    # they all still worked, the combination is still
                    # the problem.  So, we're done trying 1-by-1
                    # elimination.
                    if probes.split > len(bucket):
                        probes.split = 0
                        probes.failed.append(bucket)
                        ret = grab_bucket()
                    # (3d3b) Keep trying 1-by-1 splits.
                    else:
                        split_bucket(bucket)

    write_probe_list()
    return ret

def save_bucket():
    global probes

    f = open(config_opts['probes_current'], 'w')
    if len(probes.bucketA) > 0 and probes.bucketA_result == 0:
        f.write("\n".join(probes.bucketA))
    elif len(probes.bucketB) > 0 and probes.bucketB_result == 0:
        f.write("\n".join(probes.bucketB))
    else:
        print "Error: no bucket to write?"
    f.close()

def parse_module_output():
    global probes

    # Parse the output file, looking for probe points
    pp_re = re.compile(": (-?\d+) (\S+)$")
    f = open(config_opts['probes_result'], 'r')
    pp = dict()
    line = f.readline()
    while line:
        match = pp_re.search(line)
        if match:
            pp[match.group(2)] =  int(match.group(1))
        line = f.readline()
    f.close()

    if len(pp.keys()) == 0:
        print >>sys.stderr, "No data found?"
        return 1

    # We're done with the result file
    os.unlink(config_opts['probes_result'])

    # Parse the list of probe points.  Since the result fields haven't
    # been updated yet, pick the 1st bucket that has a status of 0
    # (untested).
    if probes.bucketA_result == 0:
        bucket = probes.bucketA
    else:
        bucket = probes.bucketB
        
    new_bucket = list()
    for probe in bucket:
        if pp.has_key(probe):
            # > 0 == passed (registered and triggered)
            if pp[probe] > 0:
                new_bucket.append(probe)
            # 0 == untriggered
            elif pp[probe] == 0:
                probes.untriggered.append(probe)
            # -1 == unregistered
            elif pp[probe] == -1:
                probes.unregistered.append(probe)
            else:
                print >>sys.stderr, "failed probe %s?" % probe
        else:
            print >>sys.stderr, "Couldn't find %s?" % probe

    # OK, we've gone through and removed all the
    # untriggered/unregistered probes from bucket.  Update the
    # proper bucket.
    if probes.bucketA_result == 0:
        # Oops, all the probes were unregistered/untriggered.
        if len(new_bucket) == 0:
            # If we've got a bucketB, we'll think it just succeeded.
            # So, put it back on the bucket list so it will get
            # correctly processed.
            if len(probes.bucketB):
                print "Re-inserting bucketB..."
                probes.buckets.insert(0, probes.bucketB)

            # Reset everything
            reset_buckets()
        else:
            probes.bucketA = new_bucket
    else:
        # If bucketB ended up with 0 entries, we need to handle
        # bucketA.
        if len(new_bucket) == 0:
            # bucketA suceeded and bucketB ended up with 0 entries.
            # Put bucketA on the 'passed' list.
            if probes.bucketA_result == 1:
                probes.passed.extend(probes.bucketA)
            # bucketA failed and bucketB ended up with 0 entries.
            # Put bucketA on the bucket list to get processed again.
            # This will cause it to get tested twice, but it is the
            # easiest way out.
            else:
                print "Re-inserting bucketA..."
                probes.buckets.insert(0, probes.bucketA)

            # Reset everything.
            reset_buckets()
        else:
            probes.bucketB = new_bucket

    return 0

def run_tests():
    status = True
    failed = True
    while status:
        status = update_buckets(failed)
        display_probe_list()
        if not status:
            break

        # Generate the module.
        save_bucket()
        rc = gen_module()
        if rc != 0:
            sys.exit(rc)

        # Run the module.
        sync_disks()
        rc = run_module()
        if rc != 0:
            sys.exit(rc)

        # Parse the module output.
        rc = parse_module_output()
        if rc != 0:
            sys.exit(rc)

        # If we're here, the current module was loaded and unloaded
        # successfully.
        failed = False

def dump_output():
    global probes
    
    f = open(config_opts['probes_passed'], 'w')
    if len(probes.passed) > 0:
        f.write("\n".join(probes.passed))
    f.close()

    # probes.failed is a list of lists.
    f = open(config_opts['probes_failed'], 'w')
    for bucket in probes.failed:
        f.write("\n".join(bucket))
        f.write("\n#\n")
    f.close()

    f = open(config_opts['probes_untriggered'], 'w')
    if len(probes.untriggered) > 0:
        f.write("\n".join(probes.untriggered))
    f.close()

    f = open(config_opts['probes_unregistered'], 'w')
    if len(probes.unregistered) > 0:
        f.write("\n".join(probes.unregistered))
    f.close()

# Make sure we're running as root.
if os.getuid() != 0:
    print >>sys.stderr, "Error: this script must be run by root"
    sys.exit(1)

# Register this script.  If this is the 1st time we've been run, start
# from scratch by removing old state files.
if register_script():
    print >>sys.stderr, "Removing old state files..."
    if os.path.exists(config_opts['log_file']):
        os.unlink(config_opts['log_file'])
    if os.path.exists(config_opts['probes_db']):
        os.unlink(config_opts['probes_db'])

# Redirect stdout and stderr
sys.stdout.flush()
sys.stderr.flush()
so = open(config_opts['log_file'], 'a+', 0)           # no buffering
os.dup2(so.fileno(), sys.stdout.fileno())
os.dup2(so.fileno(), sys.stderr.fileno())
    
# Go!
read_probe_list()
run_tests()

# Finish up.
dump_output()
register_script(False)
print >>sys.stderr, "Finished."