Implement IsMemoryValid natively on OSX. 250% faster!

Signed-off-by: Vassil Vassilev <vvasilev@cern.ch>
This commit is contained in:
Roman Zulak 2016-09-15 17:27:27 -04:00 committed by sftnight
parent caa8628784
commit 1e3509c5c4
2 changed files with 85 additions and 59 deletions

View File

@ -17,6 +17,8 @@
#include "llvm/Support/raw_ostream.h"
#include <sstream>
#include <mach/vm_map.h>
#include <mach/mach_host.h>
#include <CoreFoundation/CFBase.h> // For MAC_OS_X_VERSION_X_X macros
@ -32,6 +34,19 @@
namespace cling {
namespace utils {
namespace platform {
bool IsMemoryValid(const void *P) {
char Buf;
vm_size_t sizeRead = sizeof(Buf);
if (::vm_read_overwrite(mach_task_self(), vm_address_t(P), sizeRead,
vm_address_t(&Buf), &sizeRead) != KERN_SUCCESS)
return false;
if (sizeRead != sizeof(Buf))
return false;
return true;
}
inline namespace osx {
namespace {

View File

@ -13,18 +13,17 @@
#include "cling/Utils/Paths.h"
#include <array>
#include <atomic>
#include <string>
#include <dlfcn.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
// PATH_MAX
#ifdef __APPLE__
#include <sys/syslimits.h>
#include <sys/syslimits.h> // PATH_MAX
#else
#include <array>
#include <atomic>
#include <limits.h>
#endif
@ -34,61 +33,6 @@ namespace cling {
namespace utils {
namespace platform {
namespace {
struct PointerCheck {
private:
// A simple round-robin cache: what enters first, leaves first.
// MRU cache wasn't worth the extra CPU cycles.
std::array<const void*, 8> lines;
std::atomic<unsigned> mostRecent = {0};
int FD;
// Concurrent writes to the same cache element can result in invalid cache
// elements, causing pointer address not being available in the cache even
// though they should be, i.e. false cache misses. While can cause a
// slow-down, the cost for keeping the cache thread-local or atomic is
// much higher (yes, this was measured).
void push(const void* P) {
unsigned acquiredVal = mostRecent;
while(!mostRecent.compare_exchange_weak(acquiredVal,
(acquiredVal+1)%lines.size())) {
acquiredVal = mostRecent;
}
lines[acquiredVal] = P;
}
public:
PointerCheck() : FD(::open("/dev/random", O_WRONLY)) {
if (FD == -1) ::perror("open('/dev/random')");
}
~PointerCheck() {
if (FD != -1) ::close(FD);
}
bool operator () (const void* P) {
if (FD == -1)
return false;
if (std::find(lines.begin(), lines.end(), P) != lines.end())
return true;
// There is a POSIX way of finding whether an address
// can be accessed for reading.
if (::write(FD, P, 1/*byte*/) != 1) {
assert(errno == EFAULT && "unexpected write error at address");
return false;
}
push(P);
return true;
}
};
}
bool IsMemoryValid(const void *P) {
static PointerCheck sPointerCheck;
return sPointerCheck(P);
}
std::string GetCwd() {
char Buffer[PATH_MAXC];
if (::getcwd(Buffer, sizeof(Buffer)))
@ -167,6 +111,73 @@ bool GetSystemLibraryPaths(llvm::SmallVectorImpl<std::string>& Paths) {
return true;
}
/*
### FIXME Use OS X IsMemoryValid on all BSD variants:
#include <sys/param.h>
#if !defined(BSD)
<SNIP>
#else
*/
#if !defined(__APPLE__)
namespace {
struct PointerCheck {
private:
// A simple round-robin cache: what enters first, leaves first.
// MRU cache wasn't worth the extra CPU cycles.
std::array<const void*, 8> lines;
std::atomic<unsigned> mostRecent = {0};
int FD;
// Concurrent writes to the same cache element can result in invalid cache
// elements, causing pointer address not being available in the cache even
// though they should be, i.e. false cache misses. While can cause a
// slow-down, the cost for keeping the cache thread-local or atomic is
// much higher (yes, this was measured).
void push(const void* P) {
unsigned acquiredVal = mostRecent;
while(!mostRecent.compare_exchange_weak(acquiredVal,
(acquiredVal+1)%lines.size())) {
acquiredVal = mostRecent;
}
lines[acquiredVal] = P;
}
public:
PointerCheck() : FD(::open("/dev/random", O_WRONLY)) {
if (FD == -1) ::perror("open('/dev/random')");
}
~PointerCheck() {
if (FD != -1) ::close(FD);
}
bool operator () (const void* P) {
if (FD == -1)
return false;
if (std::find(lines.begin(), lines.end(), P) != lines.end())
return true;
// There is a POSIX way of finding whether an address
// can be accessed for reading.
if (::write(FD, P, 1/*byte*/) != 1) {
assert(errno == EFAULT && "unexpected write error at address");
return false;
}
push(P);
return true;
}
};
}
bool IsMemoryValid(const void *P) {
static PointerCheck sPointerCheck;
return sPointerCheck(P);
}
#endif // !__APPLE__
} // namespace platform
} // namespace utils
} // namespace cling