Index: lib/scudo/standalone/combined.h =================================================================== --- lib/scudo/standalone/combined.h +++ lib/scudo/standalone/combined.h @@ -369,12 +369,31 @@ Primary.enable(); } + // The function returns the amount of bytes required to store the statistics, + // which might be larger than the amount of bytes provided. Note that the + // statistics buffer is not necessarily constant between calls to this + // function. This can be called with a null buffer or zero size for buffer + // sizing purposes. + uptr getStats(char *Buffer, uptr Size) { + ScopedString Str(1024); + disable(); + const uptr Length = getStats(&Str) + 1; + enable(); + if (Length < Size) + Size = Length; + if (Buffer && Size) { + memcpy(Buffer, Str.data(), Size); + Buffer[Size - 1] = '\0'; + } + return Length; + } + void printStats() { + ScopedString Str(1024); disable(); - Primary.printStats(); - Secondary.printStats(); - Quarantine.printStats(); + getStats(&Str); enable(); + Str.output(); } void releaseToOS() { Primary.releaseToOS(); } @@ -563,6 +582,13 @@ *Size = getSize(Ptr, &Header); return P; } + + uptr getStats(ScopedString *Str) { + Primary.getStats(Str); + Secondary.getStats(Str); + Quarantine.getStats(Str); + return Str->length(); + } }; } // namespace scudo Index: lib/scudo/standalone/crc32_hw.cpp =================================================================== --- lib/scudo/standalone/crc32_hw.cpp +++ lib/scudo/standalone/crc32_hw.cpp @@ -1,4 +1,4 @@ -//===-- crc32_hw.h ----------------------------------------------*- C++ -*-===// +//===-- crc32_hw.cpp --------------------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. Index: lib/scudo/standalone/primary32.h =================================================================== --- lib/scudo/standalone/primary32.h +++ lib/scudo/standalone/primary32.h @@ -143,7 +143,7 @@ } } - void printStats() { + void getStats(ScopedString *Str) { // TODO(kostyak): get the RSS per region. uptr TotalMapped = 0; uptr PoppedBlocks = 0; @@ -154,11 +154,11 @@ PoppedBlocks += Sci->Stats.PoppedBlocks; PushedBlocks += Sci->Stats.PushedBlocks; } - Printf("Stats: SizeClassAllocator32: %zuM mapped in %zu allocations; " - "remains %zu\n", - TotalMapped >> 20, PoppedBlocks, PoppedBlocks - PushedBlocks); + Str->append("Stats: SizeClassAllocator32: %zuM mapped in %zu allocations; " + "remains %zu\n", + TotalMapped >> 20, PoppedBlocks, PoppedBlocks - PushedBlocks); for (uptr I = 0; I < NumClasses; I++) - printStats(I, 0); + getStats(Str, I, 0); } uptr releaseToOS() { @@ -328,17 +328,18 @@ return B; } - void printStats(uptr ClassId, uptr Rss) { + void getStats(ScopedString *Str, uptr ClassId, uptr Rss) { SizeClassInfo *Sci = getSizeClassInfo(ClassId); if (Sci->AllocatedUser == 0) return; const uptr InUse = Sci->Stats.PoppedBlocks - Sci->Stats.PushedBlocks; const uptr AvailableChunks = Sci->AllocatedUser / getSizeByClassId(ClassId); - Printf(" %02zu (%6zu): mapped: %6zuK popped: %7zu pushed: %7zu inuse: %6zu" - " avail: %6zu rss: %6zuK\n", - ClassId, getSizeByClassId(ClassId), Sci->AllocatedUser >> 10, - Sci->Stats.PoppedBlocks, Sci->Stats.PushedBlocks, InUse, - AvailableChunks, Rss >> 10); + Str->append( + " %02zu (%6zu): mapped: %6zuK popped: %7zu pushed: %7zu inuse: %6zu" + " avail: %6zu rss: %6zuK\n", + ClassId, getSizeByClassId(ClassId), Sci->AllocatedUser >> 10, + Sci->Stats.PoppedBlocks, Sci->Stats.PushedBlocks, InUse, + AvailableChunks, Rss >> 10); } NOINLINE uptr releaseToOSMaybe(SizeClassInfo *Sci, uptr ClassId, Index: lib/scudo/standalone/primary64.h =================================================================== --- lib/scudo/standalone/primary64.h +++ lib/scudo/standalone/primary64.h @@ -147,7 +147,7 @@ } } - void printStats() const { + void getStats(ScopedString *Str) const { // TODO(kostyak): get the RSS per region. uptr TotalMapped = 0; uptr PoppedBlocks = 0; @@ -159,12 +159,14 @@ PoppedBlocks += Region->Stats.PoppedBlocks; PushedBlocks += Region->Stats.PushedBlocks; } - Printf("Stats: Primary64: %zuM mapped (%zuM rss) in %zu allocations; " - "remains %zu\n", - TotalMapped >> 20, 0, PoppedBlocks, PoppedBlocks - PushedBlocks); + Str->append("Stats: SizeClassAllocator64: %zuM mapped (%zuM rss) in %zu " + "allocations; " + "remains %zu\n", + TotalMapped >> 20, 0, PoppedBlocks, + PoppedBlocks - PushedBlocks); for (uptr I = 0; I < NumClasses; I++) - printStats(I, 0); + getStats(Str, I, 0); } uptr releaseToOS() { @@ -269,10 +271,12 @@ if (UNLIKELY(RegionBase + MappedUser + UserMapSize > RegionSize)) { if (!Region->Exhausted) { Region->Exhausted = true; - printStats(); - Printf( + ScopedString Str(1024); + getStats(&Str); + Str.append( "Scudo OOM: The process has Exhausted %zuM for size class %zu.\n", RegionSize >> 20, Size); + Str.output(); } return nullptr; } @@ -322,21 +326,22 @@ return B; } - void printStats(uptr ClassId, uptr Rss) const { + void getStats(ScopedString *Str, uptr ClassId, uptr Rss) const { RegionInfo *Region = getRegionInfo(ClassId); if (Region->MappedUser == 0) return; const uptr InUse = Region->Stats.PoppedBlocks - Region->Stats.PushedBlocks; const uptr TotalChunks = Region->AllocatedUser / getSizeByClassId(ClassId); - Printf("%s %02zu (%6zu): mapped: %6zuK popped: %7zu pushed: %7zu inuse: " - "%6zu total: %6zu rss: %6zuK releases: %6zu last released: %6zuK " - "region: 0x%zx (0x%zx)\n", - Region->Exhausted ? "F" : " ", ClassId, getSizeByClassId(ClassId), - Region->MappedUser >> 10, Region->Stats.PoppedBlocks, - Region->Stats.PushedBlocks, InUse, TotalChunks, Rss >> 10, - Region->ReleaseInfo.RangesReleased, - Region->ReleaseInfo.LastReleasedBytes >> 10, Region->RegionBeg, - getRegionBaseByClassId(ClassId)); + Str->append( + "%s %02zu (%6zu): mapped: %6zuK popped: %7zu pushed: %7zu inuse: " + "%6zu total: %6zu rss: %6zuK releases: %6zu last released: %6zuK " + "region: 0x%zx (0x%zx)\n", + Region->Exhausted ? "F" : " ", ClassId, getSizeByClassId(ClassId), + Region->MappedUser >> 10, Region->Stats.PoppedBlocks, + Region->Stats.PushedBlocks, InUse, TotalChunks, Rss >> 10, + Region->ReleaseInfo.RangesReleased, + Region->ReleaseInfo.LastReleasedBytes >> 10, Region->RegionBeg, + getRegionBaseByClassId(ClassId)); } NOINLINE uptr releaseToOSMaybe(RegionInfo *Region, uptr ClassId, Index: lib/scudo/standalone/quarantine.h =================================================================== --- lib/scudo/standalone/quarantine.h +++ lib/scudo/standalone/quarantine.h @@ -130,7 +130,7 @@ subFromSize(ExtractedSize); } - void printStats() const { + void getStats(ScopedString *Str) const { uptr BatchCount = 0; uptr TotalOverheadBytes = 0; uptr TotalBytes = 0; @@ -152,11 +152,12 @@ (TotalQuarantinedBytes == 0) ? 0 : TotalOverheadBytes * 100 / TotalQuarantinedBytes; - Printf("Global quarantine stats: batches: %zu; bytes: %zu (user: %zu); " - "chunks: %zu (capacity: %zu); %zu%% chunks used; %zu%% memory " - "overhead\n", - BatchCount, TotalBytes, TotalQuarantinedBytes, TotalQuarantineChunks, - QuarantineChunksCapacity, ChunksUsagePercent, MemoryOverheadPercent); + Str->append("Stats: Quarantine: batches: %zu; bytes: %zu (user: %zu); " + "chunks: %zu (capacity: %zu); %zu%% chunks used; %zu%% memory " + "overhead\n", + BatchCount, TotalBytes, TotalQuarantinedBytes, + TotalQuarantineChunks, QuarantineChunksCapacity, + ChunksUsagePercent, MemoryOverheadPercent); } private: @@ -218,11 +219,11 @@ recycle(0, Cb); } - void printStats() const { + void getStats(ScopedString *Str) const { // It assumes that the world is stopped, just as the allocator's printStats. - Printf("Quarantine limits: global: %zuM; thread local: %zuK\n", - getMaxSize() >> 20, getCacheSize() >> 10); - Cache.printStats(); + Cache.getStats(Str); + Str->append("Quarantine limits: global: %zuK; thread local: %zuK\n", + getMaxSize() >> 10, getCacheSize() >> 10); } private: Index: lib/scudo/standalone/secondary.h =================================================================== --- lib/scudo/standalone/secondary.h +++ lib/scudo/standalone/secondary.h @@ -12,6 +12,7 @@ #include "common.h" #include "mutex.h" #include "stats.h" +#include "string_utils.h" namespace scudo { @@ -70,7 +71,7 @@ return getBlockEnd(Ptr) - reinterpret_cast(Ptr); } - void printStats() const; + void getStats(ScopedString *Str) const; void disable() { Mutex.lock(); } Index: lib/scudo/standalone/secondary.cpp =================================================================== --- lib/scudo/standalone/secondary.cpp +++ lib/scudo/standalone/secondary.cpp @@ -123,12 +123,13 @@ unmap(Addr, Size, UNMAP_ALL, &Data); } -void MapAllocator::printStats() const { - Printf("Stats: MapAllocator: allocated %zu times (%zuK), freed %zu times " - "(%zuK), remains %zu (%zuK) max %zuM\n", - NumberOfAllocs, AllocatedBytes >> 10, NumberOfFrees, FreedBytes >> 10, - NumberOfAllocs - NumberOfFrees, (AllocatedBytes - FreedBytes) >> 10, - LargestSize >> 20); +void MapAllocator::getStats(ScopedString *Str) const { + Str->append( + "Stats: MapAllocator: allocated %zu times (%zuK), freed %zu times " + "(%zuK), remains %zu (%zuK) max %zuM\n", + NumberOfAllocs, AllocatedBytes >> 10, NumberOfFrees, FreedBytes >> 10, + NumberOfAllocs - NumberOfFrees, (AllocatedBytes - FreedBytes) >> 10, + LargestSize >> 20); } } // namespace scudo Index: lib/scudo/standalone/size_class_map.h =================================================================== --- lib/scudo/standalone/size_class_map.h +++ lib/scudo/standalone/size_class_map.h @@ -86,6 +86,7 @@ } static void print() { + ScopedString Buffer(1024); uptr PrevS = 0; uptr TotalCached = 0; for (uptr I = 0; I < NumClasses; I++) { @@ -93,19 +94,20 @@ continue; const uptr S = getSizeByClassId(I); if (S >= MidSize / 2 && (S & (S - 1)) == 0) - Printf("\n"); + Buffer.append("\n"); const uptr D = S - PrevS; const uptr P = PrevS ? (D * 100 / PrevS) : 0; const uptr L = S ? getMostSignificantSetBitIndex(S) : 0; const uptr Cached = getMaxCachedHint(S) * S; - Printf( + Buffer.append( "C%02zu => S: %zu diff: +%zu %02zu%% L %zu Cached: %zu %zu; id %zu\n", I, getSizeByClassId(I), D, P, L, getMaxCachedHint(S), Cached, getClassIdBySize(S)); TotalCached += Cached; PrevS = S; } - Printf("Total Cached: %zu\n", TotalCached); + Buffer.append("Total Cached: %zu\n", TotalCached); + Buffer.output(); } static void validate() { Index: lib/scudo/standalone/string_utils.h =================================================================== --- lib/scudo/standalone/string_utils.h +++ lib/scudo/standalone/string_utils.h @@ -29,6 +29,7 @@ } void append(const char *Format, va_list Args); void append(const char *Format, ...); + void output() const { outputRaw(String.data()); } private: Vector String; Index: lib/scudo/standalone/string_utils.cpp =================================================================== --- lib/scudo/standalone/string_utils.cpp +++ lib/scudo/standalone/string_utils.cpp @@ -208,9 +208,18 @@ } void ScopedString::append(const char *Format, va_list Args) { - CHECK_LT(Length, String.size()); - formatString(String.data() + Length, String.size() - Length, Format, Args); - Length += strlen(String.data() + Length); + DCHECK_LT(Length, String.size()); + va_list ArgsCopy; + va_copy(ArgsCopy, Args); + // formatString doesn't currently support a null buffer or zero buffer length, + // so in order to get the resulting formatted string length, we use a one-char + // buffer. + char C[1]; + const uptr AdditionalLength = + static_cast(formatString(C, sizeof(C), Format, Args)) + 1; + String.resize(Length + AdditionalLength); + formatString(String.data() + Length, AdditionalLength, Format, ArgsCopy); + Length = strlen(String.data()); CHECK_LT(Length, String.size()); } @@ -226,7 +235,7 @@ void Printf(const char *Format, ...) { va_list Args; va_start(Args, Format); - ScopedString Msg(512); + ScopedString Msg(1024); Msg.append(Format, Args); outputRaw(Msg.data()); va_end(Args); Index: lib/scudo/standalone/tests/combined_test.cpp =================================================================== --- lib/scudo/standalone/tests/combined_test.cpp +++ lib/scudo/standalone/tests/combined_test.cpp @@ -136,7 +136,23 @@ } Allocator->releaseToOS(); - Allocator->printStats(); + + scudo::uptr BufferSize = 8192; + char *Buffer = new char[BufferSize]; + scudo::uptr ActualSize = Allocator->getStats(Buffer, BufferSize); + while (ActualSize > BufferSize) { + BufferSize = ActualSize + 1024; + delete[] Buffer; + Buffer = new char[BufferSize]; + ActualSize = Allocator->getStats(Buffer, BufferSize); + } + std::string Stats(Buffer); + // Basic checks on the contents of the statistics output, which also allows us + // to verify that we got it all. + EXPECT_NE(Stats.find("Stats: SizeClassAllocator"), std::string::npos); + EXPECT_NE(Stats.find("Stats: MapAllocator"), std::string::npos); + EXPECT_NE(Stats.find("Stats: Quarantine"), std::string::npos); + delete[] Buffer; } TEST(ScudoCombinedTest, BasicCombined) { Index: lib/scudo/standalone/tests/primary_test.cpp =================================================================== --- lib/scudo/standalone/tests/primary_test.cpp +++ lib/scudo/standalone/tests/primary_test.cpp @@ -46,7 +46,9 @@ } Cache.destroy(nullptr); Allocator->releaseToOS(); - Allocator->printStats(); + scudo::ScopedString Str(1024); + Allocator->getStats(&Str); + Str.output(); } TEST(ScudoPrimaryTest, BasicPrimary) { @@ -86,7 +88,9 @@ } Cache.destroy(nullptr); Allocator.releaseToOS(); - Allocator.printStats(); + scudo::ScopedString Str(1024); + Allocator.getStats(&Str); + Str.output(); EXPECT_EQ(AllocationFailed, true); Allocator.unmapTestOnly(); } @@ -125,7 +129,9 @@ } Cache.destroy(nullptr); Allocator->releaseToOS(); - Allocator->printStats(); + scudo::ScopedString Str(1024); + Allocator->getStats(&Str); + Str.output(); } TEST(ScudoPrimaryTest, PrimaryIterate) { @@ -180,7 +186,9 @@ for (auto &T : Threads) T.join(); Allocator->releaseToOS(); - Allocator->printStats(); + scudo::ScopedString Str(1024); + Allocator->getStats(&Str); + Str.output(); } TEST(ScudoPrimaryTest, PrimaryThreaded) { @@ -203,8 +211,7 @@ Cache.init(nullptr, Allocator.get()); const scudo::uptr Size = scudo::getPageSizeCached() * 2; EXPECT_TRUE(Primary::canAllocate(Size)); - const scudo::uptr ClassId = - Primary::SizeClassMap::getClassIdBySize(Size); + const scudo::uptr ClassId = Primary::SizeClassMap::getClassIdBySize(Size); void *P = Cache.allocate(ClassId); EXPECT_NE(P, nullptr); Cache.deallocate(ClassId, P); Index: lib/scudo/standalone/tests/quarantine_test.cpp =================================================================== --- lib/scudo/standalone/tests/quarantine_test.cpp +++ lib/scudo/standalone/tests/quarantine_test.cpp @@ -213,7 +213,9 @@ Quarantine.drainAndRecycle(&Cache, Cb); EXPECT_EQ(Cache.getSize(), 0UL); - Quarantine.printStats(); + scudo::ScopedString Str(1024); + Quarantine.getStats(&Str); + Str.output(); } void *populateQuarantine(void *Param) { @@ -236,5 +238,7 @@ for (scudo::uptr I = 0; I < NumberOfThreads; I++) pthread_join(T[I], 0); - Quarantine.printStats(); + scudo::ScopedString Str(1024); + Quarantine.getStats(&Str); + Str.output(); } Index: lib/scudo/standalone/tests/secondary_test.cpp =================================================================== --- lib/scudo/standalone/tests/secondary_test.cpp +++ lib/scudo/standalone/tests/secondary_test.cpp @@ -45,7 +45,9 @@ L->deallocate(V.back()); V.pop_back(); } - L->printStats(); + scudo::ScopedString Str(1024); + L->getStats(&Str); + Str.output(); } // This exercises a variety of combinations of size and alignment for the @@ -76,7 +78,9 @@ } } } - L->printStats(); + scudo::ScopedString Str(1024); + L->getStats(&Str); + Str.output(); } TEST(ScudoSecondaryTest, SecondaryIterate) { @@ -97,7 +101,9 @@ L->deallocate(V.back()); V.pop_back(); } - L->printStats(); + scudo::ScopedString Str(1024); + L->getStats(&Str); + Str.output(); } static std::mutex Mutex; @@ -133,5 +139,7 @@ } for (auto &T : Threads) T.join(); - L->printStats(); + scudo::ScopedString Str(1024); + L->getStats(&Str); + Str.output(); }