Subversion Repositories Code-Repo

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
141 Kevin 1
#!/usr/bin/python
2
#
3
# Check that build and output of threadpool exercise match expected output
4
#
5
# Written for CS3214 Fall 2009 by G. Back (godmar@gmail.com)
6
# Last Updated Fall 2011
7
#
8
 
9
import re, os, sys, subprocess, operator, signal
10
 
11
exe = "./threadpool_test"
12
helgrind = ["/home/courses/cs3214/valgrind-3.7.0-install/bin/valgrind", "--tool=helgrind"]
13
 
14
print "Checking correctness of threadpool exercise."
15
print "Compiling..."
16
 
17
if os.system("make clean " + exe):
18
    raise Exception("make failed, run 'make clean " + exe + "' to see why")
19
 
20
if not os.access(exe, os.X_OK):
21
    raise Exception(exe + " did not build")
22
 
23
print "Ok."
24
 
25
hex = "[0-9A-Fa-f]{8,16}"
26
print "Checking that threadpool.o does not use global variables...",
27
allowedsymbols = [ "future_free", "future_get", \
28
                   "thread_pool_new", "thread_pool_shutdown", 
29
                   "thread_pool_submit" ] 
30
 
31
symbols = subprocess.Popen(["nm", "threadpool.o"], stdout=subprocess.PIPE)\
32
    .communicate()[0].split("\n")
33
 
34
for sym in symbols:
35
    if sym == "" or re.match("\s+U (\S+)", sym):
36
        continue
37
 
38
    m = re.match(hex + " (\S) (\S+)", sym)
39
    if not m:
40
        raise Exception("unexpected line in nm:\n" + sym)
41
 
42
    if m.group(1) == "t":
43
        continue
44
    # ignore symbols produced by 'assert()'
45
    if m.group(1) == "r" and m.group(2).startswith("__PRETTY_FUNCTION__"):
46
        continue
47
    if m.group(1) == "T":
48
        if m.group(2) in allowedsymbols:
49
            continue
50
 
51
        raise Exception("threadpool.o defines global function '" + m.group(2) 
52
                        + "'\nallowed functions are: " + str(allowedsymbols));
53
 
54
    raise Exception("threadpool.o must not define any global or "
55
                    + "static variables, but you define: " + m.group(2))
56
 
57
print "Ok."
58
 
59
# test scenarios (#threads, #tasks)
60
threads2tasks = [ (4, 4), (4, 2), (4, 1), \
61
                  (4, 8), (4, 12), \
62
                  (2, 2), (2, 4), (2, 8), \
63
                  (1, 2), (1, 4), (1, 8), \
64
                  (3, 1), (3, 2), (3, 3), (3, 4), (3, 5) \
65
                ]
66
 
67
for (threads, tasks) in threads2tasks:
68
    print "Testing", threads, "threads and", tasks, "tasks"
69
    sp = subprocess.Popen([exe, str(threads), str(tasks)],
70
                           stdout=subprocess.PIPE)
71
    output = sp.communicate()[0]
72
 
73
    if sp.returncode < 0:
74
        signames = dict((k, v) for v, k in signal.__dict__.iteritems() if v.startswith('SIG'))
75
        signum = -sp.returncode
76
        raise Exception("Program terminated with signal %d (%s)\n" % (signum, signames[signum]))
77
 
78
    match = re.match("Main thread: 0x("+hex+")\n(.*)\n"\
79
        "Done\.", output, re.MULTILINE | re.DOTALL)
80
    if not match:
81
        raise Exception("Output did not match expected format:\n" + output)
82
 
83
    mainthread = match.group(1)
84
    futures = []
85
    for future in match.group(2).split("\n"):
86
        m = re.match("Future #(\d+) Thread #0x("+hex+") "\
87
                 "start=(\d+)\.(\d+) end=(\d+)\.(\d+)", future)
88
        if not m:
89
            raise Exception("Future did not match expected format:\n" + future)
90
 
91
        futures.append((m.group(1), m.group(2), 
92
            float(m.group(3)) + float(m.group(4)) / 1e6,
93
            float(m.group(5)) + float(m.group(6)) / 1e6))
94
 
95
    # print futures
96
 
97
    # consistency checks
98
    # Futures should be printed in increasing order
99
    print "Checking correct order of futures...",
100
 
101
    # all is available only in Python 2.5 and up
102
    def all(l):
103
        return reduce(lambda x, y: x and y, l, True)
104
    if not all([ int(futures[i][0]) < int(futures[i+1][0]) \
105
                for i in range(len(futures)-1) ]):
106
        raise Exception("Futures are out of order:\n" + output)
107
 
108
    print "Ok."
109
 
110
    print "Checking that correct number of threads were used...",
111
    threadsused = len(set([ f[1] for f in futures ] + [ mainthread ]))
112
    threadsexpected = min(threads, tasks) + 1
113
    if threadsexpected != threadsused:
114
        raise Exception("Thread pool used " + str(threadsused) 
115
            + " distinct threads including the main thread, should have used " 
116
            + str(threadsexpected) + ":\n" + output)
117
 
118
    print "Ok."
119
 
120
    # each pair within each 'threads'-sized chunk of tasks 
121
    # should have executed in parallel.
122
    print "Checking tasks were executed in parallel...",
123
    def inparallel((start0, end0), (start1, end1)):
124
        return start1 < end0 and start0 < end1
125
 
126
    def allpairs(l):
127
        return reduce(operator.add, [ [(l[i], e) for e in l[i+1:]] \
128
                                      for i in range(len(l)-1) ], [])
129
 
130
    for i in range(0, len(futures), threads):
131
        for (f1, f2) in allpairs(futures[i:i+threads]):
132
            if not inparallel(f1[2:4], f2[2:4]):
133
                raise Exception("Future #" + f1[0] + " and #" + f2[0] 
134
                              + " did not run in parallel:\n" + output)
135
 
136
    print "Ok."
137
 
138
print "You have met minimum requirements.\n"
139
 
140
print "Now checking for race conditions using helgrind"
141
for (threads, tasks) in threads2tasks:
142
    cmd = helgrind + [exe, str(threads), str(tasks)]
143
    print "Running", " ".join(cmd), "...",
144
    sp = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
145
    stderr = sp.communicate()[1]
146
    if not re.search("ERROR SUMMARY: 0 errors from 0 contexts", stderr):
147
        raise Exception("Your program contains race conditions:\n" + stderr)
148
 
149
    print "Ok."
150
 
151
print "Congratulations, you've passed the race condition checker tests.\n"
152
 
153
print "Testing whether your thread pool implementation runs Quicksort"
154
quicksort = "quicksort"
155
if os.system("make clean " + quicksort):
156
    print "make failed, run 'make clean " + quicksort + "' to see why"
157
    sys.exit(1)
158
 
159
sp = subprocess.Popen("./quicksort -q -s 1 1000000".split(" "), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
160
stdout = sp.communicate()[0]
161
if not re.search("Processed 14 segments of sizes: 6316 19753 62328 62520 68838 82083 104576 141938 152189 256767 325607 450365 592305 674390", stdout):
162
    print "Quicksort did not produce the expected result."
163
else:
164
    print "Quicksort appears to work, you can now start measuring parallel speedup."
165