From 8825322eafa348f620b3bef8b732c97a105ffa21 Mon Sep 17 00:00:00 2001 From: Piotr Konopka Date: Tue, 7 Dec 2021 15:05:12 +0100 Subject: [PATCH] A QC Task to try publishing different ROOT objects I used it to look for potential leaks in TCanvas, but this Task could be useful for similar investigations in the future, even if it does not cover every popular ROOT class yet. --- Modules/Example/CMakeLists.txt | 2 + Modules/Example/etc/every-object.json | 52 ++++++ Modules/Example/include/Example/EveryObject.h | 64 +++++++ Modules/Example/include/Example/LinkDef.h | 2 +- Modules/Example/src/EveryObject.cxx | 162 ++++++++++++++++++ 5 files changed, 281 insertions(+), 1 deletion(-) create mode 100644 Modules/Example/etc/every-object.json create mode 100644 Modules/Example/include/Example/EveryObject.h create mode 100644 Modules/Example/src/EveryObject.cxx diff --git a/Modules/Example/CMakeLists.txt b/Modules/Example/CMakeLists.txt index c6fb049998..628e7be0de 100644 --- a/Modules/Example/CMakeLists.txt +++ b/Modules/Example/CMakeLists.txt @@ -2,6 +2,7 @@ add_library(O2QcExample src/BenchmarkTask.cxx + src/EveryObject.cxx src/ExampleTask.cxx src/AnalysisTask.cxx src/FakeCheck.cxx @@ -19,6 +20,7 @@ target_link_libraries(O2QcExample PUBLIC O2QualityControl) add_root_dictionary(O2QcExample HEADERS include/Example/BenchmarkTask.h + include/Example/EveryObject.h include/Example/AnalysisTask.h include/Example/ExampleTask.h include/Example/FakeCheck.h diff --git a/Modules/Example/etc/every-object.json b/Modules/Example/etc/every-object.json new file mode 100644 index 0000000000..1a8fcc6b10 --- /dev/null +++ b/Modules/Example/etc/every-object.json @@ -0,0 +1,52 @@ +{ + "qc": { + "config": { + "database": { + "implementation": "CCDB", + "host": "ccdb-test.cern.ch:8080", + "username": "not_applicable", + "password": "not_applicable", + "name": "not_applicable" + }, + "Activity": { + "number": "42", + "type": "2", + "periodName": "", "": "Period name - e.g. LHC22c, LHC22c1b_test", + "passName": "", "": "Pass type - e.g. spass, cpass1", + "provenance": "qc", "": "Provenance - qc or qc_mc depending whether it is normal data or monte carlo data" + }, + "monitoring": { + "url": "infologger:///debug?qc" + }, + "consul": { + "url": "" + }, + "conditionDB": { + "url": "ccdb-test.cern.ch:8080" + }, + "infologger": { "": "Configuration of the Infologger (optional).", + "filterDiscardDebug": "false", "": "Set to true to discard debug and trace messages (default: false)", + "filterDiscardLevel": "21", "": "Message at this level or above are discarded (default: 21 - Trace)" + } + }, + "tasks": { + "EveryObject": { + "active": "true", + "className": "o2::quality_control_modules::example::EveryObject", + "moduleName": "QcExample", + "detectorName": "TST", + "cycleDurationSeconds": "10", "": "10 seconds minimum", + "maxNumberCycles": "-1", + "": "The other type of dataSource is \"direct\", see basic-no-sampling.json.", + "dataSource": { + "type": "direct", + "query": "random:TST/RAWDATA/0" + } + } + }, + "checks": { + } + }, + "dataSamplingPolicies": [ + ] +} diff --git a/Modules/Example/include/Example/EveryObject.h b/Modules/Example/include/Example/EveryObject.h new file mode 100644 index 0000000000..0ee966231f --- /dev/null +++ b/Modules/Example/include/Example/EveryObject.h @@ -0,0 +1,64 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// +/// \file EveryObject.h +/// \author Piotr Konopka +/// + +#ifndef QC_MODULE_EXAMPLE_EXAMPLEEVERYOBJECT_H +#define QC_MODULE_EXAMPLE_EXAMPLEEVERYOBJECT_H + +#include "QualityControl/TaskInterface.h" +#include + +class TH1F; +class TH2F; +class TH3F; +class THnSparse; +class TCanvas; + +using namespace o2::quality_control::core; + +namespace o2::quality_control_modules::example +{ + +/// \brief Task which publishes (not exactly) every class object used as MO. Can be used to test memory leaks. +/// \author Piotr Konopka +class EveryObject final : public TaskInterface +{ + public: + /// \brief Constructor + EveryObject() = default; + /// Destructor + ~EveryObject() override; + + // Definition of the methods for the template method pattern + void initialize(o2::framework::InitContext& ctx) override; + void startOfActivity(Activity& activity) override; + void startOfCycle() override; + void monitorData(o2::framework::ProcessingContext& ctx) override; + void endOfCycle() override; + void endOfActivity(Activity& activity) override; + void reset() override; + + private: + TH1F* mTH1F = nullptr; + TH2F* mTH2F = nullptr; + TH3F* mTH3F = nullptr; + THnSparse* mTHnSparseF = nullptr; + TCanvas* mTCanvas = nullptr; + std::array mTCanvasMembers = { nullptr }; +}; + +} // namespace o2::quality_control_modules::example + +#endif // QC_MODULE_EXAMPLE_EXAMPLEEVERYOBJECT_H diff --git a/Modules/Example/include/Example/LinkDef.h b/Modules/Example/include/Example/LinkDef.h index ff544d7c58..cb49469443 100644 --- a/Modules/Example/include/Example/LinkDef.h +++ b/Modules/Example/include/Example/LinkDef.h @@ -8,6 +8,6 @@ #pragma link C++ class o2::quality_control_modules::example::BenchmarkTask + ; #pragma link C++ class o2::quality_control_modules::example::ExampleCondition + ; #pragma link C++ class o2::quality_control_modules::example::CustomTH2F + ; - #pragma link C++ class o2::quality_control_modules::example::AnalysisTask + ; +#pragma link C++ class o2::quality_control_modules::example::EveryObject+; #endif diff --git a/Modules/Example/src/EveryObject.cxx b/Modules/Example/src/EveryObject.cxx new file mode 100644 index 0000000000..940a1c93cf --- /dev/null +++ b/Modules/Example/src/EveryObject.cxx @@ -0,0 +1,162 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// +/// \file EveryObject.cxx +/// \author Piotr Konopka +/// + +#include +#include + +#include "QualityControl/QcInfoLogger.h" +#include "Example/EveryObject.h" +#include +#include + +#include +#include +#include +#include +#include + +constexpr Double_t rangeLimiter = 16 * 64000; + +namespace o2::quality_control_modules::example +{ + +EveryObject::~EveryObject() +{ + delete mTH1F; + delete mTH2F; + delete mTH3F; + delete mTHnSparseF; + delete mTCanvas; // TCanvas should delete the contained plots, given that we set kCanDelete +} + +void EveryObject::initialize(o2::framework::InitContext& /*ctx*/) +{ + ILOG(Info, Support) << "initialize EveryObject" << ENDM; // QcInfoLogger is used. FairMQ logs will go to there as well. + + // todo: enable/disable with config flags (this is why objects are always accessed after checking if not null) + // todo: ttree, tefficiency, tprofile + + mTH1F = new TH1F("th1f", "th1f", 64000, 0, rangeLimiter); + getObjectsManager()->startPublishing(mTH1F); + + mTH2F = new TH2F("th2f", "th2f", 250, 0, rangeLimiter, 250, 0, rangeLimiter); + getObjectsManager()->startPublishing(mTH2F); + + mTH3F = new TH3F("th3f", "th3f", 40, 0, rangeLimiter, 40, 0, rangeLimiter, 40, 0, rangeLimiter); + getObjectsManager()->startPublishing(mTH3F); + { + const size_t bins = 1000; + const size_t dim = 5; + const Double_t min = 0.0; + const Double_t max = rangeLimiter; + + const std::vector binsDims(dim, bins); + const std::vector mins(dim, min); + const std::vector maxs(dim, max); + mTHnSparseF = new THnSparseF("thnsparsef", "thnsparsef", dim, binsDims.data(), mins.data(), maxs.data()); + getObjectsManager()->startPublishing(mTHnSparseF); + } + { + mTCanvas = new TCanvas("tcanvas", "tcanvas", 1000, 1000); + mTCanvas->Clear(); + mTCanvas->Divide(2, 2); + for (size_t i = 0; i < 4; i++) { + auto name = std::string("tcanvas_th2f_") + std::to_string(i); + mTCanvasMembers[i] = new TH2F(name.c_str(), name.c_str(), 250, 0, rangeLimiter, 250, 0, rangeLimiter); + + mTCanvas->cd(i + 1); + mTCanvasMembers[i]->Draw(); + mTCanvasMembers[i]->SetBit(TObject::kCanDelete); + } + getObjectsManager()->startPublishing(mTCanvas); + } +} + +void EveryObject::startOfActivity(Activity& activity) +{ + ILOG(Info, Support) << "startOfActivity " << activity.mId << ENDM; +} + +void EveryObject::startOfCycle() +{ + ILOG(Info, Support) << "startOfCycle" << ENDM; +} + +void EveryObject::monitorData(o2::framework::ProcessingContext& ctx) +{ + for (auto&& input : framework::InputRecordWalker(ctx.inputs())) { + const auto* header = header::get(input.header); + auto value1 = (Float_t)(header->payloadSize % (size_t)rangeLimiter); + auto value2 = (Float_t)((header->tfCounter + header->payloadSize) % (size_t)rangeLimiter); + auto value3 = (Float_t)((header->tfCounter * header->payloadSize) % (size_t)rangeLimiter); + + if (mTH1F) { + mTH1F->Fill(value1); + } + if (mTH2F) { + mTH2F->Fill(value1, value3); + } + if (mTH3F) { + mTH3F->Fill(value1, value2, value3); + } + if (mTHnSparseF) { + std::array values{ value1, value2, value3, value2 / (value1 + 1), value2 / (value3 + 1) }; + mTHnSparseF->Fill(values.data()); + } + if (mTCanvas) { + mTCanvasMembers[0]->Fill(value1, value3); + mTCanvasMembers[1]->Fill(value3, value1); + mTCanvasMembers[2]->Fill(value2, value3); + mTCanvasMembers[3]->Fill(value3, value2); + } + } +} + +void EveryObject::endOfCycle() +{ + ILOG(Info, Support) << "endOfCycle" << ENDM; +} + +void EveryObject::endOfActivity(Activity& /*activity*/) +{ + ILOG(Info, Support) << "endOfActivity" << ENDM; +} + +void EveryObject::reset() +{ + ILOG(Info, Support) << "Resetting the objects" << ENDM; + if (mTH1F) { + mTH1F->Reset(); + } + if (mTH2F) { + mTH2F->Reset(); + } + if (mTH3F) { + mTH3F->Reset(); + } + if (mTHnSparseF) { + mTHnSparseF->Reset(); + } + if (mTCanvas) { + for (const auto member : mTCanvasMembers) { + if (member) { + member->Reset(); + } + } + } +} + +} // namespace o2::quality_control_modules::example