Add platform::IsMemoryValid.

Signed-off-by: Vassil Vassilev <vvasilev@cern.ch>
This commit is contained in:
Roman Zulak 2016-09-15 15:06:01 -04:00 committed by sftnight
parent ffadea2c21
commit 2eae759f98
4 changed files with 84 additions and 84 deletions

View File

@ -51,6 +51,10 @@ namespace platform {
///
std::string NormalizePath(const std::string& Path);
///\brief Return true if the given pointer is in a valid memory region.
///
bool IsMemoryValid(const void *P);
#if defined(LLVM_ON_UNIX)
#if defined(__APPLE__)

View File

@ -12,8 +12,13 @@
#if defined(LLVM_ON_UNIX)
#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
@ -29,6 +34,67 @@ namespace cling {
namespace utils {
namespace platform {
namespace {
// A simple round-robin cache: what enters first, leaves first.
// MRU cache wasn't worth the extra CPU cycles.
struct Cache {
private:
std::array<const void*, 8> lines;
std::atomic<unsigned> mostRecent = {0};
public:
bool contains(const void* P) {
return std::find(lines.begin(), lines.end(), P) != lines.end();
}
// 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;
}
};
// Note: not thread safe, see comment above push().
static Cache& getCache() {
static Cache threadCache;
return threadCache;
}
static int getNullDevFileDescriptor() {
struct FileDescriptor {
int FD;
const char* file = "/dev/random";
FileDescriptor() { FD = open(file, O_WRONLY); }
~FileDescriptor() {
close(FD);
}
};
static FileDescriptor nullDev;
return nullDev.FD;
}
} // anonymous namespace
bool IsMemoryValid(const void *P) {
// Look-up the address in the cache.
Cache& currentCache = getCache();
if (currentCache.contains(P))
return true;
// There is a POSIX way of finding whether an address
// can be accessed for reading.
if (write(getNullDevFileDescriptor(), P, 1/*byte*/) != 1) {
assert(errno == EFAULT && "unexpected write error at address");
return false;
}
currentCache.push(P);
return true;
}
std::string GetCwd() {
char Buffer[PATH_MAXC];
if (::getcwd(Buffer, sizeof(Buffer)))

View File

@ -454,7 +454,7 @@ bool IsDLL(const std::string& Path) {
::CloseHandle(hFile);
return isDLL;
}
} // namespace windows
std::string GetCwd() {
@ -475,6 +475,17 @@ std::string NormalizePath(const std::string& Path) {
return std::string();
}
bool IsMemoryValid(const void *P) {
MEMORY_BASIC_INFORMATION MBI;
if (::VirtualQuery(P, &MBI, sizeof(MBI)) == 0) {
ReportLastError("VirtualQuery");
return false;
}
if (MBI.State != MEM_COMMIT)
return false;
return true;
}
const void* DLOpen(const std::string& Path, std::string* Err) {
HMODULE dyLibHandle = ::LoadLibraryExA(Path.c_str(), NULL,
DONT_RESOLVE_DLL_REFERENCES);

View File

@ -7,97 +7,16 @@
// LICENSE.TXT for details.
//------------------------------------------------------------------------------
#include "cling/Utils/Validation.h"
#include "llvm/Support/ThreadLocal.h"
#include "llvm/Config/llvm-config.h"
#include <assert.h>
#include <errno.h>
#ifdef LLVM_ON_WIN32
# include <Windows.h>
#else
# include <unistd.h>
#endif
#include <array>
#include <algorithm>
#include <fcntl.h>
#include <atomic>
#include "cling/Utils/Platform.h"
namespace cling {
namespace utils {
#ifndef LLVM_ON_WIN32
// A simple round-robin cache: what enters first, leaves first.
// MRU cache wasn't worth the extra CPU cycles.
struct Cache {
private:
std::array<const void*, 8> lines;
std::atomic<unsigned> mostRecent = {0};
public:
bool contains(const void* P) {
return std::find(lines.begin(), lines.end(), P) != lines.end();
}
// 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;
}
};
// Note: not thread safe, see comment above push().
static Cache& getCache() {
static Cache threadCache;
return threadCache;
}
static int getNullDevFileDescriptor() {
struct FileDescriptor {
int FD;
const char* file = "/dev/random";
FileDescriptor() { FD = open(file, O_WRONLY); }
~FileDescriptor() {
close(FD);
}
};
static FileDescriptor nullDev;
return nullDev.FD;
}
#endif
// Checking whether the pointer points to a valid memory location
bool isAddressValid(const void *P) {
if (!P || P == (void *) -1)
return false;
#ifdef LLVM_ON_WIN32
MEMORY_BASIC_INFORMATION MBI;
if (!VirtualQuery(P, &MBI, sizeof(MBI)))
return false;
if (MBI.State != MEM_COMMIT)
return false;
return true;
#else
// Look-up the address in the cache.
Cache& currentCache = getCache();
if (currentCache.contains(P))
return true;
// There is a POSIX way of finding whether an address
// can be accessed for reading.
if (write(getNullDevFileDescriptor(), P, 1/*byte*/) != 1) {
assert(errno == EFAULT && "unexpected write error at address");
return false;
}
currentCache.push(P);
return true;
#endif
return platform::IsMemoryValid(P);
}
}
}