--- /dev/null
+/**************************************************************************
+ * This file is property of and copyright by the ALICE HLT Project *
+ * All rights reserved. *
+ * *
+ * Primary Authors: *
+ * Artur Szostak <artursz@iafrica.com> *
+ * *
+ * Permission to use, copy, modify and distribute this software and its *
+ * documentation strictly for non-commercial purposes is hereby granted *
+ * without fee, provided that the above copyright notice appears in all *
+ * copies and that both the copyright notice and this permission notice *
+ * appear in the supporting documentation. The authors make no claims *
+ * about the suitability of this software for any purpose. It is *
+ * provided "as is" without express or implied warranty. *
+ **************************************************************************/
+
+// $Id: $
+
+/**
+ * \ingroup macros
+ * \file SanityCheckGlobalTriggerDecisions.C
+ * \brief Macro for performing sanity checks on the HLT global trigger decisions.
+ *
+ * Basic sanity checks are performed by the SanityCheckGlobalTriggerDecisions macro
+ * on the HLT global trigger counters. These checks include:
+ * - The counters must be increasing with increasing event ID.
+ * - The total events counter must be the largest and the number of events read
+ * by the raw reader must be less than or equal to the total events counter.
+ *
+ * The simplest way to run this macro with defaults is to run the following
+ * command from a terminal shell:
+ * \code
+ * > aliroot -b -q $ALICE_ROOT/HLT/trigger/macros/SanityCheckGlobalTriggerDecisions.C
+ * \endcode
+ * This will expect data in DDL directory format in the current directory.
+ *
+ * \author Artur Szostak <artursz@iafrica.com>
+ */
+
+#if !defined(__CINT__) || defined(__MAKECINT__)
+#include "AliHLTGlobalTriggerDecision.h"
+#include "TSystem.h"
+#include "TString.h"
+#include "TFile.h"
+#include "TCollection.h"
+#include "TMap.h"
+#include "TObjArray.h"
+#include "TObjString.h"
+#include "TArrayI.h"
+#include "TArrayL64.h"
+#include "Riostream.h"
+#endif
+
+#include "DumpGlobalTrigger.C"
+
+
+/**
+ * Checks to see if a file contains global HLT trigger decision objects.
+ * \param filename The name of the ROOT file to check.
+ * \returns true if the file contains AliHLTGlobalTriggerDecision objects and
+ * false otherwise.
+ */
+bool FileContainsDecisions(const char* filename)
+{
+ TFile* file = new TFile(filename, "READ");
+ if (file == NULL)
+ {
+ cerr << "ERROR: Could not create a TFile object to open '"
+ << filename << "'." << endl;
+ return false;
+ }
+ TIter next(file->GetListOfKeys());
+ TObject* key = NULL;
+ bool result = false;
+ while ((key = next()) != NULL)
+ {
+ TObject* obj = file->Get(key->GetName());
+ if (obj == NULL) continue;
+ if (TString(obj->ClassName()) == "AliHLTGlobalTriggerDecision")
+ {
+ result = true;
+ break;
+ }
+ }
+ delete file;
+ return result;
+}
+
+/**
+ * Routine to print the counters in a HLT global trigger decision object.
+ * \param key The key object storing the key name of the object in the ROOT file.
+ * \param decision The HLT global decision object to print.
+ */
+void PrintCounters(const TObject* key, const AliHLTGlobalTriggerDecision* decision)
+{
+ if (key == NULL) return;
+ if (decision == NULL) return;
+ cout << key->GetName() << " (Global component ID = " << decision->GetUniqueID() << "):";
+ const TArrayL64& counters = decision->Counters();
+ for (Int_t i = 0; i < counters.GetSize(); ++i)
+ {
+ cout << " " << counters[i];
+ }
+ if (counters.GetSize() <= 0) cout << "(none)";
+ cout << endl;
+}
+
+/**
+ * Performs sanity checks on the global HLT trigger counters found in the raw data.
+ *
+ * \param dataSource This is the path to the raw data or the ROOT/DATE file
+ * contining the raw data. (default is the current directory).
+ * One can also specify the output ROOT file as generated by the
+ * DumpGlobalTrigger.C macro directly.
+ * \param firstEvent The event number of the first event to process. (default = 0)
+ * This parameter is ignored if dataSource points to a file containing
+ * AliHLTGlobalTriggerDecision objects.
+ * \param lastEvent The event number of the last event to process. If this is
+ * less than firstEvent then it is set to maximum events available
+ * automatically. (default = -1)
+ * This parameter is ignored if dataSource points to a file containing
+ * AliHLTGlobalTriggerDecision objects.
+ * \param debug Specifies if full debug messages should be printed when running
+ * the DumpGlobalTrigger.C macro.
+ * \returns true if the data passed all validity checks and false if there was a problem.
+ */
+bool SanityCheckGlobalTriggerDecisions(
+ const char* dataSource = "./",
+ Int_t firstEvent = 0,
+ Int_t lastEvent = -1,
+ bool debug = false
+ )
+{
+ const char* outputFile = "globalTriggerDecisions.root";
+ if ((! TString(dataSource).EndsWith(".root")) ||
+ (TString(dataSource).EndsWith(".root") && ! FileContainsDecisions(dataSource))
+ )
+ {
+ if (gSystem->Exec(Form("test -f %s", outputFile)) == 0)
+ {
+ cerr << "ERROR: File " << outputFile
+ << " already exists. It must be moved or removed."
+ << " This script will not overwrite it."
+ << endl;
+ return false;
+ }
+ DumpGlobalTrigger(dataSource, firstEvent, lastEvent, outputFile, debug);
+ }
+ else
+ {
+ outputFile = dataSource;
+ }
+
+ TMap map;
+ map.SetOwnerKeyValue(kTRUE, kTRUE);
+ TObjArray eventIds;
+ eventIds.SetOwner(kFALSE);
+ TArrayI objIds;
+
+ TFile* file = new TFile(outputFile, "READ");
+ if (file == NULL)
+ {
+ cerr << "ERROR: Could not create a TFile object to open '"
+ << outputFile << "'." << endl;
+ return false;
+ }
+ TIter next(file->GetListOfKeys());
+ TObject* key = NULL;
+ while ((key = next()) != NULL)
+ {
+ TObject* obj = file->Get(key->GetName());
+ if (obj == NULL)
+ {
+ cerr << "Warning: Could not fetch object '" << key->GetName() << "'." << endl;
+ continue;
+ }
+ TObjString* id = new TObjString(key->GetName());
+ map.Add(id, obj->Clone());
+ eventIds.Add(id);
+ bool addID = true;
+ for (Int_t i = 0; i < objIds.GetSize(); ++i)
+ {
+ if (objIds[i] == Int_t(obj->GetUniqueID()))
+ {
+ addID = false;
+ break;
+ }
+ }
+ if (addID)
+ {
+ objIds.Set(objIds.GetSize()+1);
+ objIds[objIds.GetSize()-1] = Int_t(obj->GetUniqueID());
+ }
+ }
+ delete file;
+
+ eventIds.Sort(); // Must sort the events in order of increasing event ID.
+ ULong64_t totalCounters = 0;
+ bool result = true;
+
+ for (Int_t n = 0; n < objIds.GetSize(); ++n)
+ {
+ UInt_t objId = UInt_t(objIds[n]);
+ TObject* oldKey = NULL;
+ AliHLTGlobalTriggerDecision* oldDecision = NULL;
+ TIter nextEvent(&eventIds);
+ AliHLTGlobalTriggerDecision* decision = NULL;
+ while ((key = nextEvent()) != NULL)
+ {
+ TPair* pair = (TPair*) map.FindObject(key->GetName());
+ if (pair == NULL)
+ {
+ cerr << "Warning: Could not find trigger decision '" << key->GetName() << "' in the map." << endl;
+ continue;
+ }
+ decision = (AliHLTGlobalTriggerDecision*) pair->Value();
+ if (decision == NULL)
+ {
+ cerr << "Warning: The trigger decision object for '" << key->GetName() << "' was NULL in the map." << endl;
+ continue;
+ }
+ if (decision->GetUniqueID() != objId) continue; // Filter on the current ID we are handling.
+
+ bool printCounters = false;
+ const TArrayL64& counters = decision->Counters();
+
+ // Check that all current counters are larger than the old ones.
+ if (oldDecision != NULL)
+ {
+ const TArrayL64& oldCounters = oldDecision->Counters();
+ if (counters.GetSize() == oldCounters.GetSize())
+ {
+ for (Int_t i = 0; i < counters.GetSize(); ++i)
+ {
+ if (oldCounters[i] > counters[i])
+ {
+ cerr << "ERROR: Previous counter value " << oldCounters[i]
+ << " from object " << oldKey->GetName()
+ << " is larger than the new one " << counters[i]
+ << " from " << key->GetName()
+ << "." << endl;
+ printCounters = true;
+ result = false;
+ }
+ }
+ }
+ else
+ {
+ cerr << "ERROR: Number of previous counters from object " << oldKey->GetName()
+ << " do not match the current counters from " << key->GetName()
+ << "." << endl;
+ printCounters = true;
+ result = false;
+ }
+ }
+
+ // Check that all counters are larger than the total counter at the end.
+ if (counters.GetSize() > 0)
+ {
+ for (Int_t i = 0; i < counters.GetSize()-1; ++i)
+ {
+ if (counters[i] > counters[counters.GetSize()-1])
+ {
+ cerr << "ERROR: Counter " << i
+ << " with value " << counters[i]
+ << " from object " << oldKey->GetName()
+ << " is larger than the last counter " << counters[counters.GetSize()-1]
+ << "." << endl;
+ printCounters = true;
+ result = false;
+ }
+ }
+ }
+
+ if (debug && ! printCounters) PrintCounters(key, decision);
+ if (printCounters)
+ {
+ cout << "Previous counters: "; PrintCounters(oldKey, oldDecision);
+ cout << " Current counters: "; PrintCounters(key, decision);
+ cout << endl;
+ }
+ oldDecision = decision;
+ oldKey = key;
+ }
+ if (oldDecision != NULL)
+ {
+ if (oldDecision->Counters().GetSize() > 0)
+ {
+ // oldDecision will contain the last counter, which we use to get
+ // the total number of events seen by the global trigger component.
+ const TArrayL64& oldCounters = oldDecision->Counters();
+ totalCounters += oldCounters[oldCounters.GetSize()-1];
+ }
+ }
+ }
+
+ if (ULong64_t(eventIds.GetEntries()) > totalCounters)
+ {
+ cerr << "ERROR: The total number of events counters added up to " << totalCounters
+ << ", but the total number of events seen on file is " << eventIds.GetEntries()
+ << ". This is inconsistent." << endl;
+ result = false;
+ }
+ return result;
+}
+