blob: ee1200107d13ce0e21525871f04b72cb518724c7 [file] [log] [blame]
//===-- tsan_interface_java.cc --------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file is a part of ThreadSanitizer (TSan), a race detector.
//
//===----------------------------------------------------------------------===//
#include "tsan_interface_java.h"
#include "tsan_rtl.h"
#include "tsan_mutex.h"
#include "sanitizer_common/sanitizer_internal_defs.h"
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_placement_new.h"
using namespace __tsan; // NOLINT
namespace __tsan {
const uptr kHeapShadow = 0x300000000000ull;
const uptr kHeapAlignment = 8;
struct BlockDesc {
bool begin;
Mutex mtx;
SyncVar *head;
BlockDesc()
: mtx(MutexTypeJavaMBlock, StatMtxJavaMBlock)
, head() {
CHECK_EQ(begin, false);
begin = true;
}
~BlockDesc() {
CHECK_EQ(begin, true);
begin = false;
ThreadState *thr = cur_thread();
SyncVar *s = head;
while (s) {
SyncVar *s1 = s->next;
StatInc(thr, StatSyncDestroyed);
s->mtx.Lock();
s->mtx.Unlock();
thr->mset.Remove(s->GetId());
DestroyAndFree(s);
s = s1;
}
}
};
struct JavaContext {
const uptr heap_begin;
const uptr heap_size;
BlockDesc *heap_shadow;
JavaContext(jptr heap_begin, jptr heap_size)
: heap_begin(heap_begin)
, heap_size(heap_size) {
uptr size = heap_size / kHeapAlignment * sizeof(BlockDesc);
heap_shadow = (BlockDesc*)MmapFixedNoReserve(kHeapShadow, size);
if ((uptr)heap_shadow != kHeapShadow) {
Printf("ThreadSanitizer: failed to mmap Java heap shadow\n");
Die();
}
}
};
class ScopedJavaFunc {
public:
ScopedJavaFunc(ThreadState *thr, uptr pc)
: thr_(thr) {
Initialize(thr_);
FuncEntry(thr, pc);
CHECK_EQ(thr_->in_rtl, 0);
thr_->in_rtl++;
}
~ScopedJavaFunc() {
thr_->in_rtl--;
CHECK_EQ(thr_->in_rtl, 0);
FuncExit(thr_);
// FIXME(dvyukov): process pending signals.
}
private:
ThreadState *thr_;
};
static u64 jctx_buf[sizeof(JavaContext) / sizeof(u64) + 1];
static JavaContext *jctx;
static BlockDesc *getblock(uptr addr) {
uptr i = (addr - jctx->heap_begin) / kHeapAlignment;
return &jctx->heap_shadow[i];
}
static uptr USED getmem(BlockDesc *b) {
uptr i = b - jctx->heap_shadow;
uptr p = jctx->heap_begin + i * kHeapAlignment;
CHECK_GE(p, jctx->heap_begin);
CHECK_LT(p, jctx->heap_begin + jctx->heap_size);
return p;
}
static BlockDesc *getblockbegin(uptr addr) {
for (BlockDesc *b = getblock(addr);; b--) {
CHECK_GE(b, jctx->heap_shadow);
if (b->begin)
return b;
}
return 0;
}
SyncVar* GetJavaSync(ThreadState *thr, uptr pc, uptr addr,
bool write_lock, bool create) {
if (jctx == 0 || addr < jctx->heap_begin
|| addr >= jctx->heap_begin + jctx->heap_size)
return 0;
BlockDesc *b = getblockbegin(addr);
DPrintf("#%d: GetJavaSync %p->%p\n", thr->tid, addr, b);
Lock l(&b->mtx);
SyncVar *s = b->head;
for (; s; s = s->next) {
if (s->addr == addr) {
DPrintf("#%d: found existing sync for %p\n", thr->tid, addr);
break;
}
}
if (s == 0 && create) {
DPrintf("#%d: creating new sync for %p\n", thr->tid, addr);
s = CTX()->synctab.Create(thr, pc, addr);
s->next = b->head;
b->head = s;
}
if (s) {
if (write_lock)
s->mtx.Lock();
else
s->mtx.ReadLock();
}
return s;
}
SyncVar* GetAndRemoveJavaSync(ThreadState *thr, uptr pc, uptr addr) {
// We do not destroy Java mutexes other than in __tsan_java_free().
return 0;
}
} // namespace __tsan
#define SCOPED_JAVA_FUNC(func) \
ThreadState *thr = cur_thread(); \
const uptr caller_pc = GET_CALLER_PC(); \
const uptr pc = (uptr)&func; \
(void)pc; \
ScopedJavaFunc scoped(thr, caller_pc); \
/**/
void __tsan_java_init(jptr heap_begin, jptr heap_size) {
SCOPED_JAVA_FUNC(__tsan_java_init);
DPrintf("#%d: java_init(%p, %p)\n", thr->tid, heap_begin, heap_size);
CHECK_EQ(jctx, 0);
CHECK_GT(heap_begin, 0);
CHECK_GT(heap_size, 0);
CHECK_EQ(heap_begin % kHeapAlignment, 0);
CHECK_EQ(heap_size % kHeapAlignment, 0);
CHECK_LT(heap_begin, heap_begin + heap_size);
jctx = new(jctx_buf) JavaContext(heap_begin, heap_size);
}
int __tsan_java_fini() {
SCOPED_JAVA_FUNC(__tsan_java_fini);
DPrintf("#%d: java_fini()\n", thr->tid);
CHECK_NE(jctx, 0);
// FIXME(dvyukov): this does not call atexit() callbacks.
int status = Finalize(thr);
DPrintf("#%d: java_fini() = %d\n", thr->tid, status);
return status;
}
void __tsan_java_alloc(jptr ptr, jptr size) {
SCOPED_JAVA_FUNC(__tsan_java_alloc);
DPrintf("#%d: java_alloc(%p, %p)\n", thr->tid, ptr, size);
CHECK_NE(jctx, 0);
CHECK_NE(size, 0);
CHECK_EQ(ptr % kHeapAlignment, 0);
CHECK_EQ(size % kHeapAlignment, 0);
CHECK_GE(ptr, jctx->heap_begin);
CHECK_LE(ptr + size, jctx->heap_begin + jctx->heap_size);
BlockDesc *b = getblock(ptr);
new(b) BlockDesc();
}
void __tsan_java_free(jptr ptr, jptr size) {
SCOPED_JAVA_FUNC(__tsan_java_free);
DPrintf("#%d: java_free(%p, %p)\n", thr->tid, ptr, size);
CHECK_NE(jctx, 0);
CHECK_NE(size, 0);
CHECK_EQ(ptr % kHeapAlignment, 0);
CHECK_EQ(size % kHeapAlignment, 0);
CHECK_GE(ptr, jctx->heap_begin);
CHECK_LE(ptr + size, jctx->heap_begin + jctx->heap_size);
BlockDesc *beg = getblock(ptr);
BlockDesc *end = getblock(ptr + size);
for (BlockDesc *b = beg; b != end; b++) {
if (b->begin)
b->~BlockDesc();
}
}
void __tsan_java_move(jptr src, jptr dst, jptr size) {
SCOPED_JAVA_FUNC(__tsan_java_move);
DPrintf("#%d: java_move(%p, %p, %p)\n", thr->tid, src, dst, size);
CHECK_NE(jctx, 0);
CHECK_NE(size, 0);
CHECK_EQ(src % kHeapAlignment, 0);
CHECK_EQ(dst % kHeapAlignment, 0);
CHECK_EQ(size % kHeapAlignment, 0);
CHECK_GE(src, jctx->heap_begin);
CHECK_LE(src + size, jctx->heap_begin + jctx->heap_size);
CHECK_GE(dst, jctx->heap_begin);
CHECK_LE(dst + size, jctx->heap_begin + jctx->heap_size);
CHECK(dst >= src + size || src >= dst + size);
// Assuming it's not running concurrently with threads that do
// memory accesses and mutex operations (stop-the-world phase).
{ // NOLINT
BlockDesc *s = getblock(src);
BlockDesc *d = getblock(dst);
BlockDesc *send = getblock(src + size);
for (; s != send; s++, d++) {
CHECK_EQ(d->begin, false);
if (s->begin) {
DPrintf("#%d: moving block %p->%p\n", thr->tid, getmem(s), getmem(d));
new(d) BlockDesc;
d->head = s->head;
for (SyncVar *sync = d->head; sync; sync = sync->next) {
uptr newaddr = sync->addr - src + dst;
DPrintf("#%d: moving sync %p->%p\n", thr->tid, sync->addr, newaddr);
sync->addr = newaddr;
}
s->head = 0;
s->~BlockDesc();
}
}
}
{ // NOLINT
u64 *s = (u64*)MemToShadow(src);
u64 *d = (u64*)MemToShadow(dst);
u64 *send = (u64*)MemToShadow(src + size);
for (; s != send; s++, d++) {
*d = *s;
*s = 0;
}
}
}
void __tsan_java_mutex_lock(jptr addr) {
SCOPED_JAVA_FUNC(__tsan_java_mutex_lock);
DPrintf("#%d: java_mutex_lock(%p)\n", thr->tid, addr);
CHECK_NE(jctx, 0);
CHECK_GE(addr, jctx->heap_begin);
CHECK_LT(addr, jctx->heap_begin + jctx->heap_size);
MutexLock(thr, pc, addr);
}
void __tsan_java_mutex_unlock(jptr addr) {
SCOPED_JAVA_FUNC(__tsan_java_mutex_unlock);
DPrintf("#%d: java_mutex_unlock(%p)\n", thr->tid, addr);
CHECK_NE(jctx, 0);
CHECK_GE(addr, jctx->heap_begin);
CHECK_LT(addr, jctx->heap_begin + jctx->heap_size);
MutexUnlock(thr, pc, addr);
}
void __tsan_java_mutex_read_lock(jptr addr) {
SCOPED_JAVA_FUNC(__tsan_java_mutex_read_lock);
DPrintf("#%d: java_mutex_read_lock(%p)\n", thr->tid, addr);
CHECK_NE(jctx, 0);
CHECK_GE(addr, jctx->heap_begin);
CHECK_LT(addr, jctx->heap_begin + jctx->heap_size);
MutexReadLock(thr, pc, addr);
}
void __tsan_java_mutex_read_unlock(jptr addr) {
SCOPED_JAVA_FUNC(__tsan_java_mutex_read_unlock);
DPrintf("#%d: java_mutex_read_unlock(%p)\n", thr->tid, addr);
CHECK_NE(jctx, 0);
CHECK_GE(addr, jctx->heap_begin);
CHECK_LT(addr, jctx->heap_begin + jctx->heap_size);
MutexReadUnlock(thr, pc, addr);
}