Skip to content

Commit

Permalink
Introduce stats and stats_client libraries.
Browse files Browse the repository at this point in the history
This is part of a new statistics gathering feature for the sanitizers.
See clang/docs/SanitizerStats.rst for further info and docs.

Differential Revision: http://reviews.llvm.org/D16176

llvm-svn: 257972
  • Loading branch information
pcc committed Jan 16, 2016
1 parent dc13453 commit 5788e12
Show file tree
Hide file tree
Showing 13 changed files with 466 additions and 14 deletions.
1 change: 1 addition & 0 deletions compiler-rt/lib/CMakeLists.txt
Expand Up @@ -15,6 +15,7 @@ if(COMPILER_RT_BUILD_SANITIZERS)

if(COMPILER_RT_HAS_SANITIZER_COMMON)
add_subdirectory(sanitizer_common)
add_subdirectory(stats)
add_subdirectory(lsan)
add_subdirectory(ubsan)
endif()
Expand Down
13 changes: 13 additions & 0 deletions compiler-rt/lib/sanitizer_common/sanitizer_common.h
Expand Up @@ -522,6 +522,19 @@ class InternalMmapVectorNoCtor {
void clear() { size_ = 0; }
bool empty() const { return size() == 0; }

const T *begin() const {
return data();
}
T *begin() {
return data();
}
const T *end() const {
return data() + size();
}
T *end() {
return data() + size();
}

private:
void Resize(uptr new_capacity) {
CHECK_GT(new_capacity, 0);
Expand Down
45 changes: 36 additions & 9 deletions compiler-rt/lib/sanitizer_common/sanitizer_flags.cc
Expand Up @@ -45,17 +45,44 @@ void CommonFlags::CopyFrom(const CommonFlags &other) {
internal_memcpy(this, &other, sizeof(*this));
}

// Copy the string from "s" to "out", replacing "%b" with the binary basename.
static void SubstituteBinaryName(const char *s, char *out, uptr out_size) {
// Copy the string from "s" to "out", making the following substitutions:
// %b = binary basename
// %p = pid
void SubstituteForFlagValue(const char *s, char *out, uptr out_size) {
char *out_end = out + out_size;
while (*s && out < out_end - 1) {
if (s[0] != '%' || s[1] != 'b') { *out++ = *s++; continue; }
const char *base = GetProcessName();
CHECK(base);
while (*base && out < out_end - 1)
*out++ = *base++;
s += 2; // skip "%b"
if (s[0] != '%') {
*out++ = *s++;
continue;
}
switch (s[1]) {
case 'b': {
const char *base = GetProcessName();
CHECK(base);
while (*base && out < out_end - 1)
*out++ = *base++;
s += 2; // skip "%b"
break;
}
case 'p': {
int pid = internal_getpid();
char buf[32];
char *buf_pos = buf + 32;
do {
*--buf_pos = (pid % 10) + '0';
pid /= 10;
} while (pid);
while (buf_pos < buf + 32 && out < out_end - 1)
*out++ = *buf_pos++;
s += 2; // skip "%p"
break;
}
default:
*out++ = *s++;
break;
}
}
CHECK(out < out_end - 1);
*out = '\0';
}

Expand All @@ -69,7 +96,7 @@ class FlagHandlerInclude : public FlagHandlerBase {
bool Parse(const char *value) final {
if (internal_strchr(value, '%')) {
char *buf = (char *)MmapOrDie(kMaxPathLength, "FlagHandlerInclude");
SubstituteBinaryName(value, buf, kMaxPathLength);
SubstituteForFlagValue(value, buf, kMaxPathLength);
bool res = parser_->ParseFile(buf, ignore_missing_);
UnmapOrDie(buf, kMaxPathLength);
return res;
Expand Down
2 changes: 2 additions & 0 deletions compiler-rt/lib/sanitizer_common/sanitizer_flags.h
Expand Up @@ -46,6 +46,8 @@ inline void OverrideCommonFlags(const CommonFlags &cf) {
common_flags_dont_use.CopyFrom(cf);
}

void SubstituteForFlagValue(const char *s, char *out, uptr out_size);

class FlagParser;
void RegisterCommonFlags(FlagParser *parser,
CommonFlags *cf = &common_flags_dont_use);
Expand Down
17 changes: 13 additions & 4 deletions compiler-rt/lib/sanitizer_common/sanitizer_internal_defs.h
Expand Up @@ -107,10 +107,8 @@ typedef uptr operator_new_size_type;
#else
typedef u32 operator_new_size_type;
#endif
} // namespace __sanitizer


using namespace __sanitizer; // NOLINT
// ----------- ATTENTION -------------
// This header should NOT include any other headers to avoid portability issues.

Expand Down Expand Up @@ -188,14 +186,12 @@ typedef void* thread_return_t;
typedef thread_return_t (THREAD_CALLING_CONV *thread_callback_t)(void* arg);

// NOTE: Functions below must be defined in each run-time.
namespace __sanitizer {
void NORETURN Die();

// FIXME: No, this shouldn't be in the sanitizer interface.
SANITIZER_INTERFACE_ATTRIBUTE
void NORETURN CheckFailed(const char *file, int line, const char *cond,
u64 v1, u64 v2);
} // namespace __sanitizer

// Check macro
#define RAW_CHECK_MSG(expr, msg) do { \
Expand Down Expand Up @@ -287,6 +283,9 @@ enum LinkerInitialized { LINKER_INITIALIZED = 0 };
#if !defined(_MSC_VER) || defined(__clang__)
# define GET_CALLER_PC() (uptr)__builtin_return_address(0)
# define GET_CURRENT_FRAME() (uptr)__builtin_frame_address(0)
inline void Trap() {
__builtin_trap();
}
#else
extern "C" void* _ReturnAddress(void);
# pragma intrinsic(_ReturnAddress)
Expand All @@ -295,6 +294,12 @@ extern "C" void* _ReturnAddress(void);
// FIXME: This macro is still used when printing error reports though it's not
// clear if the BP value is needed in the ASan reports on Windows.
# define GET_CURRENT_FRAME() (uptr)0xDEADBEEF

extern "C" void __ud2(void);
# pragma intrinsic(__ud2)
inline void Trap() {
__ud2();
}
#endif

#define HANDLE_EINTR(res, f) \
Expand All @@ -313,4 +318,8 @@ extern "C" void* _ReturnAddress(void);
(void)enable_fp; \
} while (0)

} // namespace __sanitizer

using namespace __sanitizer; // NOLINT

#endif // SANITIZER_DEFS_H
3 changes: 2 additions & 1 deletion compiler-rt/lib/sanitizer_common/sanitizer_symbolizer.h
Expand Up @@ -113,6 +113,8 @@ class Symbolizer final {
void AddHooks(StartSymbolizationHook start_hook,
EndSymbolizationHook end_hook);

LoadedModule *FindModuleForAddress(uptr address);

private:
// GetModuleNameAndOffsetForPC has to return a string to the caller.
// Since the corresponding module might get unloaded later, we should create
Expand All @@ -139,7 +141,6 @@ class Symbolizer final {

bool FindModuleNameAndOffsetForAddress(uptr address, const char **module_name,
uptr *module_offset);
LoadedModule *FindModuleForAddress(uptr address);
LoadedModule modules_[kMaxNumberOfModules];
uptr n_modules_;
// If stale, need to reload the modules before looking up addresses.
Expand Down
27 changes: 27 additions & 0 deletions compiler-rt/lib/stats/CMakeLists.txt
@@ -0,0 +1,27 @@
include_directories(..)

add_custom_target(stats)

if(APPLE)
set(STATS_LIB_FLAVOR SHARED)
else()
set(STATS_LIB_FLAVOR STATIC)
endif()

add_compiler_rt_runtime(clang_rt.stats
${STATS_LIB_FLAVOR}
ARCHS ${SANITIZER_COMMON_SUPPORTED_ARCH}
OS ${SANITIZER_COMMON_SUPPORTED_OS}
SOURCES stats.cc
OBJECT_LIBS RTSanitizerCommon
RTSanitizerCommonLibc
CFLAGS ${SANITIZER_COMMON_CFLAGS}
PARENT_TARGET stats)

add_compiler_rt_runtime(clang_rt.stats_client
STATIC
ARCHS ${SANITIZER_COMMON_SUPPORTED_ARCH}
OS ${SANITIZER_COMMON_SUPPORTED_OS}
SOURCES stats_client.cc
CFLAGS ${SANITIZER_COMMON_CFLAGS}
PARENT_TARGET stats)
136 changes: 136 additions & 0 deletions compiler-rt/lib/stats/stats.cc
@@ -0,0 +1,136 @@
//===-- stats.cc ----------------------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Sanitizer statistics gathering. Manages statistics for a process and is
// responsible for writing the report file.
//
//===----------------------------------------------------------------------===//

#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_internal_defs.h"
#if SANITIZER_POSIX
#include "sanitizer_common/sanitizer_posix.h"
#endif
#include "sanitizer_common/sanitizer_symbolizer.h"
#include "stats/stats.h"
#if SANITIZER_POSIX
#include <signal.h>
#endif

using namespace __sanitizer;

namespace {

InternalMmapVectorNoCtor<StatModule **> modules;
StaticSpinMutex modules_mutex;

fd_t stats_fd;

void WriteLE(fd_t fd, uptr val) {
char chars[sizeof(uptr)];
for (unsigned i = 0; i != sizeof(uptr); ++i) {
chars[i] = val >> (i * 8);
}
WriteToFile(fd, chars, sizeof(uptr));
}

void OpenStatsFile(const char *path_env) {
InternalScopedBuffer<char> path(kMaxPathLength);
SubstituteForFlagValue(path_env, path.data(), kMaxPathLength);

error_t err;
stats_fd = OpenFile(path.data(), WrOnly, &err);
if (stats_fd == kInvalidFd) {
Report("stats: failed to open %s for writing (reason: %d)\n", path.data(),
err);
return;
}
char sizeof_uptr = sizeof(uptr);
WriteToFile(stats_fd, &sizeof_uptr, 1);
}

void WriteModuleReport(StatModule **smodp) {
CHECK(smodp);
const char *path_env = GetEnv("SANITIZER_STATS_PATH");
if (!path_env || stats_fd == kInvalidFd)
return;
if (!stats_fd)
OpenStatsFile(path_env);
LoadedModule *mod = Symbolizer::GetOrInit()->FindModuleForAddress(
reinterpret_cast<uptr>(smodp));
WriteToFile(stats_fd, mod->full_name(),
internal_strlen(mod->full_name()) + 1);
for (StatModule *smod = *smodp; smod; smod = smod->next) {
for (u32 i = 0; i != smod->size; ++i) {
StatInfo *s = &smod->infos[i];
if (!s->addr)
continue;
WriteLE(stats_fd, s->addr - mod->base_address());
WriteLE(stats_fd, s->data);
}
}
WriteLE(stats_fd, 0);
WriteLE(stats_fd, 0);
}

} // namespace

extern "C"
SANITIZER_INTERFACE_ATTRIBUTE
unsigned __sanitizer_stats_register(StatModule **mod) {
SpinMutexLock l(&modules_mutex);
modules.push_back(mod);
return modules.size() - 1;
}

extern "C"
SANITIZER_INTERFACE_ATTRIBUTE
void __sanitizer_stats_unregister(unsigned index) {
SpinMutexLock l(&modules_mutex);
WriteModuleReport(modules[index]);
modules[index] = 0;
}

namespace {

void WriteFullReport() {
SpinMutexLock l(&modules_mutex);
for (StatModule **mod : modules) {
if (!mod)
continue;
WriteModuleReport(mod);
}
if (stats_fd != 0 && stats_fd != kInvalidFd) {
CloseFile(stats_fd);
stats_fd = kInvalidFd;
}
}

#if SANITIZER_POSIX
void USR2Handler(int sig) {
WriteFullReport();
}
#endif

struct WriteReportOnExitOrSignal {
WriteReportOnExitOrSignal() {
#if SANITIZER_POSIX
struct sigaction sigact;
internal_memset(&sigact, 0, sizeof(sigact));
sigact.sa_handler = USR2Handler;
internal_sigaction(SIGUSR2, &sigact, nullptr);
#endif
}

~WriteReportOnExitOrSignal() {
WriteFullReport();
}
} wr;

} // namespace
43 changes: 43 additions & 0 deletions compiler-rt/lib/stats/stats.h
@@ -0,0 +1,43 @@
//===-- stats.h -------------------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Data definitions for sanitizer statistics gathering.
//
//===----------------------------------------------------------------------===//

#ifndef SANITIZER_STATS_STATS_H
#define SANITIZER_STATS_STATS_H

#include "sanitizer_common/sanitizer_internal_defs.h"

namespace __sanitizer {

// Number of bits in data that are used for the sanitizer kind. Needs to match
// llvm::kSanitizerStatKindBits in
// llvm/include/llvm/Transforms/Utils/SanitizerStats.h
enum { kKindBits = 3 };

struct StatInfo {
uptr addr;
uptr data;
};

struct StatModule {
StatModule *next;
u32 size;
StatInfo infos[1];
};

inline uptr CountFromData(uptr data) {
return data & ((1ull << (sizeof(uptr) * 8 - kKindBits)) - 1);
}

}

#endif

0 comments on commit 5788e12

Please sign in to comment.