source: tests/pybin/tools.py @ 0c13238

ADTaaron-thesisarm-ehast-experimentalcleanup-dtorsenumforall-pointer-decayjacob/cs343-translationjenkins-sandboxnew-astnew-ast-unique-exprno_listpersistent-indexerpthread-emulationqualifiedEnum
Last change on this file since 0c13238 was 0c13238, checked in by Thierry Delisle <tdelisle@…>, 5 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.