source: tests/pybin/tools.py@ 7abee38

ADT aaron-thesis arm-eh ast-experimental cleanup-dtors enum forall-pointer-decay jacob/cs343-translation jenkins-sandbox new-ast new-ast-unique-expr persistent-indexer pthread-emulation qualifiedEnum
Last change on this file since 7abee38 was 0c13238, checked in by Thierry Delisle <tdelisle@…>, 7 years ago

Aborts now create core dumps which are printed by the test harness.

  • printing is done with gdb, print-core.gdb is the list of commands that will print
  • since all core dumps are assumed to be call 'core' this doesn't handle fancy core patterns and has race conditions if multiple program core dump.
  • Property mode set to 100644
File size: 7.9 KB
Line 
1from __future__ import print_function
2
3import __main__
4import argparse
5import fileinput
6import multiprocessing
7import os
8import re
9import resource
10import signal
11import stat
12import sys
13import time
14
15from pybin import settings
16from subprocess import Popen, PIPE, STDOUT
17
18################################################################################
19# shell helpers
20################################################################################
21
22# helper functions to run terminal commands
23def sh(cmd, print2stdout = True, input = None):
24 # add input redirection if needed
25 if input and os.path.isfile(input):
26 cmd += " < %s" % input
27
28 # if this is a dry_run, only print the commands that would be ran
29 if settings.dry_run :
30 print("cmd: %s" % cmd)
31 return 0, None
32
33 # otherwise create a pipe and run the desired command
34 else :
35 proc = Popen(cmd, stdout=None if print2stdout else PIPE, stderr=STDOUT, shell=True)
36 out, err = proc.communicate()
37 return proc.returncode, out
38
39def is_ascii(fname):
40 if settings.dry_run:
41 print("is_ascii: %s" % fname)
42 return True
43
44 if not os.path.isfile(fname):
45 return False
46
47 code, out = sh("file %s" % fname, print2stdout = False)
48 if code != 0:
49 return False
50
51 match = re.search(".*: (.*)", out)
52
53 if not match:
54 return False
55
56 return match.group(1).startswith("ASCII text")
57
58# Remove 1 or more files silently
59def rm( files ):
60 if isinstance( files, basestring ):
61 sh("rm -f %s > /dev/null 2>&1" % files )
62 else:
63 for file in files:
64 sh("rm -f %s > /dev/null 2>&1" % file )
65
66# Create 1 or more directory
67def mkdir( files ):
68 if isinstance( files, basestring ):
69 sh("mkdir -p %s" % os.path.dirname(files) )
70 else:
71 for file in files:
72 sh("mkdir -p %s" % os.path.dirname(file) )
73
74
75def chdir( dest = __main__.__file__ ):
76 abspath = os.path.abspath(dest)
77 dname = os.path.dirname(abspath)
78 os.chdir(dname)
79
80# diff two files
81def diff( lhs, rhs ):
82 # diff the output of the files
83 diff_cmd = ("diff --text "
84# "--ignore-all-space "
85# "--ignore-blank-lines "
86 "--old-group-format='\t\tmissing lines :\n"
87 "%%<' \\\n"
88 "--new-group-format='\t\tnew lines :\n"
89 "%%>' \\\n"
90 "--unchanged-group-format='%%=' \\"
91 "--changed-group-format='\t\texpected :\n"
92 "%%<"
93 "\t\tgot :\n"
94 "%%>\n' \\\n"
95 "--new-line-format='\t\t%%dn\t%%L' \\\n"
96 "--old-line-format='\t\t%%dn\t%%L' \\\n"
97 "--unchanged-line-format='' \\\n"
98 "%s %s")
99
100 # fetch return code and error from the diff command
101 return sh(diff_cmd % (lhs, rhs), False)
102
103# call make
104def make(target, flags = '', redirects = '', error_file = None, silent = False):
105 test_param = """test="%s" """ % (error_file) if error_file else ''
106 cmd = ' '.join([
107 settings.make,
108 '-s' if silent else '',
109 test_param,
110 settings.arch.flags,
111 settings.debug.flags,
112 settings.install.flags,
113 flags,
114 target,
115 redirects
116 ])
117 return sh(cmd)
118
119def which(program):
120 import os
121 def is_exe(fpath):
122 return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
123
124 fpath, fname = os.path.split(program)
125 if fpath:
126 if is_exe(program):
127 return program
128 else:
129 for path in os.environ["PATH"].split(os.pathsep):
130 exe_file = os.path.join(path, program)
131 if is_exe(exe_file):
132 return exe_file
133
134 return None
135
136def run(exe, output, input):
137 ret, _ = sh("timeout %d %s > %s 2>&1" % (settings.timeout.single, exe, output), input = input)
138 return ret
139
140################################################################################
141# file handling
142################################################################################
143# move a file
144def mv(source, dest):
145 ret, _ = sh("mv %s %s" % (source, dest))
146 return ret
147
148# cat one file into the other
149def cat(source, dest):
150 ret, _ = sh("cat %s > %s" % (source, dest))
151 return ret
152
153# helper function to replace patterns in a file
154def file_replace(fname, pat, s_after):
155 if settings.dry_run:
156 print("replacing '%s' with '%s' in %s" % (pat, s_after, fname))
157 return
158
159 file = fileinput.FileInput(fname, inplace=True, backup='.bak')
160 for line in file:
161 print(line.replace(pat, s_after), end='')
162 file.close()
163
164# helper function to check if a files contains only a specific string
165def fileContainsOnly(file, text) :
166 with open(file) as f:
167 ff = f.read().strip()
168 result = ff == text.strip()
169
170 return result;
171
172# check whether or not a file is executable
173def fileIsExecutable(file) :
174 try :
175 fileinfo = os.stat(file)
176 return bool(fileinfo.st_mode & stat.S_IXUSR)
177 except Exception as inst:
178 print(type(inst)) # the exception instance
179 print(inst.args) # arguments stored in .args
180 print(inst)
181 return False
182
183# transform path to canonical form
184def canonicalPath(path):
185 abspath = os.path.abspath(__main__.__file__)
186 dname = os.path.dirname(abspath)
187 return os.path.join(dname, os.path.normpath(path) )
188
189# compare path even if form is different
190def pathCmp(lhs, rhs):
191 return canonicalPath( lhs ) == canonicalPath( rhs )
192
193# walk all files in a path
194def pathWalk( op ):
195 def step(_, dirname, names):
196 for name in names:
197 path = os.path.join(dirname, name)
198 op( path )
199
200 # Start the walk
201 dname = settings.SRCDIR
202 os.path.walk(dname, step, '')
203
204################################################################################
205# system
206################################################################################
207# count number of jobs to create
208def jobCount( options, tests ):
209 # check if the user already passed in a number of jobs for multi-threading
210 if not options.jobs:
211 make_flags = os.environ.get('MAKEFLAGS')
212 force = bool(make_flags)
213 make_jobs_fds = re.search("--jobserver-(auth|fds)=\s*([0-9]+),([0-9]+)", make_flags) if make_flags else None
214 if make_jobs_fds :
215 tokens = os.read(int(make_jobs_fds.group(2)), 1024)
216 options.jobs = len(tokens)
217 os.write(int(make_jobs_fds.group(3)), tokens)
218 else :
219 options.jobs = multiprocessing.cpu_count()
220 else :
221 force = True
222
223 # make sure we have a valid number of jobs that corresponds to user input
224 if options.jobs <= 0 :
225 print('ERROR: Invalid number of jobs', file=sys.stderr)
226 sys.exit(1)
227
228 return min( options.jobs, len(tests) ), force
229
230# setup a proper processor pool with correct signal handling
231def setupPool(jobs):
232 original_sigint_handler = signal.signal(signal.SIGINT, signal.SIG_IGN)
233 pool = multiprocessing.Pool(jobs)
234 signal.signal(signal.SIGINT, original_sigint_handler)
235
236 return pool
237
238# handle signals in scope
239class SignalHandling():
240 def __enter__(self):
241 # enable signal handling
242 signal.signal(signal.SIGINT, signal.SIG_DFL)
243
244 def __exit__(self, type, value, traceback):
245 # disable signal handling
246 signal.signal(signal.SIGINT, signal.SIG_IGN)
247
248
249# enable core dumps for all the test children
250resource.setrlimit(resource.RLIMIT_CORE, (resource.RLIM_INFINITY, resource.RLIM_INFINITY))
251
252################################################################################
253# misc
254################################################################################
255
256# check if arguments is yes or no
257def yes_no(string):
258 if string == "yes" :
259 return True
260 if string == "no" :
261 return False
262 raise argparse.ArgumentTypeError(msg)
263 return False
264
265def fancy_print(text):
266 column = which('column')
267 if column:
268 cmd = "%s 2> /dev/null" % column
269 proc = Popen(cmd, stdin=PIPE, stderr=None, shell=True)
270 proc.communicate(input=text + "\n")
271 else:
272 print(text)
273
274
275def coreInfo(path):
276 cmd = os.path.join(settings.SRCDIR, "pybin/print-core.gdb")
277 if not os.path.isfile(cmd):
278 return 1, "ERR Printing format for core dumps not found"
279
280 dname = os.path.dirname(path)
281 core = os.path.join(dname, "core" )
282 if not os.path.isfile(path):
283 return 1, "ERR Executable path is wrong"
284
285 if not os.path.isfile(core):
286 return 1, "ERR No core dump"
287
288 return sh("gdb -n %s %s -batch -x %s" % (path, core, cmd), print2stdout=False)
289
290class Timed:
291 def __enter__(self):
292 self.start = time.time()
293 return self
294
295 def __exit__(self, *args):
296 self.end = time.time()
297 self.duration = self.end - self.start
Note: See TracBrowser for help on using the repository browser.