blob: f39d3491b9bdc0f0b7998dfde671fc679c00a2db [file] [log] [blame]
#!/usr/bin/python
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
"""
Runs the static rooting analysis
"""
from subprocess import Popen
import subprocess
import os
import argparse
import sys
def env(config):
e = os.environ
e['PATH'] = '%s:%s/bin' % (e['PATH'], config['sixgill'])
e['XDB'] = '%(sixgill)s/bin/xdb.so' % config
e['SOURCE_ROOT'] = e['TARGET']
return e
def fill(command, config):
try:
return tuple(s % config for s in command)
except:
print("Substitution failed:")
for fragment in command:
try:
fragment % config
except:
print(" %s" % fragment)
raise Hell
def generate_hazards(config, outfilename):
jobs = []
for i in range(config['jobs']):
command = fill(('%(js)s',
'%(analysis_scriptdir)s/analyzeRoots.js',
'%(gcFunctions_list)s',
'%(suppressedFunctions_list)s',
'%(gcTypes)s',
str(i+1), '%(jobs)s',
'tmp.%s' % (i+1,)),
config)
outfile = 'rootingHazards.%s' % (i+1,)
output = open(outfile, 'w')
print(' '.join(command) + ' > ' + outfile)
jobs.append((command, Popen(command, stdout=output, env=env(config))))
final_status = 0
while jobs:
pid, status = os.wait()
jobs = [ job for job in jobs if job[1].pid != pid ]
final_status = final_status or status
if final_status:
raise subprocess.CalledProcessError(final_status, 'analyzeRoots.js')
with open(outfilename, 'w') as output:
command = ['cat'] + [ 'rootingHazards.%s' % (i+1,) for i in range(config['jobs']) ]
print(' '.join(command) + ' > ' + outfilename)
subprocess.call(command, stdout=output)
JOBS = { 'dbs':
(('%(CWD)s/run_complete',
'--foreground',
'--build-root=%(objdir)s',
'--work=dir=work',
'-b', '%(sixgill)s/bin',
'--buildcommand=%(buildcommand)s',
'.'),
None),
'callgraph':
(('%(js)s', '%(analysis_scriptdir)s/computeCallgraph.js'),
'callgraph.txt'),
'gcFunctions':
(('%(js)s', '%(analysis_scriptdir)s/computeGCFunctions.js', '%(callgraph)s'),
'gcFunctions.txt'),
'gcFunctions_list':
(('perl', '-lne', 'print $1 if /^GC Function: (.*)/', '%(gcFunctions)s'),
'gcFunctions.lst'),
'suppressedFunctions_list':
(('perl', '-lne', 'print $1 if /^Suppressed Function: (.*)/', '%(gcFunctions)s'),
'suppressedFunctions.lst'),
'gcTypes':
(('%(js)s', '%(analysis_scriptdir)s/computeGCTypes.js',),
'gcTypes.txt'),
'allFunctions':
(('%(sixgill)s/bin/xdbkeys', 'src_body.xdb',),
'allFunctions.txt'),
'hazards':
(generate_hazards, 'rootingHazards.txt')
}
def run_job(name, config):
command, outfilename = JOBS[name]
print("Running " + name + " to generate " + str(outfilename))
if hasattr(command, '__call__'):
command(config, outfilename)
else:
command = fill(command, config)
print(' '.join(command))
temp = '%s.tmp' % name
with open(temp, 'w') as output:
subprocess.check_call(command, stdout=output, env=env(config))
if outfilename is not None:
os.rename(temp, outfilename)
config = { 'CWD': os.path.dirname(__file__) }
defaults = [ '%s/defaults.py' % config['CWD'] ]
for default in defaults:
try:
execfile(default, config)
except:
pass
data = config.copy()
parser = argparse.ArgumentParser(description='Statically analyze build tree for rooting hazards.')
parser.add_argument('target', metavar='TARGET', type=str, nargs='?',
help='run starting from this target')
parser.add_argument('--jobs', '-j', default=4, metavar='JOBS', type=int,
help='number of simultaneous analyzeRoots.js jobs')
parser.add_argument('--list', const=True, nargs='?', type=bool,
help='display available targets')
parser.add_argument('--buildcommand', '--build', '-b', type=str, nargs='?',
help='command to build the tree being analyzed')
parser.add_argument('--tag', '-t', type=str, nargs='?',
help='name of job, also sets build command to "build.<tag>"')
args = parser.parse_args()
data.update(vars(args))
if args.tag and not args.buildcommand:
args.buildcommand="build.%s" % args.tag
if args.buildcommand:
data['buildcommand'] = args.buildcommand
elif 'BUILD' in os.environ:
data['buildcommand'] = os.environ['BUILD']
else:
data['buildcommand'] = 'make -j4 -s'
targets = [ 'dbs',
'callgraph',
'gcTypes',
'gcFunctions',
'gcFunctions_list',
'suppressedFunctions_list',
'allFunctions',
'hazards' ]
if args.list:
for target in targets:
command, outfilename = JOBS[target]
if outfilename:
print("%s -> %s" % (target, outfilename))
else:
print(target)
sys.exit(0)
for target in targets:
command, outfilename = JOBS[target]
data[target] = outfilename
if args.target:
targets = targets[targets.index(args.target):]
for target in targets:
run_job(target, data)