2 from __future__
import print_function
3 from builtins
import range
4 from itertools
import groupby
5 from operator
import attrgetter,itemgetter
7 from collections
import defaultdict
13 To Use: Add the StallMonitor Service to the cmsRun job you want to check for 14 stream stalls. Use something like this in the configuration: 16 process.add_(cms.Service("StallMonitor", fileName = cms.untracked.string("stallMonitor.log"))) 18 After running the job, execute this script and pass the name of the 19 StallMonitor log file to the script. 21 By default, the script will then print an 'ASCII art' stall graph 22 which consists of a line of text for each time a module or the 23 source stops or starts. Each line contains the name of the module 24 which either started or stopped running, and the number of modules 25 running on each stream at that moment in time. After that will be 26 the time and stream number. Then if a module just started, you 27 will also see the amount of time the module spent between finishing 28 its prefetching and starting. The state of a module is represented 31 plus ("+") the stream has just finished waiting and is starting a module 32 minus ("-") the stream just finished running a module 34 If a module had to wait more than 0.1 seconds, the end of the line 35 will have "STALLED". Startup actions, e.g. reading conditions, 36 may affect results for the first few events. 38 Using the command line arguments described above you can make the 39 program create a PDF file with actual graphs instead of the 'ASCII art' 42 Once the graph is completed, the program outputs the list of modules 43 which had the greatest total stall times. The list is sorted by 44 total stall time and written in descending order. In addition, the 45 list of all stall times for the module is given. 47 There is an inferior alternative (an old obsolete way). 48 Instead of using the StallMonitor Service, you can use the 49 Tracer Service. Make sure to use the 'printTimestamps' option 50 cms.Service("Tracer", printTimestamps = cms.untracked.bool(True)) 51 There are problems associated with this and it is not recommended.''' 54 kStallThreshold=100000
65 kStartedSourceDelayedRead=7
66 kFinishedSourceDelayedRead=8
69 kSourceFindEvent =
"sourceFindEvent" 70 kSourceDelayedRead =
"sourceDelayedRead" 76 if not l
or l[0] ==
'#':
78 (step,payload) = tuple(l.split(
None,1))
79 payload=payload.split()
82 if step ==
'E' or step ==
'e':
87 stream =
int(payload[0])
88 time =
int(payload[-1])
95 if step ==
'S' or step ==
's':
96 name = kSourceFindEvent
97 trans = kStartedSource
100 trans = kFinishedSource
103 moduleID = payload[1]
108 if step ==
'p' or step ==
'M' or step ==
'm':
114 if step ==
'm' or step ==
'M':
115 isEvent = (
int(payload[2]) == 0)
116 name = moduleNames[moduleID]
120 elif step ==
'A' or step ==
'a':
121 trans = kStartedAcquire
123 trans = kFinishedAcquire
124 name = moduleNames[moduleID]
129 elif step ==
'R' or step == 'r': 130 trans = kStartedSourceDelayedRead 132 trans = kFinishedSourceDelayedRead 133 name = kSourceDelayedRead 135 if trans
is not None:
136 yield (name,trans,stream,time, isEvent)
143 numStreamsFromSource = 0
147 if l
and l[0] ==
'M':
151 numStreams =
int(i[1])+1
153 if numStreams == 0
and l
and l[0] ==
'S':
154 s =
int(l.split(
' ')[1])
155 if s > numStreamsFromSource:
156 numStreamsFromSource = s
157 if len(l) > 5
and l[0:2] ==
"#M":
158 (id,name)=tuple(l[2:].
split())
159 moduleNames[id] = name
163 numStreams = numStreamsFromSource +1
167 for n
in six.iteritems(moduleNames):
172 """Create a generator which can step through the file and return each processing step. 173 Using a generator reduces the memory overhead when parsing a large file. 181 time = line.split(
" ")[1]
182 time = time.split(
":")
183 time =
int(time[0])*60*60+
int(time[1])*60+
float(time[2])
184 time =
int(1000000*time)
215 streamsThatSawFirstEvent = set()
223 if l.find(
"processing event :") != -1:
224 name = kSourceFindEvent
225 trans = kStartedSource
227 if l.find(
"starting:") != -1:
228 trans = kFinishedSource
229 elif l.find(
"processing event for module") != -1:
231 if l.find(
"finished:") != -1:
232 if l.find(
"prefetching") != -1:
237 if l.find(
"prefetching") != -1:
240 name = l.split(
"'")[1]
241 elif l.find(
"processing event acquire for module:") != -1:
242 trans = kStartedAcquire
243 if l.find(
"finished:") != -1:
244 trans = kFinishedAcquire
245 name = l.split(
"'")[1]
246 elif l.find(
"event delayed read from source") != -1:
247 trans = kStartedSourceDelayedRead
248 if l.find(
"finished:") != -1:
249 trans = kFinishedSourceDelayedRead
250 name = kSourceDelayedRead
251 if trans
is not None:
255 time = time - startTime
256 streamIndex = l.find(
"stream = ")
257 stream =
int(l[streamIndex+9:l.find(
" ",streamIndex+10)])
258 maxNameSize =
max(maxNameSize, len(name))
260 if trans == kFinishedSource
and not stream
in streamsThatSawFirstEvent:
263 processingSteps.append((name,kStartedSource,stream,time,
True))
264 streamsThatSawFirstEvent.add(stream)
266 processingSteps.append((name,trans,stream,time,
True))
267 numStreams =
max(numStreams, stream+1)
270 return (processingSteps,numStreams,maxNameSize)
276 return self._processingSteps
281 firstLine = inputFile.readline().rstrip()
285 fifthLine = inputFile.readline().rstrip()
287 if (firstLine.find(
"# Transition") != -1)
or (firstLine.find(
"# Step") != -1):
288 print(
"> ... Parsing StallMonitor output.")
289 return StallMonitorParser
291 if firstLine.find(
"++") != -1
or fifthLine.find(
"++") != -1:
294 print(
"> ... Parsing Tracer output.")
298 print(
"Unknown input format.")
304 return parseInput(inputFile)
318 streamTime = [0]*numStreams
319 streamState = [0]*numStreams
321 modulesActiveOnStream = [{}
for x
in range(numStreams)]
322 for n,trans,s,time,isEvent
in processingSteps:
325 modulesOnStream = modulesActiveOnStream[s]
326 if trans == kPrefetchEnd:
327 modulesOnStream[n] = time
328 elif trans == kStarted
or trans == kStartedAcquire:
329 if n
in modulesOnStream:
330 waitTime = time - modulesOnStream[n]
331 modulesOnStream.pop(n,
None)
333 elif trans == kFinished
or trans == kFinishedAcquire:
336 elif trans == kStartedSourceDelayedRead:
337 if streamState[s] == 0:
338 waitTime = time - streamTime[s]
339 elif trans == kStartedSource:
340 modulesOnStream.clear()
341 elif trans == kFinishedSource
or trans == kFinishedSourceDelayedRead:
343 if waitTime
is not None:
344 if waitTime > kStallThreshold:
345 t = stalledModules.setdefault(n,[])
347 return stalledModules
352 streamTime = [0]*numStreams
353 streamState = [0]*numStreams
354 moduleTimings = defaultdict(list)
355 modulesActiveOnStream = [defaultdict(int)
for x
in range(numStreams)]
356 for n,trans,s,time,isEvent
in processingSteps:
358 modulesOnStream = modulesActiveOnStream[s]
360 if trans == kStarted:
362 modulesOnStream[n]=time
363 elif trans == kFinished:
364 waitTime = time - modulesOnStream[n]
365 modulesOnStream.pop(n,
None)
369 with open(
'module-timings.json',
'w')
as outfile:
370 outfile.write(json.dumps(moduleTimings, indent=4))
374 streamTime = [0]*numStreams
375 streamState = [0]*numStreams
376 modulesActiveOnStreams = [{}
for x
in range(numStreams)]
377 for n,trans,s,time,isEvent
in processingSteps:
379 modulesActiveOnStream = modulesActiveOnStreams[s]
380 if trans == kPrefetchEnd:
381 modulesActiveOnStream[n] = time
383 elif trans == kStartedAcquire
or trans == kStarted:
384 if n
in modulesActiveOnStream:
385 waitTime = time - modulesActiveOnStream[n]
386 modulesActiveOnStream.pop(n,
None)
388 elif trans == kFinishedAcquire
or trans == kFinished:
391 elif trans == kStartedSourceDelayedRead:
392 if streamState[s] == 0:
393 waitTime = time - streamTime[s]
394 elif trans == kStartedSource:
395 modulesActiveOnStream.clear()
396 elif trans == kFinishedSource
or trans == kFinishedSourceDelayedRead:
398 states =
"%-*s: " % (maxNameSize,n)
399 if trans == kStartedAcquire
or trans == kStarted
or trans == kStartedSourceDelayedRead
or trans == kStartedSource:
403 for index, state
in enumerate(streamState):
404 if n==kSourceFindEvent
and index == s:
407 states +=
str(state)+
" " 408 states +=
" -- " +
str(time/1000.) +
" " +
str(s) +
" " 409 if waitTime
is not None:
410 states +=
" %.2f"% (waitTime/1000.)
411 if waitTime > kStallThreshold:
420 for name,t
in six.iteritems(stalledModules):
421 maxNameSize =
max(maxNameSize, len(name))
423 priorities.append((name,sum(t),t))
426 return cmp(i[1],j[1])
427 priorities.sort(cmp=sumSort, reverse=
True)
429 nameColumn =
"Stalled Module" 430 maxNameSize =
max(maxNameSize, len(nameColumn))
432 stallColumn =
"Tot Stall Time" 433 stallColumnLength = len(stallColumn)
435 print(
"%-*s" % (maxNameSize, nameColumn),
"%-*s"%(stallColumnLength,stallColumn),
" Stall Times")
436 for n,s,t
in priorities:
437 paddedName =
"%-*s:" % (maxNameSize,n)
438 print(paddedName,
"%-*.2f"%(stallColumnLength,s/1000.),
", ".
join([
"%.2f"%(x/1000.)
for x
in t]))
447 return "(x: {}, y: {})".
format(self.
x,self.
y)
457 tmp =
Point(ps[0].x, ps[0].y)
462 reducedPoints.append(tmp)
463 tmp =
Point(p.x, p.y)
464 reducedPoints.append(tmp)
465 reducedPoints = [p
for p
in reducedPoints
if p.y != 0]
471 for pairList
in pairLists:
472 points += [
Point(x[0], 1)
for x
in pairList
if x[1] != 0]
473 points += [
Point(sum(x),-1)
for x
in pairList
if x[1] != 0]
474 points.sort(key=attrgetter(
'x'))
486 if len(self.
data) != 0:
487 tmp += self.
data[-1][1]
489 tmp.sort(key=attrgetter(
'x'))
491 self.data.append((graphType, tmp))
508 oldStreamInfo = streamInfo
509 streamInfo = [[]
for x
in range(numStreams)]
511 for s
in range(numStreams):
513 lastStartTime,lastTimeLength,lastColor = oldStreamInfo[s][0].
unpack()
514 for info
in oldStreamInfo[s][1:]:
515 start,length,color = info.unpack()
516 if color == lastColor
and lastStartTime+lastTimeLength == start:
517 lastTimeLength += length
520 lastStartTime = start
521 lastTimeLength = length
538 lastStartTime,lastTimeLength,lastHeight = oldBlocks[0]
539 for start,length,height
in oldBlocks[1:]:
540 if height == lastHeight
and lastStartTime+lastTimeLength == start:
541 lastTimeLength += length
543 blocks.append((lastStartTime,lastTimeLength,lastHeight))
544 lastStartTime = start
545 lastTimeLength = length
547 blocks.append((lastStartTime,lastTimeLength,lastHeight))
553 points = sorted(points, key=attrgetter(
'x'))
557 for t1,t2
in zip(points, points[1:]):
565 if streamHeight < streamHeightCut:
567 preparedTimes.append((t1.x,t2.x-t1.x, streamHeight))
568 preparedTimes.sort(key=itemgetter(2))
571 for nthreads, ts
in groupby(preparedTimes, itemgetter(2)):
572 theTS = [(t[0],t[1])
for t
in ts]
574 theTimes = [(t[0]/1000.,t[1]/1000.)
for t
in theTS]
575 yspan = (stream-0.4+height,height*(nthreads-1))
576 ax.broken_barh(theTimes, yspan, facecolors=color, edgecolors=color, linewidth=0)
578 allStackTimes[color].extend(theTS*(nthreads-threadOffset))
581 def createPDFImage(pdfFile, shownStacks, processingSteps, numStreams, stalledModuleInfo, displayExternalWork, checkOrder):
583 stalledModuleNames = set([x
for x
in stalledModuleInfo.iterkeys()])
584 streamLowestRow = [[]
for x
in range(numStreams)]
585 modulesActiveOnStreams = [set()
for x
in range(numStreams)]
586 acquireActiveOnStreams = [set()
for x
in range(numStreams)]
587 externalWorkOnStreams = [set()
for x
in range(numStreams)]
588 previousFinishTime = [
None for x
in range(numStreams)]
589 streamRunningTimes = [[]
for x
in range(numStreams)]
590 streamExternalWorkRunningTimes = [[]
for x
in range(numStreams)]
591 maxNumberOfConcurrentModulesOnAStream = 1
592 externalWorkModulesInJob =
False 593 previousTime = [0
for x
in range(numStreams)]
596 finishBeforeStart = [set()
for x
in range(numStreams)]
597 finishAcquireBeforeStart = [set()
for x
in range(numStreams)]
598 countSource = [0
for x
in range(numStreams)]
599 countDelayedSource = [0
for x
in range(numStreams)]
600 countExternalWork = [defaultdict(int)
for x
in range(numStreams)]
603 for n,trans,s,time,isEvent
in processingSteps:
604 if timeOffset
is None:
609 if time < previousTime[s]:
610 time = previousTime[s]
611 previousTime[s] = time
613 activeModules = modulesActiveOnStreams[s]
614 acquireModules = acquireActiveOnStreams[s]
615 externalWorkModules = externalWorkOnStreams[s]
617 if trans == kStarted
or trans == kStartedSourceDelayedRead
or trans == kStartedAcquire
or trans == kStartedSource :
624 if trans == kStarted:
625 countExternalWork[s][n] -= 1
626 if n
in finishBeforeStart[s]:
627 finishBeforeStart[s].
remove(n)
629 elif trans == kStartedAcquire:
630 if n
in finishAcquireBeforeStart[s]:
631 finishAcquireBeforeStart[s].
remove(n)
634 if trans == kStartedSourceDelayedRead:
635 countDelayedSource[s] += 1
636 if countDelayedSource[s] < 1:
638 elif trans == kStartedSource:
640 if countSource[s] < 1:
643 moduleNames = activeModules.copy()
644 moduleNames.update(acquireModules)
645 if trans == kStartedAcquire:
646 acquireModules.add(n)
650 if moduleNames
or externalWorkModules:
651 startTime = previousFinishTime[s]
652 previousFinishTime[s] = time
654 if trans == kStarted
and n
in externalWorkModules:
655 externalWorkModules.remove(n)
656 streamExternalWorkRunningTimes[s].
append(
Point(time, -1))
658 nTotalModules = len(activeModules) + len(acquireModules) + len(externalWorkModules)
659 maxNumberOfConcurrentModulesOnAStream =
max(maxNumberOfConcurrentModulesOnAStream, nTotalModules)
660 elif trans == kFinished
or trans == kFinishedSourceDelayedRead
or trans == kFinishedAcquire
or trans == kFinishedSource :
662 if trans == kFinished:
663 if n
not in activeModules:
664 finishBeforeStart[s].
add(n)
667 if trans == kFinishedSourceDelayedRead:
668 countDelayedSource[s] -= 1
669 if countDelayedSource[s] < 0:
671 elif trans == kFinishedSource:
673 if countSource[s] < 0:
676 if trans == kFinishedAcquire:
678 countExternalWork[s][n] += 1
679 if displayExternalWork:
680 externalWorkModulesInJob =
True 681 if (
not checkOrder)
or countExternalWork[s][n] > 0:
682 externalWorkModules.add(n)
683 streamExternalWorkRunningTimes[s].
append(
Point(time,+1))
684 if checkOrder
and n
not in acquireModules:
685 finishAcquireBeforeStart[s].
add(n)
688 startTime = previousFinishTime[s]
689 previousFinishTime[s] = time
690 moduleNames = activeModules.copy()
691 moduleNames.update(acquireModules)
693 if trans == kFinishedAcquire:
694 acquireModules.remove(n)
695 elif trans == kFinishedSourceDelayedRead:
696 if countDelayedSource[s] == 0:
697 activeModules.remove(n)
698 elif trans == kFinishedSource:
699 if countSource[s] == 0:
700 activeModules.remove(n)
702 activeModules.remove(n)
704 if startTime
is not None:
710 elif (kSourceDelayedRead
in moduleNames)
or (kSourceFindEvent
in moduleNames):
713 for n
in moduleNames:
714 if n
in stalledModuleNames:
723 fig, ax = plt.subplots(nrows=nr, squeeze=
True)
726 [xH,yH] = fig.get_size_inches()
727 fig.set_size_inches(xH,yH*4/3)
728 ax = plt.subplot2grid((4,1),(0,0), rowspan=3)
729 axStack = plt.subplot2grid((4,1),(3,0))
731 ax.set_xlabel(
"Time (sec)")
732 ax.set_ylabel(
"Stream ID")
733 ax.set_ylim(-0.5,numStreams-0.5)
734 ax.yaxis.set_ticks(range(numStreams))
736 height = 0.8/maxNumberOfConcurrentModulesOnAStream
737 allStackTimes={
'green': [],
'limegreen':[],
'red': [],
'blue': [],
'orange': [],
'darkviolet': []}
738 for iStream,lowestRow
in enumerate(streamLowestRow):
739 times=[(x.begin/1000., x.delta/1000.)
for x
in lowestRow]
740 colors=[x.color
for x
in lowestRow]
742 ax.broken_barh(times,(iStream-0.4,height),facecolors=colors,edgecolors=colors,linewidth=0)
745 for info
in lowestRow:
746 if not info.color ==
'darkviolet':
747 allStackTimes[info.color].
append((info.begin, info.delta))
750 if maxNumberOfConcurrentModulesOnAStream > 1
or externalWorkModulesInJob:
752 for i,perStreamRunningTimes
in enumerate(streamRunningTimes):
754 perStreamTimesWithExtendedWork =
list(perStreamRunningTimes)
755 perStreamTimesWithExtendedWork.extend(streamExternalWorkRunningTimes[i])
758 allStackTimes, ax, i, height,
761 addToStackTimes=
False,
766 allStackTimes, ax, i, height,
769 addToStackTimes=
True,
774 allStackTimes, ax, i, height,
777 addToStackTimes=
True,
782 print(
"> ... Generating stack")
784 for color
in [
'green',
'limegreen',
'blue',
'red',
'orange',
'darkviolet']:
785 tmp = allStackTimes[color]
787 stack.update(color, tmp)
789 for stk
in reversed(stack.data):
795 for p1,p2
in zip(stk[1], stk[1][1:]):
797 xs.append((p1.x, p2.x-p1.x, height))
798 xs.sort(key = itemgetter(2))
801 for height, xpairs
in groupby(xs, itemgetter(2)):
802 finalxs = [(e[0]/1000.,e[1]/1000.)
for e
in xpairs]
804 axStack.broken_barh(finalxs, (0, height), facecolors=color, edgecolors=color, linewidth=0)
806 axStack.set_xlabel(
"Time (sec)");
807 axStack.set_ylabel(
"# modules");
808 axStack.set_xlim(ax.get_xlim())
809 axStack.tick_params(top=
'off')
811 fig.text(0.1, 0.95,
"modules running event", color =
"green", horizontalalignment =
'left')
812 fig.text(0.1, 0.92,
"modules running other", color =
"limegreen", horizontalalignment =
'left')
813 fig.text(0.5, 0.95,
"stalled module running", color =
"red", horizontalalignment =
'center')
814 fig.text(0.9, 0.95,
"read from input", color =
"orange", horizontalalignment =
'right')
815 fig.text(0.5, 0.92,
"multiple modules running", color =
"blue", horizontalalignment =
'center')
816 if displayExternalWork:
817 fig.text(0.9, 0.92,
"external work", color =
"darkviolet", horizontalalignment =
'right')
818 print(
"> ... Saving to file: '{}'".
format(pdfFile))
822 if __name__==
"__main__":
828 parser = argparse.ArgumentParser(description=
'Convert a text file created by cmsRun into a stream stall graph.',
829 formatter_class=argparse.RawDescriptionHelpFormatter,
831 parser.add_argument(
'filename',
832 type=argparse.FileType(
'r'), # open file 833 help='file to process')
834 parser.add_argument(
'-g',
'--graph',
836 metavar=
"'stall.pdf'",
839 help=
'''Create pdf file of stream stall graph. If -g is specified 840 by itself, the default file name is \'stall.pdf\'. Otherwise, the 841 argument to the -g option is the filename.''')
842 parser.add_argument(
'-s',
'--stack',
844 help=
'''Create stack plot, combining all stream-specific info. 845 Can be used only when -g is specified.''')
846 parser.add_argument(
'-e',
'--external',
847 action=
'store_false',
848 help=
'''Suppress display of external work in graphs.''')
849 parser.add_argument(
'-o',
'--order',
851 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.''')
852 parser.add_argument(
'-t',
'--timings',
854 help=
'''Create a dictionary of module labels and their timings from the stall monitor log. Write the dictionary filea as a json file modules-timings.json.''')
855 args = parser.parse_args()
858 inputFile = args.filename
860 shownStacks = args.stack
861 displayExternalWork = args.external
862 checkOrder = args.order
863 doModuleTimings =
False 865 doModuleTimings =
True 868 if pdfFile
is not None:
872 matplotlib.use(
"PDF")
873 import matplotlib.pyplot
as plt
874 if not re.match(
r'^[\w\.]+$', pdfFile):
875 print(
"Malformed file name '{}' supplied with the '-g' option.".
format(pdfFile))
876 print(
"Only characters 0-9, a-z, A-Z, '_', and '.' are allowed.")
880 extension = pdfFile.split(
'.')[-1]
881 supported_filetypes = plt.figure().canvas.get_supported_filetypes()
882 if not extension
in supported_filetypes:
883 print(
"A graph cannot be saved to a filename with extension '{}'.".
format(extension))
884 print(
"The allowed extensions are:")
885 for filetype
in supported_filetypes:
889 if pdfFile
is None and shownStacks:
890 print(
"The -s (--stack) option can be used only when the -g (--graph) option is specified.")
893 sys.stderr.write(
">reading file: '{}'\n".
format(inputFile.name))
897 sys.stderr.write(
">processing data\n")
902 sys.stderr.write(
">preparing ASCII art\n")
903 createAsciiImage(reader.processingSteps(), reader.numStreams, reader.maxNameSize)
905 sys.stderr.write(
">creating PDF\n")
906 createPDFImage(pdfFile, shownStacks, reader.processingSteps(), reader.numStreams, stalledModules, displayExternalWork, checkOrder)
909 sys.stderr.write(
">creating module-timings.json\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