[scudo] Move noinline functions definitions out of line

Summary:
Mark `isRssLimitExceeded` as `NOINLINE`, and move it's definition as well as
the one of `performSanityChecks` out of the class definition, as requested.

Reviewers: filcab, alekseyshl

Reviewed By: alekseyshl

Subscribers: delcypher, #sanitizers, llvm-commits

Differential Revision: https://reviews.llvm.org/D48228

llvm-svn: 335054
This commit is contained in:
Kostya Kortchinsky 2018-06-19 15:36:30 +00:00
parent 9e80c340f7
commit 63c33b1c9b
1 changed files with 67 additions and 63 deletions

View File

@ -243,38 +243,7 @@ struct ScudoAllocator {
explicit ScudoAllocator(LinkerInitialized) explicit ScudoAllocator(LinkerInitialized)
: AllocatorQuarantine(LINKER_INITIALIZED) {} : AllocatorQuarantine(LINKER_INITIALIZED) {}
NOINLINE void performSanityChecks() { NOINLINE void performSanityChecks();
// Verify that the header offset field can hold the maximum offset. In the
// case of the Secondary allocator, it takes care of alignment and the
// offset will always be 0. In the case of the Primary, the worst case
// scenario happens in the last size class, when the backend allocation
// would already be aligned on the requested alignment, which would happen
// to be the maximum alignment that would fit in that size class. As a
// result, the maximum offset will be at most the maximum alignment for the
// last size class minus the header size, in multiples of MinAlignment.
UnpackedHeader Header = {};
const uptr MaxPrimaryAlignment =
1 << MostSignificantSetBitIndex(SizeClassMap::kMaxSize - MinAlignment);
const uptr MaxOffset =
(MaxPrimaryAlignment - Chunk::getHeaderSize()) >> MinAlignmentLog;
Header.Offset = MaxOffset;
if (Header.Offset != MaxOffset)
dieWithMessage("maximum possible offset doesn't fit in header\n");
// Verify that we can fit the maximum size or amount of unused bytes in the
// header. Given that the Secondary fits the allocation to a page, the worst
// case scenario happens in the Primary. It will depend on the second to
// last and last class sizes, as well as the dynamic base for the Primary.
// The following is an over-approximation that works for our needs.
const uptr MaxSizeOrUnusedBytes = SizeClassMap::kMaxSize - 1;
Header.SizeOrUnusedBytes = MaxSizeOrUnusedBytes;
if (Header.SizeOrUnusedBytes != MaxSizeOrUnusedBytes)
dieWithMessage("maximum possible unused bytes doesn't fit in header\n");
const uptr LargestClassId = SizeClassMap::kLargestClassID;
Header.ClassId = LargestClassId;
if (Header.ClassId != LargestClassId)
dieWithMessage("largest class ID doesn't fit in header\n");
}
void init() { void init() {
SanitizerToolName = "Scudo"; SanitizerToolName = "Scudo";
@ -323,37 +292,7 @@ struct ScudoAllocator {
return Chunk::isValid(Ptr); return Chunk::isValid(Ptr);
} }
// Opportunistic RSS limit check. This will update the RSS limit status, if NOINLINE bool isRssLimitExceeded();
// it can, every 100ms, otherwise it will just return the current one.
bool isRssLimitExceeded() {
u64 LastCheck = atomic_load_relaxed(&RssLastCheckedAtNS);
const u64 CurrentCheck = MonotonicNanoTime();
if (LIKELY(CurrentCheck < LastCheck + (100ULL * 1000000ULL)))
return atomic_load_relaxed(&RssLimitExceeded);
if (!atomic_compare_exchange_weak(&RssLastCheckedAtNS, &LastCheck,
CurrentCheck, memory_order_relaxed))
return atomic_load_relaxed(&RssLimitExceeded);
// TODO(kostyak): We currently use sanitizer_common's GetRSS which reads the
// RSS from /proc/self/statm by default. We might want to
// call getrusage directly, even if it's less accurate.
const uptr CurrentRssMb = GetRSS() >> 20;
if (HardRssLimitMb && UNLIKELY(HardRssLimitMb < CurrentRssMb))
dieWithMessage("hard RSS limit exhausted (%zdMb vs %zdMb)\n",
HardRssLimitMb, CurrentRssMb);
if (SoftRssLimitMb) {
if (atomic_load_relaxed(&RssLimitExceeded)) {
if (CurrentRssMb <= SoftRssLimitMb)
atomic_store_relaxed(&RssLimitExceeded, false);
} else {
if (CurrentRssMb > SoftRssLimitMb) {
atomic_store_relaxed(&RssLimitExceeded, true);
Printf("Scudo INFO: soft RSS limit exhausted (%zdMb vs %zdMb)\n",
SoftRssLimitMb, CurrentRssMb);
}
}
}
return atomic_load_relaxed(&RssLimitExceeded);
}
// Allocates a chunk. // Allocates a chunk.
void *allocate(uptr Size, uptr Alignment, AllocType Type, void *allocate(uptr Size, uptr Alignment, AllocType Type,
@ -622,6 +561,71 @@ struct ScudoAllocator {
} }
}; };
NOINLINE void ScudoAllocator::performSanityChecks() {
// Verify that the header offset field can hold the maximum offset. In the
// case of the Secondary allocator, it takes care of alignment and the
// offset will always be 0. In the case of the Primary, the worst case
// scenario happens in the last size class, when the backend allocation
// would already be aligned on the requested alignment, which would happen
// to be the maximum alignment that would fit in that size class. As a
// result, the maximum offset will be at most the maximum alignment for the
// last size class minus the header size, in multiples of MinAlignment.
UnpackedHeader Header = {};
const uptr MaxPrimaryAlignment =
1 << MostSignificantSetBitIndex(SizeClassMap::kMaxSize - MinAlignment);
const uptr MaxOffset =
(MaxPrimaryAlignment - Chunk::getHeaderSize()) >> MinAlignmentLog;
Header.Offset = MaxOffset;
if (Header.Offset != MaxOffset)
dieWithMessage("maximum possible offset doesn't fit in header\n");
// Verify that we can fit the maximum size or amount of unused bytes in the
// header. Given that the Secondary fits the allocation to a page, the worst
// case scenario happens in the Primary. It will depend on the second to
// last and last class sizes, as well as the dynamic base for the Primary.
// The following is an over-approximation that works for our needs.
const uptr MaxSizeOrUnusedBytes = SizeClassMap::kMaxSize - 1;
Header.SizeOrUnusedBytes = MaxSizeOrUnusedBytes;
if (Header.SizeOrUnusedBytes != MaxSizeOrUnusedBytes)
dieWithMessage("maximum possible unused bytes doesn't fit in header\n");
const uptr LargestClassId = SizeClassMap::kLargestClassID;
Header.ClassId = LargestClassId;
if (Header.ClassId != LargestClassId)
dieWithMessage("largest class ID doesn't fit in header\n");
}
// Opportunistic RSS limit check. This will update the RSS limit status, if
// it can, every 100ms, otherwise it will just return the current one.
NOINLINE bool ScudoAllocator::isRssLimitExceeded() {
u64 LastCheck = atomic_load_relaxed(&RssLastCheckedAtNS);
const u64 CurrentCheck = MonotonicNanoTime();
if (LIKELY(CurrentCheck < LastCheck + (100ULL * 1000000ULL)))
return atomic_load_relaxed(&RssLimitExceeded);
if (!atomic_compare_exchange_weak(&RssLastCheckedAtNS, &LastCheck,
CurrentCheck, memory_order_relaxed))
return atomic_load_relaxed(&RssLimitExceeded);
// TODO(kostyak): We currently use sanitizer_common's GetRSS which reads the
// RSS from /proc/self/statm by default. We might want to
// call getrusage directly, even if it's less accurate.
const uptr CurrentRssMb = GetRSS() >> 20;
if (HardRssLimitMb && UNLIKELY(HardRssLimitMb < CurrentRssMb))
dieWithMessage("hard RSS limit exhausted (%zdMb vs %zdMb)\n",
HardRssLimitMb, CurrentRssMb);
if (SoftRssLimitMb) {
if (atomic_load_relaxed(&RssLimitExceeded)) {
if (CurrentRssMb <= SoftRssLimitMb)
atomic_store_relaxed(&RssLimitExceeded, false);
} else {
if (CurrentRssMb > SoftRssLimitMb) {
atomic_store_relaxed(&RssLimitExceeded, true);
Printf("Scudo INFO: soft RSS limit exhausted (%zdMb vs %zdMb)\n",
SoftRssLimitMb, CurrentRssMb);
}
}
}
return atomic_load_relaxed(&RssLimitExceeded);
}
static ScudoAllocator Instance(LINKER_INITIALIZED); static ScudoAllocator Instance(LINKER_INITIALIZED);
static ScudoBackendAllocator &getBackendAllocator() { static ScudoBackendAllocator &getBackendAllocator() {