From e5d05814ac964a37eb336fb5a743227e55e85a41 Mon Sep 17 00:00:00 2001 From: Kostya Kortchinsky Date: Wed, 9 Oct 2019 15:09:28 +0000 Subject: [scudo][standalone] Get statistics in a char buffer Summary: Following up on D68471, this CL introduces some `getStats` APIs to gather statistics in char buffers (`ScopedString` really) instead of printing them out right away. Ultimately `printStats` will just output the buffer, but that allows us to potentially do some work on the intermediate buffer, and can be used for a `mallocz` type of functionality. This allows us to pretty much get rid of all the `Printf` calls around, but I am keeping the function in for debugging purposes. This changes the existing tests to use the new APIs when required. I will add new tests as suggested in D68471 in another CL. Reviewers: morehouse, hctim, vitalybuka, eugenis, cferris Reviewed By: morehouse Subscribers: delcypher, #sanitizers, llvm-commits Tags: #llvm, #sanitizers Differential Revision: https://reviews.llvm.org/D68653 git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@374173 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/scudo/standalone/combined.h | 32 +++++++++++++++++++--- lib/scudo/standalone/crc32_hw.cpp | 2 +- lib/scudo/standalone/primary32.h | 22 +++++++-------- lib/scudo/standalone/primary64.h | 37 ++++++++++++++------------ lib/scudo/standalone/quarantine.h | 20 +++++++------- lib/scudo/standalone/secondary.cpp | 13 ++++----- lib/scudo/standalone/secondary.h | 3 ++- lib/scudo/standalone/size_class_map.h | 8 +++--- lib/scudo/standalone/string_utils.cpp | 17 +++++++++--- lib/scudo/standalone/string_utils.h | 1 + lib/scudo/standalone/tests/combined_test.cpp | 16 ++++++++++- lib/scudo/standalone/tests/primary_test.cpp | 19 ++++++++----- lib/scudo/standalone/tests/quarantine_test.cpp | 8 ++++-- lib/scudo/standalone/tests/secondary_test.cpp | 16 ++++++++--- 14 files changed, 145 insertions(+), 69 deletions(-) diff --git a/lib/scudo/standalone/combined.h b/lib/scudo/standalone/combined.h index b2dc25f78..60be1dd20 100644 --- a/lib/scudo/standalone/combined.h +++ b/lib/scudo/standalone/combined.h @@ -369,12 +369,31 @@ public: 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 @@ private: *Size = getSize(Ptr, &Header); return P; } + + uptr getStats(ScopedString *Str) { + Primary.getStats(Str); + Secondary.getStats(Str); + Quarantine.getStats(Str); + return Str->length(); + } }; } // namespace scudo diff --git a/lib/scudo/standalone/crc32_hw.cpp b/lib/scudo/standalone/crc32_hw.cpp index f4dae7b5f..62841ba51 100644 --- a/lib/scudo/standalone/crc32_hw.cpp +++ b/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. diff --git a/lib/scudo/standalone/primary32.h b/lib/scudo/standalone/primary32.h index a9fbb70bc..9123d07b4 100644 --- a/lib/scudo/standalone/primary32.h +++ b/lib/scudo/standalone/primary32.h @@ -143,7 +143,7 @@ public: } } - void printStats() { + void getStats(ScopedString *Str) { // TODO(kostyak): get the RSS per region. uptr TotalMapped = 0; uptr PoppedBlocks = 0; @@ -154,11 +154,11 @@ public: 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,17 @@ private: 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, diff --git a/lib/scudo/standalone/primary64.h b/lib/scudo/standalone/primary64.h index f56387b05..8f443ea7f 100644 --- a/lib/scudo/standalone/primary64.h +++ b/lib/scudo/standalone/primary64.h @@ -147,7 +147,7 @@ public: } } - void printStats() const { + void getStats(ScopedString *Str) const { // TODO(kostyak): get the RSS per region. uptr TotalMapped = 0; uptr PoppedBlocks = 0; @@ -159,12 +159,13 @@ public: 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 +270,12 @@ private: 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 +325,21 @@ private: 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, diff --git a/lib/scudo/standalone/quarantine.h b/lib/scudo/standalone/quarantine.h index 732373aba..35fd0bc19 100644 --- a/lib/scudo/standalone/quarantine.h +++ b/lib/scudo/standalone/quarantine.h @@ -130,7 +130,7 @@ public: subFromSize(ExtractedSize); } - void printStats() const { + void getStats(ScopedString *Str) const { uptr BatchCount = 0; uptr TotalOverheadBytes = 0; uptr TotalBytes = 0; @@ -152,11 +152,11 @@ public: (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 +218,11 @@ public: 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: diff --git a/lib/scudo/standalone/secondary.cpp b/lib/scudo/standalone/secondary.cpp index d725034ee..db7361d71 100644 --- a/lib/scudo/standalone/secondary.cpp +++ b/lib/scudo/standalone/secondary.cpp @@ -123,12 +123,13 @@ void MapAllocator::deallocate(void *Ptr) { 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 diff --git a/lib/scudo/standalone/secondary.h b/lib/scudo/standalone/secondary.h index 80cae9fd2..9d074a57c 100644 --- a/lib/scudo/standalone/secondary.h +++ b/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 @@ public: return getBlockEnd(Ptr) - reinterpret_cast(Ptr); } - void printStats() const; + void getStats(ScopedString *Str) const; void disable() { Mutex.lock(); } diff --git a/lib/scudo/standalone/size_class_map.h b/lib/scudo/standalone/size_class_map.h index 3b494afb3..dfef0865b 100644 --- a/lib/scudo/standalone/size_class_map.h +++ b/lib/scudo/standalone/size_class_map.h @@ -86,6 +86,7 @@ public: } static void print() { + ScopedString Buffer(1024); uptr PrevS = 0; uptr TotalCached = 0; for (uptr I = 0; I < NumClasses; I++) { @@ -93,19 +94,20 @@ public: 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() { diff --git a/lib/scudo/standalone/string_utils.cpp b/lib/scudo/standalone/string_utils.cpp index 22b4fbd74..5de8b57bf 100644 --- a/lib/scudo/standalone/string_utils.cpp +++ b/lib/scudo/standalone/string_utils.cpp @@ -208,9 +208,18 @@ int formatString(char *Buffer, uptr BufferLength, const char *Format, } 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 @@ FORMAT(1, 2) 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); diff --git a/lib/scudo/standalone/string_utils.h b/lib/scudo/standalone/string_utils.h index aea7b3ffd..acd60bda9 100644 --- a/lib/scudo/standalone/string_utils.h +++ b/lib/scudo/standalone/string_utils.h @@ -29,6 +29,7 @@ public: } void append(const char *Format, va_list Args); void append(const char *Format, ...); + void output() const { outputRaw(String.data()); } private: Vector String; diff --git a/lib/scudo/standalone/tests/combined_test.cpp b/lib/scudo/standalone/tests/combined_test.cpp index 3f971a304..d74c07e84 100644 --- a/lib/scudo/standalone/tests/combined_test.cpp +++ b/lib/scudo/standalone/tests/combined_test.cpp @@ -136,7 +136,21 @@ template static void testAllocator() { } Allocator->releaseToOS(); - Allocator->printStats(); + + scudo::uptr BufferSize = 8192; + std::vector Buffer(BufferSize); + scudo::uptr ActualSize = Allocator->getStats(Buffer.data(), BufferSize); + while (ActualSize > BufferSize) { + BufferSize = ActualSize + 1024; + Buffer.resize(BufferSize); + ActualSize = Allocator->getStats(Buffer.data(), BufferSize); + } + std::string Stats(Buffer.begin(), Buffer.end()); + // 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); } TEST(ScudoCombinedTest, BasicCombined) { diff --git a/lib/scudo/standalone/tests/primary_test.cpp b/lib/scudo/standalone/tests/primary_test.cpp index a6cfc6bdb..7da7b25ca 100644 --- a/lib/scudo/standalone/tests/primary_test.cpp +++ b/lib/scudo/standalone/tests/primary_test.cpp @@ -46,7 +46,9 @@ template static void testPrimary() { } Cache.destroy(nullptr); Allocator->releaseToOS(); - Allocator->printStats(); + scudo::ScopedString Str(1024); + Allocator->getStats(&Str); + Str.output(); } TEST(ScudoPrimaryTest, BasicPrimary) { @@ -86,7 +88,9 @@ TEST(ScudoPrimaryTest, Primary64OOM) { } 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 @@ template static void testIteratePrimary() { } Cache.destroy(nullptr); Allocator->releaseToOS(); - Allocator->printStats(); + scudo::ScopedString Str(1024); + Allocator->getStats(&Str); + Str.output(); } TEST(ScudoPrimaryTest, PrimaryIterate) { @@ -180,7 +186,9 @@ template static void testPrimaryThreaded() { 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 @@ template static void testReleaseToOS() { 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); diff --git a/lib/scudo/standalone/tests/quarantine_test.cpp b/lib/scudo/standalone/tests/quarantine_test.cpp index d7453aa42..28baf8feb 100644 --- a/lib/scudo/standalone/tests/quarantine_test.cpp +++ b/lib/scudo/standalone/tests/quarantine_test.cpp @@ -213,7 +213,9 @@ TEST(ScudoQuarantineTest, GlobalQuarantine) { 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 @@ TEST(ScudoQuarantineTest, ThreadedGlobalQuarantine) { for (scudo::uptr I = 0; I < NumberOfThreads; I++) pthread_join(T[I], 0); - Quarantine.printStats(); + scudo::ScopedString Str(1024); + Quarantine.getStats(&Str); + Str.output(); } diff --git a/lib/scudo/standalone/tests/secondary_test.cpp b/lib/scudo/standalone/tests/secondary_test.cpp index 84c375449..b602b8d63 100644 --- a/lib/scudo/standalone/tests/secondary_test.cpp +++ b/lib/scudo/standalone/tests/secondary_test.cpp @@ -45,7 +45,9 @@ TEST(ScudoSecondaryTest, SecondaryBasic) { 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 @@ TEST(ScudoSecondaryTest, SecondaryCombinations) { } } } - L->printStats(); + scudo::ScopedString Str(1024); + L->getStats(&Str); + Str.output(); } TEST(ScudoSecondaryTest, SecondaryIterate) { @@ -97,7 +101,9 @@ TEST(ScudoSecondaryTest, SecondaryIterate) { 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 @@ TEST(ScudoSecondaryTest, SecondaryThreadsRace) { } for (auto &T : Threads) T.join(); - L->printStats(); + scudo::ScopedString Str(1024); + L->getStats(&Str); + Str.output(); } -- cgit v1.2.1