2 from __future__
import print_function
12 from shutil
import copy, rmtree
13 from collections
import defaultdict
18 OUTFILE_TREE =
"configtree.sqlite"
19 IGNORE_PACKAGES = [
'FWCore/ParameterSet',
'DQMOffline/Configuration/scripts',
"cmsRun"]
21 os.environ[
"CMSSW_BASE"] +
"/python/", os.environ[
"CMSSW_RELEASE_BASE"] +
"/python/",
22 os.environ[
"CMSSW_BASE"] +
"/cfipython/", os.environ[
"CMSSW_RELEASE_BASE"] +
"/cfipython/"]
33 del FWCore.ParameterSet.Mixins._Labelable.__str__
39 stack = inspect.stack()
41 while i < len(stack)
and len(stack[i])>=2
and any(
map(
lambda p: p
in stack[i][1], IGNORE_PACKAGES)):
47 while j < len(res)
and not 'cmsRun' in res[j][1]:
50 if len(res)>=1
and len(res[0])>=3:
53 return [(
"unknown",
"unknown",
"unknown")]
55 def trace_location(thing, name, extra = lambda thing, *args, **kwargs: thing):
56 old_method = getattr(thing, name)
57 def trace_location_hook(self, *args, **kwargs):
58 retval = old_method(self, *args, **kwargs)
61 event = (name, tuple(w[1:3]
for w
in where), extra(self, *args, **kwargs))
62 if hasattr(self,
"_trace_events"):
63 getattr(self,
"_trace_events").
append(event)
66 self.__dict__[
"_trace_events"] = [ event ]
69 setattr(thing, name, trace_location_hook)
73 return [x
for x
in args
if not isinstance(x, list)] + sum(
74 [
flatten(*x)
for x
in args
if isinstance(x, list)], [])
77 from FWCore.ParameterSet.Modules
import _Module, Source, ESSource, ESPrefer, ESProducer, Service, Looper
78 from FWCore.ParameterSet.Config
import Process
91 trace_location(_ModuleSequenceType,
'__init__',
lambda self, *arg: {
'args': list(arg)})
93 trace_location(_ModuleSequenceType,
'associate',
lambda self, *tasks: {
'args': list(tasks)})
94 trace_location(_ModuleSequenceType,
'__imul__',
lambda self, rhs: {
'rhs': rhs})
95 trace_location(_ModuleSequenceType,
'__iadd__',
lambda self, rhs: {
'rhs': rhs})
96 trace_location(_ModuleSequenceType,
'copyAndExclude',
lambda self, listOfModulesToExclude: {
'olds': list(listOfModulesToExclude)})
97 trace_location(_ModuleSequenceType,
'replace',
lambda self, original, replacement: {
'old': original,
'new': replacement})
98 trace_location(_ModuleSequenceType,
'insert',
lambda self, index, item: {
'rhs': item})
99 trace_location(_ModuleSequenceType,
'remove',
lambda self, something: {
'old': something})
101 trace_location(Task,
'add',
lambda self, *items: {
'args': list(items)})
103 trace_location(Task,
'copyAndExclude',
lambda self, listOfModulesToExclude: {
'olds': list(listOfModulesToExclude)})
104 trace_location(Schedule,
'__init__',
lambda self, *args, **kwargs: {
'args':
flatten(list(args), kwargs.values())})
105 trace_location(Schedule,
'associate',
lambda self, *tasks: {
'args': list(tasks)})
113 items += [(
"source", self.source)]
115 items += [(
"looper", self.looper)]
117 items += self.outputModules.
items()
119 items += six.iteritems(self.paths)
120 items += self.endpaths.
items()
121 items += self.services.
items()
122 items += self.es_producers.
items()
123 items += self.es_sources.
items()
124 items += self.es_prefers.
items()
128 items += [(
"schedule", self.schedule)]
130 Process.items_=new_items_
137 classname = thing.__class__.__name__
138 if hasattr(thing,
'_trace_events'):
139 events = list(getattr(thing,
'_trace_events'))
140 getattr(thing,
'_trace_events')[:] = []
141 for action, loc, extra
in events:
142 entry = (action, classname, loc)
143 graph.append((entry, parent, name))
147 if hasattr(extra,
'items_'):
148 items += extra.items_()
149 if hasattr(extra,
'_seq'):
150 seq = getattr(extra,
'_seq')
152 items += [(
'seqitem', x)
for x
in getattr(seq,
'_collection')]
153 if hasattr(extra,
'_tasks'):
154 items += [(
'task', x)
for x
in getattr(extra,
'_tasks')]
155 if hasattr(extra,
'_collection'):
156 items += [(
'subtask', x)
for x
in getattr(extra,
'_collection')]
157 if hasattr(extra,
'_operand'):
158 items += [(
'operand', getattr(extra,
'_operand'))]
159 if isinstance(extra, dict):
160 for key, value
in extra.items():
161 if isinstance(value, list):
162 items += [(key, x)
for x
in value]
164 items += [(key, value)]
166 for name, child
in items:
170 if not thing
is None:
171 print(
"No _trace_events found in %s.\nMaybe turn on tracing for %s?" % (thing, classname))
172 print(
" Found in %s" % (parent,))
176 progname =
" ".
join(PREFIXINFO)
177 print(
"+Done running %s, writing output..." % progname)
180 filename = os.path.abspath(filename)
181 for pfx
in STRIPPATHS:
182 if filename.startswith(pfx):
183 filename = filename[len(pfx):]
187 for child, parent, relation
in graph:
188 files.add(child[2][0][0])
189 files.add(parent[2][0][0])
191 conn = sqlite3.connect(os.environ[
"CMSSWCALLTREE"])
193 cur.executescript(
"""
194 CREATE TABLE IF NOT EXISTS file(id INTEGER PRIMARY KEY,
195 name TEXT, UNIQUE(name)
197 CREATE TABLE IF NOT EXISTS trace(id INTEGER PRIMARY KEY,
198 parent INTEGER, -- points into same table, recursively
199 file INTEGER, line INTEGER,
200 FOREIGN KEY(parent) REFERENCES trace(id),
201 FOREIGN KEY(file) REFERENCES file(id),
202 UNIQUE(parent, file, line)
204 CREATE TABLE IF NOT EXISTS relation(id INTEGER PRIMARY KEY,
211 FOREIGN KEY(place) REFERENCES trace(id),
212 FOREIGN KEY(usedby) REFERENCES trace(id)
214 CREATE INDEX IF NOT EXISTS placeidx ON relation(place);
215 CREATE INDEX IF NOT EXISTS usedbyidx ON relation(usedby);
216 CREATE INDEX IF NOT EXISTS traceidx ON trace(file);
217 -- SQLite does not optimise that one well, but a VIEW is still nice to have...
218 CREATE VIEW IF NOT EXISTS fulltrace AS
219 WITH RECURSIVE fulltrace(level, baseid, parent, file, name, line) AS (
220 SELECT 1 AS level, trace.id, parent, trace.file, file.name, line FROM trace
221 INNER JOIN file ON file.id = trace.file
222 UNION SELECT level+1, baseid, trace.parent, trace.file, file.name, trace.line FROM fulltrace
223 INNER JOIN trace ON trace.id = fulltrace.parent
224 INNER JOIN file ON file.id = trace.file)
225 SELECT * FROM fulltrace;
227 cur.executemany(
"INSERT OR IGNORE INTO file(name) VALUES (?);",
229 def inserttrace(loc):
231 for filename, line
in reversed(loc):
232 conn.execute(
"INSERT OR IGNORE INTO trace(parent, file, line) SELECT ?, id, ? FROM file WHERE name == ?;", (parent, line,
formatfile(filename)))
233 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))
238 for child, parent, relation
in graph:
239 cevt, cclassname, cloc = child
240 pevt, pclassname, ploc = parent
241 place = inserttrace(cloc)
242 usedby = inserttrace(ploc)
243 cur.execute(
"INSERT OR IGNORE INTO relation(place, place_type, usedby, usedby_type, relation, source) VALUES (?,?,?,?,?,?);", (
244 place,
"%s::%s" % (cclassname, cevt),
245 usedby,
"%s::%s" % (pclassname, pevt),
254 cwd = os.path.abspath(os.getcwd())
255 wf = re.match(
".*/(\d+\.\d+)_", cwd)
257 PREFIXINFO.append(
"wf")
258 PREFIXINFO.append(wf.groups()[0])
259 online = re.match(
"(.*/)?(.*)_dqm_sourceclient-live_cfg\.py", argv[0])
261 PREFIXINFO.append(
"online")
262 PREFIXINFO.append(online.groups()[1])
263 step = re.match(
"(step\d+)_.*\.py", argv[0])
265 PREFIXINFO.append(step.groups()[0])
266 processing = re.match(
"step\d+_.*(RECO|ALCA|HARVEST).*\.py", argv[0])
268 PREFIXINFO.append(processing.groups()[0])
270 PREFIXINFO.append(argv[0])
273 bindir = tempfile.mkdtemp()
274 print(
"+Setting up in ", bindir)
275 os.symlink(ARGV0, bindir +
"/cmsRun")
276 os.environ[
"PATH"] = bindir +
":" + os.environ[
"PATH"]
277 os.environ[
"CMSSWCALLTREE"] = bindir +
"/" + OUTFILE_TREE
278 os.environ[
"CMSSWCALLBASE"] = os.path.abspath(os.getcwd()) +
"/"
279 with open(os.environ[
"CMSSWCALLTREE"],
"w")
as f:
284 print(
"+Cleaning up ", tmpdir)
285 copy(os.environ[
"CMSSWCALLTREE"],
".")
291 if not "CMSSWCALLTREE" in os.environ:
294 subprocess.call(argv)
302 progname = prog_argv[0]
305 with open(file_path)
as fp:
306 code = compile(fp.read(), progname,
'exec')
309 exec code
in globals, globals
311 print(traceback.format_exc())
315 print(
"+Collecting trace information from %s..." % globals[
"process"])
316 collect_trace(globals[
"process"],
'cmsrun', graph, (
'cmsRun',
'', ((progname, 0),)))
319 except OSError
as err:
320 print(
"+Cannot run file %r because: %s" % (sys.argv[0], err))
330 file_path = os.path.join(entry, progname)
331 if os.path.isfile(file_path):
333 if not os.path.isfile(file_path):
334 print(
"+Cannot find program (%s) in modified $PATH (%s)." % (progname, path))
336 print(
"+Found %s as %s in %s." % (progname, file_path, path))
341 print(
"Usage: %s <some cmssw commandline>" % (sys.argv[0]))
342 print(
" The given programs will be executed, instrumenting calls to cmsRun.")
343 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)
344 print(
" The callgraph output lists edges pointing from each function to the one calling it.")
345 print(
" To view the results using a simple webpage, use\n %s serve" % sys.argv[0])
347 print(
" %s runTheMatrix.py -l 1000 --ibeos" % sys.argv[0])
350 print(
"+Running cmsswConfigtrace...")
353 if sys.argv[0].endswith(
'cmsRun'):
354 print(
"+Wrapping cmsRun...")
356 STRIPPATHS.append(os.environ[
"CMSSWCALLBASE"])
359 if len(sys.argv) <= 1:
363 print(
"+Running command with tracing %s..." % sys.argv[1:])
370 STRIPPATHS.append(os.path.abspath(os.getcwd()) +
"/")
372 import SimpleHTTPServer
376 conn = sqlite3.connect(OUTFILE_TREE)
381 def formatsource(source, formatstr = '<em class="%s">%s</em>'):
382 processings = [
"ALCA",
"RECO",
"HARVEST",
"online"]
383 info = source.split(
" ")
384 processing =
" ".
join(
filter(
lambda x: x
in processings, info))
385 source =
" ".
join(
filter(
lambda x: x
not in processings, info))
386 return formatstr % (processing, escape(source))
388 def formatplace(filename, line):
390 shortname = filename[ :(MAXLEN-3)/2] +
"..." + filename[-(MAXLEN-3)/2: ]
if len(filename) > MAXLEN
else filename
391 return '<a href="/%s#%s" target="_blank">%s:%s</a>' % (escape(filename), line, escape(shortname), line)
394 out = [escape(
'goto to /<filename> for info about a file')]
396 wfs = defaultdict(list)
397 for source, wf
in conn.execute(
""" -- some SQL hackery here to parse "source"
398 SELECT DISTINCT source, substr(source, instr(source, "wf ")+3)*1 FROM relation ORDER BY 2, 1;"""):
399 wfs[wf].
append(
'<a href="/workflow/%s">%s</a>' % (urllib.quote(source), formatsource(source)))
402 out.append(
'<li>' +
" ".
join(wfs[wf]) +
"</li>")
406 for f
in conn.execute(
"SELECT name FROM file ORDER BY name;"):
408 out.append(
'<li><a href="/%s">%s</a></li>' % (name, name))
411 return "\n".
join(out)
413 def showworkflow(source):
414 source = urllib.unquote(source)
415 cur = conn.execute(
"""
416 SELECT DISTINCT file.name FROM relation
417 INNER JOIN trace ON place = trace.id
418 INNER JOIN file ON file.id = trace.file
419 WHERE relation.source = ?
422 out = [
"Files used by workflow %s: <ul>" % formatsource(source)]
425 out.append(
'<li><a href="/%s">%s</a></li>' % (name, name))
427 return "\n".
join(out)
429 def showfile(filename):
431 out.append(
'<script src="https://rawgit.com/google/code-prettify/master/src/prettify.js"></script>')
432 out.append(
'<link rel="stylesheet" href="https://rawgit.com/google/code-prettify/master/src/prettify.css"></link>')
437 with open(d + filename)
as f:
438 lines = f.readlines()
439 out.append(
"Read %s" % f.name)
445 cur = conn.execute(
"""
446 SELECT DISTINCT trace.line, source FROM relation
447 INNER JOIN trace on relation.place = trace.id
448 INNER JOIN file ON trace.file == file.id
449 WHERE file.name == ? ORDER BY line, source;""", (filename,))
450 sourceinfo = defaultdict(list)
451 for line, source
in cur:
452 sourceinfo[line].
append(source)
454 out.append(
'<pre class="prettyprint linenums">')
455 for i, l
in enumerate(lines):
457 tags = [formatsource(source,
'<em class="%%s" data-tag="%%s" data-line="%d"></em>' % (i+1))
for source
in sourceinfo[i+1]]
458 out.append(escape(l).rstrip() +
"".
join(tags))
461 out.append(
"""<script type="text/javascript">
463 clickfunc = function(evt) {
464 document.querySelectorAll("li > iframe, li > br").forEach(function(e) {e.remove()});
465 dest = "/info" + window.location.pathname + ":" + this.getAttribute("data-line");
466 this.parentElement.insertAdjacentHTML("beforeend", '<br><iframe width="90%" height="500px" frameborder="0" src="' + dest + '"></iframe><br>');
468 document.querySelectorAll("li > em").forEach(function(e) {
469 e.innerText = e.getAttribute("data-tag");
470 e.onclick = clickfunc;
473 n = 1*window.location.hash.replace("#", "");
475 li = document.querySelectorAll("li")[n-1];
476 li.style = "background: #ee7";
482 out.append(
"Could not find %s" % filename)
484 return "\n".
join(out)
487 def showinfo(filename, line):
490 def queryandoutput(startfrom, to, directiontext):
492 cur = conn.execute(
"""
493 SELECT place_type, -- why did we trace this line?
494 <to>file.name, <to>trace.line, -- where was it used?
495 usedby, usedby_type, relation, -- what was it used for?
496 place, source -- why did this code run?
498 INNER JOIN trace AS placetrace ON placetrace.id = relation.place
499 INNER JOIN trace AS usedbytrace ON usedbytrace.id = relation.usedby
500 INNER JOIN file AS placefile ON placefile.id = placetrace.file
501 INNER JOIN file AS usedbyfile ON usedbyfile.id = usedbytrace.file
502 WHERE <from>file.name = ? AND <from>trace.line = ?
504 .
replace(
"<from>", startfrom).
replace(
"<to>", to), (filename, line))
505 out.append(
"<p>%s %s <ul>" % (formatplace(filename, line), directiontext))
506 for place_type, pname, pline, usedby, usedby_type, relation, place, source
in cur:
508 '<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>'
509 % (escape(place_type), formatplace(pname, pline), escape(usedby_type), usedby, escape(relation), place, formatsource(source)))
510 out.append(
"</ul></p>")
512 queryandoutput(
"place",
"usedby",
"is used as")
513 queryandoutput(
"usedby",
"place",
"uses")
515 return "\n".
join(out)
520 cur = conn.execute(
"""
521 WITH RECURSIVE fulltrace(level, baseid, parent, file, name, line) AS (
522 SELECT 1 AS level, trace.id, parent, trace.file, file.name, line FROM trace
523 INNER JOIN file ON file.id = trace.file
525 UNION SELECT level+1, baseid, trace.parent, trace.file, file.name, trace.line FROM fulltrace
526 INNER JOIN trace ON trace.id = fulltrace.parent
527 INNER JOIN file ON file.id = trace.file)
528 SELECT name, line FROM fulltrace ORDER BY level;""", (id,))
530 out.append(
"Full stack trace:<ul>")
531 for name, line
in cur:
532 out.append(
'<li>%s</li>' % formatplace(name, line))
534 return "\n".
join(out)
537 (re.compile(
'/workflow/(.*)$'), showworkflow),
538 (re.compile(
'/info/(.*):(\d+)$'), showinfo),
539 (re.compile(
'/why/(\d+)$'), showwhy),
540 (re.compile(
'/([^.]*[.]?[^.]+[.]?[^.]*)$'), showfile),
541 (re.compile(
'/$'), index),
547 for pattern, func
in ROUTES:
548 m = pattern.match(self.path)
550 res =
func(*m.groups())
554 self.send_response(200,
"Here you go")
555 self.send_header(
"Content-Type",
"text/html; charset=utf-8")
557 self.wfile.
write(
"""<html><style>
567 em.ALCA {background: #ee9; }
568 em.RECO {background: #9e9; }
569 em.HARVEST {background: #99e; }
570 em.online {background: #e99; }
572 self.wfile.
write(res)
573 self.wfile.
write(
"</body></html>")
576 self.send_response(400,
"Something went wrong")
578 trace = traceback.format_exc()
579 self.send_response(500,
"Things went very wrong")
580 self.send_header(
"Content-Type",
"text/plain; charset=utf-8")
582 self.wfile.
write(trace)
585 Handler = SimpleHTTPServer.SimpleHTTPRequestHandler
586 Handler.do_GET = do_GET
587 httpd = SocketServer.TCPServer((
"",SERVE_PORT), Handler)
588 print(
"serving at port", SERVE_PORT)
589 httpd.serve_forever()
591 if __name__ ==
'__main__':
592 if sys.argv[1] ==
"serve":