invalidate textures through process signals

This commit is contained in:
Anthony Pesch 2015-09-11 22:13:29 -07:00
parent b540e5b80b
commit 3897fe557f
21 changed files with 1127 additions and 120 deletions

View File

@ -115,6 +115,7 @@ set(DREAVM_SOURCES
src/hw/holly/pvr2.cc
src/hw/holly/tile_accelerator.cc
src/hw/holly/tile_renderer.cc
src/hw/holly/tile_texture_cache.cc
src/hw/maple/maple.cc
src/hw/maple/maple_controller.cc
src/hw/sh4/sh4.cc
@ -141,12 +142,22 @@ set(DREAVM_SOURCES
src/jit/runtime.cc
src/renderer/gl_backend.cc
src/renderer/gl_shader.cc
src/sigsegv/sigsegv_handler.cc
src/system/keys.cc
src/system/system.cc
src/trace/trace.cc
src/trace/trace_viewer.cc
src/main.cc)
if(WIN32)
list(APPEND DREAVM_SOURCES src/sigsegv/sigsegv_handler_win.cc)
elseif(APPLE)
list(APPEND DREAVM_SOURCES src/sigsegv/sigsegv_handler_mac.cc)
else()
list(APPEND DREAVM_SOURCES src/sigsegv/sigsegv_handler_linux.cc)
endif()
# assign source groups for visual studio projects
source_group_by_dir(DREAVM_SOURCES)
@ -277,6 +288,7 @@ endif()
set(DREAVM_TEST_SOURCES
${DREAVM_SOURCES}
test/sh4_test.cc
test/test_interval_tree.cc
test/test_intrusive_list.cc
test/test_ring_buffer.cc
test/test_sh4.cc

View File

@ -5,11 +5,9 @@
#include <gflags/gflags.h>
#include "core/arena.h"
#include "core/assert.h"
#include "core/intrusive_list.h"
#include "core/log.h"
#include "core/math.h"
#include "core/filesystem.h"
#include "core/platform.h"
#include "core/ring_buffer.h"
#endif

309
src/core/interval_tree.h Normal file
View File

@ -0,0 +1,309 @@
#ifndef INTERVAL_TREE_H
#define INTERVAL_TREE_H
#include <algorithm>
#include <functional>
#include <set>
#include <string>
#include <assert.h>
#include <stdlib.h>
#include "core/assert.h"
namespace dreavm {
// Interval tree implemented using a randomized bst. Based on implementation at
// http://algs4.cs.princeton.edu/93intersection/IntervalST.java
//
// Parent pointers have been added in order to make removal by node (as opposed
// to key) possible.
template <typename IT, typename VT>
class IntervalTree {
public:
struct Node;
typedef IT interval_type;
typedef VT value_type;
typedef IntervalTree<interval_type, value_type> self_type;
typedef std::function<void(const self_type &, Node *)> iterate_cb;
struct Node {
Node(const interval_type &low, const interval_type &high,
const value_type &value)
: parent(nullptr),
left(nullptr),
right(nullptr),
low(low),
high(high),
max(high),
value(value),
num(1) {}
bool operator<(const Node &rhs) const {
return low < rhs.low || (low == rhs.low && high < rhs.high);
}
Node *parent, *left, *right;
interval_type low, high, max;
value_type value;
int num;
};
IntervalTree() : root_(nullptr) {}
~IntervalTree() { Clear(); }
int Size() { return Size(root_); }
int Height() { return Height(root_); }
Node *Insert(const interval_type &low, const interval_type &high,
const value_type &value) {
Node *n = new Node(low, high, value);
SetRoot(RandomizedInsert(root_, n));
return n;
}
void Remove(Node *n) {
// join left and right subtrees, assign new joined subtree to parent
Node *joined = Join(n->left, n->right);
if (!n->parent) {
// removed node had no parent, must have been root
CHECK_EQ(root_, n);
SetRoot(joined);
} else if (n->parent->left == n) {
SetLeft(n->parent, joined);
} else {
SetRight(n->parent, joined);
}
// fix up each node in the parent chain
Node *parent = n->parent;
while (parent) {
FixCounts(parent);
parent = parent->parent;
}
// remove the node
delete n;
}
void Clear() {
Clear(root_);
SetRoot(nullptr);
}
Node *Find(interval_type low, interval_type high) {
Node *n = root_;
while (n) {
if (high >= n->low && n->high >= low) {
return n;
} else if (!n->left || n->left->max < low) {
n = n->right;
} else {
n = n->left;
}
}
return nullptr;
}
void Iterate(interval_type low, interval_type high, iterate_cb cb) {
Iterate(root_, low, high, cb);
}
private:
int Size(Node *n) { return n ? n->num : 0; }
int Height(Node *n) {
return n ? 1 + std::max(Height(n->left), Height(n->right)) : 0;
}
//
// insertion
//
Node *RootInsert(Node *root, Node *n) {
if (!root) {
return n;
}
if (*n < *root) {
SetLeft(root, RootInsert(root->left, n));
root = RotateRight(root);
} else {
SetRight(root, RootInsert(root->right, n));
root = RotateLeft(root);
}
return root;
}
Node *RandomizedInsert(Node *root, Node *n) {
if (!root) {
return n;
}
// make new node the root with uniform probability
if (rand() % (Size(root) + 1) == 0) {
return RootInsert(root, n);
}
if (*n < *root) {
SetLeft(root, RandomizedInsert(root->left, n));
} else {
SetRight(root, RandomizedInsert(root->right, n));
}
return root;
}
//
// removal
//
void Clear(Node *n) {
if (!n) {
return;
}
Clear(n->left);
Clear(n->right);
delete n;
}
//
// iteration
//
bool Iterate(Node *n, interval_type low, interval_type high, iterate_cb cb) {
if (!n) {
return false;
}
bool found1 = false;
bool found2 = false;
bool found3 = false;
if (high >= n->low && n->high >= low) {
cb(*this, n);
found1 = true;
}
if (n->left && n->left->max >= low) {
found2 = Iterate(n->left, low, high, cb);
}
if (found2 || !n->left || n->left->max < low) {
found3 = Iterate(n->right, low, high, cb);
}
return found1 || found2 || found3;
}
//
// helper methods
//
void SetRoot(Node *n) {
root_ = n;
if (root_) {
root_->parent = nullptr;
}
}
void SetLeft(Node *parent, Node *n) {
parent->left = n;
if (parent->left) {
parent->left->parent = parent;
}
FixCounts(parent);
}
void SetRight(Node *parent, Node *n) {
parent->right = n;
if (parent->right) {
parent->right->parent = parent;
}
FixCounts(parent);
}
void FixCounts(Node *n) {
if (!n) {
return;
}
n->num = 1 + Size(n->left) + Size(n->right);
n->max = n->high;
if (n->left) {
n->max = std::max(n->max, n->left->max);
}
if (n->right) {
n->max = std::max(n->max, n->right->max);
}
}
Node *RotateRight(Node *root) {
Node *parent = root->parent;
Node *n = root->left;
SetLeft(root, n->right);
SetRight(n, root);
if (parent) {
if (parent->left == root) {
SetLeft(parent, n);
} else {
SetRight(parent, n);
}
}
return n;
}
Node *RotateLeft(Node *root) {
Node *parent = root->parent;
Node *n = root->right;
SetRight(root, n->left);
SetLeft(n, root);
if (parent) {
if (parent->left == root) {
SetLeft(parent, n);
} else {
SetRight(parent, n);
}
}
return n;
}
Node *Join(Node *a, Node *b) {
if (!a) {
return b;
} else if (!b) {
return a;
}
if ((rand() % (Size(a) + Size(b))) < Size(a)) {
SetRight(a, Join(a->right, b));
return a;
} else {
SetLeft(b, Join(a, b->left));
return b;
}
}
Node *root_;
};
}
#endif

View File

@ -16,6 +16,7 @@ using namespace dreavm::jit::backend::interpreter;
using namespace dreavm::jit::backend::x64;
using namespace dreavm::jit::frontend::sh4;
using namespace dreavm::renderer;
using namespace dreavm::sigsegv;
using namespace dreavm::trace;
Dreamcast::Dreamcast()
@ -43,16 +44,16 @@ Dreamcast::Dreamcast()
#include "hw/holly/pvr2_regs.inc"
#undef PVR_REG
aica_regs_ = new uint8_t[AICA_REG_SIZE]();
bios_ = new uint8_t[BIOS_SIZE]();
expdev_mem_ = new uint8_t[EXPDEV_SIZE]();
flash_ = new uint8_t[FLASH_SIZE]();
modem_mem_ = new uint8_t[MODEM_REG_SIZE]();
palette_ram_ = new uint8_t[PVR_PALETTE_SIZE]();
ram_ = new uint8_t[MAIN_RAM_SIZE]();
unassigned_ = new uint8_t[UNASSIGNED_SIZE]();
modem_mem_ = new uint8_t[MODEM_REG_SIZE]();
aica_regs_ = new uint8_t[AICA_REG_SIZE]();
wave_ram_ = new uint8_t[WAVE_RAM_SIZE]();
expdev_mem_ = new uint8_t[EXPDEV_SIZE]();
video_ram_ = new uint8_t[PVR_VRAM32_SIZE]();
palette_ram_ = new uint8_t[PVR_PALETTE_SIZE]();
wave_ram_ = new uint8_t[WAVE_RAM_SIZE]();
scheduler_ = new Scheduler();
memory_ = new Memory();
@ -98,6 +99,10 @@ Dreamcast::~Dreamcast() {
}
bool Dreamcast::Init() {
if (!(sigsegv_ = SIGSEGVHandler::Install())) {
return false;
}
MapMemory();
if (!aica_->Init()) {

View File

@ -14,6 +14,7 @@
#include "jit/frontend/frontend.h"
#include "jit/runtime.h"
#include "renderer/backend.h"
#include "sigsegv/sigsegv_handler.h"
#include "trace/trace.h"
namespace dreavm {
@ -116,6 +117,8 @@ class Dreamcast {
hw::sh4::SH4 *sh4() { return sh4_; }
hw::holly::TileAccelerator *ta() { return ta_; }
sigsegv::SIGSEGVHandler *sigsegv() { return sigsegv_; }
renderer::Backend *rb() { return rb_; }
void set_rb(renderer::Backend *rb) { rb_ = rb; }
@ -148,16 +151,16 @@ class Dreamcast {
private:
void MapMemory();
uint8_t *aica_regs_;
uint8_t *bios_;
uint8_t *expdev_mem_;
uint8_t *flash_;
uint8_t *modem_mem_;
uint8_t *palette_ram_;
uint8_t *ram_;
uint8_t *unassigned_;
uint8_t *modem_mem_;
uint8_t *aica_regs_;
uint8_t *wave_ram_;
uint8_t *expdev_mem_;
uint8_t *video_ram_;
uint8_t *palette_ram_;
uint8_t *wave_ram_;
hw::Memory *memory_;
hw::Scheduler *scheduler_;
@ -173,6 +176,7 @@ class Dreamcast {
hw::holly::TileAccelerator *ta_;
// not owned by us
sigsegv::SIGSEGVHandler *sigsegv_;
renderer::Backend *rb_;
trace::TraceWriter *trace_writer_;
};

View File

@ -164,95 +164,6 @@ static int GetVertexType_raw(const PCW &pcw) {
return 0;
}
TileTextureCache::TileTextureCache(Dreamcast *dc)
: dc_(dc), trace_writer_(nullptr) {}
void TileTextureCache::Clear() {
for (auto it : textures_) {
if (!it.second) {
continue;
}
dc_->rb()->FreeTexture(it.second);
}
textures_.clear();
}
// void TileTextureCache::RemoveTexture(uint32_t addr) {
// auto it = textures_.find(addr);
// if (it == textures_.end()) {
// return;
// }
// TextureHandle handle = it->second;
// dc_->rb()->FreeTexture(handle);
// textures_.erase(it);
// }
TextureHandle TileTextureCache::GetTexture(
const TSP &tsp, const TCW &tcw, RegisterTextureCallback register_cb) {
TextureKey texture_key = TextureCache::GetTextureKey(tsp, tcw);
// if the trace writer has changed, clear the cache to force insert events
if (dc_->trace_writer() != trace_writer_) {
Clear();
trace_writer_ = dc_->trace_writer();
}
// see if we already have an entry
auto it = textures_.find(texture_key);
if (it != textures_.end()) {
return it->second;
}
// TCW texture_addr field is in 64-bit units
uint32_t texture_addr = tcw.texture_addr << 3;
// get the texture data
const uint8_t *video_ram = dc_->video_ram();
const uint8_t *texture = &video_ram[texture_addr];
int width = 8 << tsp.texture_u_size;
int height = 8 << tsp.texture_v_size;
int element_size_bits = tcw.pixel_format == TA_PIXEL_8BPP
? 8
: tcw.pixel_format == TA_PIXEL_4BPP ? 4 : 16;
int texture_size = (width * height * element_size_bits) >> 3;
// get the palette data
const uint8_t *palette = dc_->palette_ram();
int palette_size = 0;
if (tcw.pixel_format == TA_PIXEL_4BPP || tcw.pixel_format == TA_PIXEL_8BPP) {
// palette ram is 4096 bytes, with each palette entry being 4 bytes each,
// resulting in 1 << 10 indexes
if (tcw.pixel_format == TA_PIXEL_4BPP) {
// in 4bpp mode, the palette selector represents the upper 6 bits of the
// palette index, with the remaining 4 bits being filled in by the texture
palette += (tcw.p.palette_selector << 4) * 4;
palette_size = (1 << 4) * 4;
} else if (tcw.pixel_format == TA_PIXEL_8BPP) {
// in 4bpp mode, the palette selector represents the upper 2 bits of the
// palette index, with the remaining 8 bits being filled in by the texture
palette += ((tcw.p.palette_selector & 0x30) << 4) * 4;
palette_size = (1 << 8) * 4;
}
}
// register and insert into the cache
TextureHandle handle = register_cb(palette, texture);
auto result = textures_.insert(std::make_pair(texture_key, handle));
CHECK(result.second, "Texture already in the map?");
// add insert to trace
if (trace_writer_) {
trace_writer_->WriteInsertTexture(tsp, tcw, palette, palette_size, texture,
texture_size);
}
return result.first->second;
}
int TileAccelerator::GetParamSize(const PCW &pcw, int vertex_type) {
int size =
param_size_lookup[pcw.obj_control * TA_NUM_PARAMS * TA_NUM_VERT_TYPES +
@ -399,9 +310,6 @@ void TileAccelerator::WriteCommand32(uint32_t addr, uint32_t value) {
void TileAccelerator::WriteTexture32(uint32_t addr, uint32_t value) {
addr &= 0xeeffffff;
// // FIXME this is terrible
// texcache_.RemoveTexture(addr);
*reinterpret_cast<uint32_t *>(&video_ram_[addr]) = value;
}

View File

@ -4,6 +4,7 @@
#include <memory>
#include <unordered_map>
#include "hw/holly/tile_renderer.h"
#include "hw/holly/tile_texture_cache.h"
#include "renderer/backend.h"
namespace dreavm {
@ -474,22 +475,6 @@ struct TileContext {
int vertex_type;
};
class TileTextureCache : public TextureCache {
public:
TileTextureCache(hw::Dreamcast *dc);
void Clear();
// void RemoveTexture(uint32_t addr);
renderer::TextureHandle GetTexture(const TSP &tsp, const TCW &tcw,
RegisterTextureCallback register_cb);
private:
hw::Dreamcast *dc_;
trace::TraceWriter *trace_writer_;
std::unordered_map<TextureKey, renderer::TextureHandle> textures_;
};
typedef std::unordered_map<TextureKey, TileContext *> TileContextMap;
typedef TileContextMap::iterator TileContextIterator;

View File

@ -0,0 +1,127 @@
#include "core/core.h"
#include "hw/dreamcast.h"
using namespace dreavm::hw;
using namespace dreavm::hw::holly;
using namespace dreavm::renderer;
TileTextureCache::TileTextureCache(Dreamcast *dc)
: dc_(dc), trace_writer_(nullptr) {}
TextureHandle TileTextureCache::GetTexture(
const TSP &tsp, const TCW &tcw, RegisterTextureCallback register_cb) {
TextureKey texture_key = TextureCache::GetTextureKey(tsp, tcw);
// if there are any pending removals, do so at this time
if (pending_invalidations_.size()) {
ClearPending();
}
// if the trace writer has changed, clear the cache to force insert events
if (dc_->trace_writer() != trace_writer_) {
ClearAll();
trace_writer_ = dc_->trace_writer();
}
// see if an an entry already exists
auto it = textures_.find(texture_key);
if (it != textures_.end()) {
return it->second;
}
// TCW texture_addr field is in 64-bit units
uint32_t texture_addr = tcw.texture_addr << 3;
// get the texture data
uint8_t *video_ram = dc_->video_ram();
uint8_t *texture = &video_ram[texture_addr];
int width = 8 << tsp.texture_u_size;
int height = 8 << tsp.texture_v_size;
int element_size_bits = tcw.pixel_format == TA_PIXEL_8BPP
? 8
: tcw.pixel_format == TA_PIXEL_4BPP ? 4 : 16;
int texture_size = (width * height * element_size_bits) >> 3;
// get the palette data
uint8_t *palette = nullptr;
int palette_size = 0;
if (tcw.pixel_format == TA_PIXEL_4BPP || tcw.pixel_format == TA_PIXEL_8BPP) {
// palette ram is 4096 bytes, with each palette entry being 4 bytes each,
// resulting in 1 << 10 indexes
if (tcw.pixel_format == TA_PIXEL_4BPP) {
// in 4bpp mode, the palette selector represents the upper 6 bits of the
// palette index, with the remaining 4 bits being filled in by the texture
palette = dc_->palette_ram() + (tcw.p.palette_selector << 4) * 4;
palette_size = (1 << 4) * 4;
} else if (tcw.pixel_format == TA_PIXEL_8BPP) {
// in 4bpp mode, the palette selector represents the upper 2 bits of the
// palette index, with the remaining 8 bits being filled in by the texture
palette = dc_->palette_ram() + ((tcw.p.palette_selector & 0x30) << 4) * 4;
palette_size = (1 << 8) * 4;
}
}
// register and insert into the cache
TextureHandle handle = register_cb(palette, texture);
auto result = textures_.insert(std::make_pair(texture_key, handle));
CHECK(result.second, "Texture already in the map?");
// add write callback in order to invalidate on future writes. the callback
// address will be page aligned, therefore it will be triggered falsely in
// some cases. over invalidate in these cases
TileTextureCacheMap::value_type *map_entry = &(*result.first);
auto callback = [](void *ctx, void *data) {
TileTextureCache *texcache = reinterpret_cast<TileTextureCache *>(ctx);
TileTextureCacheMap::value_type *map_entry =
reinterpret_cast<TileTextureCacheMap::value_type *>(data);
TextureKey texture_key = map_entry->first;
// add to pending invalidation list (can't remove inside of signal handler)
texcache->pending_invalidations_.insert(texture_key);
};
dc_->sigsegv()->AddWriteWatch(texture, texture_size, callback, this,
map_entry);
// TODO generate id for watch, so it can be cleared by both callbacks
// if (palette) {
// dc_->mmio()->AddWriteWatch(palette, palette_size, callback, this,
// map_entry);
// }
// add insert to trace
if (trace_writer_) {
trace_writer_->WriteInsertTexture(tsp, tcw, palette, palette_size, texture,
texture_size);
}
return handle;
}
void TileTextureCache::ClearPending() {
for (auto texture_key : pending_invalidations_) {
auto it = textures_.find(texture_key);
CHECK_NE(it, textures_.end());
TextureHandle handle = it->second;
textures_.erase(it);
dc_->rb()->FreeTexture(handle);
}
pending_invalidations_.clear();
}
void TileTextureCache::ClearAll() {
for (auto it : textures_) {
TextureHandle handle = it.second;
dc_->rb()->FreeTexture(handle);
}
textures_.clear();
pending_invalidations_.clear();
}

View File

@ -0,0 +1,41 @@
#ifndef TILE_TEXTURE_CACHE_H
#define TILE_TEXTURE_CACHE_H
#include <set>
#include "hw/holly/tile_renderer.h"
#include "renderer/backend.h"
namespace dreavm {
namespace trace {
class TraceWriter;
}
namespace hw {
class Dreamcast;
namespace holly {
typedef std::unordered_map<TextureKey, renderer::TextureHandle>
TileTextureCacheMap;
class TileTextureCache : public TextureCache {
public:
TileTextureCache(hw::Dreamcast *dc);
renderer::TextureHandle GetTexture(const TSP &tsp, const TCW &tcw,
RegisterTextureCallback register_cb);
private:
void ClearPending();
void ClearAll();
hw::Dreamcast *dc_;
trace::TraceWriter *trace_writer_;
TileTextureCacheMap textures_;
std::set<TextureKey> pending_invalidations_;
};
}
}
}
#endif

View File

@ -3,6 +3,7 @@
#include <unordered_map>
#include "core/core.h"
#include "core/intrusive_list.h"
namespace dreavm {
namespace jit {

View File

@ -0,0 +1,70 @@
#include "core/core.h"
#include "core/interval_tree.h"
#include "sigsegv/sigsegv_handler.h"
using namespace dreavm::sigsegv;
SIGSEGVHandler *dreavm::sigsegv::SIGSEGVHandler::global_handler_ = nullptr;
SIGSEGVHandler *SIGSEGVHandler::Install() {
if (global_handler_) {
return global_handler_;
}
global_handler_ = CreateSIGSEGVHandler();
if (!global_handler_->Init()) {
LOG_WARNING("Failed to install SIGSEGV handler");
delete global_handler_;
global_handler_ = nullptr;
}
return global_handler_;
}
SIGSEGVHandler::~SIGSEGVHandler() { global_handler_ = nullptr; }
// TODO track handler counts with microprofile
void SIGSEGVHandler::AddWriteWatch(void *ptr, int size,
WriteWatchHandler handler, void *ctx,
void *data) {
int page_size = GetPageSize();
uintptr_t physical_start = dreavm::align(reinterpret_cast<uintptr_t>(ptr),
static_cast<uintptr_t>(page_size));
uintptr_t physical_end =
dreavm::align(reinterpret_cast<uintptr_t>(ptr) + size,
static_cast<uintptr_t>(page_size));
// write-protect the pages
CHECK(Protect(reinterpret_cast<void *>(physical_start),
static_cast<int>(physical_end - physical_start), ACC_READONLY));
write_watches_.Insert(
physical_start, physical_end - 1,
WriteWatch(handler, ctx, data, physical_start, physical_end));
}
bool SIGSEGVHandler::HandleAccessFault(uintptr_t rip, uintptr_t fault_addr) {
WatchTree::Node *node = write_watches_.Find(fault_addr, fault_addr);
bool handled = node != nullptr;
while (node) {
WriteWatch &watch = node->value;
watch.handler(watch.ctx, watch.data);
// remove write-protection
CHECK(Protect(reinterpret_cast<void *>(watch.physical_start),
static_cast<int>(watch.physical_end - watch.physical_start),
ACC_READWRITE));
write_watches_.Remove(node);
node = write_watches_.Find(fault_addr, fault_addr);
}
return handled;
}

View File

@ -0,0 +1,61 @@
#ifndef SIGSEGV_HANDLER_H
#define SIGSEGV_HANDLER_H
#include <functional>
#include <memory>
#include "core/interval_tree.h"
namespace dreavm {
namespace sigsegv {
// implemented in the platform specific souce file
class SIGSEGVHandler;
extern SIGSEGVHandler *CreateSIGSEGVHandler();
enum PageAccess { ACC_READONLY, ACC_READWRITE };
typedef std::function<void(void *, void *)> WriteWatchHandler;
struct WriteWatch {
WriteWatch(WriteWatchHandler handler, void *ctx, void *data,
uintptr_t physical_start, uintptr_t physical_end)
: handler(handler),
ctx(ctx),
data(data),
physical_start(physical_start),
physical_end(physical_end) {}
WriteWatchHandler handler;
void *ctx;
void *data;
uintptr_t physical_start;
uintptr_t physical_end;
};
typedef IntervalTree<uintptr_t, WriteWatch> WatchTree;
class SIGSEGVHandler {
public:
static SIGSEGVHandler *global_handler() { return global_handler_; }
static SIGSEGVHandler *Install();
virtual ~SIGSEGVHandler();
void AddWriteWatch(void *ptr, int size, WriteWatchHandler handler, void *ctx,
void *data);
bool HandleAccessFault(uintptr_t rip, uintptr_t fault_addr);
protected:
static SIGSEGVHandler *global_handler_;
virtual bool Init() = 0;
virtual int GetPageSize() = 0;
virtual bool Protect(void *ptr, int size, PageAccess access) = 0;
WatchTree write_watches_;
};
}
}
#endif

View File

@ -0,0 +1,61 @@
#include <sys/mman.h>
#include <signal.h>
#include <unistd.h>
#include "core/core.h"
#include "sigsegv/sigsegv_handler_linux.h"
using namespace dreavm::sigsegv;
SIGSEGVHandler *dreavm::sigsegv::CreateSIGSEGVHandler() {
return new SIGSEGVHandlerLinux();
}
static struct sigaction old_sa;
static void SignalHandler(int signo, siginfo_t *info, void *ctx) {
ucontext_t *uctx = reinterpret_cast<ucontext_t *>(ctx);
uintptr_t rip = uctx->uc_mcontext.gregs[REG_RIP];
uintptr_t fault_addr = reinterpret_cast<uintptr_t>(info->si_addr);
bool handled =
SIGSEGVHandler::global_handler()->HandleAccessFault(rip, fault_addr);
if (!handled) {
// call into the original handler if the installed handler fails to handle
// the signal
(*old_sa.sa_sigaction)(signo, info, ctx);
}
}
SIGSEGVHandlerLinux::~SIGSEGVHandlerLinux() {
sigaction(SIGSEGV, &old_sa, nullptr);
}
bool SIGSEGVHandlerLinux::Init() {
struct sigaction new_sa;
new_sa.sa_flags = SA_SIGINFO;
sigemptyset(&new_sa.sa_mask);
new_sa.sa_sigaction = &SignalHandler;
if (sigaction(SIGSEGV, &new_sa, &old_sa) != 0) {
return false;
}
return true;
}
int SIGSEGVHandlerLinux::GetPageSize() { return getpagesize(); }
bool SIGSEGVHandlerLinux::Protect(void *ptr, int size, PageAccess access) {
int prot = PROT_NONE;
switch (access) {
case ACC_READONLY:
prot = PROT_READ;
break;
case ACC_READWRITE:
prot = PROT_READ | PROT_WRITE;
break;
}
return mprotect(ptr, size, prot) == 0;
}

View File

@ -0,0 +1,24 @@
#ifndef SIGSEGV_HANDLER_LINUX
#define SIGSEGV_HANDLER_LINUX
#include <thread>
#include "sigsegv/sigsegv_handler.h"
namespace dreavm {
namespace sigsegv {
class SIGSEGVHandlerLinux : public SIGSEGVHandler {
public:
~SIGSEGVHandlerLinux();
protected:
bool Init();
int GetPageSize();
bool Protect(void *ptr, int size, PageAccess access);
private:
};
}
}
#endif

View File

@ -0,0 +1,141 @@
#include <mach/mach.h>
#include <sys/mman.h>
#include <unistd.h>
#include "core/core.h"
#include "sigsegv/sigsegv_handler_mac.h"
using namespace dreavm::sigsegv;
SIGSEGVHandler *dreavm::sigsegv::CreateSIGSEGVHandler() {
return new SIGSEGVHandlerMac();
}
// http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/exc_server.html
extern "C" boolean_t exc_server(mach_msg_header_t *request_msg,
mach_msg_header_t *reply_msg);
// http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/catch_exception_raise.html
extern "C" kern_return_t catch_exception_raise(
mach_port_t exception_port, mach_port_t thread, mach_port_t task,
exception_type_t exception, exception_data_t code,
mach_msg_type_number_t code_count) {
// get exception state
mach_msg_type_number_t state_count = x86_EXCEPTION_STATE64_COUNT;
x86_exception_state64_t exc_state;
if (thread_get_state(thread, x86_EXCEPTION_STATE64,
reinterpret_cast<thread_state_t>(&exc_state),
&state_count) != KERN_SUCCESS) {
return KERN_FAILURE;
}
// get thread state
state_count = x86_THREAD_STATE64_COUNT;
x86_thread_state64_t thread_state;
if (thread_get_state(thread, x86_THREAD_STATE64,
reinterpret_cast<thread_state_t>(&thread_state),
&state_count) != KERN_SUCCESS) {
return KERN_FAILURE;
}
uintptr_t rip = thread_state.__rip;
uintptr_t fault_addr = exc_state.__faultvaddr;
bool handled =
SIGSEGVHandler::global_handler()->HandleAccessFault(rip, fault_addr);
if (!handled) {
return KERN_FAILURE;
}
// reset thread state
if (thread_set_state(thread, x86_THREAD_STATE64,
reinterpret_cast<thread_state_t>(&thread_state),
state_count) != KERN_SUCCESS) {
return KERN_FAILURE;
}
return KERN_SUCCESS;
}
SIGSEGVHandlerMac::~SIGSEGVHandlerMac() {
task_set_exception_ports(mach_task_self(), EXC_MASK_BAD_ACCESS, 0,
EXCEPTION_DEFAULT, 0);
mach_port_deallocate(mach_task_self(), listen_port_);
}
bool SIGSEGVHandlerMac::Init() {
// allocate port to listen for exceptions
if (mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE,
&listen_port_) != KERN_SUCCESS) {
return false;
}
// http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/mach_port_insert_right.html
if (mach_port_insert_right(mach_task_self(), listen_port_, listen_port_,
MACH_MSG_TYPE_MAKE_SEND) != KERN_SUCCESS) {
return false;
}
// filter out any exception other than EXC_BAD_ACCESS
// http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/task_set_exception_ports.html
if (task_set_exception_ports(mach_task_self(), EXC_MASK_BAD_ACCESS,
listen_port_, EXCEPTION_DEFAULT,
MACHINE_THREAD_STATE) != KERN_SUCCESS) {
return false;
}
// launch thread to listen for exceptions
thread_ = std::thread([this] { ThreadEntry(); });
thread_.detach();
return true;
}
int SIGSEGVHandlerMac::GetPageSize() { return getpagesize(); }
bool SIGSEGVHandlerMac::Protect(void *ptr, int size, PageAccess access) {
int prot = PROT_NONE;
switch (access) {
case ACC_READONLY:
prot = PROT_READ;
break;
case ACC_READWRITE:
prot = PROT_READ | PROT_WRITE;
break;
}
return mprotect(ptr, size, prot) == 0;
}
void SIGSEGVHandlerMac::ThreadEntry() {
while (true) {
struct {
mach_msg_header_t head;
mach_msg_body_t msgh_body;
char data[1024];
} msg;
struct {
mach_msg_header_t head;
char data[1024];
} reply;
// wait for a message on the exception port
mach_msg_return_t ret =
mach_msg(&msg.head, MACH_RCV_MSG | MACH_RCV_LARGE, 0, sizeof(msg),
listen_port_, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
if (ret != MACH_MSG_SUCCESS) {
LOG_INFO("mach_msg receive failed with %d %s", ret,
mach_error_string(ret));
break;
}
// call exc_server, which will call back into catch_exception_raise
exc_server(&msg.head, &reply.head);
// send the reply
ret = mach_msg(&reply.head, MACH_SEND_MSG, reply.head.msgh_size, 0,
MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
if (ret != MACH_MSG_SUCCESS) {
LOG_INFO("mach_msg send failed with %d %s", ret, mach_error_string(ret));
break;
}
}
}

View File

@ -0,0 +1,28 @@
#ifndef SIGSEGV_HANDLER_MAC
#define SIGSEGV_HANDLER_MAC
#include <thread>
#include "sigsegv/sigsegv_handler.h"
namespace dreavm {
namespace sigsegv {
class SIGSEGVHandlerMac : public SIGSEGVHandler {
public:
~SIGSEGVHandlerMac();
protected:
bool Init();
int GetPageSize();
bool Protect(void *ptr, int size, PageAccess access);
private:
void ThreadEntry();
mach_port_t listen_port_;
std::thread thread_;
};
}
}
#endif

View File

@ -0,0 +1,56 @@
#include <windows.h>
#include "core/core.h"
#include "sigsegv/sigsegv_handler_win.h"
using namespace dreavm::sigsegv;
SIGSEGVHandler *dreavm::sigsegv::CreateSIGSEGVHandler() {
return new SIGSEGVHandlerWin();
}
static LONG CALLBACK ExceptionHandler(PEXCEPTION_POINTERS ex_info) {
auto code = ex_info->ExceptionRecord->ExceptionCode;
if (code != STATUS_ACCESS_VIOLATION) {
return EXCEPTION_CONTINUE_SEARCH;
}
uintptr_t rip = ex_info->ContextRecord->Rip;
uintptr_t fault_addr = ex_info->ExceptionRecord->ExceptionInformation[1];
bool handled =
SIGSEGVHandler::global_handler()->HandleAccessFault(rip, fault_addr);
if (!handled) {
return EXCEPTION_CONTINUE_SEARCH;
}
return EXCEPTION_CONTINUE_EXECUTION;
}
SIGSEGVHandlerWin::~SIGSEGVHandlerWin() {
RemoveVectoredExceptionHandler(ExceptionHandler);
}
bool SIGSEGVHandlerWin::Init() {
return AddVectoredExceptionHandler(1, ExceptionHandler) != nullptr;
}
int SIGSEGVHandlerWin::GetPageSize() {
SYSTEM_INFO system_info;
GetSystemInfo(&system_info);
return system_info.dwPageSize;
}
bool SIGSEGVHandlerWin::Protect(void *ptr, int size, PageAccess access) {
DWORD new_protect = PAGE_NOACCESS;
DWORD old_protect;
switch (access) {
case ACC_READONLY:
new_protect = PAGE_READONLY;
break;
case ACC_READWRITE:
new_protect = PAGE_READWRITE;
break;
}
return VirtualProtect(ptr, size, new_protect, &old_protect);
}

View File

@ -0,0 +1,24 @@
#ifndef SIGSEGV_HANDLER_WIN
#define SIGSEGV_HANDLER_WIN
#include <thread>
#include "sigsegv/sigsegv_handler.h"
namespace dreavm {
namespace sigsegv {
class SIGSEGVHandlerWin : public SIGSEGVHandler {
public:
~SIGSEGVHandlerWin();
protected:
bool Init();
int GetPageSize();
bool Protect(void *ptr, int size, PageAccess access);
private:
};
}
}
#endif

150
test/test_interval_tree.cc Normal file
View File

@ -0,0 +1,150 @@
#include <math.h>
#include <set>
#include "gtest/gtest.h"
#include "core/core.h"
#include "core/interval_tree.h"
using namespace dreavm;
typedef IntervalTree<uint32_t, void *> TestTree;
class IntervalTreeTest : public ::testing::Test {
public:
IntervalTreeTest() {}
static const int LOW = 0x0;
static const int HIGH = 0x10000;
static const int INTERVAL = 0x2000;
void SetUp() {
// insert dummy intervals
for (int i = 0; i < 0x10000; i++) {
uint32_t low = 0;
uint32_t high = 0;
while (high <= low) {
low = LOW + (rand() % (HIGH - LOW));
high = low + INTERVAL;
}
TestTree::Node *n = intervals.Insert(low, high, nullptr);
nodes.insert(n);
}
}
TestTree intervals;
std::set<TestTree::Node *> nodes;
};
TEST_F(IntervalTreeTest, ValidateRelations) {
// make sure all children parent pointers match
for (auto n : nodes) {
if (n->left) {
ASSERT_EQ(n->left->parent, n);
}
if (n->right) {
ASSERT_EQ(n->right->parent, n);
}
}
}
TEST_F(IntervalTreeTest, Size) {
ASSERT_EQ(intervals.Size(), (int)nodes.size());
}
TEST_F(IntervalTreeTest, Height) {
// make sure height is within expected range of ~2*log2(n)
int height = intervals.Height();
int size = intervals.Size();
ASSERT_TRUE(height >= log2(size) && height < (3 * log2(size)));
}
TEST_F(IntervalTreeTest, Remove) {
// remove all results and ensure size is updated in the process
int size = nodes.size();
for (auto n : nodes) {
intervals.Remove(n);
ASSERT_EQ(intervals.Size(), --size);
}
}
TEST_F(IntervalTreeTest, Clear) {
int original_size = intervals.Size();
intervals.Clear();
ASSERT_NE(original_size, intervals.Size());
ASSERT_EQ(0, intervals.Size());
}
TEST_F(IntervalTreeTest, Find) {
for (uint32_t i = 0; i < HIGH; i += 0x1000) {
// manually generate a list of results
std::set<TestTree::Node *> expected;
for (auto n : nodes) {
if (i < n->low || i > n->high) {
continue;
}
expected.insert(n);
}
// query the tree for nodes and compare with the expected results
int found = 0;
TestTree::Node *n = intervals.Find(i, i);
while (n) {
// validate that it's in the expected set
auto it = expected.find(n);
ASSERT_NE(it, expected.end());
found++;
// remove from nodes so the node isn't expected by the next loop iteration
auto it2 = nodes.find(n);
ASSERT_NE(it2, nodes.end());
nodes.erase(it2);
// remove from intervals so Find() can locate the next node
intervals.Remove(n);
// locate the next node
n = intervals.Find(i, i);
}
// validate that the same number of nodes were matched
ASSERT_EQ(found, (int)expected.size());
}
}
TEST_F(IntervalTreeTest, Iterate) {
for (uint32_t i = 0; i < HIGH; i += 0x1000) {
// manually generate a list of expected nodes
std::set<TestTree::Node *> expected;
for (auto n : nodes) {
if (i < n->low || i > n->high) {
continue;
}
expected.insert(n);
}
// query the tree for nodes
std::set<TestTree::Node *> results;
intervals.Iterate(i, i, [&](const TestTree &tree, TestTree::Node *node) {
results.insert(node);
});
// compare the results
ASSERT_EQ(expected.size(), results.size());
for (auto n : results) {
auto it = expected.find(n);
ASSERT_NE(it, expected.end());
}
}
}

View File

@ -1,5 +1,6 @@
#include "gtest/gtest.h"
#include "core/core.h"
#include "core/intrusive_list.h"
using namespace dreavm;

View File

@ -1,5 +1,6 @@
#include "gtest/gtest.h"
#include "core/core.h"
#include "core/ring_buffer.h"
using namespace dreavm;