2 from __future__
import print_function
11 from shutil
import copy, rmtree
12 from collections
import defaultdict
17 OUTFILE_TREE =
"configtree.sqlite" 18 IGNORE_PACKAGES = [
'FWCore/ParameterSet',
'DQMOffline/Configuration/scripts',
"cmsRun"]
20 os.environ[
"CMSSW_BASE"] +
"/python/", os.environ[
"CMSSW_RELEASE_BASE"] +
"/python/",
21 os.environ[
"CMSSW_BASE"] +
"/cfipython/", os.environ[
"CMSSW_RELEASE_BASE"] +
"/cfipython/"]
32 del FWCore.ParameterSet.Mixins._Labelable.__str__
38 stack = inspect.stack()
40 while i < len(stack)
and len(stack[i])>=2
and any(
map(
lambda p: p
in stack[i][1], IGNORE_PACKAGES)):
46 while j < len(res)
and not 'cmsRun' in res[j][1]:
49 if len(res)>=1
and len(res[0])>=3:
52 return [(
"unknown",
"unknown",
"unknown")]
54 def trace_location(thing, name, extra = lambda thing, *args, **kwargs: thing):
55 old_method = getattr(thing, name)
56 def trace_location_hook(self, *args, **kwargs):
57 retval = old_method(self, *args, **kwargs)
60 event = (name, tuple(w[1:3]
for w
in where), extra(self, *args, **kwargs))
61 if hasattr(self,
"_trace_events"):
62 getattr(self,
"_trace_events").
append(event)
65 self.__dict__[
"_trace_events"] = [ event ]
68 setattr(thing, name, trace_location_hook)
72 return [x
for x
in args
if not isinstance(x, list)] + sum(
73 [
flatten(*x)
for x
in args
if isinstance(x, list)], [])
76 from FWCore.ParameterSet.Modules
import _Module, Source, ESSource, ESPrefer, ESProducer, Service, Looper
77 from FWCore.ParameterSet.Config
import Process
90 trace_location(_ModuleSequenceType,
'__init__',
lambda self, *arg: {
'args': list(arg)})
92 trace_location(_ModuleSequenceType,
'associate',
lambda self, *tasks: {
'args': list(tasks)})
93 trace_location(_ModuleSequenceType,
'__imul__',
lambda self, rhs: {
'rhs': rhs})
94 trace_location(_ModuleSequenceType,
'__iadd__',
lambda self, rhs: {
'rhs': rhs})
95 trace_location(_ModuleSequenceType,
'copyAndExclude',
lambda self, listOfModulesToExclude: {
'olds': list(listOfModulesToExclude)})
96 trace_location(_ModuleSequenceType,
'replace',
lambda self, original, replacement: {
'old': original,
'new': replacement})
97 trace_location(_ModuleSequenceType,
'insert',
lambda self, index, item: {
'rhs': item})
98 trace_location(_ModuleSequenceType,
'remove',
lambda self, something: {
'old': something})
100 trace_location(Task,
'add',
lambda self, *items: {
'args': list(items)})
102 trace_location(Task,
'copyAndExclude',
lambda self, listOfModulesToExclude: {
'olds': list(listOfModulesToExclude)})
103 trace_location(Schedule,
'__init__',
lambda self, *args, **kwargs: {
'args':
flatten(list(args), kwargs.values())})
104 trace_location(Schedule,
'associate',
lambda self, *tasks: {
'args': list(tasks)})
112 items += [(
"source", self.source)]
114 items += [(
"looper", self.looper)]
116 items += self.outputModules.
items()
118 items += self.paths.
items()
119 items += self.endpaths.
items()
120 items += self.services.
items()
121 items += self.es_producers.
items()
122 items += self.es_sources.
items()
123 items += self.es_prefers.
items()
127 items += [(
"schedule", self.schedule)]
129 Process.items_=new_items_
136 classname = thing.__class__.__name__
137 if hasattr(thing,
'_trace_events'):
138 events = list(getattr(thing,
'_trace_events'))
139 getattr(thing,
'_trace_events')[:] = []
140 for action, loc, extra
in events:
141 entry = (action, classname, loc)
142 graph.append((entry, parent, name))
146 if hasattr(extra,
'items_'):
147 items += extra.items_()
148 if hasattr(extra,
'_seq'):
149 seq = getattr(extra,
'_seq')
151 items += [(
'seqitem', x)
for x
in getattr(seq,
'_collection')]
152 if hasattr(extra,
'_tasks'):
153 items += [(
'task', x)
for x
in getattr(extra,
'_tasks')]
154 if hasattr(extra,
'_collection'):
155 items += [(
'subtask', x)
for x
in getattr(extra,
'_collection')]
156 if hasattr(extra,
'_operand'):
157 items += [(
'operand', getattr(extra,
'_operand'))]
158 if isinstance(extra, dict):
159 for key, value
in extra.items():
160 if isinstance(value, list):
161 items += [(key, x)
for x
in value]
163 items += [(key, value)]
165 for name, child
in items:
169 if not thing
is None:
170 print(
"No _trace_events found in %s.\nMaybe turn on tracing for %s?" % (thing, classname))
171 print(
" Found in %s" % (parent,))
175 progname =
" ".
join(PREFIXINFO)
176 print(
"+Done running %s, writing output..." % progname)
179 filename = os.path.abspath(filename)
180 for pfx
in STRIPPATHS:
181 if filename.startswith(pfx):
182 filename = filename[len(pfx):]
186 for child, parent, relation
in graph:
187 files.add(child[2][0][0])
188 files.add(parent[2][0][0])
190 conn = sqlite3.connect(os.environ[
"CMSSWCALLTREE"])
192 cur.executescript(
""" 193 CREATE TABLE IF NOT EXISTS file(id INTEGER PRIMARY KEY, 194 name TEXT, UNIQUE(name) 196 CREATE TABLE IF NOT EXISTS trace(id INTEGER PRIMARY KEY, 197 parent INTEGER, -- points into same table, recursively 198 file INTEGER, line INTEGER, 199 FOREIGN KEY(parent) REFERENCES trace(id), 200 FOREIGN KEY(file) REFERENCES file(id), 201 UNIQUE(parent, file, line) 203 CREATE TABLE IF NOT EXISTS relation(id INTEGER PRIMARY KEY, 210 FOREIGN KEY(place) REFERENCES trace(id), 211 FOREIGN KEY(usedby) REFERENCES trace(id) 213 CREATE INDEX IF NOT EXISTS placeidx ON relation(place); 214 CREATE INDEX IF NOT EXISTS usedbyidx ON relation(usedby); 215 CREATE INDEX IF NOT EXISTS traceidx ON trace(file); 216 -- SQLite does not optimise that one well, but a VIEW is still nice to have... 217 CREATE VIEW IF NOT EXISTS fulltrace AS 218 WITH RECURSIVE fulltrace(level, baseid, parent, file, name, line) AS ( 219 SELECT 1 AS level, trace.id, parent, trace.file, file.name, line FROM trace 220 INNER JOIN file ON file.id = trace.file 221 UNION SELECT level+1, baseid, trace.parent, trace.file, file.name, trace.line FROM fulltrace 222 INNER JOIN trace ON trace.id = fulltrace.parent 223 INNER JOIN file ON file.id = trace.file) 224 SELECT * FROM fulltrace; 226 cur.executemany(
"INSERT OR IGNORE INTO file(name) VALUES (?);",
228 def inserttrace(loc):
230 for filename, line
in reversed(loc):
231 conn.execute(
"INSERT OR IGNORE INTO trace(parent, file, line) SELECT ?, id, ? FROM file WHERE name == ?;", (parent, line,
formatfile(filename)))
232 cur = conn.execute(
"SELECT trace.id FROM trace LEFT JOIN file ON trace.file == file.id WHERE trace.parent = ? AND file.name = ? AND trace.line = ?;", (parent,
formatfile(filename), line))
237 for child, parent, relation
in graph:
238 cevt, cclassname, cloc = child
239 pevt, pclassname, ploc = parent
240 place = inserttrace(cloc)
241 usedby = inserttrace(ploc)
242 cur.execute(
"INSERT OR IGNORE INTO relation(place, place_type, usedby, usedby_type, relation, source) VALUES (?,?,?,?,?,?);", (
243 place,
"%s::%s" % (cclassname, cevt),
244 usedby,
"%s::%s" % (pclassname, pevt),
253 cwd = os.path.abspath(os.getcwd())
254 wf = re.match(
".*/(\d+\.\d+)_", cwd)
256 PREFIXINFO.append(
"wf")
257 PREFIXINFO.append(wf.groups()[0])
258 online = re.match(
"(.*/)?(.*)_dqm_sourceclient-live_cfg\.py", argv[0])
260 PREFIXINFO.append(
"online")
261 PREFIXINFO.append(online.groups()[1])
262 step = re.match(
"(step\d+)_.*\.py", argv[0])
264 PREFIXINFO.append(step.groups()[0])
265 processing = re.match(
"step\d+_.*(RECO|ALCA|HARVEST).*\.py", argv[0])
267 PREFIXINFO.append(processing.groups()[0])
269 PREFIXINFO.append(argv[0])
272 bindir = tempfile.mkdtemp()
273 print(
"+Setting up in ", bindir)
274 os.symlink(ARGV0, bindir +
"/cmsRun")
275 os.environ[
"PATH"] = bindir +
":" + os.environ[
"PATH"]
276 os.environ[
"CMSSWCALLTREE"] = bindir +
"/" + OUTFILE_TREE
277 os.environ[
"CMSSWCALLBASE"] = os.path.abspath(os.getcwd()) +
"/" 278 with open(os.environ[
"CMSSWCALLTREE"],
"w")
as f:
283 print(
"+Cleaning up ", tmpdir)
284 copy(os.environ[
"CMSSWCALLTREE"],
".")
290 if not "CMSSWCALLTREE" in os.environ:
293 subprocess.call(argv)
301 progname = prog_argv[0]
304 with open(file_path)
as fp:
305 code = compile(fp.read(), progname,
'exec')
308 exec code
in globals, globals
310 print(traceback.format_exc())
314 print(
"+Collecting trace information from %s..." % globals[
"process"])
315 collect_trace(globals[
"process"],
'cmsrun', graph, (
'cmsRun',
'', ((progname, 0),)))
318 except OSError
as err:
319 print(
"+Cannot run file %r because: %s" % (sys.argv[0], err))
329 file_path = os.path.join(entry, progname)
330 if os.path.isfile(file_path):
332 if not os.path.isfile(file_path):
333 print(
"+Cannot find program (%s) in modified $PATH (%s)." % (progname, path))
335 print(
"+Found %s as %s in %s." % (progname, file_path, path))
340 print(
"Usage: %s <some cmssw commandline>" % (sys.argv[0]))
341 print(
" The given programs will be executed, instrumenting calls to cmsRun.")
342 print(
" cmsRun will not actually run cmssw, but all the Python code will be executed and instrumentd. The results are written to the file `%s` in the same directory." % OUTFILE_TREE)
343 print(
" The callgraph output lists edges pointing from each function to the one calling it.")
344 print(
" To view the results using a simple webpage, use\n %s serve" % sys.argv[0])
346 print(
" %s runTheMatrix.py -l 1000 --ibeos" % sys.argv[0])
349 print(
"+Running cmsswConfigtrace...")
352 if sys.argv[0].endswith(
'cmsRun'):
353 print(
"+Wrapping cmsRun...")
355 STRIPPATHS.append(os.environ[
"CMSSWCALLBASE"])
358 if len(sys.argv) <= 1:
362 print(
"+Running command with tracing %s..." % sys.argv[1:])
369 STRIPPATHS.append(os.path.abspath(os.getcwd()) +
"/")
371 import SimpleHTTPServer
375 conn = sqlite3.connect(OUTFILE_TREE)
380 def formatsource(source, formatstr = '<em class="%s">%s</em>'):
381 processings = [
"ALCA",
"RECO",
"HARVEST",
"online"]
382 info = source.split(
" ")
383 processing =
" ".
join(
filter(
lambda x: x
in processings, info))
384 source =
" ".
join(
filter(
lambda x: x
not in processings, info))
385 return formatstr % (processing, escape(source))
387 def formatplace(filename, line):
389 shortname = filename[ :(MAXLEN-3)/2] +
"..." + filename[-(MAXLEN-3)/2: ]
if len(filename) > MAXLEN
else filename
390 return '<a href="/%s#%s" target="_blank">%s:%s</a>' % (escape(filename), line, escape(shortname), line)
393 out = [escape(
'goto to /<filename> for info about a file')]
395 wfs = defaultdict(list)
396 for source, wf
in conn.execute(
""" -- some SQL hackery here to parse "source" 397 SELECT DISTINCT source, substr(source, instr(source, "wf ")+3)*1 FROM relation ORDER BY 2, 1;"""):
398 wfs[wf].
append(
'<a href="/workflow/%s">%s</a>' % (urllib.quote(source), formatsource(source)))
401 out.append(
'<li>' +
" ".
join(wfs[wf]) +
"</li>")
405 for f
in conn.execute(
"SELECT name FROM file ORDER BY name;"):
407 out.append(
'<li><a href="/%s">%s</a></li>' % (name, name))
410 return "\n".
join(out)
412 def showworkflow(source):
413 source = urllib.unquote(source)
414 cur = conn.execute(
""" 415 SELECT DISTINCT file.name FROM relation 416 INNER JOIN trace ON place = trace.id 417 INNER JOIN file ON file.id = trace.file 418 WHERE relation.source = ? 421 out = [
"Files used by workflow %s: <ul>" % formatsource(source)]
424 out.append(
'<li><a href="/%s">%s</a></li>' % (name, name))
426 return "\n".
join(out)
428 def showfile(filename):
430 out.append(
'<script src="https://rawgit.com/google/code-prettify/master/src/prettify.js"></script>')
431 out.append(
'<link rel="stylesheet" href="https://rawgit.com/google/code-prettify/master/src/prettify.css"></link>')
436 with open(d + filename)
as f:
437 lines = f.readlines()
438 out.append(
"Read %s" % f.name)
444 cur = conn.execute(
""" 445 SELECT DISTINCT trace.line, source FROM relation 446 INNER JOIN trace on relation.place = trace.id 447 INNER JOIN file ON trace.file == file.id 448 WHERE file.name == ? ORDER BY line, source;""", (filename,))
449 sourceinfo = defaultdict(list)
450 for line, source
in cur:
451 sourceinfo[line].
append(source)
453 out.append(
'<pre class="prettyprint linenums">')
454 for i, l
in enumerate(lines):
456 tags = [formatsource(source,
'<em class="%%s" data-tag="%%s" data-line="%d"></em>' % (i+1))
for source
in sourceinfo[i+1]]
457 out.append(escape(l).rstrip() +
"".
join(tags))
460 out.append(
"""<script type="text/javascript"> 462 clickfunc = function(evt) { 463 document.querySelectorAll("li > iframe, li > br").forEach(function(e) {e.remove()}); 464 dest = "/info" + window.location.pathname + ":" + this.getAttribute("data-line"); 465 this.parentElement.insertAdjacentHTML("beforeend", '<br><iframe width="90%" height="500px" frameborder="0" src="' + dest + '"></iframe><br>'); 467 document.querySelectorAll("li > em").forEach(function(e) { 468 e.innerText = e.getAttribute("data-tag"); 469 e.onclick = clickfunc; 472 n = 1*window.location.hash.replace("#", ""); 474 li = document.querySelectorAll("li")[n-1]; 475 li.style = "background: #ee7"; 481 out.append(
"Could not find %s" % filename)
483 return "\n".
join(out)
486 def showinfo(filename, line):
489 def queryandoutput(startfrom, to, directiontext):
491 cur = conn.execute(
""" 492 SELECT place_type, -- why did we trace this line? 493 <to>file.name, <to>trace.line, -- where was it used? 494 usedby, usedby_type, relation, -- what was it used for? 495 place, source -- why did this code run? 497 INNER JOIN trace AS placetrace ON placetrace.id = relation.place 498 INNER JOIN trace AS usedbytrace ON usedbytrace.id = relation.usedby 499 INNER JOIN file AS placefile ON placefile.id = placetrace.file 500 INNER JOIN file AS usedbyfile ON usedbyfile.id = usedbytrace.file 501 WHERE <from>file.name = ? AND <from>trace.line = ? 503 .
replace(
"<from>", startfrom).
replace(
"<to>", to), (filename, line))
504 out.append(
"<p>%s %s <ul>" % (formatplace(filename, line), directiontext))
505 for place_type, pname, pline, usedby, usedby_type, relation, place, source
in cur:
507 '<li><tt>%s</tt> at %s by <tt>%s</tt> as <a href="/why/%d">%s</a> <a href="/why/%d">in</a> %s</li>' 508 % (escape(place_type), formatplace(pname, pline), escape(usedby_type), usedby, escape(relation), place, formatsource(source)))
509 out.append(
"</ul></p>")
511 queryandoutput(
"place",
"usedby",
"is used as")
512 queryandoutput(
"usedby",
"place",
"uses")
514 return "\n".
join(out)
519 cur = conn.execute(
""" 520 WITH RECURSIVE fulltrace(level, baseid, parent, file, name, line) AS ( 521 SELECT 1 AS level, trace.id, parent, trace.file, file.name, line FROM trace 522 INNER JOIN file ON file.id = trace.file 524 UNION SELECT level+1, baseid, trace.parent, trace.file, file.name, trace.line FROM fulltrace 525 INNER JOIN trace ON trace.id = fulltrace.parent 526 INNER JOIN file ON file.id = trace.file) 527 SELECT name, line FROM fulltrace ORDER BY level;""", (id,))
529 out.append(
"Full stack trace:<ul>")
530 for name, line
in cur:
531 out.append(
'<li>%s</li>' % formatplace(name, line))
533 return "\n".
join(out)
536 (re.compile(
'/workflow/(.*)$'), showworkflow),
537 (re.compile(
'/info/(.*):(\d+)$'), showinfo),
538 (re.compile(
'/why/(\d+)$'), showwhy),
539 (re.compile(
'/([^.]*[.]?[^.]+[.]?[^.]*)$'), showfile),
540 (re.compile(
'/$'), index),
546 for pattern, func
in ROUTES:
547 m = pattern.match(self.path)
549 res =
func(*m.groups())
553 self.send_response(200,
"Here you go")
554 self.send_header(
"Content-Type",
"text/html; charset=utf-8")
556 self.wfile.
write(
"""<html><style> 566 em.ALCA {background: #ee9; } 567 em.RECO {background: #9e9; } 568 em.HARVEST {background: #99e; } 569 em.online {background: #e99; } 571 self.wfile.
write(res)
572 self.wfile.
write(
"</body></html>")
575 self.send_response(400,
"Something went wrong")
577 trace = traceback.format_exc()
578 self.send_response(500,
"Things went very wrong")
579 self.send_header(
"Content-Type",
"text/plain; charset=utf-8")
581 self.wfile.
write(trace)
584 Handler = SimpleHTTPServer.SimpleHTTPRequestHandler
585 Handler.do_GET = do_GET
586 httpd = SocketServer.TCPServer((
"",SERVE_PORT), Handler)
587 print(
"serving at port", SERVE_PORT)
588 httpd.serve_forever()
590 if __name__ ==
'__main__':
591 if sys.argv[1] ==
"serve":
def collect_trace(thing, name, graph, parent)
bool any(const std::vector< T > &v, const T &what)
def replace(string, replacements)
def searchinpath(progname, path)
void print(TMatrixD &m, const char *label=nullptr, bool mathematicaFormat=false)
def trace_python(prog_argv, path)
static std::string join(char **cmd)