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})
103 trace_location(Task,
'copyAndExclude',
lambda self, listOfModulesToExclude: {
'olds':
list(listOfModulesToExclude)})
109 import FWCore.ParameterSet.Utilities
111 print(
"+ Blocking convertToUnscheduled to not mess up the process.")
113 raise Exception(
"Aborting in convertToUnscheduled.")
115 FWCore.ParameterSet.Utilities.convertToUnscheduled = convertToUnscheduled
121 items += [(
"source", self.source)]
123 items += [(
"looper", self.looper)]
125 items += self.outputModules.
items()
127 items += six.iteritems(self.paths)
128 items += self.endpaths.
items()
129 items += self.services.
items()
130 items += self.es_producers.
items()
131 items += self.es_sources.
items()
132 items += self.es_prefers.
items()
136 items += [(
"schedule", self.schedule)]
138 Process.items_=new_items_
145 classname = thing.__class__.__name__
146 if hasattr(thing,
'_trace_events'):
147 events =
list(getattr(thing,
'_trace_events'))
148 getattr(thing,
'_trace_events')[:] = []
149 for action, loc, extra
in events:
150 entry = (action, classname, loc)
151 graph.append((entry, parent, name))
155 if hasattr(extra,
'items_'):
156 items += extra.items_()
157 if hasattr(extra,
'_seq'):
158 seq = getattr(extra,
'_seq')
160 items += [(
'seqitem', x)
for x
in getattr(seq,
'_collection')]
161 if hasattr(extra,
'_tasks'):
162 items += [(
'task', x)
for x
in getattr(extra,
'_tasks')]
163 if hasattr(extra,
'_collection'):
164 items += [(
'subtask', x)
for x
in getattr(extra,
'_collection')]
165 if hasattr(extra,
'_operand'):
166 items += [(
'operand', getattr(extra,
'_operand'))]
167 if isinstance(extra, dict):
168 for key, value
in extra.items():
169 if isinstance(value, list):
170 items += [(key, x)
for x
in value]
172 items += [(key, value)]
174 for name, child
in items:
178 if not thing
is None:
179 print(
"No _trace_events found in %s.\nMaybe turn on tracing for %s?" % (thing, classname))
180 print(
" Found in %s" % (parent,))
184 progname =
" ".
join(PREFIXINFO)
185 print(
"+Done running %s, writing output..." % progname)
188 filename = os.path.abspath(filename)
189 for pfx
in STRIPPATHS:
190 if filename.startswith(pfx):
191 filename = filename[len(pfx):]
195 for child, parent, relation
in graph:
196 files.add(child[2][0][0])
197 files.add(parent[2][0][0])
199 conn = sqlite3.connect(os.environ[
"CMSSWCALLTREE"])
201 cur.executescript(
"""
202 CREATE TABLE IF NOT EXISTS file(id INTEGER PRIMARY KEY,
203 name TEXT, UNIQUE(name)
205 CREATE TABLE IF NOT EXISTS trace(id INTEGER PRIMARY KEY,
206 parent INTEGER, -- points into same table, recursively
207 file INTEGER, line INTEGER,
208 FOREIGN KEY(parent) REFERENCES trace(id),
209 FOREIGN KEY(file) REFERENCES file(id),
210 UNIQUE(parent, file, line)
212 CREATE TABLE IF NOT EXISTS relation(id INTEGER PRIMARY KEY,
219 FOREIGN KEY(place) REFERENCES trace(id),
220 FOREIGN KEY(usedby) REFERENCES trace(id)
222 CREATE INDEX IF NOT EXISTS placeidx ON relation(place);
223 CREATE INDEX IF NOT EXISTS usedbyidx ON relation(usedby);
224 CREATE INDEX IF NOT EXISTS traceidx ON trace(file);
225 -- SQLite does not optimise that one well, but a VIEW is still nice to have...
226 CREATE VIEW IF NOT EXISTS fulltrace AS
227 WITH RECURSIVE fulltrace(level, baseid, parent, file, name, line) AS (
228 SELECT 1 AS level, trace.id, parent, trace.file, file.name, line FROM trace
229 INNER JOIN file ON file.id = trace.file
230 UNION SELECT level+1, baseid, trace.parent, trace.file, file.name, trace.line FROM fulltrace
231 INNER JOIN trace ON trace.id = fulltrace.parent
232 INNER JOIN file ON file.id = trace.file)
233 SELECT * FROM fulltrace;
235 cur.executemany(
"INSERT OR IGNORE INTO file(name) VALUES (?);",
237 def inserttrace(loc):
239 for filename, line
in reversed(loc):
240 conn.execute(
"INSERT OR IGNORE INTO trace(parent, file, line) SELECT ?, id, ? FROM file WHERE name == ?;", (parent, line,
formatfile(filename)))
241 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))
246 for child, parent, relation
in graph:
247 cevt, cclassname, cloc = child
248 pevt, pclassname, ploc = parent
249 place = inserttrace(cloc)
250 usedby = inserttrace(ploc)
251 cur.execute(
"INSERT OR IGNORE INTO relation(place, place_type, usedby, usedby_type, relation, source) VALUES (?,?,?,?,?,?);", (
252 place,
"%s::%s" % (cclassname, cevt),
253 usedby,
"%s::%s" % (pclassname, pevt),
262 cwd = os.path.abspath(os.getcwd())
263 wf = re.match(
".*/(\d+\.\d+)_", cwd)
265 PREFIXINFO.append(
"wf")
266 PREFIXINFO.append(wf.groups()[0])
267 online = re.match(
"(.*/)?(.*)_dqm_sourceclient-live_cfg\.py", argv[0])
269 PREFIXINFO.append(
"online")
270 PREFIXINFO.append(online.groups()[1])
271 step = re.match(
"(step\d+)_.*\.py", argv[0])
273 PREFIXINFO.append(step.groups()[0])
274 processing = re.match(
"step\d+_.*(RECO|ALCA|HARVEST).*\.py", argv[0])
276 PREFIXINFO.append(processing.groups()[0])
278 PREFIXINFO.append(argv[0])
281 bindir = tempfile.mkdtemp()
282 print(
"+Setting up in ", bindir)
283 os.symlink(ARGV0, bindir +
"/cmsRun")
284 os.environ[
"PATH"] = bindir +
":" + os.environ[
"PATH"]
285 os.environ[
"CMSSWCALLTREE"] = bindir +
"/" + OUTFILE_TREE
286 os.environ[
"CMSSWCALLBASE"] = os.path.abspath(os.getcwd()) +
"/"
287 with open(os.environ[
"CMSSWCALLTREE"],
"w")
as f:
292 print(
"+Cleaning up ", tmpdir)
293 copy(os.environ[
"CMSSWCALLTREE"],
".")
299 if not "CMSSWCALLTREE" in os.environ:
302 subprocess.call(argv)
310 progname = prog_argv[0]
313 with open(file_path)
as fp:
314 code = compile(fp.read(), progname,
'exec')
317 exec code
in globals, globals
319 print(traceback.format_exc())
323 print(
"+Collecting trace information from %s..." % globals[
"process"])
324 collect_trace(globals[
"process"],
'cmsrun', graph, (
'cmsRun',
'', ((progname, 0),)))
327 except OSError
as err:
328 print(
"+Cannot run file %r because: %s" % (sys.argv[0], err))
338 file_path = os.path.join(entry, progname)
339 if os.path.isfile(file_path):
341 if not os.path.isfile(file_path):
342 print(
"+Cannot find program (%s) in modified $PATH (%s)." % (progname, path))
344 print(
"+Found %s as %s in %s." % (progname, file_path, path))
349 print(
"Usage: %s <some cmssw commandline>" % (sys.argv[0]))
350 print(
" The given programs will be executed, instrumenting calls to cmsRun.")
351 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)
352 print(
" The callgraph output lists edges pointing from each function to the one calling it.")
353 print(
" To view the results using a simple webpage, use\n %s serve" % sys.argv[0])
355 print(
" %s runTheMatrix.py -l 1000 --ibeos" % sys.argv[0])
358 print(
"+Running cmsswConfigtrace...")
361 if sys.argv[0].endswith(
'cmsRun'):
362 print(
"+Wrapping cmsRun...")
364 STRIPPATHS.append(os.environ[
"CMSSWCALLBASE"])
367 if len(sys.argv) <= 1:
371 print(
"+Running command with tracing %s..." % sys.argv[1:])
378 STRIPPATHS.append(os.path.abspath(os.getcwd()) +
"/")
380 import SimpleHTTPServer
384 conn = sqlite3.connect(OUTFILE_TREE)
389 def formatsource(source, formatstr = '<em class="%s">%s</em>'):
390 processings = [
"ALCA",
"RECO",
"HARVEST",
"online"]
391 info = source.split(
" ")
392 processing =
" ".
join(
filter(
lambda x: x
in processings, info))
393 source =
" ".
join(
filter(
lambda x: x
not in processings, info))
394 return formatstr % (processing, escape(source))
396 def formatplace(filename, line):
398 shortname = filename[ :(MAXLEN-3)/2] +
"..." + filename[-(MAXLEN-3)/2: ]
if len(filename) > MAXLEN
else filename
399 return '<a href="/%s#%s" target="_blank">%s:%s</a>' % (escape(filename), line, escape(shortname), line)
402 out = [escape(
'goto to /<filename> for info about a file')]
404 wfs = defaultdict(list)
405 for source, wf
in conn.execute(
""" -- some SQL hackery here to parse "source"
406 SELECT DISTINCT source, substr(source, instr(source, "wf ")+3)*1 FROM relation ORDER BY 2, 1;"""):
407 wfs[wf].
append(
'<a href="/workflow/%s">%s</a>' % (urllib.quote(source), formatsource(source)))
410 out.append(
'<li>' +
" ".
join(wfs[wf]) +
"</li>")
414 for f
in conn.execute(
"SELECT name FROM file ORDER BY name;"):
416 out.append(
'<li><a href="/%s">%s</a></li>' % (name, name))
419 return "\n".
join(out)
421 def showworkflow(source):
422 source = urllib.unquote(source)
423 cur = conn.execute(
"""
424 SELECT DISTINCT file.name FROM relation
425 INNER JOIN trace ON place = trace.id
426 INNER JOIN file ON file.id = trace.file
427 WHERE relation.source = ?
430 out = [
"Files used by workflow %s: <ul>" % formatsource(source)]
433 out.append(
'<li><a href="/%s">%s</a></li>' % (name, name))
435 return "\n".
join(out)
437 def showfile(filename):
439 out.append(
'<script src="https://rawgit.com/google/code-prettify/master/src/prettify.js"></script>')
440 out.append(
'<link rel="stylesheet" href="https://rawgit.com/google/code-prettify/master/src/prettify.css"></link>')
445 with open(d + filename)
as f:
446 lines = f.readlines()
447 out.append(
"Read %s" % f.name)
453 cur = conn.execute(
"""
454 SELECT DISTINCT trace.line, source FROM relation
455 INNER JOIN trace on relation.place = trace.id
456 INNER JOIN file ON trace.file == file.id
457 WHERE file.name == ? ORDER BY line, source;""", (filename,))
458 sourceinfo = defaultdict(list)
459 for line, source
in cur:
460 sourceinfo[line].
append(source)
462 out.append(
'<pre class="prettyprint linenums">')
463 for i, l
in enumerate(lines):
465 tags = [formatsource(source,
'<em class="%%s" data-tag="%%s" data-line="%d"></em>' % (i+1))
for source
in sourceinfo[i+1]]
466 out.append(escape(l).rstrip() +
"".
join(tags))
469 out.append(
"""<script type="text/javascript">
471 clickfunc = function(evt) {
472 document.querySelectorAll("li > iframe, li > br").forEach(function(e) {e.remove()});
473 dest = "/info" + window.location.pathname + ":" + this.getAttribute("data-line");
474 this.parentElement.insertAdjacentHTML("beforeend", '<br><iframe width="90%" height="500px" frameborder="0" src="' + dest + '"></iframe><br>');
476 document.querySelectorAll("li > em").forEach(function(e) {
477 e.innerText = e.getAttribute("data-tag");
478 e.onclick = clickfunc;
481 n = 1*window.location.hash.replace("#", "");
483 li = document.querySelectorAll("li")[n-1];
484 li.style = "background: #ee7";
490 out.append(
"Could not find %s" % filename)
492 return "\n".
join(out)
495 def showinfo(filename, line):
498 def queryandoutput(startfrom, to, directiontext):
500 cur = conn.execute(
"""
501 SELECT place_type, -- why did we trace this line?
502 <to>file.name, <to>trace.line, -- where was it used?
503 usedby, usedby_type, relation, -- what was it used for?
504 place, source -- why did this code run?
506 INNER JOIN trace AS placetrace ON placetrace.id = relation.place
507 INNER JOIN trace AS usedbytrace ON usedbytrace.id = relation.usedby
508 INNER JOIN file AS placefile ON placefile.id = placetrace.file
509 INNER JOIN file AS usedbyfile ON usedbyfile.id = usedbytrace.file
510 WHERE <from>file.name = ? AND <from>trace.line = ?
512 .
replace(
"<from>", startfrom).
replace(
"<to>", to), (filename, line))
513 out.append(
"<p>%s %s <ul>" % (formatplace(filename, line), directiontext))
514 for place_type, pname, pline, usedby, usedby_type, relation, place, source
in cur:
516 '<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>'
517 % (escape(place_type), formatplace(pname, pline), escape(usedby_type), usedby, escape(relation), place, formatsource(source)))
518 out.append(
"</ul></p>")
520 queryandoutput(
"place",
"usedby",
"is used as")
521 queryandoutput(
"usedby",
"place",
"uses")
523 return "\n".
join(out)
528 cur = conn.execute(
"""
529 WITH RECURSIVE fulltrace(level, baseid, parent, file, name, line) AS (
530 SELECT 1 AS level, trace.id, parent, trace.file, file.name, line FROM trace
531 INNER JOIN file ON file.id = trace.file
533 UNION SELECT level+1, baseid, trace.parent, trace.file, file.name, trace.line FROM fulltrace
534 INNER JOIN trace ON trace.id = fulltrace.parent
535 INNER JOIN file ON file.id = trace.file)
536 SELECT name, line FROM fulltrace ORDER BY level;""", (id,))
538 out.append(
"Full stack trace:<ul>")
539 for name, line
in cur:
540 out.append(
'<li>%s</li>' % formatplace(name, line))
542 return "\n".
join(out)
545 (re.compile(
'/workflow/(.*)$'), showworkflow),
546 (re.compile(
'/info/(.*):(\d+)$'), showinfo),
547 (re.compile(
'/why/(\d+)$'), showwhy),
548 (re.compile(
'/([^.]*[.]?[^.]+[.]?[^.]*)$'), showfile),
549 (re.compile(
'/$'), index),
555 for pattern, func
in ROUTES:
556 m = pattern.match(self.path)
558 res =
func(*m.groups())
562 self.send_response(200,
"Here you go")
563 self.send_header(
"Content-Type",
"text/html; charset=utf-8")
565 self.wfile.
write(
"""<html><style>
575 em.ALCA {background: #ee9; }
576 em.RECO {background: #9e9; }
577 em.HARVEST {background: #99e; }
578 em.online {background: #e99; }
580 self.wfile.
write(res)
581 self.wfile.
write(
"</body></html>")
584 self.send_response(400,
"Something went wrong")
586 trace = traceback.format_exc()
587 self.send_response(500,
"Things went very wrong")
588 self.send_header(
"Content-Type",
"text/plain; charset=utf-8")
590 self.wfile.
write(trace)
593 Handler = SimpleHTTPServer.SimpleHTTPRequestHandler
594 Handler.do_GET = do_GET
595 httpd = SocketServer.TCPServer((
"",SERVE_PORT), Handler)
596 print(
"serving at port", SERVE_PORT)
597 httpd.serve_forever()
599 if __name__ ==
'__main__':
600 if sys.argv[1] ==
"serve":