Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(359)

Side by Side Diff: pydir/bisection-tool.py

Issue 2162123002: Bisection debugging helper script (Closed) Base URL: https://chromium.googlesource.com/native_client/pnacl-subzero.git@master
Patch Set: Use half open ranges instead of closed ranges Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « pydir/bisection-test.py ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 #!/usr/bin/env python2
2 import argparse
3 import os
4 import signal
5 import subprocess
6
7 class Runner(object):
8 def __init__(self, input_cmd, timeout, comma_join = False):
9 self._input_cmd = input_cmd
10 self._timeout = timeout
11 self._num_tries = 0
12 self._comma_join = comma_join
13
14 def Run(self, included_ranges):
15 def timeout_handler(signum, frame):
16 raise RuntimeError("Timeout")
17
18 self._num_tries += 1
19 cmd = self._input_cmd
20 for i in included_ranges:
21 if isinstance(i, int):
John 2016/07/20 22:23:05 I would always pass a range. If you want a single
22 range_str = str(i)
23 else:
24 range_str = "{start}:{end}".format(start=i[0], end=i[1])
25 if self._comma_join:
26 cmd += "," + range_str
27 else:
28 cmd += " -i " + range_str
29 print cmd
30 p = subprocess.Popen(cmd, shell = True, cwd = None,
31 stdout = subprocess.PIPE, stderr = subprocess.PIPE, env = None)
32 if self._timeout != -1:
33 signal.signal(signal.SIGALRM, timeout_handler)
34 signal.alarm(self._timeout)
35
36 try:
37 _, _ = p.communicate()
38 if self._timeout != -1:
39 signal.alarm(0)
40 except:
41 try:
42 os.kill(p.pid, signal.SIGKILL)
43 except OSError:
44 pass
45 print "Timeout"
46 return -9
47 print "Return Code: " + str(p.returncode)
48 return p.returncode
49
50 def flatten(tree):
51 if isinstance(tree, list):
52 result = []
53 for node in tree:
54 result.extend(flatten(node))
55 return result
56 else:
57 return [tree] # leaf
58
59 def find_crashes(runner, current_interval, include_ranges, find_all):
60 if current_interval[0] == current_interval[1]:
61 return []
62 mid = (current_interval[0] + current_interval[1])/2
63
64 first_half = (current_interval[0], mid)
65 second_half = (mid, current_interval[1])
66
67 exit_code_2 = 0
68
69 exit_code_1 = runner.Run([first_half] + include_ranges)
70 if find_all or exit_code_1 == 0:
71 exit_code_2 = runner.Run([second_half] + include_ranges)
72
73 if exit_code_1 == 0 and exit_code_2 == 0:
74 # Whole range fails but both halves pass
75 # So, some conjunction of functions cause a failure, but none individually.
76 partial_result = flatten(find_crashes(runner, first_half, [second_half]
John 2016/07/20 22:23:05 you **REALLY** shouldn't need to flatten your resu
77 + include_ranges, find_all))
78 # Heavy list concatenation, but this is insignificant compared to the
79 # process run times
80 partial_result.extend(flatten(find_crashes(runner, second_half,
81 partial_result + include_ranges, find_all)))
82 return [partial_result]
John 2016/07/20 22:23:05 why do you return partial result wrapped in a list
83 else:
84 result = []
85 if exit_code_1 != 0:
John 2016/07/20 22:23:05 I don't see you using "find_all" here, so you migh
86 if first_half[1] == first_half[0] + 1:
87 result.append(first_half[0])
88 else:
89 result.extend(find_crashes(runner, first_half,
90 include_ranges, find_all))
91 if exit_code_2 != 0:
92 if second_half[1] == second_half[0] + 1:
93 result.append(second_half[0])
94 else:
95 result.extend(find_crashes(runner, second_half,
96 include_ranges, find_all))
97 return result
98
99
100 def main():
101 desc = 'Bisection debugging helper script'
102 argparser = argparse.ArgumentParser(description=desc)
103 argparser.add_argument('--cmd', required=True,
104 dest='cmd',
105 help='Runnable command')
106 argparser.add_argument('--start', dest='start', default=0,
107 help='Start of initial range')
108 argparser.add_argument('--end', dest='end', default=50000,
109 help='End of initial range')
110 argparser.add_argument('--timeout', dest='timeout', default=60,
111 help='Timeout for each invocation of the input')
112
113 argparser.add_argument('--all', dest='all', action='store_true')
114 argparser.add_argument('--no-all', dest='all', action='store_false')
115 argparser.set_defaults(all=True)
116
117 argparser.add_argument('--comma-join', dest='comma_join', action='store_true')
118 argparser.add_argument('--separate', dest='comma_join', action='store_false')
119 argparser.set_defaults(all=True)
120
121 args = argparser.parse_args()
122
123 fail_list = []
124
125 initial_range = (int(args.start), int(args.end))
126 timeout = int(args.timeout)
127 runner = Runner(args.cmd, timeout, args.comma_join)
128 if runner.Run([initial_range]) != 0:
John 2016/07/20 22:23:05 why not crashes = find_crashes(runner, initial_ra
129 fail_list = find_crashes(runner, initial_range, [], args.all)
130 else:
131 print "Pass" # The whole input range works, maybe check subzero build flags?
132
133 if fail_list:
134 print "Failing Functions:"
135 for fail in fail_list:
136 print fail
137 print "Number of tries: " + str(runner._num_tries)
138 # TODO(manasijm): Pretty print, associate these numbers with filenames
139
140 if __name__ == '__main__':
141 main()
OLDNEW
« no previous file with comments | « pydir/bisection-test.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698