2 from __future__
import print_function
3 from itertools
import groupby
4 from operator
import attrgetter,itemgetter
6 from collections
import defaultdict
12 To Use: Add the StallMonitor Service to the cmsRun job you want to check for 13 stream stalls. Use something like this in the configuration: 15 process.add_(cms.Service("StallMonitor", fileName = cms.untracked.string("stallMonitor.log"))) 17 After running the job, execute this script and pass the name of the 18 StallMonitor log file to the script. 20 By default, the script will then print an 'ASCII art' stall graph 21 which consists of a line of text for each time a module or the 22 source stops or starts. Each line contains the name of the module 23 which either started or stopped running, and the number of modules 24 running on each stream at that moment in time. After that will be 25 the time and stream number. Then if a module just started, you 26 will also see the amount of time the module spent between finishing 27 its prefetching and starting. The state of a module is represented 30 plus ("+") the stream has just finished waiting and is starting a module 31 minus ("-") the stream just finished running a module 33 If a module had to wait more than 0.1 seconds, the end of the line 34 will have "STALLED". Startup actions, e.g. reading conditions, 35 may affect results for the first few events. 37 Using the command line arguments described above you can make the 38 program create a PDF file with actual graphs instead of the 'ASCII art' 41 Once the graph is completed, the program outputs the list of modules 42 which had the greatest total stall times. The list is sorted by 43 total stall time and written in descending order. In addition, the 44 list of all stall times for the module is given. 46 There is an inferior alternative (an old obsolete way). 47 Instead of using the StallMonitor Service, you can use the 48 Tracer Service. Make sure to use the 'printTimestamps' option 49 cms.Service("Tracer", printTimestamps = cms.untracked.bool(True)) 50 There are problems associated with this and it is not recommended.''' 64 kStartedSourceDelayedRead=7
65 kFinishedSourceDelayedRead=8
68 kSourceFindEvent =
"sourceFindEvent" 69 kSourceDelayedRead =
"sourceDelayedRead" 75 if not l
or l[0] ==
'#':
77 (step,payload) = tuple(l.split(
None,1))
78 payload=payload.split()
81 if step ==
'E' or step ==
'e':
86 stream =
int(payload[0])
87 time =
int(payload[-1])
94 if step ==
'S' or step ==
's':
95 name = kSourceFindEvent
96 trans = kStartedSource
99 trans = kFinishedSource
102 moduleID = payload[1]
107 if step ==
'p' or step ==
'M' or step ==
'm':
113 if step ==
'm' or step ==
'M':
114 isEvent = (
int(payload[2]) == 0)
115 name = moduleNames[moduleID]
119 elif step ==
'A' or step ==
'a':
120 trans = kStartedAcquire
122 trans = kFinishedAcquire
123 name = moduleNames[moduleID]
128 elif step ==
'R' or step == 'r': 129 trans = kStartedSourceDelayedRead 131 trans = kFinishedSourceDelayedRead 132 name = kSourceDelayedRead 134 if trans
is not None:
135 yield (name,trans,stream,time, isEvent)
142 numStreamsFromSource = 0
146 if l
and l[0] ==
'M':
150 numStreams =
int(i[1])+1
152 if numStreams == 0
and l
and l[0] ==
'S':
153 s =
int(l.split(
' ')[1])
154 if s > numStreamsFromSource:
155 numStreamsFromSource = s
156 if len(l) > 5
and l[0:2] ==
"#M":
157 (id,name)=tuple(l[2:].
split())
158 moduleNames[id] = name
162 numStreams = numStreamsFromSource +1
166 for n
in six.iteritems(moduleNames):
171 """Create a generator which can step through the file and return each processing step. 172 Using a generator reduces the memory overhead when parsing a large file. 180 time = line.split(
" ")[1]
181 time = time.split(
":")
182 time =
int(time[0])*60*60+
int(time[1])*60+
float(time[2])
183 time =
int(1000*time)
214 streamsThatSawFirstEvent = set()
222 if l.find(
"processing event :") != -1:
223 name = kSourceFindEvent
224 trans = kStartedSource
226 if l.find(
"starting:") != -1:
227 trans = kFinishedSource
228 elif l.find(
"processing event for module") != -1:
230 if l.find(
"finished:") != -1:
231 if l.find(
"prefetching") != -1:
236 if l.find(
"prefetching") != -1:
239 name = l.split(
"'")[1]
240 elif l.find(
"processing event acquire for module:") != -1:
241 trans = kStartedAcquire
242 if l.find(
"finished:") != -1:
243 trans = kFinishedAcquire
244 name = l.split(
"'")[1]
245 elif l.find(
"event delayed read from source") != -1:
246 trans = kStartedSourceDelayedRead
247 if l.find(
"finished:") != -1:
248 trans = kFinishedSourceDelayedRead
249 name = kSourceDelayedRead
250 if trans
is not None:
254 time = time - startTime
255 streamIndex = l.find(
"stream = ")
256 stream =
int(l[streamIndex+9:l.find(
" ",streamIndex+10)])
257 maxNameSize =
max(maxNameSize, len(name))
259 if trans == kFinishedSource
and not stream
in streamsThatSawFirstEvent:
262 processingSteps.append((name,kStartedSource,stream,time,
True))
263 streamsThatSawFirstEvent.add(stream)
265 processingSteps.append((name,trans,stream,time,
True))
266 numStreams =
max(numStreams, stream+1)
269 return (processingSteps,numStreams,maxNameSize)
275 return self._processingSteps
280 firstLine = inputFile.readline().rstrip()
284 fifthLine = inputFile.readline().rstrip()
286 if (firstLine.find(
"# Transition") != -1)
or (firstLine.find(
"# Step") != -1):
287 print(
"> ... Parsing StallMonitor output.")
288 return StallMonitorParser
290 if firstLine.find(
"++") != -1
or fifthLine.find(
"++") != -1:
293 print(
"> ... Parsing Tracer output.")
297 print(
"Unknown input format.")
303 return parseInput(inputFile)
317 streamTime = [0]*numStreams
318 streamState = [0]*numStreams
320 modulesActiveOnStream = [{}
for x
in xrange(numStreams)]
321 for n,trans,s,time,isEvent
in processingSteps:
324 modulesOnStream = modulesActiveOnStream[s]
325 if trans == kPrefetchEnd:
326 modulesOnStream[n] = time
327 elif trans == kStarted
or trans == kStartedAcquire:
328 if n
in modulesOnStream:
329 waitTime = time - modulesOnStream[n]
330 modulesOnStream.pop(n,
None)
332 elif trans == kFinished
or trans == kFinishedAcquire:
335 elif trans == kStartedSourceDelayedRead:
336 if streamState[s] == 0:
337 waitTime = time - streamTime[s]
338 elif trans == kStartedSource:
339 modulesOnStream.clear()
340 elif trans == kFinishedSource
or trans == kFinishedSourceDelayedRead:
342 if waitTime
is not None:
343 if waitTime > kStallThreshold:
344 t = stalledModules.setdefault(n,[])
346 return stalledModules
351 streamTime = [0]*numStreams
352 streamState = [0]*numStreams
353 moduleTimings = defaultdict(list)
354 modulesActiveOnStream = [defaultdict(int)
for x
in xrange(numStreams)]
355 for n,trans,s,time,isEvent
in processingSteps:
357 modulesOnStream = modulesActiveOnStream[s]
358 if trans == kStarted:
360 modulesOnStream[n]=time
361 elif trans == kFinished:
362 waitTime = time - modulesOnStream[n]
363 modulesOnStream.pop(n,
None)
366 with open(
'module-timings.yaml',
'w')
as outfile:
367 outfile.write(yaml.dump(moduleTimings, default_flow_style=
True))
371 streamTime = [0]*numStreams
372 streamState = [0]*numStreams
373 modulesActiveOnStreams = [{}
for x
in xrange(numStreams)]
374 for n,trans,s,time,isEvent
in processingSteps:
376 modulesActiveOnStream = modulesActiveOnStreams[s]
377 if trans == kPrefetchEnd:
378 modulesActiveOnStream[n] = time
380 elif trans == kStartedAcquire
or trans == kStarted:
381 if n
in modulesActiveOnStream:
382 waitTime = time - modulesActiveOnStream[n]
383 modulesActiveOnStream.pop(n,
None)
385 elif trans == kFinishedAcquire
or trans == kFinished:
388 elif trans == kStartedSourceDelayedRead:
389 if streamState[s] == 0:
390 waitTime = time - streamTime[s]
391 elif trans == kStartedSource:
392 modulesActiveOnStream.clear()
393 elif trans == kFinishedSource
or trans == kFinishedSourceDelayedRead:
395 states =
"%-*s: " % (maxNameSize,n)
396 if trans == kStartedAcquire
or trans == kStarted
or trans == kStartedSourceDelayedRead
or trans == kStartedSource:
400 for index, state
in enumerate(streamState):
401 if n==kSourceFindEvent
and index == s:
404 states +=
str(state)+
" " 405 states +=
" -- " +
str(time/1000.) +
" " +
str(s) +
" " 406 if waitTime
is not None:
407 states +=
" %.2f"% (waitTime/1000.)
408 if waitTime > kStallThreshold:
417 for name,t
in six.iteritems(stalledModules):
418 maxNameSize =
max(maxNameSize, len(name))
420 priorities.append((name,sum(t),t))
423 return cmp(i[1],j[1])
424 priorities.sort(cmp=sumSort, reverse=
True)
426 nameColumn =
"Stalled Module" 427 maxNameSize =
max(maxNameSize, len(nameColumn))
429 stallColumn =
"Tot Stall Time" 430 stallColumnLength = len(stallColumn)
432 print(
"%-*s" % (maxNameSize, nameColumn),
"%-*s"%(stallColumnLength,stallColumn),
" Stall Times")
433 for n,s,t
in priorities:
434 paddedName =
"%-*s:" % (maxNameSize,n)
435 print(paddedName,
"%-*.2f"%(stallColumnLength,s/1000.),
", ".
join([
"%.2f"%(x/1000.)
for x
in t]))
444 return "(x: {}, y: {})".
format(self.
x,self.
y)
454 tmp =
Point(ps[0].x, ps[0].y)
459 reducedPoints.append(tmp)
460 tmp =
Point(p.x, p.y)
461 reducedPoints.append(tmp)
462 reducedPoints = [p
for p
in reducedPoints
if p.y != 0]
468 for pairList
in pairLists:
469 points += [
Point(x[0], 1)
for x
in pairList
if x[1] != 0]
470 points += [
Point(sum(x),-1)
for x
in pairList
if x[1] != 0]
471 points.sort(key=attrgetter(
'x'))
483 if len(self.
data) != 0:
484 tmp += self.
data[-1][1]
486 tmp.sort(key=attrgetter(
'x'))
488 self.data.append((graphType, tmp))
505 oldStreamInfo = streamInfo
506 streamInfo = [[]
for x
in xrange(numStreams)]
508 for s
in xrange(numStreams):
510 lastStartTime,lastTimeLength,lastColor = oldStreamInfo[s][0].
unpack()
511 for info
in oldStreamInfo[s][1:]:
512 start,length,color = info.unpack()
513 if color == lastColor
and lastStartTime+lastTimeLength == start:
514 lastTimeLength += length
517 lastStartTime = start
518 lastTimeLength = length
535 lastStartTime,lastTimeLength,lastHeight = oldBlocks[0]
536 for start,length,height
in oldBlocks[1:]:
537 if height == lastHeight
and lastStartTime+lastTimeLength == start:
538 lastTimeLength += length
540 blocks.append((lastStartTime,lastTimeLength,lastHeight))
541 lastStartTime = start
542 lastTimeLength = length
544 blocks.append((lastStartTime,lastTimeLength,lastHeight))
550 points = sorted(points, key=attrgetter(
'x'))
554 for t1,t2
in zip(points, points[1:]):
562 if streamHeight < streamHeightCut:
564 preparedTimes.append((t1.x,t2.x-t1.x, streamHeight))
565 preparedTimes.sort(key=itemgetter(2))
568 for nthreads, ts
in groupby(preparedTimes, itemgetter(2)):
569 theTS = [(t[0],t[1])
for t
in ts]
571 theTimes = [(t[0]/1000.,t[1]/1000.)
for t
in theTS]
572 yspan = (stream-0.4+height,height*(nthreads-1))
573 ax.broken_barh(theTimes, yspan, facecolors=color, edgecolors=color, linewidth=0)
575 allStackTimes[color].extend(theTS*(nthreads-threadOffset))
578 def createPDFImage(pdfFile, shownStacks, processingSteps, numStreams, stalledModuleInfo, displayExternalWork, checkOrder):
580 stalledModuleNames = set([x
for x
in stalledModuleInfo.iterkeys()])
581 streamLowestRow = [[]
for x
in xrange(numStreams)]
582 modulesActiveOnStreams = [set()
for x
in xrange(numStreams)]
583 acquireActiveOnStreams = [set()
for x
in xrange(numStreams)]
584 externalWorkOnStreams = [set()
for x
in xrange(numStreams)]
585 previousFinishTime = [
None for x
in xrange(numStreams)]
586 streamRunningTimes = [[]
for x
in xrange(numStreams)]
587 streamExternalWorkRunningTimes = [[]
for x
in xrange(numStreams)]
588 maxNumberOfConcurrentModulesOnAStream = 1
589 externalWorkModulesInJob =
False 590 previousTime = [0
for x
in xrange(numStreams)]
593 finishBeforeStart = [set()
for x
in xrange(numStreams)]
594 finishAcquireBeforeStart = [set()
for x
in xrange(numStreams)]
595 countSource = [0
for x
in xrange(numStreams)]
596 countDelayedSource = [0
for x
in xrange(numStreams)]
597 countExternalWork = [defaultdict(int)
for x
in xrange(numStreams)]
600 for n,trans,s,time,isEvent
in processingSteps:
601 if timeOffset
is None:
606 if time < previousTime[s]:
607 time = previousTime[s]
608 previousTime[s] = time
610 activeModules = modulesActiveOnStreams[s]
611 acquireModules = acquireActiveOnStreams[s]
612 externalWorkModules = externalWorkOnStreams[s]
614 if trans == kStarted
or trans == kStartedSourceDelayedRead
or trans == kStartedAcquire
or trans == kStartedSource :
621 if trans == kStarted:
622 countExternalWork[s][n] -= 1
623 if n
in finishBeforeStart[s]:
624 finishBeforeStart[s].
remove(n)
626 elif trans == kStartedAcquire:
627 if n
in finishAcquireBeforeStart[s]:
628 finishAcquireBeforeStart[s].
remove(n)
631 if trans == kStartedSourceDelayedRead:
632 countDelayedSource[s] += 1
633 if countDelayedSource[s] < 1:
635 elif trans == kStartedSource:
637 if countSource[s] < 1:
640 moduleNames = activeModules.copy()
641 moduleNames.update(acquireModules)
642 if trans == kStartedAcquire:
643 acquireModules.add(n)
647 if moduleNames
or externalWorkModules:
648 startTime = previousFinishTime[s]
649 previousFinishTime[s] = time
651 if trans == kStarted
and n
in externalWorkModules:
652 externalWorkModules.remove(n)
653 streamExternalWorkRunningTimes[s].
append(
Point(time, -1))
655 nTotalModules = len(activeModules) + len(acquireModules) + len(externalWorkModules)
656 maxNumberOfConcurrentModulesOnAStream =
max(maxNumberOfConcurrentModulesOnAStream, nTotalModules)
657 elif trans == kFinished
or trans == kFinishedSourceDelayedRead
or trans == kFinishedAcquire
or trans == kFinishedSource :
659 if trans == kFinished:
660 if n
not in activeModules:
661 finishBeforeStart[s].
add(n)
664 if trans == kFinishedSourceDelayedRead:
665 countDelayedSource[s] -= 1
666 if countDelayedSource[s] < 0:
668 elif trans == kFinishedSource:
670 if countSource[s] < 0:
673 if trans == kFinishedAcquire:
675 countExternalWork[s][n] += 1
676 if displayExternalWork:
677 externalWorkModulesInJob =
True 678 if (
not checkOrder)
or countExternalWork[s][n] > 0:
679 externalWorkModules.add(n)
680 streamExternalWorkRunningTimes[s].
append(
Point(time,+1))
681 if checkOrder
and n
not in acquireModules:
682 finishAcquireBeforeStart[s].
add(n)
685 startTime = previousFinishTime[s]
686 previousFinishTime[s] = time
687 moduleNames = activeModules.copy()
688 moduleNames.update(acquireModules)
690 if trans == kFinishedAcquire:
691 acquireModules.remove(n)
692 elif trans == kFinishedSourceDelayedRead:
693 if countDelayedSource[s] == 0:
694 activeModules.remove(n)
695 elif trans == kFinishedSource:
696 if countSource[s] == 0:
697 activeModules.remove(n)
699 activeModules.remove(n)
701 if startTime
is not None:
707 elif (kSourceDelayedRead
in moduleNames)
or (kSourceFindEvent
in moduleNames):
710 for n
in moduleNames:
711 if n
in stalledModuleNames:
720 fig, ax = plt.subplots(nrows=nr, squeeze=
True)
723 [xH,yH] = fig.get_size_inches()
724 fig.set_size_inches(xH,yH*4/3)
725 ax = plt.subplot2grid((4,1),(0,0), rowspan=3)
726 axStack = plt.subplot2grid((4,1),(3,0))
728 ax.set_xlabel(
"Time (sec)")
729 ax.set_ylabel(
"Stream ID")
730 ax.set_ylim(-0.5,numStreams-0.5)
731 ax.yaxis.set_ticks(xrange(numStreams))
733 height = 0.8/maxNumberOfConcurrentModulesOnAStream
734 allStackTimes={
'green': [],
'limegreen':[],
'red': [],
'blue': [],
'orange': [],
'darkviolet': []}
735 for iStream,lowestRow
in enumerate(streamLowestRow):
736 times=[(x.begin/1000., x.delta/1000.)
for x
in lowestRow]
737 colors=[x.color
for x
in lowestRow]
739 ax.broken_barh(times,(iStream-0.4,height),facecolors=colors,edgecolors=colors,linewidth=0)
742 for info
in lowestRow:
743 if not info.color ==
'darkviolet':
744 allStackTimes[info.color].
append((info.begin, info.delta))
747 if maxNumberOfConcurrentModulesOnAStream > 1
or externalWorkModulesInJob:
749 for i,perStreamRunningTimes
in enumerate(streamRunningTimes):
751 perStreamTimesWithExtendedWork =
list(perStreamRunningTimes)
752 perStreamTimesWithExtendedWork.extend(streamExternalWorkRunningTimes[i])
755 allStackTimes, ax, i, height,
758 addToStackTimes=
False,
763 allStackTimes, ax, i, height,
766 addToStackTimes=
True,
771 allStackTimes, ax, i, height,
774 addToStackTimes=
True,
779 print(
"> ... Generating stack")
781 for color
in [
'green',
'limegreen',
'blue',
'red',
'orange',
'darkviolet']:
782 tmp = allStackTimes[color]
784 stack.update(color, tmp)
786 for stk
in reversed(stack.data):
792 for p1,p2
in zip(stk[1], stk[1][1:]):
794 xs.append((p1.x, p2.x-p1.x, height))
795 xs.sort(key = itemgetter(2))
798 for height, xpairs
in groupby(xs, itemgetter(2)):
799 finalxs = [(e[0]/1000.,e[1]/1000.)
for e
in xpairs]
801 axStack.broken_barh(finalxs, (0, height), facecolors=color, edgecolors=color, linewidth=0)
803 axStack.set_xlabel(
"Time (sec)");
804 axStack.set_ylabel(
"# modules");
805 axStack.set_xlim(ax.get_xlim())
806 axStack.tick_params(top=
'off')
808 fig.text(0.1, 0.95,
"modules running event", color =
"green", horizontalalignment =
'left')
809 fig.text(0.1, 0.92,
"modules running other", color =
"limegreen", horizontalalignment =
'left')
810 fig.text(0.5, 0.95,
"stalled module running", color =
"red", horizontalalignment =
'center')
811 fig.text(0.9, 0.95,
"read from input", color =
"orange", horizontalalignment =
'right')
812 fig.text(0.5, 0.92,
"multiple modules running", color =
"blue", horizontalalignment =
'center')
813 if displayExternalWork:
814 fig.text(0.9, 0.92,
"external work", color =
"darkviolet", horizontalalignment =
'right')
815 print(
"> ... Saving to file: '{}'".
format(pdfFile))
819 if __name__==
"__main__":
825 parser = argparse.ArgumentParser(description=
'Convert a text file created by cmsRun into a stream stall graph.',
826 formatter_class=argparse.RawDescriptionHelpFormatter,
828 parser.add_argument(
'filename',
829 type=argparse.FileType(
'r'), # open file 830 help='file to process')
831 parser.add_argument(
'-g',
'--graph',
833 metavar=
"'stall.pdf'",
836 help=
'''Create pdf file of stream stall graph. If -g is specified 837 by itself, the default file name is \'stall.pdf\'. Otherwise, the 838 argument to the -g option is the filename.''')
839 parser.add_argument(
'-s',
'--stack',
841 help=
'''Create stack plot, combining all stream-specific info. 842 Can be used only when -g is specified.''')
843 parser.add_argument(
'-e',
'--external',
844 action=
'store_false',
845 help=
'''Suppress display of external work in graphs.''')
846 parser.add_argument(
'-o',
'--order',
848 help=
'''Enable checks for and repair of transitions in the input that are in the wrong order (for example a finish transition before a corresponding start). This is always enabled for Tracer input, but is usually an unnecessary waste of CPU time and memory with StallMonitor input and by default not enabled.''')
849 parser.add_argument(
'-t',
'--timings',
851 help=
'''Create a dictionary of module labels and their timings from the stall monitor log. Write the dictionary filea as a yaml file modules-timings.yaml.''')
852 args = parser.parse_args()
855 inputFile = args.filename
857 shownStacks = args.stack
858 displayExternalWork = args.external
859 checkOrder = args.order
860 doModuleTimings =
False 862 doModuleTimings =
True 865 if pdfFile
is not None:
869 matplotlib.use(
"PDF")
870 import matplotlib.pyplot
as plt
871 if not re.match(
r'^[\w\.]+$', pdfFile):
872 print(
"Malformed file name '{}' supplied with the '-g' option.".
format(pdfFile))
873 print(
"Only characters 0-9, a-z, A-Z, '_', and '.' are allowed.")
877 extension = pdfFile.split(
'.')[-1]
878 supported_filetypes = plt.figure().canvas.get_supported_filetypes()
879 if not extension
in supported_filetypes:
880 print(
"A graph cannot be saved to a filename with extension '{}'.".
format(extension))
881 print(
"The allowed extensions are:")
882 for filetype
in supported_filetypes:
886 if pdfFile
is None and shownStacks:
887 print(
"The -s (--stack) option can be used only when the -g (--graph) option is specified.")
890 sys.stderr.write(
">reading file: '{}'\n".
format(inputFile.name))
894 sys.stderr.write(
">processing data\n")
899 sys.stderr.write(
">preparing ASCII art\n")
900 createAsciiImage(reader.processingSteps(), reader.numStreams, reader.maxNameSize)
902 sys.stderr.write(
">creating PDF\n")
903 createPDFImage(pdfFile, shownStacks, reader.processingSteps(), reader.numStreams, stalledModules, displayExternalWork, checkOrder)
906 sys.stderr.write(
">creating module-timings.yaml\n")
def createPDFImage(pdfFile, shownStacks, processingSteps, numStreams, stalledModuleInfo, displayExternalWork, checkOrder)
def update(self, graphType, points)
def findStalledModules(processingSteps, numStreams)
def mergeContiguousBlocks(blocks)
def consolidateContiguousBlocks(numStreams, streamInfo)
S & print(S &os, JobReport::InputFile const &f)
def __init__(self, begin_, delta_, color_)
OutputIterator zip(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, OutputIterator result, Compare comp)
def chooseParser(inputFile)
def createModuleTiming(processingSteps, numStreams)
def printStalledModulesInOrder(stalledModules)
def createAsciiImage(processingSteps, numStreams, maxNameSize)
def reduceSortedPoints(ps)
void add(std::map< std::string, TH1 * > &h, TH1 *hist)
static std::string join(char **cmd)
def processingSteps(self)
def processingStepsFromStallMonitorOutput(f, moduleNames)
def processingSteps(self)
def remove(d, key, TELL=False)
def readLogFile(inputFile)
def __init__(self, x_, y_)
def adjacentDiff(pairLists)
def plotPerStreamAboveFirstAndPrepareStack(points, allStackTimes, ax, stream, height, streamHeightCut, doPlot, addToStackTimes, color, threadOffset)
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