js/src/devtools/rootAnalysis/analyze.py

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rwxr-xr-x

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

michael@0 1 #!/usr/bin/python
michael@0 2
michael@0 3 #
michael@0 4 # This Source Code Form is subject to the terms of the Mozilla Public
michael@0 5 # License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 6 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
michael@0 7
michael@0 8 """
michael@0 9 Runs the static rooting analysis
michael@0 10 """
michael@0 11
michael@0 12 from subprocess import Popen
michael@0 13 import subprocess
michael@0 14 import os
michael@0 15 import argparse
michael@0 16 import sys
michael@0 17 import re
michael@0 18
michael@0 19 def env(config):
michael@0 20 e = dict(os.environ)
michael@0 21 e['PATH'] = '%s:%s' % (e['PATH'], config['sixgill_bin'])
michael@0 22 e['XDB'] = '%(sixgill_bin)s/xdb.so' % config
michael@0 23 e['SOURCE'] = config['source']
michael@0 24 e['ANALYZED_OBJDIR'] = config['objdir']
michael@0 25 return e
michael@0 26
michael@0 27 def fill(command, config):
michael@0 28 try:
michael@0 29 return tuple(s % config for s in command)
michael@0 30 except:
michael@0 31 print("Substitution failed:")
michael@0 32 problems = []
michael@0 33 for fragment in command:
michael@0 34 try:
michael@0 35 fragment % config
michael@0 36 except:
michael@0 37 problems.append(fragment)
michael@0 38 raise Exception("\n".join(["Substitution failed:"] + [ " %s" % s for s in problems ]))
michael@0 39
michael@0 40 def print_command(command, outfile=None, env=None):
michael@0 41 output = ' '.join(command)
michael@0 42 if outfile:
michael@0 43 output += ' > ' + outfile
michael@0 44 if env:
michael@0 45 changed = {}
michael@0 46 e = os.environ
michael@0 47 for key,value in env.items():
michael@0 48 if (key not in e) or (e[key] != value):
michael@0 49 changed[key] = value
michael@0 50 if changed:
michael@0 51 outputs = []
michael@0 52 for key, value in changed.items():
michael@0 53 if key in e and e[key] in value:
michael@0 54 start = value.index(e[key])
michael@0 55 end = start + len(e[key])
michael@0 56 outputs.append('%s="%s${%s}%s"' % (key,
michael@0 57 value[:start],
michael@0 58 key,
michael@0 59 value[end:]))
michael@0 60 else:
michael@0 61 outputs.append("%s='%s'" % (key, value))
michael@0 62 output = ' '.join(outputs) + " " + output
michael@0 63
michael@0 64 print output
michael@0 65
michael@0 66 def generate_hazards(config, outfilename):
michael@0 67 jobs = []
michael@0 68 for i in range(config['jobs']):
michael@0 69 command = fill(('%(js)s',
michael@0 70 '%(analysis_scriptdir)s/analyzeRoots.js',
michael@0 71 '%(gcFunctions_list)s',
michael@0 72 '%(gcEdges)s',
michael@0 73 '%(suppressedFunctions_list)s',
michael@0 74 '%(gcTypes)s',
michael@0 75 str(i+1), '%(jobs)s',
michael@0 76 'tmp.%s' % (i+1,)),
michael@0 77 config)
michael@0 78 outfile = 'rootingHazards.%s' % (i+1,)
michael@0 79 output = open(outfile, 'w')
michael@0 80 print_command(command, outfile=outfile, env=env(config))
michael@0 81 jobs.append((command, Popen(command, stdout=output, env=env(config))))
michael@0 82
michael@0 83 final_status = 0
michael@0 84 while jobs:
michael@0 85 pid, status = os.wait()
michael@0 86 jobs = [ job for job in jobs if job[1].pid != pid ]
michael@0 87 final_status = final_status or status
michael@0 88
michael@0 89 if final_status:
michael@0 90 raise subprocess.CalledProcessError(final_status, 'analyzeRoots.js')
michael@0 91
michael@0 92 with open(outfilename, 'w') as output:
michael@0 93 command = ['cat'] + [ 'rootingHazards.%s' % (i+1,) for i in range(config['jobs']) ]
michael@0 94 print_command(command, outfile=outfilename)
michael@0 95 subprocess.call(command, stdout=output)
michael@0 96
michael@0 97 JOBS = { 'dbs':
michael@0 98 (('%(ANALYSIS_SCRIPTDIR)s/run_complete',
michael@0 99 '--foreground',
michael@0 100 '--no-logs',
michael@0 101 '--build-root=%(objdir)s',
michael@0 102 '--wrap-dir=%(sixgill)s/scripts/wrap_gcc',
michael@0 103 '--work-dir=work',
michael@0 104 '-b', '%(sixgill_bin)s',
michael@0 105 '--buildcommand=%(buildcommand)s',
michael@0 106 '.'),
michael@0 107 ()),
michael@0 108
michael@0 109 'callgraph':
michael@0 110 (('%(js)s', '%(analysis_scriptdir)s/computeCallgraph.js'),
michael@0 111 'callgraph.txt'),
michael@0 112
michael@0 113 'gcFunctions':
michael@0 114 (('%(js)s', '%(analysis_scriptdir)s/computeGCFunctions.js', '%(callgraph)s',
michael@0 115 '[gcFunctions]', '[gcFunctions_list]', '[gcEdges]', '[suppressedFunctions_list]'),
michael@0 116 ('gcFunctions.txt', 'gcFunctions.lst', 'gcEdges.txt', 'suppressedFunctions.lst')),
michael@0 117
michael@0 118 'gcTypes':
michael@0 119 (('%(js)s', '%(analysis_scriptdir)s/computeGCTypes.js',),
michael@0 120 'gcTypes.txt'),
michael@0 121
michael@0 122 'allFunctions':
michael@0 123 (('%(sixgill_bin)s/xdbkeys', 'src_body.xdb',),
michael@0 124 'allFunctions.txt'),
michael@0 125
michael@0 126 'hazards':
michael@0 127 (generate_hazards, 'rootingHazards.txt'),
michael@0 128
michael@0 129 'explain':
michael@0 130 (('python', '%(analysis_scriptdir)s/explain.py',
michael@0 131 '%(hazards)s', '%(gcFunctions)s',
michael@0 132 '[explained_hazards]', '[unnecessary]', '[refs]'),
michael@0 133 ('hazards.txt', 'unnecessary.txt', 'refs.txt'))
michael@0 134 }
michael@0 135
michael@0 136 def out_indexes(command):
michael@0 137 for i in range(len(command)):
michael@0 138 m = re.match(r'^\[(.*)\]$', command[i])
michael@0 139 if m:
michael@0 140 yield (i, m.group(1))
michael@0 141
michael@0 142 def run_job(name, config):
michael@0 143 cmdspec, outfiles = JOBS[name]
michael@0 144 print("Running " + name + " to generate " + str(outfiles))
michael@0 145 if hasattr(cmdspec, '__call__'):
michael@0 146 cmdspec(config, outfiles)
michael@0 147 else:
michael@0 148 temp_map = {}
michael@0 149 cmdspec = fill(cmdspec, config)
michael@0 150 if isinstance(outfiles, basestring):
michael@0 151 stdout_filename = '%s.tmp' % name
michael@0 152 temp_map[stdout_filename] = outfiles
michael@0 153 print_command(cmdspec, outfile=outfiles, env=env(config))
michael@0 154 else:
michael@0 155 stdout_filename = None
michael@0 156 pc = list(cmdspec)
michael@0 157 outfile = 0
michael@0 158 for (i, name) in out_indexes(cmdspec):
michael@0 159 pc[i] = outfiles[outfile]
michael@0 160 outfile += 1
michael@0 161 print_command(pc, env=env(config))
michael@0 162
michael@0 163 command = list(cmdspec)
michael@0 164 outfile = 0
michael@0 165 for (i, name) in out_indexes(cmdspec):
michael@0 166 command[i] = '%s.tmp' % name
michael@0 167 temp_map[command[i]] = outfiles[outfile]
michael@0 168 outfile += 1
michael@0 169
michael@0 170 sys.stdout.flush()
michael@0 171 if stdout_filename is None:
michael@0 172 subprocess.check_call(command, env=env(config))
michael@0 173 else:
michael@0 174 with open(stdout_filename, 'w') as output:
michael@0 175 subprocess.check_call(command, stdout=output, env=env(config))
michael@0 176 for (temp, final) in temp_map.items():
michael@0 177 try:
michael@0 178 os.rename(temp, final)
michael@0 179 except OSError:
michael@0 180 print("Error renaming %s -> %s" % (temp, final))
michael@0 181 raise
michael@0 182
michael@0 183 config = { 'ANALYSIS_SCRIPTDIR': os.path.dirname(__file__) }
michael@0 184
michael@0 185 defaults = [ '%s/defaults.py' % config['ANALYSIS_SCRIPTDIR'],
michael@0 186 '%s/defaults.py' % os.getcwd() ]
michael@0 187
michael@0 188 for default in defaults:
michael@0 189 try:
michael@0 190 execfile(default, config)
michael@0 191 print("Loaded %s" % default)
michael@0 192 except:
michael@0 193 pass
michael@0 194
michael@0 195 data = config.copy()
michael@0 196
michael@0 197 parser = argparse.ArgumentParser(description='Statically analyze build tree for rooting hazards.')
michael@0 198 parser.add_argument('step', metavar='STEP', type=str, nargs='?',
michael@0 199 help='run starting from this step')
michael@0 200 parser.add_argument('--source', metavar='SOURCE', type=str, nargs='?',
michael@0 201 help='source code to analyze')
michael@0 202 parser.add_argument('--upto', metavar='UPTO', type=str, nargs='?',
michael@0 203 help='last step to execute')
michael@0 204 parser.add_argument('--jobs', '-j', default=None, metavar='JOBS', type=int,
michael@0 205 help='number of simultaneous analyzeRoots.js jobs')
michael@0 206 parser.add_argument('--list', const=True, nargs='?', type=bool,
michael@0 207 help='display available steps')
michael@0 208 parser.add_argument('--buildcommand', '--build', '-b', type=str, nargs='?',
michael@0 209 help='command to build the tree being analyzed')
michael@0 210 parser.add_argument('--tag', '-t', type=str, nargs='?',
michael@0 211 help='name of job, also sets build command to "build.<tag>"')
michael@0 212 parser.add_argument('--expect-file', type=str, nargs='?',
michael@0 213 help='deprecated option, temporarily still present for backwards compatibility')
michael@0 214
michael@0 215 args = parser.parse_args()
michael@0 216 for k,v in vars(args).items():
michael@0 217 if v is not None:
michael@0 218 data[k] = v
michael@0 219
michael@0 220 if args.tag and not args.buildcommand:
michael@0 221 args.buildcommand="build.%s" % args.tag
michael@0 222
michael@0 223 if args.jobs is not None:
michael@0 224 data['jobs'] = args.jobs
michael@0 225 if not data.get('jobs'):
michael@0 226 data['jobs'] = subprocess.check_output(['nproc', '--ignore=1'])
michael@0 227
michael@0 228 if args.buildcommand:
michael@0 229 data['buildcommand'] = args.buildcommand
michael@0 230 elif 'BUILD' in os.environ:
michael@0 231 data['buildcommand'] = os.environ['BUILD']
michael@0 232 else:
michael@0 233 data['buildcommand'] = 'make -j4 -s'
michael@0 234
michael@0 235 if 'ANALYZED_OBJDIR' in os.environ:
michael@0 236 data['objdir'] = os.environ['ANALYZED_OBJDIR']
michael@0 237
michael@0 238 if 'SOURCE' in os.environ:
michael@0 239 data['source'] = os.environ['SOURCE']
michael@0 240 if not data.get('source') and data.get('sixgill_bin'):
michael@0 241 path = subprocess.check_output(['sh', '-c', data['sixgill_bin'] + '/xdbkeys file_source.xdb | grep jsapi.cpp'])
michael@0 242 data['source'] = path.replace("/js/src/jsapi.cpp", "")
michael@0 243
michael@0 244 steps = [ 'dbs',
michael@0 245 'callgraph',
michael@0 246 'gcTypes',
michael@0 247 'gcFunctions',
michael@0 248 'allFunctions',
michael@0 249 'hazards',
michael@0 250 'explain' ]
michael@0 251
michael@0 252 if args.list:
michael@0 253 for step in steps:
michael@0 254 command, outfilename = JOBS[step]
michael@0 255 if outfilename:
michael@0 256 print("%s -> %s" % (step, outfilename))
michael@0 257 else:
michael@0 258 print(step)
michael@0 259 sys.exit(0)
michael@0 260
michael@0 261 for step in steps:
michael@0 262 command, outfiles = JOBS[step]
michael@0 263 if isinstance(outfiles, basestring):
michael@0 264 data[step] = outfiles
michael@0 265 else:
michael@0 266 outfile = 0
michael@0 267 for (i, name) in out_indexes(command):
michael@0 268 data[name] = outfiles[outfile]
michael@0 269 outfile += 1
michael@0 270 assert len(outfiles) == outfile, 'step \'%s\': mismatched number of output files and params' % step
michael@0 271
michael@0 272 if args.step:
michael@0 273 steps = steps[steps.index(args.step):]
michael@0 274
michael@0 275 if args.upto:
michael@0 276 steps = steps[:steps.index(args.upto)+1]
michael@0 277
michael@0 278 for step in steps:
michael@0 279 run_job(step, data)

mercurial