ATLAS Offline Software
Loading...
Searching...
No Matches
TriggerEDMSerialiserTool.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3*/
4
6#include "TriggerEDMCLIDs.h"
7
8#include "Gaudi/Interfaces/IOptionsSvc.h"
9#include "GaudiKernel/System.h"
15#include "AthContainers/debug.h"
21
22#include <ranges>
23#include <string>
24#include <numeric>
25
26namespace {
27 // Special module ID used internally to store total result size limit in the truncation threshold map
28 constexpr uint16_t fullResultTruncationID = std::numeric_limits<uint16_t>::max();
29}
30
32 const std::string& name,
33 const IInterface* parent )
34 : base_class( type, name, parent ),
35 m_dynAuxAddress(AuxIdTypeMap_t::Updater_t()) {}
36
38 // Initialise tools and services
39 ATH_CHECK( m_serializerSvc.retrieve() );
40 ATH_CHECK( m_clidSvc.retrieve() );
41 ATH_CHECK( m_debugInfoWHKey.initialize() );
43 if (!m_monTool.empty()) ATH_CHECK(m_monTool.retrieve());
44 // Parse the list of collections to serialise
45 for ( const std::string& typeKeyAuxIDs : m_collectionsToSerialize.value() ) {
47 }
48
49 // Retrieve the total result size limit from DataFlowConfig which is a special object
50 // used online to hold DF properties passed from TDAQ to HLT as run parameters
51 auto jobOptionsSvc = service<Gaudi::Interfaces::IOptionsSvc>("JobOptionsSvc", /*createIf=*/ false);
52 if (!jobOptionsSvc.isValid()) {
53 ATH_MSG_WARNING("Could not retrieve JobOptionsSvc, will not update the EventSizeHardLimitMB property");
54 }
55 else {
56 if (jobOptionsSvc->has("DataFlowConfig.DF_MaxEventSizeMB")) {
57 if (m_eventSizeHardLimitMB.fromString(jobOptionsSvc->get("DataFlowConfig.DF_MaxEventSizeMB")).isSuccess()) {
58 ATH_MSG_DEBUG("Updated EventSizeHardLimitMB to " << m_eventSizeHardLimitMB.value()
59 << " from DataFlowConfig.DF_MaxEventSizeMB");
60 }
61 else {
62 ATH_MSG_ERROR("Could not convert DataFlowConfig.DF_MaxEventSizeMB to integer. Leaving EventSizeHardLimitMB="
63 << m_eventSizeHardLimitMB.value());
64 }
65 }
66 else {
67 ATH_MSG_DEBUG("Could not retrieve DataFlowConfig.DF_MaxEventSizeMB from JobOptionsSvc. This is fine if running "
68 << "offline, but should not happen online. Leaving EventSizeHardLimitMB="
69 << m_eventSizeHardLimitMB.value());
70 }
71 }
72
73 // Add the total result size limit to truncation threshold map
74 if (m_eventSizeHardLimitMB >= 0) {
76 ATH_MSG_ERROR("Fraction cannot be > 1.0, but FullResultTruncationFrac is set to " << m_fullResultTruncationFrac);
77 return StatusCode::FAILURE;
78 }
79 float totalResultSizeLimitBytes = m_fullResultTruncationFrac * m_eventSizeHardLimitMB * 1024. * 1024.;
80 m_truncationThresholds[fullResultTruncationID] = static_cast<uint32_t>(totalResultSizeLimitBytes);
81 }
82 else {
83 m_truncationThresholds[fullResultTruncationID] = std::numeric_limits<uint32_t>::max();
84 }
85
86 return StatusCode::SUCCESS;
87}
88
89StatusCode TriggerEDMSerialiserTool::addCollectionToSerialise(const std::string& typeKeyAuxIDs, std::vector<Address>& addressVec) const {
90 ATH_MSG_DEBUG("Parsing " << typeKeyAuxIDs);
91
92 // Syntax: 'collectionKeyType;module1,module2,...[;allowTruncation]'
93 std::vector<std::string_view> def = CxxUtils::tokenize<std::string_view, char>(typeKeyAuxIDs, ';');
94
95 if ( def.size() < 2 ) {
96 ATH_MSG_ERROR("Invalid EDM collection specification: " << typeKeyAuxIDs);
97 return StatusCode::FAILURE;
98 }
99
100 const std::string_view typeKeyAux = def[0];
101 const std::string_view configuredType = typeKeyAux.substr( 0, typeKeyAux.find('#') );
102 const std::string_view key = typeKeyAux.substr( typeKeyAux.find('#')+1, typeKeyAux.find('.')-typeKeyAux.find('#') );
103
105
106 const size_t pos = configuredType.find('_');
107 const std::string transientType{ pos==std::string::npos ? configuredType : configuredType.substr(0, pos) };
108
109 CLID clid{0};
110 std::string persistentType;
111 if ( m_clidSvc->getIDOfTypeName(transientType, clid).isFailure() ) {
112 ATH_MSG_ERROR( "Can not find CLID for " << transientType << " that is needed for serialisation " << key );
113 return StatusCode::FAILURE;
114 }
115 ATH_MSG_VERBOSE("Decoded transient type: " << transientType << " with the CLID " << clid );
116
117 if ( transientType == configuredType ) {
118 std::string realTypeName;
119 if( m_clidSvc->getTypeInfoNameOfID( clid, realTypeName ).isFailure() ) {
120 ATH_MSG_ERROR( "Can not find real type name for " << transientType << " that is needed for serialisation " << key );
121 return StatusCode::FAILURE;
122 }
123 persistentType = transientType + version( realTypeName );
124 ATH_MSG_VERBOSE(transientType << " = "<< configuredType << " thus obtained real type name from clid svc " << realTypeName << " forming persistent type name "<< persistentType );
125 } else {
126 persistentType = configuredType;
127 }
128
129 ATH_MSG_DEBUG( "Persistent type: " << persistentType );
130
131 RootType classDesc = RootType::ByNameNoQuiet( persistentType );
132 if ( ! classDesc.IsComplete() ) {
133 ATH_MSG_ERROR( "The type " << persistentType << " is not known to ROOT serialiser" );
134 return StatusCode::FAILURE;
135 }
136
137 // Set truncation mode
138 if ( def.size() > 2 && def[2].contains("allowTruncation") ) {
139 ATH_MSG_DEBUG("Truncation allowed for " << configuredType << "#" << key);
140 truncationMode = Address::Truncation::Allowed;
141 }
142
143 std::vector<uint16_t> moduleIdVec = CxxUtils::tokenize<uint16_t, char>(def[1], ',');
144 std::sort(moduleIdVec.begin(), moduleIdVec.end());
145
146 if (moduleIdVec.empty()) {
147 ATH_MSG_ERROR( "No HLT result module IDs given for " << typeKeyAux );
148 return StatusCode::FAILURE;
149 }
150
151 ATH_MSG_DEBUG( "Transient type " << transientType << " persistent type " << persistentType << " will be written to " << moduleIdVec.size() << " result ROBFragments with IDs: "
152 << moduleIdVec << "" );
153
154 if ( persistentType.starts_with("xAOD") ) { // xAOD - either interface of Aux
156 if ( typeKeyAux.contains('.') ) { // Aux, possibly with selection of variables
157 ATH_MSG_DEBUG( "with aux content: " );
158 const std::string allVars = std::string(typeKeyAux.substr( typeKeyAux.find('.')+1 ));
159 if (!allVars.empty()) {
160 const auto variableNames = allVars | std::views::split('.') | std::ranges::to<std::set<std::string>>();
161 if (msgLvl(MSG::DEBUG)) {
162 for ( const auto& el: variableNames ) {
163 ATH_MSG_DEBUG( " \"" << el << "\"" );
164 }
165 }
166 sel.selectAux( variableNames );
167 }
168 addressVec.push_back( {transientType, persistentType, std::move(classDesc), clid, std::string(key), moduleIdVec, Address::Category::xAODAux, truncationMode, sel} );
169 } else {
170 addressVec.push_back( {transientType, persistentType, std::move(classDesc), clid, std::string(key), moduleIdVec, Address::Category::xAODInterface, truncationMode} );
171 }
172 } else { // an old T/P type
173 addressVec.push_back( {transientType, persistentType, std::move(classDesc), clid, std::string(key), moduleIdVec, Address::Category::OldTP, truncationMode} );
174 }
175 return StatusCode::SUCCESS;
176}
177
178StatusCode TriggerEDMSerialiserTool::makeHeader(const Address& address, std::vector<uint32_t>& buffer ) {
179 buffer.push_back(0); // fragment size placeholder
180 buffer.push_back( address.clid ); // type info via CLID
181
182 std::vector<uint32_t> serializedLabel;
184 std::vector<std::string> descr({address.persType, address.key});
185 ss.serialize( descr, serializedLabel );
186 buffer.push_back( serializedLabel.size() );
187 buffer.insert( buffer.end(), serializedLabel.begin(), serializedLabel.end() ); // plain SG key
188 return StatusCode::SUCCESS;
189}
190
191StatusCode TriggerEDMSerialiserTool::fillPayload( const void* data, size_t sz, std::vector<uint32_t>& buffer ) const {
192 ATH_CHECK( sz != 0 );
193 ATH_CHECK( data != nullptr );
194
195 buffer.push_back( sz ); // size in bytes
196 const size_t neededSize = std::ceil( double(sz)/sizeof(uint32_t) );
197 const size_t existingSize = buffer.size();
198 buffer.resize(existingSize + neededSize);
199 std::memcpy(buffer.data() + existingSize, data, sz);
200 return StatusCode::SUCCESS;
201}
202
203
204
205StatusCode TriggerEDMSerialiserTool::serialiseDynAux( DataObject* dObj, const Address& address, std::vector<uint32_t>& buffer, size_t& nDynWritten ) const {
206 ATH_MSG_DEBUG( "About to start streaming aux data of " << address.key );
207 DataBucketBase* dObjAux = dynamic_cast<DataBucketBase*>(dObj);
208 ATH_CHECK( dObjAux != nullptr );
209
210 const SG::IAuxStoreIO* auxStoreIO = dObjAux->template cast<SG::IAuxStoreIO> (nullptr, true);
211 if ( auxStoreIO == nullptr ) {
212 ATH_MSG_DEBUG( "Can't obtain AuxContainerBase of " << address.key << " no dynamic variables presumably" );
213 return StatusCode::SUCCESS;
214 }
215
216 const SG::auxid_set_t& selected = address.sel.getSelectedAuxIDs( auxStoreIO->getDynamicAuxIDs() );
217
218 if ( selected.empty() ) {
219 ATH_MSG_VERBOSE( "Empty set of dynamic variables to store, do nothing" );
220 return StatusCode::SUCCESS;
221 }
222 ATH_MSG_DEBUG("Ready for serialisation of " << selected.size() << " dynamic variables");
223
224 for (SG::auxid_t auxVarID : selected ) {
225
226 const Address* auxAddress{};
227
228 // Find type information for dynamic variable
229 const auto itr = m_dynAuxAddress.find(auxVarID);
230 if ( itr != m_dynAuxAddress.end() ) {
231 auxAddress = &(itr->second);
232 }
233 // Or create and cache it on first encounter. We cannot do this during addCollectionToSerialise
234 // because variables with AuxDataOptions set (e.g. PackedContainer) don't report their actual
235 // type through the AuxTypeRegistry (see ATR-32953).
236 else {
237 const std::string decorationName = SG::AuxTypeRegistry::instance().getName(auxVarID);
238 const std::string typeName = SG::AuxTypeRegistry::instance().getVecTypeName(auxVarID);
239 const std::type_info* tinfo = auxStoreIO->getIOType (auxVarID);
240
241 ATH_CHECK( tinfo != nullptr );
242 TClass* cls = TClass::GetClass (*tinfo);
243 ATH_CHECK( cls != nullptr );
244
245 CLID clid{0};
246 if ( m_clidSvc->getIDOfTypeName(typeName, clid).isFailure() ) { // First try
247 const std::string fullTypeName = System::typeinfoName( *tinfo );
248 if ( m_clidSvc->getIDOfTypeInfoName(fullTypeName, clid).isFailure() ) { // Second try
249 ATH_MSG_ERROR("Unable to obtain CLID for either typeName:" << typeName << " or fullTypeName:" << fullTypeName);
250 ATH_MSG_ERROR("Please check if this is something which should obtain a CLID via TriggerEDMCLIDs.h");
251 return StatusCode::FAILURE;
252 }
253 }
254
255 // Create the type information for the dynamic variable
256 Address addr { typeName, cls->GetName(), RootType(cls), clid,
257 decorationName, address.moduleIdVec, Address::Category::xAODDecoration };
258
259 // Store type and get pointer to it
260 const auto& [itr, inserted] = m_dynAuxAddress.emplace( auxVarID, std::move(addr) );
261 auxAddress = &(itr->second);
262 }
263
264 ATH_MSG_DEBUG( "Streaming '" << auxAddress->key << "' of type '" << auxAddress->transType <<
265 "', aux ID " << auxVarID << ", class '" << auxAddress->classDesc.Class()->GetName() <<
266 "'" << ", CLID " << auxAddress->clid );
267
268 const void* rawptr = auxStoreIO->getIOData( auxVarID );
269 ATH_CHECK( rawptr != nullptr );
270
271 size_t sz=0;
272 void* mem = m_serializerSvc->serialize( rawptr, auxAddress->classDesc, sz );
273
274 if ( mem == nullptr or sz == 0 ) {
275 ATH_MSG_ERROR( "Serialisation of " << address.persTypeName() << "."<< auxAddress->key << " unsuccessful" );
276 return StatusCode::FAILURE;
277 }
278 ATH_MSG_DEBUG( "Serialised " << address.persTypeName() << "."<< auxAddress->key << " memory size " << sz );
279
280 std::vector<uint32_t> fragment;
281 ATH_CHECK( makeHeader( *auxAddress, fragment ) );
282 ATH_CHECK( fillPayload( mem, sz, fragment ) );
283 fragment[0] = fragment.size();
284
285 delete [] static_cast<const char*>( mem );
286
287 buffer.insert( buffer.end(), fragment.begin(), fragment.end() );
288 ++nDynWritten;
289 }
290 return StatusCode::SUCCESS;
291}
292
293
294StatusCode TriggerEDMSerialiserTool::serialiseContainer( void* data, const Address& address, std::vector<uint32_t>& buffer ) const {
295
296 size_t sz=0;
297 void* mem = m_serializerSvc->serialize( data, address.classDesc, sz );
298
299 ATH_MSG_DEBUG( "Streamed to buffer at address " << mem << " of " << sz << " bytes" );
300
301 if ( mem == nullptr or sz == 0 ) {
302 ATH_MSG_ERROR( "Serialisation of " << address.persType << " " << address.key << " unsuccessful" );
303 return StatusCode::FAILURE;
304 }
305
306 // prepare fragment
307 std::vector<uint32_t> fragment;
308 ATH_CHECK( makeHeader( address, fragment ) );
309 ATH_CHECK( fillPayload( mem, sz, fragment ) );
310 if ( mem != nullptr ) delete [] static_cast<const char*>( mem );
311
312
313 ATH_MSG_DEBUG( address.transType << "#" << address.key << " Fragment size: " << fragment.size()*sizeof(uint32_t) << " bytes");
314 fragment[0] = fragment.size();
315 buffer.insert( buffer.end(), fragment.begin(), fragment.end() );
316
317 return StatusCode::SUCCESS;
318}
319
321 void* data,
322 const Address& address,
323 std::vector<uint32_t>& buffer,
324 SGImplSvc* evtStore) const
325{
326 ATH_MSG_DEBUG("xAOD Aux Container");
327
328 void* copy = data;
329 //Get the Base Info given clid.
330 const SG::BaseInfoBase* bib = SG::BaseInfoBase::find (address.clid);
331 //cast data to xAOD::AuxContainerBase
332 void* data_interface = bib->cast (data, ClassID_traits<xAOD::AuxContainerBase>::ID());
333 if (data_interface != nullptr) {
334 const xAOD::AuxContainerBase* store = reinterpret_cast<const xAOD::AuxContainerBase*> (data_interface);
335 copy = address.classDesc.Construct();
336 //cast copy to xAOD::AuxContainerBase
337 void* copy_interface = bib->cast (copy, ClassID_traits<xAOD::AuxContainerBase>::ID());
338 xAOD::AuxContainerBase* copy_store = reinterpret_cast<xAOD::AuxContainerBase*> (copy_interface);
340 *copy_store,
341 nullptr);
342 }
343
344 ATH_CHECK( serialiseContainer( copy, address, buffer ) );
345
346 if (copy != data) {
347 address.classDesc.Destruct (copy);
348 }
349
350 size_t baseSize = buffer.size();
351 if ( not m_saveDynamic )
352 return StatusCode::SUCCESS;
353
354 DataObject* dObj = evtStore->accessData( address.clid, address.key );
355 ATH_CHECK( dObj != nullptr );
356 size_t nDynWritten = 0;
357 ATH_CHECK( serialiseDynAux( dObj, address, buffer, nDynWritten ) );
358 if ( nDynWritten > 0 ) {
359 ATH_MSG_DEBUG( " Fragment size including " << (buffer.size() - baseSize)*sizeof(uint32_t) << " bytes from "
360 << nDynWritten << "x DynAux : " << buffer.size()*sizeof(uint32_t) );
361 }
362 return StatusCode::SUCCESS;
363}
364
365StatusCode TriggerEDMSerialiserTool::serialiseTPContainer( void* data, const Address& address, std::vector<uint32_t>& buffer ) const {
366
367 ATH_MSG_DEBUG("TP Container, converting from: " << address.transType << " to " << address.persType );
368 std::string converterPersistentType;
369 void * persistent = m_tpTool->convertTP( address.transType, data, converterPersistentType );
370 ATH_CHECK( persistent != nullptr );
371 ATH_CHECK ( converterPersistentType == address.persType );
372 ATH_CHECK( serialiseContainer( persistent, address, buffer ) );
373
374 address.classDesc.Destruct( persistent );
375
376 return StatusCode::SUCCESS;
377}
378
379StatusCode TriggerEDMSerialiserTool::serialise( const Address& address, std::vector<uint32_t>& buffer, SGImplSvc* evtStore ) const {
380 DataObject* dObj = evtStore->accessData( address.clid, address.key );
381 if ( dObj == nullptr ) {
382 ATH_MSG_DEBUG("Data Object with the CLID " << address.clid << " and the key " << address.key << " is missing");
383 return StatusCode::SUCCESS;
384 }
385
386 void* rawptr = SG::fromStorable( dObj, address.clid, nullptr, msgLvl(MSG::DEBUG) );
387 if ( rawptr == nullptr ) {
388 ATH_MSG_DEBUG( "Data Object with key " << address.key << " can not be converted to void* for streaming" );
389 return StatusCode::SUCCESS;
390 }
391 ATH_MSG_DEBUG("Obtained raw pointer " << rawptr );
392
394 return serialiseContainer( rawptr, address, buffer );
395 }
396 if ( address.category == Address::Category::xAODAux ) {
397 return serialisexAODAuxContainer( rawptr, address, buffer, evtStore );
398 }
399 if ( address.category == Address::Category::OldTP ) {
400 return serialiseTPContainer( rawptr, address, buffer );
401 }
402 ATH_MSG_ERROR("Unknown Address category - neither of xAODInterface, xAODAux, OldTP");
403 return StatusCode::FAILURE;
404}
405
406StatusCode TriggerEDMSerialiserTool::fill( HLT::HLTResultMT& resultToFill, const EventContext& ctx ) const {
407
408 // Leave this check until there is a justified case for appending data to an existing result
409 if (not resultToFill.getSerialisedData().empty()) {
410 ATH_MSG_ERROR("Trying to fill a result which is not empty! Likely misconfiguration, returning a FAILURE");
411 return StatusCode::FAILURE;
412 }
413
414 SGImplSvc* evtStore = dynamic_cast<SGImplSvc*>(Atlas::getExtendedEventContext(ctx).proxy());
415 ATH_CHECK( evtStore != nullptr );
416
417 // Map storing information to be written out in case of truncation for each module
418 TruncationInfoMap truncationInfoMap;
419
420 // Record debug info container to be filled in case of truncation
421 auto debugInfo = SG::makeHandle(m_debugInfoWHKey, ctx);
422 auto debugInfoData = std::make_unique<xAOD::TrigCompositeContainer>();
423 auto debugInfoAux = std::make_unique<xAOD::TrigCompositeAuxContainer>();
424 debugInfoData->setStore(debugInfoAux.get());
425 ATH_CHECK(debugInfo.record(std::move(debugInfoData), std::move(debugInfoAux)));
426
427 // Find a list of active moduleIDs in this event to skip inactive ones
428 std::set<uint16_t> activeModules = activeModuleIDs(resultToFill);
429 if (activeModules.empty()) {
430 ATH_MSG_DEBUG("No active module IDs in this event. This is normal for events accepted "
431 << "only to calibration streams. Skip all EDM serialisation.");
432 return StatusCode::SUCCESS;
433 }
434
435 // Create buffer for serialised data
436 std::vector<uint32_t> buffer;
437 buffer.reserve(1000);
438
439 std::unordered_map<uint16_t, std::vector<uint32_t>> deferredInterfaceBuffer;
440 for ( const Address& address: m_toSerialise ) {
441 // Check if we need to serialise this object for this event
442 std::vector<uint16_t> addressActiveModuleIds;
443 std::set_intersection(address.moduleIdVec.begin(), address.moduleIdVec.end(),
444 activeModules.begin(), activeModules.end(),
445 std::back_inserter(addressActiveModuleIds));
446 if (addressActiveModuleIds.empty()) {
447 ATH_MSG_DEBUG("Streaming of " << address.persTypeName() << " is skipped "
448 << "because its module IDs are not active in this event");
449 continue;
450 }
451
452 buffer.clear();
453 ATH_MSG_DEBUG( "Streaming " << address.persTypeName() );
454 ATH_CHECK( serialise(address, buffer, evtStore) );
455 if (buffer.empty()) {
456 ATH_MSG_DEBUG("Streaming of " << address.persTypeName() << " is skipped");
457 continue;
458 }
459
460 const size_t thisFragmentSize = buffer.size()*sizeof(uint32_t);
461 ATH_MSG_DEBUG( "Serialised size of " << address.persTypeName() << " is " << thisFragmentSize << " bytes" );
462 for (const uint16_t id : addressActiveModuleIds) {
463 // If result not yet truncated, try adding the serialised data
464 if (resultToFill.getTruncatedModuleIds().count(id)==0) {
465 // for truncation allowed collections, save the interface for a deferred addition
466 if (address.truncationMode==Address::Truncation::Allowed &&
467 address.category == Address::Category::xAODInterface){
468 deferredInterfaceBuffer[id]=buffer;
469 continue;
470 }
471 else {
472 ATH_CHECK(tryAddData(resultToFill, id, buffer, address.truncationMode, deferredInterfaceBuffer));
473 // clean up the deferred interface buffer, if used
474 deferredInterfaceBuffer.erase(id);
475 }
476 }
477 // Check for truncation after adding data
478 if (resultToFill.getTruncatedModuleIds().count(id)==0) {
479 ATH_MSG_DEBUG("Module " << id << " payload after inserting " << address.persTypeName() << " has "
480 << resultToFill.getSerialisedData().at(id).size()*sizeof(uint32_t) << " bytes");
481 truncationInfoMap[id].push_back({&address, thisFragmentSize, true});
482 }
483 else {
484 ATH_MSG_WARNING("HLTResult with module ID " << id <<
485 " truncated - could not add " << address.persTypeName() <<
486 (address.truncationMode==Address::Truncation::Allowed ? " (truncation allowed)" : ""));
487 truncationInfoMap[id].push_back({&address, thisFragmentSize, false});
488 }
489 }
490 }
491
492 // Handle truncation
493 ATH_CHECK(fillDebugInfo(truncationInfoMap, *debugInfo, resultToFill, evtStore));
494
495 return StatusCode::SUCCESS;
496}
497
499 const uint16_t id,
500 const std::vector<uint32_t>& data,
501 Address::Truncation truncationMode,
502 const std::unordered_map<uint16_t, std::vector<uint32_t>> & deferredInterfaceBuffer) const {
503 if (m_truncationThresholds.value().count(id)==0) {
504 ATH_MSG_ERROR("Module ID " << id << " missing from TruncationThresholds map. Cannot determine if result needs truncation");
505 return StatusCode::FAILURE;
506 }
507
508 // Size in this module
509 const uint32_t currentSizeBytes = hltResult.getSerialisedData().count(id)==0
510 ? 0 : hltResult.getSerialisedData().at(id).size()*sizeof(uint32_t);
511 // Total size
512 size_t currentTotalSizeWords = 0;
513 for (const auto& [id, data] : hltResult.getSerialisedData()) currentTotalSizeWords += data.size();
514 const uint32_t currentTotalSizeBytes = currentTotalSizeWords*sizeof(uint32_t);
515 // Size to be added
516 const uint32_t extraSizeBytes = data.size()*sizeof(uint32_t);
517
518 bool severeTruncation = truncationMode==Address::Truncation::Error;
519 if (currentTotalSizeBytes+extraSizeBytes > m_truncationThresholds.value().at(fullResultTruncationID)) {
520 // The data doesn't fit, flag the full result as truncated
521 ATH_MSG_DEBUG("Skipping adding data to result with module ID " << id << " because of full-result truncation");
522 hltResult.addTruncatedModuleId(fullResultTruncationID, severeTruncation);
523 hltResult.addTruncatedModuleId(id, severeTruncation);
524 }
525 else if (currentSizeBytes+extraSizeBytes > m_truncationThresholds.value().at(id)) {
526 // The data doesn't fit, flag this module's result as truncated
527 ATH_MSG_DEBUG("Skipping adding data to truncated result with module ID " << id);
528 hltResult.addTruncatedModuleId(id, severeTruncation);
529 }
530 else {
531 // for truncation allowed collections, add first the interface, only if the Aux is can be stored
532 if (truncationMode==Address::Truncation::Allowed){
533 hltResult.addSerialisedData(id, deferredInterfaceBuffer.at(id));
534 }
535 // The data fits, so add it to the result
536 ATH_MSG_DEBUG("Adding data to result with module ID " << id);
537 hltResult.addSerialisedData(id, data);
538 }
539 return StatusCode::SUCCESS;
540}
541
543 xAOD::TrigCompositeContainer& debugInfoCont,
544 HLT::HLTResultMT& resultToFill,
545 SGImplSvc* evtStore) const {
546 // If full result truncation happened, flag all results as truncated to produce debug info for all
547 if (resultToFill.getTruncatedModuleIds().count(fullResultTruncationID)>0) {
548 ATH_MSG_ERROR("HLT result truncation on total size! Limit of "
549 << m_truncationThresholds.value().at(fullResultTruncationID)/1024./1024.
550 << " MB exceeded. Flagging all module IDs as truncated.");
551 for (const auto& [id, data] : resultToFill.getSerialisedData()) {
552 resultToFill.addTruncatedModuleId(id);
553 }
554 }
555 // Loop over truncation info and fill histograms and debug info objects
556 for (const auto& [id, truncationInfoVec] : truncationInfoMap) {
557 if (resultToFill.getTruncatedModuleIds().count(id)>0) {
558 // Create and fill a debug info object
559 xAOD::TrigComposite* debugInfoThisModule = new xAOD::TrigComposite;
560 debugInfoCont.push_back(debugInfoThisModule); // the container in event store takes ownership of debugInfoThisModule
561 xAOD::TrigComposite::Accessor<uint16_t> moduleId("moduleId");
562 xAOD::TrigComposite::Accessor<uint32_t> totalSize("totalSize");
563 xAOD::TrigComposite::Accessor<std::vector<std::string>> typeNameVec("typeName");
564 xAOD::TrigComposite::Accessor<std::vector<uint32_t>> sizeVec("size");
565 xAOD::TrigComposite::Accessor<std::vector<char>> isRecordedVec("isRecorded");
566 std::pair<std::string, size_t> largestRecorded{"None", 0};
567 std::pair<std::string, size_t> largestDropped{"None", 0};
568 std::pair<std::string, size_t> firstDropped{"None", 0};
569 moduleId(*debugInfoThisModule) = id;
570 uint32_t sizeSum = 0;
571 bool severeTruncation = false;
572 bool truncated = false;
573 for (const TruncationInfo& truncationInfo : truncationInfoVec) {
574 // Store typeName and size information
575 sizeSum += truncationInfo.size;
576 typeNameVec(*debugInfoThisModule).push_back(truncationInfo.addrPtr->persTypeName());
577 sizeVec(*debugInfoThisModule).push_back(truncationInfo.size);
578 isRecordedVec(*debugInfoThisModule).push_back(static_cast<char>(truncationInfo.recorded));
579 if (truncationInfo.recorded && truncationInfo.size > largestRecorded.second) {
580 largestRecorded = {truncationInfo.addrPtr->persTypeName(), truncationInfo.size};
581 }
582 if (!truncationInfo.recorded && truncationInfo.size > largestDropped.second) {
583 largestDropped = {truncationInfo.addrPtr->persTypeName(), truncationInfo.size};
584 }
585 if (!truncationInfo.recorded && !truncated) {
586 firstDropped = {truncationInfo.addrPtr->persTypeName(), truncationInfo.size};
587 truncated = true;
588 }
589 // Decide if this was a severe truncation (event goes to debug stream)
590 if (!truncationInfo.recorded) {
591 severeTruncation |= (truncationInfo.addrPtr->truncationMode==Address::Truncation::Error);
592 }
593 }
594 totalSize(*debugInfoThisModule) = sizeSum;
595 msg(severeTruncation ? MSG::ERROR : MSG::WARNING)
596 << "HLT result truncation" << (severeTruncation ? "." : " in low priority collections.")
597 << " Module ID: " << id << ", limit: "
598 << m_truncationThresholds.value().at(id)/1024. << " kB, total size: "
599 << sizeSum/1024. << " kB, largest recorded collection: " << largestRecorded.first
600 << " (" << largestRecorded.second/1024. << " kB), largest dropped collection: "
601 << largestDropped.first << " (" << largestDropped.second/1024. << " kB), "
602 << " first dropped collection: " <<firstDropped.first << " (" << firstDropped.second/1024. << " kB)"
603 << endmsg;
604 // Give more information on the chains which accepted this event
605 using namespace TrigCompositeUtils;
606 SG::ReadHandle<DecisionContainer> navigation(m_debugNavigationSummaryRHKey); // Implicit ctx lookup - OK, rare code path
607 const Decision* terminus = getTerminusNode(navigation);
608 if (terminus) {
609 std::stringstream ss;
610 ss << "Passing chains in this event: ";
611 for (const DecisionID chainID : decisionIDs(terminus)) {
612 ss << HLT::Identifier(chainID).name() << ", ";
613 addDecisionID(chainID, debugInfoThisModule); // Save this list also into the output, it is then accessible offline too
614 }
615 ATH_MSG_WARNING(ss.str());
616 }
617 // Monitoring
618 const std::string prefix = severeTruncation ? "" : "Allowed";
619 auto monModuleId = Monitored::Scalar<int>(prefix+"Truncation_ModuleId", id);
620 auto monTotalSize = Monitored::Scalar<float>(prefix+"Truncation_TotalSize", sizeSum/1024.);
621 auto monLargestName = Monitored::Scalar<std::string>(
622 prefix+"Truncation_LargestName",
623 largestRecorded.second > largestDropped.second ? largestRecorded.first : largestDropped.first);
624 auto monLargestSize = Monitored::Scalar<float>(
625 prefix+"Truncation_LargestSize",
626 largestRecorded.second > largestDropped.second ? largestRecorded.second/1024. : largestDropped.second/1024.);
627 auto mon = Monitored::Group(m_monTool, monModuleId, monTotalSize, monLargestName, monLargestSize);
628 }
629 }
630 // Serialise and write the debug info only in case of truncation
631 if (!debugInfoCont.empty()) {
632 std::vector<Address> debugInfoAddressVec;
633 const std::string debugInfoID = std::string("xAOD::TrigCompositeContainer#")+m_debugInfoWHKey.key()+";0";
634 const std::string debugInfoAuxID = std::string("xAOD::TrigCompositeAuxContainer#")+m_debugInfoWHKey.key()+"Aux.;0";
635 ATH_CHECK(addCollectionToSerialise(debugInfoID, debugInfoAddressVec));
636 ATH_CHECK(addCollectionToSerialise(debugInfoAuxID, debugInfoAddressVec));
637 std::vector<uint32_t> buffer;
638 for (const Address& address : debugInfoAddressVec) {
639 buffer.clear();
640 ATH_CHECK( serialise(address, buffer, evtStore) );
641 resultToFill.addSerialisedData(0, buffer); // 0 is the main result ID
642 // It would be better to avoid hard-coding main result ID but it should not be configurable either
643 }
644 }
645 return StatusCode::SUCCESS;
646}
647
648std::string TriggerEDMSerialiserTool::version( const std::string& name ) {
649 if ( name.contains("DataVector") ) {
650 const size_t start = name.find('_');
651 return name.substr( start, name.find('>') - start );
652 }
653 const size_t pos = name.find('_');
654 return (pos == std::string::npos ? std::string{} : name.substr( pos ));
655}
656
658 std::set<uint16_t> activeIDs;
659 for (const eformat::helper::StreamTag& st : result.getStreamTags()) {
660 if (st.robs.empty() && st.dets.empty()) { // Full Event Building stream
661 activeIDs.insert(0); // 0 is the main result ID
662 continue;
663 }
664 for (const uint32_t robid : st.robs) {
665 eformat::helper::SourceIdentifier sid(robid);
666 if (sid.subdetector_id() != eformat::SubDetector::TDAQ_HLT) {
667 continue;
668 }
669 activeIDs.insert(sid.module_id());
670 }
671 }
672 return activeIDs;
673}
#define endmsg
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_ERROR(x)
#define ATH_MSG_VERBOSE(x)
#define ATH_MSG_WARNING(x)
#define ATH_MSG_DEBUG(x)
Handle mappings between names and auxid_t.
Helper functions intended to be called from the debugger.
Provide an interface for finding inheritance information at run time.
uint32_t CLID
The Class ID type.
Interface providing I/O for a generic auxiliary store.
static Double_t sz
static Double_t ss
unsigned int DecisionID
void decisionIDs(const Decision *d, DecisionIDContainer &id)
Extracts DecisionIDs stored in the Decision object.
static unsigned int totalSize(const MultiDimArray< T, N > &ht)
TTypeAdapter RootType
Definition RootType.h:211
convert to and from a SG storable
void addDecisionID(DecisionID id, Decision *d)
Appends the decision (given as ID) to the decision object.
A non-templated base class for DataBucket, allows to access the transient object address as a void*.
value_type push_back(value_type pElem)
Add an element to the end of the collection.
bool empty() const noexcept
Returns true if the collection is empty.
A container class for data required to build online output from HLT.
Definition HLTResultMT.h:38
const std::set< uint16_t > & getTruncatedModuleIds() const
Getter for the truncation information.
void addTruncatedModuleId(const uint16_t moduleId, bool severeTruncation=true)
Add module ID to the list of truncated results.
void addSerialisedData(const uint16_t moduleId, const std::vector< uint32_t > &data)
Append serialised data (copy of input) for a given moduleId, doesn't remove existing data.
const std::unordered_map< uint16_t, std::vector< uint32_t > > & getSerialisedData() const
Serialised data getter.
std::string name() const
reports human redable name
Group of local monitoring quantities and retain correlation when filling histograms
Declare a monitored scalar variable.
The Athena Transient Store API.
Definition SGImplSvc.h:109
DataObject * accessData(const CLID &id) const
find proxy and access its data. Returns 0 to flag failure
std::string getName(SG::auxid_t auxid) const
Return the name of an aux data item.
std::string getVecTypeName(SG::auxid_t auxid) const
Return the type of the STL vector used to hold an aux data item.
static AuxTypeRegistry & instance()
Return the singleton registry instance.
The non-template portion of the BaseInfo implementation.
static const BaseInfoBase * find(CLID clid)
Find the BaseInfoBase instance for clid.
Definition BaseInfo.cxx:570
void * cast(void *p, CLID clid) const
Cast to a base pointer.
Definition BaseInfo.cxx:166
Interface providing I/O for a generic auxiliary store.
Definition IAuxStoreIO.h:44
virtual const std::type_info * getIOType(SG::auxid_t auxid) const =0
Return the type of the data to be stored for one aux data item.
virtual const void * getIOData(SG::auxid_t auxid) const =0
Return a pointer to the data to be stored for one aux data item.
virtual const SG::auxid_set_t & getDynamicAuxIDs() const =0
Get the list of all dynamically created variables.
A set of aux data identifiers.
Definition AuxTypes.h:47
Utility class (not a tool or so) to serialize strings into stream of 32bit integers.
static TScopeAdapter ByNameNoQuiet(const std::string &name, Bool_t load=kTRUE)
Definition RootType.cxx:586
void Destruct(void *place) const
Definition RootType.cxx:677
Bool_t IsComplete() const
Definition RootType.cxx:895
TClass * Class() const
Definition RootType.h:183
void * Construct() const
Definition RootType.cxx:666
Gaudi::Property< bool > m_saveDynamic
static StatusCode makeHeader(const TriggerEDMSerialiserTool::Address &address, std::vector< uint32_t > &buffer)
Given the ID of the collection (in address arg) insert basic streaming info into the buffer.
Gaudi::Property< int > m_eventSizeHardLimitMB
StatusCode serialiseTPContainer(void *data, const Address &address, std::vector< uint32_t > &buffer) const
Place inside the buffer the serialised old type of container involves T/P conversion.
SG::ReadHandleKey< TrigCompositeUtils::DecisionContainer > m_debugNavigationSummaryRHKey
StoreGate key for the navigation summary object - with this we can print which chains accepted the ev...
std::vector< Address > m_toSerialise
ToolHandle< TrigSerTPTool > m_tpTool
CxxUtils::ConcurrentToValMap< SG::auxid_t, Address, CxxUtils::SimpleUpdater, std::hash< SG::auxid_t >, std::equal_to< SG::auxid_t >, SG::null_auxid > AuxIdTypeMap_t
A thread-safe map to store the AuxId to Address mapping for dynamic variables.
StatusCode fillDebugInfo(const TruncationInfoMap &truncationInfoMap, xAOD::TrigCompositeContainer &debugInfoCont, HLT::HLTResultMT &resultToFill, SGImplSvc *evtStore) const
Parse the truncation debug information, fill monitoring histograms, fill and record the debug info co...
static std::string version(const std::string &name)
Obtain version from the actual type name.
TriggerEDMSerialiserTool(const std::string &type, const std::string &name, const IInterface *parent)
StatusCode fillPayload(const void *data, size_t sz, std::vector< uint32_t > &buffer) const
Copy bytes from the memory into the buffer converting from char[] to uint32_t[] This function is cand...
Gaudi::Property< std::vector< std::string > > m_collectionsToSerialize
virtual StatusCode fill(HLT::HLTResultMT &resultToFill, const EventContext &ctx) const override
StatusCode serialiseContainer(void *data, const Address &address, std::vector< uint32_t > &buffer) const
Place inside the buffer the serialised container (can be either TP, xAOD) involves simple invocation ...
StatusCode serialise(const Address &address, std::vector< uint32_t > &buffer, SGImplSvc *evtStore) const
Retrieve data from event store, serialise and fill the buffer using one of the specific serialise met...
std::unordered_map< uint16_t, std::vector< TruncationInfo > > TruncationInfoMap
Typedef for collection of TruncationInfo objects for full event.
SG::WriteHandleKey< xAOD::TrigCompositeContainer > m_debugInfoWHKey
StoreGate key for the truncation debug info object.
ServiceHandle< IAthenaSerializeSvc > m_serializerSvc
Gaudi::Property< std::map< uint16_t, uint32_t > > m_truncationThresholds
StatusCode addCollectionToSerialise(const std::string &typeKeyAuxIDs, std::vector< Address > &addressVec) const
Parse entry from m_collectionsToSerialize and add it to m_toSerialise.
StatusCode serialisexAODAuxContainer(void *data, const Address &address, std::vector< uint32_t > &buffer, SGImplSvc *evtStore) const
Place inside the buffer serialised the xAOD Aux container involves selection and recording of dynamic...
ServiceHandle< IClassIDSvc > m_clidSvc
StatusCode serialiseDynAux(DataObject *dObject, const Address &address, std::vector< uint32_t > &buffer, size_t &nDynWritten) const
Add dynamic variables to the payload.
StatusCode tryAddData(HLT::HLTResultMT &hltResult, const uint16_t id, const std::vector< uint32_t > &data, Address::Truncation truncationMode, const std::unordered_map< uint16_t, std::vector< uint32_t > > &deferredInterfaceBuffer) const
Try appending serialised data to HLT result.
ToolHandle< GenericMonitoringTool > m_monTool
static std::set< uint16_t > activeModuleIDs(const HLT::HLTResultMT &result)
Build a list of module IDs to serialise based on the stream tags.
Gaudi::Property< float > m_fullResultTruncationFrac
virtual StatusCode initialize() override
Common base class for the auxiliary containers.
Class helping in dealing with dynamic branch selection.
virtual SG::auxid_set_t getSelectedAuxIDs(const SG::auxid_set_t &fullset) const
Return which variables were selected to be written out.
Helper to copy an aux store while applying thinning.
const ExtendedEventContext & getExtendedEventContext(const EventContext &ctx)
Retrieve an extended context from a context object.
std::vector< std::string > tokenize(std::string_view the_str, std::string_view delimiters)
Splits the string into smaller substrings.
void copyAuxStoreThinned(const SG::IConstAuxStore &orig, SG::IAuxStore &copy, const SG::ThinningInfo *info)
Helper to copy an aux store while applying thinning.
bool fromStorable(DataObject *pDObj, T *&pTrans, bool quiet=false, IRegisterTransient *irt=0, bool isConst=true)
SG::ReadCondHandle< T > makeHandle(const SG::ReadCondHandleKey< T > &key, const EventContext &ctx=Gaudi::Hive::currentContext())
size_t auxid_t
Identifier for a particular aux data item.
Definition AuxTypes.h:27
const Decision * getTerminusNode(SG::ReadHandle< DecisionContainer > &container)
void sort(typename DataModel_detail::iterator< DVL > beg, typename DataModel_detail::iterator< DVL > end)
Specialization of sort for DataVector/List.
TrigCompositeContainer_v1 TrigCompositeContainer
Declare the latest version of the container.
TrigComposite_v1 TrigComposite
Declare the latest version of the class.
setWord1 uint16_t
Internal structure to keep configuration organised conveniently.
xAOD::AuxSelection sel
xAOD dynamic variables selection, relevant only for xAODAux category
Internal structure to keep information for truncation debugging.
MsgStream & msg
Definition testRead.cxx:32