[scudo][standalone] Adding a stats class

Summary:
This adds simple local & global stats classes to be used by the Primary
and Secondary, and associated test. Note that we don't need the strict
atomicity of the addition & subtraction (as is in sanitizer_common) so
we just use load & store.

Reviewers: morehouse, vitalybuka, eugenis, flowerhack, dmmoore415

Reviewed By: morehouse, vitalybuka

Subscribers: mgorny, delcypher, jfb, #sanitizers, llvm-commits

Tags: #llvm, #sanitizers

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

llvm-svn: 355643
This commit is contained in:
Kostya Kortchinsky 2019-03-07 21:44:35 +00:00
parent ed77926f99
commit 16d9a3a4b6
4 changed files with 152 additions and 0 deletions

View File

@ -46,6 +46,7 @@ set(SCUDO_HEADERS
list.h
mutex.h
platform.h
stats.h
vector.h)
if(COMPILER_RT_HAS_SCUDO_STANDALONE)

View File

@ -0,0 +1,105 @@
//===-- stats.h -------------------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef SCUDO_STATS_H_
#define SCUDO_STATS_H_
#include "atomic_helpers.h"
#include "mutex.h"
#include <string.h>
namespace scudo {
// Memory allocator statistics
enum StatType { StatAllocated, StatMapped, StatCount };
typedef uptr StatCounters[StatCount];
// Per-thread stats, live in per-thread cache. We use atomics so that the
// numbers themselves are consistent. But we don't use atomic_{add|sub} or a
// lock, because those are expensive operations , and we only care for the stats
// to be "somewhat" correct: eg. if we call GlobalStats::get while a thread is
// LocalStats::add'ing, this is OK, we will still get a meaningful number.
class LocalStats {
public:
void initLinkerInitialized() {}
void init() { memset(this, 0, sizeof(*this)); }
void add(StatType I, uptr V) {
V += atomic_load_relaxed(&StatsArray[I]);
atomic_store_relaxed(&StatsArray[I], V);
}
void sub(StatType I, uptr V) {
V = atomic_load_relaxed(&StatsArray[I]) - V;
atomic_store_relaxed(&StatsArray[I], V);
}
void set(StatType I, uptr V) { atomic_store_relaxed(&StatsArray[I], V); }
uptr get(StatType I) const { return atomic_load_relaxed(&StatsArray[I]); }
private:
friend class GlobalStats;
atomic_uptr StatsArray[StatCount];
LocalStats *Next;
LocalStats *Prev;
};
// Global stats, used for aggregation and querying.
class GlobalStats : public LocalStats {
public:
void initLinkerInitialized() {
Next = this;
Prev = this;
}
void init() {
memset(this, 0, sizeof(*this));
initLinkerInitialized();
}
void link(LocalStats *S) {
SpinMutexLock L(&Mutex);
S->Next = Next;
S->Prev = this;
Next->Prev = S;
Next = S;
}
void unlink(LocalStats *S) {
SpinMutexLock L(&Mutex);
S->Prev->Next = S->Next;
S->Next->Prev = S->Prev;
for (uptr I = 0; I < StatCount; I++)
add(static_cast<StatType>(I), S->get(static_cast<StatType>(I)));
}
void get(uptr *S) const {
memset(S, 0, StatCount * sizeof(uptr));
SpinMutexLock L(&Mutex);
const LocalStats *Stats = this;
for (;;) {
for (uptr I = 0; I < StatCount; I++)
S[I] += Stats->get(static_cast<StatType>(I));
Stats = Stats->Next;
if (Stats == this)
break;
}
// All stats must be non-negative.
for (uptr I = 0; I < StatCount; I++)
S[I] = static_cast<sptr>(S[I]) >= 0 ? S[I] : 0;
}
private:
mutable StaticSpinMutex Mutex;
};
} // namespace scudo
#endif // SCUDO_STATS_H_

View File

@ -54,6 +54,7 @@ set(SCUDO_UNIT_TEST_SOURCES
list_test.cc
map_test.cc
mutex_test.cc
stats_test.cc
vector_test.cc
scudo_unit_test_main.cc)

View File

@ -0,0 +1,45 @@
//===-- stats_test.cc -------------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "scudo/standalone/stats.h"
#include "gtest/gtest.h"
TEST(ScudoStatsTest, LocalStats) {
scudo::LocalStats LStats;
LStats.init();
for (scudo::uptr I = 0; I < scudo::StatCount; I++)
EXPECT_EQ(LStats.get(static_cast<scudo::StatType>(I)), 0U);
LStats.add(scudo::StatAllocated, 4096U);
EXPECT_EQ(LStats.get(scudo::StatAllocated), 4096U);
LStats.sub(scudo::StatAllocated, 4096U);
EXPECT_EQ(LStats.get(scudo::StatAllocated), 0U);
LStats.set(scudo::StatAllocated, 4096U);
EXPECT_EQ(LStats.get(scudo::StatAllocated), 4096U);
}
TEST(ScudoStatsTest, GlobalStats) {
scudo::GlobalStats GStats;
GStats.init();
scudo::uptr Counters[scudo::StatCount] = {};
GStats.get(Counters);
for (scudo::uptr I = 0; I < scudo::StatCount; I++)
EXPECT_EQ(Counters[I], 0U);
scudo::LocalStats LStats;
LStats.init();
GStats.link(&LStats);
for (scudo::uptr I = 0; I < scudo::StatCount; I++)
LStats.add(static_cast<scudo::StatType>(I), 4096U);
GStats.get(Counters);
for (scudo::uptr I = 0; I < scudo::StatCount; I++)
EXPECT_EQ(Counters[I], 4096U);
// Unlinking the local stats move numbers to the global stats.
GStats.unlink(&LStats);
GStats.get(Counters);
for (scudo::uptr I = 0; I < scudo::StatCount; I++)
EXPECT_EQ(Counters[I], 4096U);
}