/********************************************************************** Filename : GMemReport.cpp Content : Forming string buffer with memory report. Created : July 15, 2009 Authors : Boris Rayskiy Copyright : (c) 2005-2008 Scaleform Corp. All Rights Reserved. This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR ANY PURPOSE. **********************************************************************/ #include "GMemoryHeap.h" #include "GHeapTypes.h" #include "GMemory.h" #include "GString.h" #include "GMsgFormat.h" #include "GRefCount.h" #include "GAlgorithm.h" #include "GStats.h" #include "GFxLog.h" #include "GFxAmpProfileFrame.h" #include "GHeapNew.h" void GMemoryHeap::MemReport(GFxLog* log, MemReportType reportType) { GStringBuffer buffer; MemReport(buffer, reportType); if (log != NULL) { // Breaking string into chunks so that it doesn't get truncated by some log implementations static const UPInt chunkSize = 100; for (UPInt i = 0; i < buffer.GetLength(); i += chunkSize) { log->LogMessage("%s", GString(buffer.ToCStr() + i, G_Min(chunkSize, buffer.GetLength() - i)).ToCStr()); } } } #ifndef GFC_NO_STAT // This class creates the memory report // It lives here so we don't have to pollute the public GMemoryHeap header with internals // class StatsUpdate { public: StatsUpdate() : NextHandle(0) { } // Public interface // Called by GMemoryHeap::MemReport void MemReport(GFxAmpMemItem* rootItem, GMemoryHeap::MemReportType reportType); private: // Heap visitor that adds all the child heaps to an array struct HolderVisitor : public GMemoryHeap::HeapVisitor { GArray Heaps; virtual void Visit(GMemoryHeap* pParent, GMemoryHeap *heap) { GUNUSED(pParent); Heaps.PushBack(heap); } }; // This struct holds the memory stats for a given heap // It is part of the heap visitor, StatIdVisitor, below struct HeapStats : public GRefCountBase { HeapStats(GMemoryHeap* heap) : Heap(heap) { Heap->GetStats(&StatBag); } GMemoryHeap* Heap; GStatBag StatBag; }; // Heap visitor that traverses the tree and adds all non-debug heaps to an array // The actual HeapStats for each heap is not set by the visitor struct StatIdVisitor : public GMemoryHeap::HeapVisitor { GArray< GPtr > HeapStatsArray; GArray HeapUsedMemory; virtual void Visit(GMemoryHeap* parent, GMemoryHeap* heap) { GUNUSED(parent); if (((heap->GetFlags() & GMemoryHeap::Heap_UserDebug) == 0) && heap->GetFootprint()) { HeapStatsArray.PushBack(*GNEW HeapStats(heap)); HeapUsedMemory.PushBack(0); } heap->VisitChildHeaps(this); } }; // Custom heap visitor class that keeps track of memory per StatID class SummaryStatIdVisitor : public GMemoryHeap::HeapVisitor { public: virtual void Visit(GMemoryHeap* parent, GMemoryHeap *heap) { GUNUSED(parent); if (((heap->GetFlags() & GMemoryHeap::Heap_UserDebug) == 0)) { if (heap->GetId() != GHeapId_FontCache) { GStatBag bag; heap->GetStats(&bag); StatIdBag.Add(bag); heap->VisitChildHeaps(this); } } } UPInt GetStatIdMemory(GStatDesc::Iterator it) const { GStatInfo statInfo; UPInt totalMemory = 0; if (StatIdBag.GetStat(&statInfo, it->GetId())) { GStat::StatValue statValue; statInfo.GetStat(&statValue, 0); totalMemory = statValue.IValue; } for (GStatDesc::Iterator itChild = it->GetChildIterator(); !itChild.IsEnd(); ++itChild) { totalMemory += GetStatIdMemory(itChild); } return totalMemory; } private: GStatBag StatIdBag; }; // Custom heap visitor class that keeps track of Movie, Mesh, and Font memory class SummaryMemoryVisitor : public GMemoryHeap::HeapVisitor { public: SummaryMemoryVisitor() : MovieViewMemory(0), MovieDataMemory(0), MeshCacheMemory(0), FontCacheMemory(0) {} virtual void Visit(GMemoryHeap* parent, GMemoryHeap *heap) { GUNUSED(parent); if (((heap->GetFlags() & GMemoryHeap::Heap_UserDebug) == 0)) { switch (heap->GetId()) { case GHeapId_MovieView: MovieViewMemory += heap->GetUsedSpace(); break; case GHeapId_MovieData: MovieDataMemory += heap->GetUsedSpace(); break; case GHeapId_MeshCache: MeshCacheMemory += heap->GetUsedSpace(); break; case GHeapId_FontCache: FontCacheMemory += heap->GetUsedSpace(); break; } } heap->VisitChildHeaps(this); } UPInt GetMovieViewMemory() const { return MovieViewMemory; } UPInt GetMovieDataMemory() const { return MovieDataMemory; } UPInt GetMeshCacheMemory() const { return MeshCacheMemory; } UPInt GetFontCacheMemory() const { return FontCacheMemory; } private: UPInt MovieViewMemory; UPInt MovieDataMemory; UPInt MeshCacheMemory; UPInt FontCacheMemory; }; class HeapStatBagVisitor : public GMemoryHeap::HeapVisitor { public: HeapStatBagVisitor(GStatBag* statBag) : StatBag(statBag) { } virtual void Visit(GMemoryHeap*, GMemoryHeap* heap) { if (heap->GetStats(StatBag)) { heap->VisitChildHeaps(this); } } const GStatBag* GetStatBag() const { return StatBag; } private: GStatBag* StatBag; }; // Custom heap visitor class that keeps track of Movie, Mesh, and Font memory class HeapTypeVisitor : public GMemoryHeap::HeapVisitor { public: HeapTypeVisitor(GMemoryHeap* heap, GFxAmpMemItem* rootItem, StatsUpdate* memReporter) : RootHeap(heap), MovieViewMemory(0), MovieDataMemory(0), OtherMemory(0), NextHandle(rootItem->ID + 1), MemReporter(memReporter) { heap->GetRootStats(&RootHeapStats); SysTotalItem = rootItem->AddChild(NextHandle++, "Total Footprint", static_cast(RootHeapStats.SysMemFootprint)); SysTotalItem->StartExpanded = true; SysUsedItem = SysTotalItem->AddChild(NextHandle++, "Used Space"); SysUsedItem->StartExpanded = true; SysTotalItem->AddChild(NextHandle++, "Debug Data", static_cast(RootHeapStats.UserDebugFootprint + RootHeapStats.DebugInfoFootprint)); if (RootHeapStats.PageMapFootprint > 0) { SysTotalItem->AddChild(NextHandle++, "Page Map", static_cast(RootHeapStats.PageMapFootprint)); } if (RootHeapStats.BookkeepingFootprint > 0) { SysTotalItem->AddChild(NextHandle++, "Bookkeeping", static_cast(RootHeapStats.BookkeepingFootprint)); } SysUnusedItem = SysTotalItem->AddChild(NextHandle++, "Unused Space"); GFxAmpMemItem* globalMemItem = SysUsedItem->AddChild(NextHandle++, "Global Heap", static_cast(heap->GetUsedSpace())); ViewMemItem = SysUsedItem->AddChild(NextHandle++, "Movie View Heaps"); DataMemItem = SysUsedItem->AddChild(NextHandle++, "Movie Data Heaps"); OtherMemItem = SysUsedItem->AddChild(NextHandle++, "Other Heaps"); #ifdef GHEAP_DEBUG_INFO GStatBag statBag; heap->GetStats(&statBag); MemReporter->GetStatMemory(GStatDesc::GetGroupIterator(GStat_Mem), &statBag, globalMemItem); #else GUNUSED(globalMemItem); #endif SysUnusedItem->AddChild(NextHandle++, heap->GetName(), static_cast(heap->GetFootprint() - heap->GetUsedSpace())); } ~HeapTypeVisitor() { SysUsedItem->SetValue(static_cast(RootHeap->GetUsedSpace() + GetTotalMemory())); SysUnusedItem->SetValue(static_cast(RootHeapStats.SysMemFootprint - RootHeapStats.UserDebugFootprint - RootHeapStats.DebugInfoFootprint - RootHeap->GetUsedSpace() - GetTotalMemory())); ViewMemItem->SetValue(static_cast(MovieViewMemory)); DataMemItem->SetValue(static_cast(MovieDataMemory)); OtherMemItem->SetValue(static_cast(OtherMemory)); } virtual void Visit(GMemoryHeap* parent, GMemoryHeap* heap) { GUNUSED(parent); if (((heap->GetFlags() & GMemoryHeap::Heap_UserDebug) == 0)) { GFxAmpMemItem* heapGroupMemItem = NULL; switch (heap->GetId()) { case GHeapId_MovieView: MovieViewMemory += heap->GetTotalUsedSpace(); heapGroupMemItem = ViewMemItem; break; case GHeapId_MovieData: MovieDataMemory += heap->GetTotalUsedSpace(); heapGroupMemItem = DataMemItem; break; default: OtherMemory += heap->GetTotalUsedSpace(); heapGroupMemItem = OtherMemItem; break; } GFxAmpMemItem* heapMemItem = heapGroupMemItem->AddChild(NextHandle++, heap->GetName(), 0); #ifdef GHEAP_DEBUG_INFO GStatBag statBag; HeapStatBagVisitor visitor(&statBag); visitor.Visit(NULL, heap); MemReporter->GetStatMemory(GStatDesc::GetGroupIterator(GStat_Mem), &statBag, heapMemItem); #endif heapMemItem->SetValue(static_cast(heap->GetTotalUsedSpace())); SysUnusedItem->AddChild(NextHandle++, heap->GetName(), static_cast(heap->GetFootprint() - heap->GetUsedSpace())); } } private: UPInt GetTotalMemory() const { return MovieViewMemory + MovieDataMemory + OtherMemory; } GMemoryHeap::RootStats RootHeapStats; GMemoryHeap* RootHeap; GFxAmpMemItem* SysTotalItem; GFxAmpMemItem* SysUsedItem; GFxAmpMemItem* SysUnusedItem; GFxAmpMemItem* ViewMemItem; GFxAmpMemItem* DataMemItem; GFxAmpMemItem* OtherMemItem; UPInt MovieViewMemory; UPInt MovieDataMemory; UPInt OtherMemory; UInt32 NextHandle; StatsUpdate* MemReporter; }; // This struct holds the memory stats for a file // It is part of the heap visitor, FileVisitor, below struct FileStats { FileStats() : TotalMemory(0) { } GStatBag StatBag; UPInt TotalMemory; }; // Heap visitor that traverses the tree and creates a map of // file names to memory statistics // The total memory for each file is not updated by the visitor struct FileVisitor : public GMemoryHeap::HeapVisitor { GStringHash FileStatsMap; // recursively updates the file statistics by examining child heaps void UpdateMovieHeap(GMemoryHeap* heap, GStatBag* fileStatBag) { if ((heap->GetFlags() & GMemoryHeap::Heap_UserDebug) == 0) { GStatBag statBag; heap->GetStats(&statBag); *fileStatBag += statBag; HolderVisitor visitor; heap->VisitChildHeaps(&visitor); for (UPInt i = 0; i < visitor.Heaps.GetSize(); ++i) { UpdateMovieHeap(visitor.Heaps[i], fileStatBag); } } } virtual void Visit(GMemoryHeap* parent, GMemoryHeap* heap) { GUNUSED(parent); if ((heap->GetFlags() & GMemoryHeap::Heap_UserDebug) != 0) { return; } switch (heap->GetId()) { case GHeapId_MovieDef: case GHeapId_MovieData: case GHeapId_MovieView: { // Recover the file name GString heapName = heap->GetName(); UPInt heapNameLength = heapName.GetLength(); UPInt startIndex = 0; UPInt endIndex = heapNameLength; for (UPInt i = 0; i < heapNameLength; ++i) { UPInt iIndex = heapNameLength - i - 1; if (heapName[iIndex] == '"') { if (endIndex == heapNameLength) { endIndex = iIndex; } else { startIndex = iIndex + 1; break; } } } GASSERT(startIndex < endIndex); GString filename = heapName.Substring(startIndex, endIndex); // Add the file name to the map GStringHash::Iterator it = FileStatsMap.FindCaseInsensitive(filename); if (it == FileStatsMap.End()) { FileStatsMap.SetCaseInsensitive(filename, FileStats()); it = FileStatsMap.FindCaseInsensitive(filename); } // update the stats for this file UpdateMovieHeap(heap, &(it->Second.StatBag)); } break; default: break; } } }; UInt32 GetRoundUpKilobytes(UPInt bytes); UInt32 GetNearestKilobytes(UPInt bytes); void MemReportStatId(GFxAmpMemItem* rootItem, GMemoryHeap::MemReportType reportType, UPInt systemMemory); UPInt GetStatIdMemory(GStatDesc::Iterator it, StatIdVisitor& heapVisitor, GFxAmpMemItem* rootItem, GMemoryHeap::MemReportType reportType); void MemReportHeaps(GMemoryHeap* pHeap, GFxAmpMemItem* rootItem, GMemoryHeap::MemReportType reportType); void GetHeapMemory(GStatDesc::Iterator it, GStatBag* statBag, GFxAmpMemItem* rootItem); UInt32 GetStatMemory(GStatDesc::Iterator it, GStatBag* statBag, GFxAmpMemItem* rootItem); void MemReportFile(GFxAmpMemItem* rootItem, GMemoryHeap::MemReportType reportType); void MemReportHeapsDetailed(GFxAmpMemItem* rootItem, GMemoryHeap* heap); UPInt GetFileMemory(GStatDesc::Iterator it, FileStats& kFileStats, GFxAmpMemItem* rootItem, GMemoryHeap::MemReportType reportType); UInt32 NextHandle; }; void StatsUpdate::MemReport(GFxAmpMemItem* rootItem, GMemoryHeap::MemReportType reportType) { rootItem->ID = NextHandle++; rootItem->StartExpanded = true; GMemoryHeap::RootStats rootStats; GMemory::pGlobalHeap->GetRootStats(&rootStats); switch (reportType) { case GMemoryHeap::MemReportHeapDetailed: MemReportHeapsDetailed(rootItem, GMemory::pGlobalHeap); break; case GMemoryHeap::MemReportSimple: case GMemoryHeap::MemReportSimpleBrief: #ifdef GHEAP_DEBUG_INFO MemReportStatId(rootItem, reportType, rootStats.SysMemFootprint - rootStats.UserDebugFootprint - rootStats.DebugInfoFootprint); #endif break; case GMemoryHeap::MemReportFileSummary: MemReportFile(rootItem, reportType); break; default: if (reportType != GMemoryHeap::MemReportSummary && reportType != GMemoryHeap::MemReportHeapsOnly) { G_Format(rootItem->Name, "Memory {0:sep:,}K / {1:sep:,}K", GetNearestKilobytes(rootStats.SysMemUsedSpace - rootStats.UserDebugFootprint - rootStats.DebugInfoFootprint), GetNearestKilobytes(rootStats.SysMemFootprint - rootStats.UserDebugFootprint - rootStats.DebugInfoFootprint)); if (reportType != GMemoryHeap::MemReportBrief) { GFxAmpMemItem* sysSummaryItem = rootItem->AddChild(NextHandle++, "System Summary"); if (rootStats.SysMemFootprint > 0) { sysSummaryItem->AddChild(NextHandle++, "System Memory Footprint", static_cast(rootStats.SysMemFootprint)); } if (rootStats.SysMemUsedSpace > 0) { sysSummaryItem->AddChild(NextHandle++, "System Memory Used Space", static_cast(rootStats.SysMemUsedSpace)); } if (rootStats.PageMapFootprint > 0) { sysSummaryItem->AddChild(NextHandle++, "Page Mapping Footprint", static_cast(rootStats.PageMapFootprint)); } if (rootStats.SysMemFootprint > 0) { sysSummaryItem->AddChild(NextHandle++, "Page Mapping Used Space", static_cast(rootStats.PageMapUsedSpace)); } if (rootStats.BookkeepingFootprint > 0) { sysSummaryItem->AddChild(NextHandle++, "Bookkeeping Footprint", static_cast(rootStats.BookkeepingFootprint)); } if (rootStats.BookkeepingUsedSpace > 0) { sysSummaryItem->AddChild(NextHandle++, "Bookkeeping Used Space", static_cast(rootStats.BookkeepingUsedSpace)); } if (rootStats.DebugInfoFootprint > 0) { sysSummaryItem->AddChild(NextHandle++, "Debug Info Footprint", static_cast(rootStats.DebugInfoFootprint)); } if (rootStats.DebugInfoUsedSpace > 0) { sysSummaryItem->AddChild(NextHandle++, "Debug Info Used Space", static_cast(rootStats.DebugInfoUsedSpace)); } if (rootStats.UserDebugFootprint > 0) { sysSummaryItem->AddChild(NextHandle++, "HUD Footprint", static_cast(rootStats.UserDebugFootprint)); } if (rootStats.UserDebugUsedSpace > 0) { sysSummaryItem->AddChild(NextHandle++, "HUD Used Space", static_cast(rootStats.UserDebugUsedSpace)); } } } if (reportType != GMemoryHeap::MemReportHeapsOnly) { GFxAmpMemItem* summaryItem; if (reportType == GMemoryHeap::MemReportBrief) { summaryItem = rootItem; } else { summaryItem = rootItem->AddChild(NextHandle++, "Summary"); } summaryItem->StartExpanded = true; #ifdef GHEAP_DEBUG_INFO SummaryStatIdVisitor statVisit; statVisit.Visit(NULL, GMemory::GetGlobalHeap()); UPInt imageMemory = statVisit.GetStatIdMemory(GStatDesc::GetGroupIterator(GStat_Image_Mem)); if (imageMemory > 0) { summaryItem->AddChild(NextHandle++, "Image", static_cast(imageMemory)); } #ifndef GFC_NO_SOUND UPInt soundMemory = statVisit.GetStatIdMemory(GStatDesc::GetGroupIterator(GStat_Sound_Mem)); if (soundMemory > 0) { summaryItem->AddChild(NextHandle++, "Sound", static_cast(soundMemory)); } #endif #ifndef GFC_NO_VIDEO UPInt videoMemory = statVisit.GetStatIdMemory(GStatDesc::GetGroupIterator(GStat_Video_Mem)); if (videoMemory > 0) { summaryItem->AddChild(NextHandle++, "Video", static_cast(videoMemory)); } #endif #endif SummaryMemoryVisitor heapVisit; heapVisit.Visit(NULL, GMemory::GetGlobalHeap()); summaryItem->AddChild(NextHandle++, "Movie View", static_cast(heapVisit.GetMovieViewMemory())); summaryItem->AddChild(NextHandle++, "Movie Data", static_cast(heapVisit.GetMovieDataMemory())); summaryItem->AddChild(NextHandle++, "Mesh Cache", static_cast(heapVisit.GetMeshCacheMemory())); summaryItem->AddChild(NextHandle++, "Font Cache", static_cast(heapVisit.GetFontCacheMemory())); } if (reportType != GMemoryHeap::MemReportSummary && reportType != GMemoryHeap::MemReportBrief) { MemReportHeaps(GMemory::pGlobalHeap, rootItem, reportType); } break; } } UInt32 StatsUpdate::GetRoundUpKilobytes(UPInt bytes) { return (static_cast(bytes) + 1023) / 1024; } UInt32 StatsUpdate::GetNearestKilobytes(UPInt bytes) { return (static_cast(bytes) + 512) / 1024; } // Adds the memory per StatId // Under each StatId the memory is broken down per heap void StatsUpdate::MemReportStatId(GFxAmpMemItem* rootItem, GMemoryHeap::MemReportType reportType, UPInt systemMemory) { rootItem->Name = "Total Allocated Memory"; rootItem->SetValue(static_cast(systemMemory)); StatIdVisitor heapVisitor; heapVisitor.Visit(GMemory::pGlobalHeap, GMemory::pGlobalHeap); UPInt totalUsed = GetStatIdMemory(GStatDesc::GetGroupIterator(GStat_Mem), heapVisitor, rootItem, reportType); UPInt totalOverhead = 0; UPInt numNonZeroHeaps = 0; int totalUnused = 0; UPInt totalFootprint = 0; GPtr overheadItem = *GHEAP_AUTO_NEW(rootItem) GFxAmpMemItem(NextHandle++); GPtr unusedItem = *GHEAP_AUTO_NEW(rootItem) GFxAmpMemItem(NextHandle++); GStatInfo footprintInfo; GStat::StatValue footprintValue; //GStatInfo usedInfo; //GStat::StatValue usedValue; GStatInfo overheadInfo; GStat::StatValue overheadValue; for (UPInt i = 0; i < heapVisitor.HeapStatsArray.GetSize(); ++i) { HeapStats* heapStats = heapVisitor.HeapStatsArray[i]; heapStats->StatBag.GetStat(&footprintInfo, GStatHeap_LocalFootprint); footprintInfo.GetStat(&footprintValue, 0); //heapStats->StatBag.GetStat(&usedInfo, GStatHeap_LocalUsedSpace); //usedInfo.GetStat(&usedValue, 0); heapStats->StatBag.GetStat(&overheadInfo, GStatHeap_Bookkeeping); overheadInfo.GetStat(&overheadValue, 0); //int unusedAmount = G_Max(0, static_cast(footprintValue.IValue) - static_cast(usedValue.IValue) - static_cast(overheadValue.IValue)); int unusedAmount = G_Max(0, static_cast(footprintValue.IValue) - static_cast(heapVisitor.HeapUsedMemory[i]) - static_cast(overheadValue.IValue)); if (reportType != GMemoryHeap::MemReportSimpleBrief) { GString heapName("[Heap] "); heapName += heapStats->Heap->GetName(); GMemoryHeap::HeapInfo kInfo; heapStats->Heap->GetHeapInfo(&kInfo); if (kInfo.pParent != NULL && kInfo.pParent != GMemory::GetGlobalHeap()) { heapName += kInfo.pParent->GetName(); } overheadItem->AddChild(NextHandle++, heapName, static_cast(overheadValue.IValue)); if (unusedAmount > 0) { unusedItem->AddChild(NextHandle++, heapName, static_cast(unusedAmount)); } } totalOverhead += overheadValue.IValue; totalUnused += unusedAmount; totalFootprint += footprintValue.IValue; ++numNonZeroHeaps; } if (totalOverhead != 0) { overheadItem->Name = "Overhead"; overheadItem->SetValue(static_cast(totalOverhead)); rootItem->Children.PushBack(overheadItem); } unusedItem->Name = "Unused Memory"; unusedItem->SetValue(static_cast(systemMemory - totalUsed - totalOverhead)); rootItem->Children.PushBack(unusedItem); UInt32 systemUnused = static_cast(totalFootprint - totalUsed) - static_cast(totalUnused); if (systemUnused != 0 && reportType != GMemoryHeap::MemReportSimpleBrief) { unusedItem->AddChild(NextHandle++, "System Unused Memory", systemUnused); } } UPInt StatsUpdate::GetStatIdMemory(GStatDesc::Iterator it, StatIdVisitor& heapVisitor, GFxAmpMemItem* rootItem, GMemoryHeap::MemReportType reportType) { GStatInfo statInfo; GStat::StatValue statValue; GPtr statIdItem = *GHEAP_AUTO_NEW(rootItem) GFxAmpMemItem(NextHandle++); statIdItem->StartExpanded = true; UPInt total = 0; UPInt numNonZeroHeaps = 0; for (UPInt i = 0; i < heapVisitor.HeapStatsArray.GetSize(); ++i) { HeapStats* heapStats = heapVisitor.HeapStatsArray[i]; if (heapStats->StatBag.GetStat(&statInfo, it.GetId())) { statInfo.GetStat(&statValue, 0); if (reportType != GMemoryHeap::MemReportSimpleBrief) { GMemoryHeap::HeapInfo kInfo; heapStats->Heap->GetHeapInfo(&kInfo); GString heapName("[Heap] "); heapName += heapStats->Heap->GetName(); if (kInfo.pParent != NULL && kInfo.pParent != GMemory::GetGlobalHeap()) { heapName += kInfo.pParent->GetName(); } statIdItem->AddChild(NextHandle++, heapName, static_cast(statValue.IValue)); statIdItem->StartExpanded = false; } heapVisitor.HeapUsedMemory[i] += statValue.IValue; total += statValue.IValue; ++numNonZeroHeaps; } } for (GStatDesc::Iterator itChild = it->GetChildIterator(); !itChild.IsEnd(); ++itChild) { UPInt childTotal = GetStatIdMemory(itChild, heapVisitor, statIdItem, reportType); if (childTotal > 0) { total += childTotal; ++numNonZeroHeaps; } } if (total > 0) { statIdItem->Name = it->GetName(); statIdItem->SetValue(static_cast(total)); rootItem->Children.PushBack(statIdItem); } return total; } void StatsUpdate::MemReportHeaps(GMemoryHeap* pHeap, GFxAmpMemItem* rootItem, GMemoryHeap::MemReportType reportType) { if (((pHeap->GetFlags() & GMemoryHeap::Heap_UserDebug) == 0) && pHeap->GetFootprint() > 0) { GStatBag statBag; GStatInfo statInfo; GStat::StatValue statValue; pHeap->GetStats(&statBag); statBag.GetStat(&statInfo, GStatHeap_TotalFootprint); statInfo.GetStat(&statValue, 0); GString buffer; G_Format(buffer, "[Heap] {0}", pHeap->GetName()); GFxAmpMemItem* heapItem = rootItem->AddChild(NextHandle++, buffer, static_cast(statValue.IValue)); #ifdef GHEAP_DEBUG_INFO if (reportType == GMemoryHeap::MemReportFull || reportType == GMemoryHeap::MemReportMedium) { GStatDesc::Iterator it = GStatDesc::GetGroupIterator(GStatGroup_Core); #ifdef GFC_BUILD_DEBUG if (reportType == GMemoryHeap::MemReportFull) { it = GStatDesc::GetGroupIterator(GStatGroup_Default); } #endif GetHeapMemory(it, &statBag, heapItem); statBag.UpdateGroups(); GStatInfo sumStat; if (statBag.GetStat(&sumStat, GStat_Mem)) { heapItem->AddChild(NextHandle++, "Allocations Count", static_cast(sumStat.ToMemoryStat()->GetAllocCount())); } } #endif if (reportType != GMemoryHeap::MemReportMedium) { HolderVisitor hv; pHeap->VisitChildHeaps(&hv); for (UPInt i = 0; i < hv.Heaps.GetSize(); ++i) { MemReportHeaps(hv.Heaps[i], heapItem, reportType); } } } } // Invokes recursively memory statistic data belonging to the given heap. void StatsUpdate::GetHeapMemory(GStatDesc::Iterator it, GStatBag* statBag, GFxAmpMemItem* rootItem) { const GStatDesc* pdesc = *it; if (pdesc) { GPtr childItem; if (pdesc->GetGroupId() == 0) { if (pdesc->GetId() != 0) { if ((pdesc->GetType() == (UByte)GStat::Stat_Memory) || (pdesc->GetType() == (UByte)GStat::Stat_LogicalGroup)) { childItem = *GHEAP_AUTO_NEW(rootItem) GFxAmpMemItem(NextHandle++); childItem->Name = pdesc->GetName(); if (pdesc->GetId() != GStatHeap_Summary) { childItem->StartExpanded = true; } } } } if (!childItem) { childItem = *GHEAP_AUTO_NEW(rootItem) GFxAmpMemItem(NextHandle++); childItem->Name = pdesc->GetName(); } GStatDesc::Iterator ichild = pdesc->GetChildIterator(); while (!ichild.IsEnd()) { if ((pdesc->GetGroupId() == 0) && (pdesc->GetId() == 0)) { GetHeapMemory(ichild, statBag, rootItem); } else if (childItem) { GetHeapMemory(ichild, statBag, childItem); } if (childItem) { GStatInfo statInfo; GStat::StatValue statValue; if (statBag->GetStat(&statInfo, ichild.GetId())) { statInfo.GetStat(&statValue, 0); if (statValue.IValue > 0) { childItem->AddChild(NextHandle++, statInfo.GetName(), static_cast(statValue.IValue)); } } } ++ichild; } if (childItem && childItem->Children.GetSize() > 0) { rootItem->Children.PushBack(childItem); } } } // Invokes recursively memory statistic data belonging to the given heap. UInt32 StatsUpdate::GetStatMemory(GStatDesc::Iterator it, GStatBag* statBag, GFxAmpMemItem* rootItem) { UInt32 totalMemory = 0; const GStatDesc* pdesc = *it; if (pdesc) { GStatDesc::Iterator ichild = pdesc->GetChildIterator(); while (!ichild.IsEnd()) { GStatInfo statInfo; GStat::StatValue statValue; GPtr childItem = *GHEAP_AUTO_NEW(rootItem) GFxAmpMemItem(NextHandle++); childItem->Name = ichild->GetName(); UInt32 childMemory = GetStatMemory(ichild, statBag, childItem); if (statBag->GetStat(&statInfo, ichild.GetId())) { statInfo.GetStat(&statValue, 0); childMemory += static_cast(statValue.IValue); } childItem->SetValue(childMemory); if (childMemory > 0) { rootItem->Children.PushBack(childItem); } totalMemory += childMemory; ++ichild; } } return totalMemory; } void StatsUpdate::MemReportFile(GFxAmpMemItem* rootItem, GMemoryHeap::MemReportType reportType) { FileVisitor visitor; GMemory::pGlobalHeap->VisitChildHeaps(&visitor); GStringHash& statsMap = visitor.FileStatsMap; GStringHash::Iterator itFile; for (itFile = statsMap.Begin(); itFile != statsMap.End(); ++itFile) { GString buffer; G_Format(buffer, "Movie File {0}", itFile->First); GFxAmpMemItem* childItem = rootItem->AddChild(NextHandle++, buffer); GetFileMemory(GStatDesc::GetGroupIterator(GStat_Mem), itFile->Second, childItem, reportType); } } void StatsUpdate::MemReportHeapsDetailed(GFxAmpMemItem* rootItem, GMemoryHeap* heap) { HeapTypeVisitor heapVisit(heap, rootItem, this); heap->VisitChildHeaps(&heapVisit); } // Recursively adds the memory per StatId for the given file UPInt StatsUpdate::GetFileMemory(GStatDesc::Iterator it, FileStats& fileStats, GFxAmpMemItem* rootItem, GMemoryHeap::MemReportType reportType) { GStatInfo statInfo; GStat::StatValue statValue; UPInt total = 0; if (fileStats.StatBag.GetStat(&statInfo, it.GetId())) { statInfo.GetStat(&statValue, 0); fileStats.TotalMemory += statValue.IValue; total += statValue.IValue; } GPtr childItem = *GHEAP_AUTO_NEW(rootItem) GFxAmpMemItem(NextHandle++); for (GStatDesc::Iterator itChild = it->GetChildIterator(); !itChild.IsEnd(); ++itChild) { UPInt childTotal = GetFileMemory(itChild, fileStats, childItem, reportType); if (childTotal > 0) { total += childTotal; } } if (total > 0) { childItem->Name = it->GetName(); childItem->SetValue(static_cast(total)); rootItem->Children.PushBack(childItem); } return total; } //------------------------------------------------------------------------ // Collects memory statistic data from heaps and puts obtained data in string // buffer. void GMemoryHeap::MemReport(GStringBuffer& buffer, MemReportType reportType) { GPtr rootItem = *GNEW GFxAmpMemItem(0); StatsUpdate().MemReport(rootItem, reportType); rootItem->ToString(&buffer); } void GMemoryHeap::MemReport(GFxAmpMemItem* rootItem, MemReportType reportType) { StatsUpdate().MemReport(rootItem, reportType); } #else // GFC_NO_STAT void GMemoryHeap::MemReport(GStringBuffer& buffer, MemReportType reportType) { GUNUSED(buffer); GUNUSED(reportType); } void GMemoryHeap::MemReport(GFxAmpMemItem* rootItem, MemReportType reportType) { GUNUSED(rootItem); GUNUSED(reportType); } #endif // GFC_NO_STAT