#include "AutoDefs.h"
#include "AutoMemoryScanner.h"
#include "AutoThread.h"
#include "AutoZone.h"
#if defined(__ppc__) || defined(__ppc64__)
#include <architecture/ppc/cframe.h>
#elif defined(__i386__)
#define C_RED_ZONE 0
#elif defined(__x86_64__)
#define C_RED_ZONE 128
#else
#error Unknown Architecture
#endif
extern "C" char *__crashreporter_info__;
namespace Auto {
void Thread::scan_current_thread(MemoryScanner &scanner) {
NonVolatileRegisters registers;
Range range = registers.buffer_range();
scanner.scan_range_from_registers(range, this, 0);
range.set_range(scanner.current_stack_bottom(), _stack);
scanner.scan_range_from_thread(range, this);
}
union ThreadState {
#if defined(__i386__)
i386_thread_state_t regs;
#define THREAD_STATE_COUNT i386_THREAD_STATE_COUNT
#define THREAD_STATE_FLAVOR i386_THREAD_STATE
#define THREAD_STATE_SP __esp
#elif defined(__ppc__)
ppc_thread_state_t regs;
#define THREAD_STATE_COUNT PPC_THREAD_STATE_COUNT
#define THREAD_STATE_FLAVOR PPC_THREAD_STATE
#define THREAD_STATE_SP __r1
#elif defined(__ppc64__)
ppc_thread_state64_t regs;
#define THREAD_STATE_COUNT PPC_THREAD_STATE64_COUNT
#define THREAD_STATE_FLAVOR PPC_THREAD_STATE64
#define THREAD_STATE_SP __r1
#elif defined(__x86_64__)
x86_thread_state64_t regs;
#define THREAD_STATE_COUNT x86_THREAD_STATE64_COUNT
#define THREAD_STATE_FLAVOR x86_THREAD_STATE64
#define THREAD_STATE_SP __rsp
#else
#error Unknown Architecture
#endif
thread_state_data_t data;
void* get_stack_pointer() {
return reinterpret_cast<void*>(regs.THREAD_STATE_SP - C_RED_ZONE);
}
};
void Thread::scan_other_thread(MemoryScanner &scanner) {
if (_deadPort) return;
unsigned user_count = THREAD_STATE_COUNT;
thread_state_flavor_t flavor = THREAD_STATE_FLAVOR;
ThreadState state;
kern_return_t err = thread_get_state(_thread, flavor, state.data, &user_count);
int retryCount = 0;
while (err == KERN_ABORTED && retryCount < 10) {
retryCount++;
err = thread_get_state(_thread, flavor, state.data, &user_count);
}
if (err == MACH_SEND_INVALID_DEST) { malloc_printf("*** %s: mach thread port invalid, cannot scan registers or stack\n", prelude());
_deadPort = true;
} else if (err) {
static char buffer[256];
snprintf(buffer, sizeof(buffer), "scan_other_thread: unable to get thread state: err = %d, this = %p, _thread = %p, _exiting = %s\n", err, this, (void*)_thread, _exiting ? "YES" : "NO");
__crashreporter_info__ = buffer;
__builtin_trap();
} else {
Range range((void *)state.data, user_count * sizeof(natural_t));
scanner.scan_range_from_registers(range, this, 0);
range.set_range(state.get_stack_pointer(), _stack);
scanner.scan_range_from_thread(range, this);
}
}
void Thread::scan_thread_with_suspend_and_closure(MemoryScanner &scanner) {
if (_deadPort) return;
suspend();
if (is_current_thread()) scan_current_thread(scanner);
else scan_other_thread(scanner);
scanner.scan_pending_until_done();
resume();
}
void Thread::scan_thread_without_suspend(MemoryScanner &scanner) {
if (_deadPort) return;
if (is_current_thread()) scan_current_thread(scanner);
else scan_other_thread(scanner);
}
bool Thread::suspend() {
if (is_current_thread()) return true;
if (_suspended == 0) {
kern_return_t err = thread_suspend(_thread);
if (err != KERN_SUCCESS) {
if (!_exiting) {
static char buffer[256];
snprintf(buffer, sizeof(buffer), "Thread::suspend: unable to suspend a thread: err = %d, this = %p, _thread = %p\n", err, this, (void*)_thread);
__crashreporter_info__ = buffer;
__builtin_trap();
}
return false;
}
}
_suspended++;
return true;
}
bool Thread::resume() {
if (is_current_thread()) return true;
if (_suspended == 1) {
kern_return_t err = thread_resume(_thread);
if (err != KERN_SUCCESS) {
if (!_exiting) malloc_printf("*** %s: unable to resume a thread, err = %d\n", prelude(), err);
return false;
}
}
_suspended--;
return true;
}
void Thread::destroy_registered_thread(void *data) {
Thread *thread = (Thread *)data;
pthread_setspecific(thread->_zone->registered_thread_key(), thread);
thread->_exiting = true;
}
extern "C" void auto_print_registered_threads() {
Zone *zone = Zone::zone();
SpinLock lock(zone->threads_lock());
Thread *thread = zone->threads();
while (thread != NULL) {
malloc_printf("thread = 0x%x, is_exiting = %s, _deadPort = %s\n", thread->thread(), thread->is_exiting() ? "YES" : "NO", thread->deadPort() ? "YES" : "NO");
thread = thread->next();
}
}
};