CMS 3D CMS Logo

cmsswConfigtrace.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 from __future__ import print_function
3 import os
4 import re
5 import six
6 import sys
7 import inspect
8 import sqlite3
9 import tempfile
10 import traceback
11 import subprocess
12 from shutil import copy, rmtree
13 from collections import defaultdict
14 
15 ### 1. Some configuration options.
16 SERVE_PORT = 1234
17 
18 OUTFILE_TREE = "configtree.sqlite"
19 IGNORE_PACKAGES = ['FWCore/ParameterSet', 'FWCore/GuiBrowsers', 'DQMOffline/Configuration/scripts', "cmsRun"]
20 STRIPPATHS = [ # we will add the base dir from CMSSWCALLBASE env var here
21  os.environ["CMSSW_BASE"] + "/python/", os.environ["CMSSW_RELEASE_BASE"] + "/python/",
22  os.environ["CMSSW_BASE"] + "/cfipython/", os.environ["CMSSW_RELEASE_BASE"] + "/cfipython/"]
23 PREFIXINFO = [] # what we will show as "source" later
24 ARGV0 = "" # set in main
25 
26 ### 2. The enable all the tracing hooks.
27 
28 # this already does a good job, but it is not enough
29 #import FWCore.GuiBrowsers.EnablePSetHistory
30 
31 # we need to patch out this to allow printing unlabled things
33 del FWCore.ParameterSet.Mixins._Labelable.__str__
34 
35 # then also trace Sequence construction so we can get a full tree
36 # (PSetHistory only tracks leaves)
38  # stolen from FWCore.GuiBrowsers.EnablePSetHistory, but needs more black-list
39  stack = inspect.stack()
40  i = 0
41  while i < len(stack) and len(stack[i])>=2 and any(map(lambda p: p in stack[i][1], IGNORE_PACKAGES)):
42  i += 1
43  res = stack[i: ]
44  j = 0
45  # for the other end we only use cmsRun (instead of IGNORE_PACKAGES) to avoid
46  # cutting traces in the middle that enter and leave IGNORE_PACKAGES
47  while j < len(res) and not 'cmsRun' in res[j][1]:
48  j += 1
49  res = res[:j]
50  if len(res)>=1 and len(res[0])>=3:
51  return res
52  else:
53  return [("unknown","unknown","unknown")]
54 
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)
59  where = auto_inspect()
60  #print("Called %s::%s at %s" % (thing.__name__, name, where[0][1:3]))
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)
64  else:
65  # this bypasses setattr checks
66  self.__dict__["_trace_events"] = [ event ]
67 
68  return retval
69  setattr(thing, name, trace_location_hook)
70 
71 def flatten(*args):
72  # that was surprisingly hard...
73  return [x for x in args if not isinstance(x, list)] + sum(
74  [flatten(*x) for x in args if isinstance(x, list)], [])
75 
76 from FWCore.ParameterSet.SequenceTypes import _ModuleSequenceType, _SequenceCollection, Task, _UnarySequenceOperator, Schedule
77 from FWCore.ParameterSet.Modules import _Module, Source, ESSource, ESPrefer, ESProducer, Service, Looper
78 from FWCore.ParameterSet.Config import Process
79 # with this we can also track the '+' and '*' of modules, but it is slow
80 trace_location(_SequenceCollection, '__init__')
81 trace_location(_Module, '__init__')
82 trace_location(Source, '__init__')
83 trace_location(ESSource, '__init__')
84 trace_location(ESPrefer, '__init__')
85 trace_location(ESProducer, '__init__')
86 trace_location(Service, '__init__')
87 trace_location(Looper, '__init__')
88 trace_location(Process, '__init__')
89 trace_location(_UnarySequenceOperator, '__init__')
90 # lambda agrument names all match the original declarations, to make kwargs work
91 trace_location(_ModuleSequenceType, '__init__', lambda self, *arg: {'args': list(arg)})
92 trace_location(_ModuleSequenceType, 'copy')
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})
100 trace_location(Task, '__init__')
101 trace_location(Task, 'add', lambda self, *items: {'args': list(items)})
102 trace_location(Task, 'copy')
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)})
106 trace_location(Schedule, 'copy')
107 # TODO: we could go deeper into Types and PSet, but that should not be needed for now.
108 
109 import FWCore.ParameterSet.Utilities
111  print("+ Blocking convertToUnscheduled to not mess up the process.")
112  # we could continue tracing, but the later manipulations don't make much sense and take forever.
113  raise Exception("Aborting in convertToUnscheduled.")
114  return proc
115 FWCore.ParameterSet.Utilities.convertToUnscheduled = convertToUnscheduled
116 
117 # lifted from EnablePSetHistory, we don't need all of that stuff.
118 def new_items_(self):
119  items = []
120  if self.source:
121  items += [("source", self.source)]
122  if self.looper:
123  items += [("looper", self.looper)]
124  #items += self.moduleItems_()
125  items += self.outputModules.items()
126  #items += self.sequences.items() # TODO: we don't need sequences that are not paths?
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()
133  #items += self.psets.items()
134  #items += self.vpsets.items()
135  if self.schedule:
136  items += [("schedule", self.schedule)]
137  return tuple(items)
138 Process.items_=new_items_
139 
140 ### 3. The logic to collect, process, and save the information from the process.
141 
142 def collect_trace(thing, name, graph, parent):
143  # thing is what to look at, graph is the output list (of child, parent tuple pairs)
144  # thing could be pretty much anything.
145  classname = thing.__class__.__name__
146  if hasattr(thing, '_trace_events'):
147  events = list(getattr(thing, '_trace_events'))
148  getattr(thing, '_trace_events')[:] = [] # erase events so we can't end up in cycles
149  for action, loc, extra in events:
150  entry = (action, classname, loc)
151  graph.append((entry, parent, name))
152 
153  # items shall be a list of tuples (type, object) of the immediate children of the thing.
154  items = []
155  if hasattr(extra, 'items_'): # for cms.Process
156  items += extra.items_()
157  if hasattr(extra, '_seq'): # for sequences and similar
158  seq = getattr(extra, '_seq')
159  if seq:
160  items += [('seqitem', x) for x in getattr(seq, '_collection')]
161  if hasattr(extra, '_tasks'): # same
162  items += [('task', x) for x in getattr(extra, '_tasks')]
163  if hasattr(extra, '_collection'): # for cms.Task
164  items += [('subtask', x) for x in getattr(extra, '_collection')]
165  if hasattr(extra, '_operand'): # for _SeqenceNegation etc.
166  items += [('operand', getattr(extra, '_operand'))]
167  if isinstance(extra, dict): # stuff that we track explicitly^
168  for key, value in extra.items():
169  if isinstance(value, list):
170  items += [(key, x) for x in value]
171  else:
172  items += [(key, value)]
173 
174  for name, child in items:
175  collect_trace(child, name, graph, entry)
176 
177  else:
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,))
181 
182 
183 def writeoutput(graph):
184  progname = " ".join(PREFIXINFO)
185  print("+Done running %s, writing output..." % progname)
186 
187  def formatfile(filename):
188  filename = os.path.abspath(filename)
189  for pfx in STRIPPATHS:
190  if filename.startswith(pfx):
191  filename = filename[len(pfx):]
192  return filename
193 
194  files = set()
195  for child, parent, relation in graph:
196  files.add(child[2][0][0])
197  files.add(parent[2][0][0])
198 
199  conn = sqlite3.connect(os.environ["CMSSWCALLTREE"])
200  cur = conn.cursor()
201  cur.executescript("""
202  CREATE TABLE IF NOT EXISTS file(id INTEGER PRIMARY KEY,
203  name TEXT, UNIQUE(name)
204  );
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)
211  );
212  CREATE TABLE IF NOT EXISTS relation(id INTEGER PRIMARY KEY,
213  place INTEGER,
214  place_type TEXT,
215  usedby INTEGER,
216  usedby_type TEXT,
217  relation TEXT,
218  source TEXT,
219  FOREIGN KEY(place) REFERENCES trace(id),
220  FOREIGN KEY(usedby) REFERENCES trace(id)
221  );
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;
234  """)
235  cur.executemany("INSERT OR IGNORE INTO file(name) VALUES (?);",
236  ((formatfile(f),) for f in files))
237  def inserttrace(loc):
238  parent = 0
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))
242  for row in cur:
243  parent = row[0]
244  return parent
245 
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),
254  relation, progname
255  ))
256  conn.commit()
257  conn.close()
258 
259 ### 4. Launch and keep track of all the processes.
260 
261 def addprefixinfo(argv):
262  cwd = os.path.abspath(os.getcwd())
263  wf = re.match(".*/(\d+\.\d+)_", cwd)
264  if wf:
265  PREFIXINFO.append("wf")
266  PREFIXINFO.append(wf.groups()[0])
267  online = re.match("(.*/)?(.*)_dqm_sourceclient-live_cfg\.py", argv[0])
268  if online:
269  PREFIXINFO.append("online")
270  PREFIXINFO.append(online.groups()[1])
271  step = re.match("(step\d+)_.*\.py", argv[0])
272  if step:
273  PREFIXINFO.append(step.groups()[0])
274  processing = re.match("step\d+_.*(RECO|ALCA|HARVEST).*\.py", argv[0])
275  if processing:
276  PREFIXINFO.append(processing.groups()[0])
277  if not PREFIXINFO:
278  PREFIXINFO.append(argv[0])
279 
280 def setupenv():
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:
288  pass
289  return bindir
290 
291 def cleanupenv(tmpdir):
292  print("+Cleaning up ", tmpdir)
293  copy(os.environ["CMSSWCALLTREE"], ".")
294  rmtree(tmpdir)
295 
296 
297 def trace_command(argv):
298  tmpdir = None
299  if not "CMSSWCALLTREE" in os.environ:
300  tmpdir = setupenv()
301 
302  subprocess.call(argv)
303 
304  if tmpdir:
305  cleanupenv(tmpdir)
306 
307 def trace_python(prog_argv, path):
308  graph = []
309  sys.argv = prog_argv
310  progname = prog_argv[0]
311  file_path = searchinpath(progname, path)
312  try:
313  with open(file_path) as fp:
314  code = compile(fp.read(), progname, 'exec')
315  globals = {}
316  try:
317  exec code in globals, globals
318  except:
319  print(traceback.format_exc())
320  finally:
321  # reporting is only possible if the config was executed successfully.
322  # we still do it in case of an exception, which can happen after convertToUnscheduled()
323  print("+Collecting trace information from %s..." % globals["process"])
324  collect_trace(globals["process"], 'cmsrun', graph, ('cmsRun', '', ((progname, 0),)))
325  writeoutput(graph)
326 
327  except OSError as err:
328  print("+Cannot run file %r because: %s" % (sys.argv[0], err))
329  sys.exit(1)
330  except SystemExit:
331  pass
332  # this is not necessarily reached at all.
333  sys.exit(0)
334 
335 def searchinpath(progname, path):
336  # Search $PATH. There seems to be no pre-made function for this.
337  for entry in path:
338  file_path = os.path.join(entry, progname)
339  if os.path.isfile(file_path):
340  break
341  if not os.path.isfile(file_path):
342  print("+Cannot find program (%s) in modified $PATH (%s)." % (progname, path))
343  sys.exit(1)
344  print("+Found %s as %s in %s." % (progname, file_path, path))
345  return file_path
346 
347 
348 def help():
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])
354  print("Examples:")
355  print(" %s runTheMatrix.py -l 1000 --ibeos" % sys.argv[0])
356 
357 def main():
358  print("+Running cmsswConfigtrace...")
359  global ARGV0
360  ARGV0 = sys.argv[0]
361  if sys.argv[0].endswith('cmsRun'):
362  print("+Wrapping cmsRun...")
363  addprefixinfo(sys.argv[1:])
364  STRIPPATHS.append(os.environ["CMSSWCALLBASE"])
365  trace_python(sys.argv[1:], ["."])
366  return
367  if len(sys.argv) <= 1:
368  help()
369  return
370  # else
371  print("+Running command with tracing %s..." % sys.argv[1:])
372  trace_command(sys.argv[1:])
373 
374 ### 5. The web-based GUI.
375 
377  # we use STRIPPATHS as a search path to find code files.
378  STRIPPATHS.append(os.path.abspath(os.getcwd()) + "/")
379 
380  import SimpleHTTPServer
381  import SocketServer
382  import urllib
383 
384  conn = sqlite3.connect(OUTFILE_TREE)
385 
386  def escape(s):
387  return str(s).replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;").replace('"', "&quot;")
388 
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))
395 
396  def formatplace(filename, line):
397  MAXLEN = 80
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)
400 
401  def index():
402  out = [escape('goto to /<filename> for info about a file')]
403 
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)))
408  out.append("<ul>")
409  for wf in wfs:
410  out.append('<li>' + " ".join(wfs[wf]) + "</li>")
411  out.append("</ul>")
412 
413  out.append("<ul>")
414  for f in conn.execute("SELECT name FROM file ORDER BY name;"):
415  name = escape(f[0])
416  out.append('<li><a href="/%s">%s</a></li>' % (name, name))
417  out.append("</ul>")
418 
419  return "\n".join(out)
420 
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 = ?
428  ORDER BY file.name;
429  """, (source, ))
430  out = ["Files used by workflow %s: <ul>" % formatsource(source)]
431  for f in cur:
432  name = escape(f[0])
433  out.append('<li><a href="/%s">%s</a></li>' % (name, name))
434  out.append("</ul>")
435  return "\n".join(out)
436 
437  def showfile(filename):
438  out = []
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>')
441 
442  lines = None
443  for d in STRIPPATHS:
444  try:
445  with open(d + filename) as f:
446  lines = f.readlines()
447  out.append("Read %s" % f.name)
448  break
449  except:
450  pass
451 
452  if lines:
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)
461 
462  out.append('<pre class="prettyprint linenums">')
463  for i, l in enumerate(lines):
464  # put the text into data-tag here and move it later, to not mess up syntax HL
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))
467  out.append('</pre>')
468 
469  out.append("""<script type="text/javascript">
470  PR.prettyPrint();
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>');
475  };
476  document.querySelectorAll("li > em").forEach(function(e) {
477  e.innerText = e.getAttribute("data-tag");
478  e.onclick = clickfunc;
479  })
480 
481  n = 1*window.location.hash.replace("#", "");
482  if (n > 0) {
483  li = document.querySelectorAll("li")[n-1];
484  li.style = "background: #ee7";
485  li.scrollIntoView();
486  }
487  </script>""")
488 
489  else:
490  out.append("Could not find %s" % filename)
491 
492  return "\n".join(out)
493 
494 
495  def showinfo(filename, line):
496  line = int(line)
497  out = []
498  def queryandoutput(startfrom, to, directiontext):
499  # we format in the side of the relation to query for here...
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?
505  FROM relation
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 = ?
511  LIMIT 1000; """
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:
515  out.append(
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>")
519 
520  queryandoutput("place", "usedby", "is used as")
521  queryandoutput("usedby", "place", "uses")
522 
523  return "\n".join(out)
524 
525  def showwhy(id):
526  id = int(id)
527  # this (WHERE before recursion) will optimize better than the view.
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
532  WHERE trace.id = ?
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,))
537  out = []
538  out.append("Full stack trace:<ul>")
539  for name, line in cur:
540  out.append('<li>%s</li>' % formatplace(name, line))
541  out.append("</ul>")
542  return "\n".join(out)
543 
544  ROUTES = [
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),
550  ]
551 
552  def do_GET(self):
553  try:
554  res = None
555  for pattern, func in ROUTES:
556  m = pattern.match(self.path)
557  if m:
558  res = func(*m.groups())
559  break
560 
561  if res:
562  self.send_response(200, "Here you go")
563  self.send_header("Content-Type", "text/html; charset=utf-8")
564  self.end_headers()
565  self.wfile.write("""<html><style>
566  body {
567  font-family: sans;
568  }
569  em {
570  cursor: pointer;
571  padding: 0 2px;
572  margin: 1 2px;
573  background: #999;
574  }
575  em.ALCA {background: #ee9; }
576  em.RECO {background: #9e9; }
577  em.HARVEST {background: #99e; }
578  em.online {background: #e99; }
579  </style><body>""")
580  self.wfile.write(res)
581  self.wfile.write("</body></html>")
582  self.wfile.close()
583  else:
584  self.send_response(400, "Something went wrong")
585  except:
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")
589  self.end_headers()
590  self.wfile.write(trace)
591  self.wfile.close()
592 
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()
598 
599 if __name__ == '__main__':
600  if sys.argv[1] == "serve":
601  serve_main()
602  else:
603  main()
604 
def collect_trace(thing, name, graph, parent)
bool any(const std::vector< T > &v, const T &what)
Definition: ECalSD.cc:40
def replace(string, replacements)
def writeoutput(graph)
S & print(S &os, JobReport::InputFile const &f)
Definition: JobReport.cc:66
def searchinpath(progname, path)
def convertToUnscheduled(proc)
def cleanupenv(tmpdir)
def trace_python(prog_argv, path)
static std::string join(char **cmd)
Definition: RemoteFile.cc:17
def formatfile(filename)
Definition: main.py:1
#define str(s)
How EventSelector::AcceptEvent() decides whether to accept an event for output otherwise it is excluding the probing of A single or multiple positive and the trigger will pass if any such matching triggers are PASS or EXCEPTION[A criterion thatmatches no triggers at all is detected and causes a throw.] A single negative with an expectation of appropriate bit checking in the decision and the trigger will pass if any such matching triggers are FAIL or EXCEPTION A wildcarded negative criterion that matches more than one trigger in the trigger list("!*","!HLTx*"if it matches 2 triggers or more) will accept the event if all the matching triggers are FAIL.It will reject the event if any of the triggers are PASS or EXCEPTION(this matches the behavior of"!*"before the partial wildcard feature was incorporated).Triggers which are in the READY state are completely ignored.(READY should never be returned since the trigger paths have been run