@@ -565,3 +565,113 @@ void ROOT::Experimental::RNTupleInspector::PrintFieldTreeAsDot(const ROOT::RFiel
565565 if (isZeroField)
566566 output << " }" ;
567567}
568+
569+ void ROOT::Experimental::RNTupleInspector::PrintFieldTreeAsFlamegraphSpecification (
570+ EFlamegraphSpecificationFormat format, std::ostream &output) const
571+ {
572+ (void )format; // There is only one format at the moment
573+
574+ const auto &tupleDescriptor = GetDescriptor ();
575+ ROOT ::DescriptorId_t rootId = tupleDescriptor.GetFieldZeroId ();
576+ const auto &rootFieldDescriptor = tupleDescriptor.GetFieldDescriptor (rootId);
577+
578+ struct FrameDescription {
579+ std::string name;
580+ std::string type;
581+ size_t byteSize = 0 ;
582+ char kind; // 'F' or 'C' for field or column
583+ };
584+
585+ struct TimelineOcurrence {
586+ size_t frameDescriptionIndex;
587+ unsigned int timestamp;
588+ char type; // 'O' or 'C' for open or close
589+ };
590+
591+ std::vector<FrameDescription> frameDescriptions;
592+ std::vector<TimelineOcurrence> timelineOcurrences;
593+ unsigned int currentTime = 0 ;
594+
595+ auto visitFieldsDFS = [&](auto &self, const ROOT ::RFieldDescriptor &fieldDescriptor) -> size_t {
596+ FrameDescription fieldFrame;
597+ fieldFrame.name = tupleDescriptor.GetQualifiedFieldName (fieldDescriptor.GetId ());
598+ fieldFrame.type = fieldDescriptor.GetTypeName ();
599+ fieldFrame.kind = ' F' ;
600+ frameDescriptions.push_back (fieldFrame);
601+
602+ size_t frameDescriptionIndex = frameDescriptions.size () - 1 ;
603+
604+ timelineOcurrences.push_back ({frameDescriptionIndex, currentTime, ' O' });
605+
606+ size_t subTreeSize = 0 ;
607+ const auto &childIds = fieldDescriptor.GetLinkIds ();
608+
609+ for (const auto &childFieldId : childIds) {
610+ const auto &childFieldDescriptor = tupleDescriptor.GetFieldDescriptor (childFieldId);
611+ subTreeSize += self (self, childFieldDescriptor);
612+ }
613+
614+ for (const auto &columnDescriptor : tupleDescriptor.GetColumnIterable (fieldDescriptor.GetId ())) {
615+ const auto &columnInfo = GetColumnInspector (columnDescriptor.GetPhysicalId ());
616+ size_t columnSize = columnInfo.GetCompressedSize ();
617+
618+ FrameDescription columnFrame;
619+
620+ columnFrame.name = tupleDescriptor.GetQualifiedFieldName (fieldDescriptor.GetId ()) + " [col#" +
621+ std::to_string (columnDescriptor.GetPhysicalId ()) + " ]" ;
622+ columnFrame.type = ROOT::Internal::RColumnElementBase::GetColumnTypeName (columnDescriptor.GetType ());
623+ columnFrame.byteSize = columnSize;
624+ columnFrame.kind = ' C' ;
625+ frameDescriptions.push_back (columnFrame);
626+
627+ size_t columnFrameIdx = frameDescriptions.size () - 1 ;
628+
629+ timelineOcurrences.push_back ({columnFrameIdx, currentTime, ' O' });
630+ currentTime += columnSize;
631+ timelineOcurrences.push_back ({columnFrameIdx, currentTime, ' C' });
632+
633+ subTreeSize += columnSize;
634+ }
635+
636+ frameDescriptions[frameDescriptionIndex].byteSize = subTreeSize;
637+
638+ timelineOcurrences.push_back ({frameDescriptionIndex, currentTime, ' C' });
639+
640+ return subTreeSize;
641+ };
642+
643+ visitFieldsDFS (visitFieldsDFS, rootFieldDescriptor);
644+
645+ output << " {\n " ;
646+ output << " \" $schema\" :\" https://www.speedscope.app/file-format-schema.json\" ,\n " ;
647+ output << " \" shared\" :{\n " ;
648+ output << " \" frames\" :[\n " ;
649+
650+ for (size_t i = 0 ; i < frameDescriptions.size (); ++i) {
651+ output << " { \" name\" :\" " << frameDescriptions[i].name
652+ << " \" , \" file\" :\" Type: " << frameDescriptions[i].type << " , Size: " << frameDescriptions[i].byteSize
653+ << " B\" }" << (i + 1 < frameDescriptions.size () ? " ,\n " : " \n " );
654+ }
655+
656+ output << " ]\n " ;
657+ output << " },\n " ;
658+ output << " \" profiles\" :[\n " ;
659+ output << " {\n " ;
660+ output << " \" type\" :\" evented\" ,\n " ;
661+ output << " \" name\" :\" Flattened Timeline\" ,\n " ;
662+ output << " \" unit\" :\" bytes\" ,\n " ;
663+ output << " \" startValue\" :0,\n " ;
664+ output << " \" endValue\" :" << currentTime << " ,\n " ;
665+ output << " \" events\" :[\n " ;
666+
667+ for (size_t i = 0 ; i < timelineOcurrences.size (); ++i) {
668+ const auto &e = timelineOcurrences[i];
669+ output << " {\" type\" :\" " << e.type << " \" ,\" frame\" :" << e.frameDescriptionIndex
670+ << " ,\" at\" :" << e.timestamp << " }" << (i + 1 < timelineOcurrences.size () ? " ,\n " : " \n " );
671+ }
672+
673+ output << " ]\n " ;
674+ output << " }\n " ;
675+ output << " ]\n " ;
676+ output << " }\n " ;
677+ }
0 commit comments