forked from OSchip/llvm-project
208 lines
6.8 KiB
Plaintext
208 lines
6.8 KiB
Plaintext
// Mac OS X 10.6 or higher only.
|
|
#include <dispatch/dispatch.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#import <CoreFoundation/CFBase.h>
|
|
#import <Foundation/NSObject.h>
|
|
|
|
void CFAllocatorDefaultDoubleFree() {
|
|
void *mem = CFAllocatorAllocate(kCFAllocatorDefault, 5, 0);
|
|
CFAllocatorDeallocate(kCFAllocatorDefault, mem);
|
|
CFAllocatorDeallocate(kCFAllocatorDefault, mem);
|
|
}
|
|
|
|
void CFAllocatorSystemDefaultDoubleFree() {
|
|
void *mem = CFAllocatorAllocate(kCFAllocatorSystemDefault, 5, 0);
|
|
CFAllocatorDeallocate(kCFAllocatorSystemDefault, mem);
|
|
CFAllocatorDeallocate(kCFAllocatorSystemDefault, mem);
|
|
}
|
|
|
|
void CFAllocatorMallocDoubleFree() {
|
|
void *mem = CFAllocatorAllocate(kCFAllocatorMalloc, 5, 0);
|
|
CFAllocatorDeallocate(kCFAllocatorMalloc, mem);
|
|
CFAllocatorDeallocate(kCFAllocatorMalloc, mem);
|
|
}
|
|
|
|
void CFAllocatorMallocZoneDoubleFree() {
|
|
void *mem = CFAllocatorAllocate(kCFAllocatorMallocZone, 5, 0);
|
|
CFAllocatorDeallocate(kCFAllocatorMallocZone, mem);
|
|
CFAllocatorDeallocate(kCFAllocatorMallocZone, mem);
|
|
}
|
|
|
|
__attribute__((noinline))
|
|
void access_memory(char *a) {
|
|
*a = 0;
|
|
}
|
|
|
|
// Test the +load instrumentation.
|
|
// Because the +load methods are invoked before anything else is initialized,
|
|
// it makes little sense to wrap the code below into a gTest test case.
|
|
// If AddressSanitizer doesn't instrument the +load method below correctly,
|
|
// everything will just crash.
|
|
|
|
char kStartupStr[] =
|
|
"If your test didn't crash, AddressSanitizer is instrumenting "
|
|
"the +load methods correctly.";
|
|
|
|
@interface LoadSomething : NSObject {
|
|
}
|
|
@end
|
|
|
|
@implementation LoadSomething
|
|
|
|
+(void) load {
|
|
for (int i = 0; i < strlen(kStartupStr); i++) {
|
|
access_memory(&kStartupStr[i]); // make sure no optimizations occur.
|
|
}
|
|
// Don't print anything here not to interfere with the death tests.
|
|
}
|
|
|
|
@end
|
|
|
|
void worker_do_alloc(int size) {
|
|
char * volatile mem = malloc(size);
|
|
mem[0] = 0; // Ok
|
|
free(mem);
|
|
}
|
|
|
|
void worker_do_crash(int size) {
|
|
char * volatile mem = malloc(size);
|
|
access_memory(&mem[size]); // BOOM
|
|
free(mem);
|
|
}
|
|
|
|
// Tests for the Grand Central Dispatch. See
|
|
// http://developer.apple.com/library/mac/#documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html
|
|
// for the reference.
|
|
|
|
void TestGCDDispatchAsync() {
|
|
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
|
|
dispatch_block_t block = ^{ worker_do_crash(1024); };
|
|
// dispatch_async() runs the task on a worker thread that does not go through
|
|
// pthread_create(). We need to verify that AddressSanitizer notices that the
|
|
// thread has started.
|
|
dispatch_async(queue, block);
|
|
// TODO(glider): this is hacky. Need to wait for the worker instead.
|
|
sleep(1);
|
|
}
|
|
|
|
void TestGCDDispatchSync() {
|
|
dispatch_queue_t queue = dispatch_get_global_queue(2, 0);
|
|
dispatch_block_t block = ^{ worker_do_crash(1024); };
|
|
// dispatch_sync() runs the task on a worker thread that does not go through
|
|
// pthread_create(). We need to verify that AddressSanitizer notices that the
|
|
// thread has started.
|
|
dispatch_sync(queue, block);
|
|
// TODO(glider): this is hacky. Need to wait for the worker instead.
|
|
sleep(1);
|
|
}
|
|
|
|
// libdispatch spawns a rather small number of threads and reuses them. We need
|
|
// to make sure AddressSanitizer handles the reusing correctly.
|
|
void TestGCDReuseWqthreadsAsync() {
|
|
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
|
|
dispatch_block_t block_alloc = ^{ worker_do_alloc(1024); };
|
|
dispatch_block_t block_crash = ^{ worker_do_crash(1024); };
|
|
for (int i = 0; i < 100; i++) {
|
|
dispatch_async(queue, block_alloc);
|
|
}
|
|
dispatch_async(queue, block_crash);
|
|
// TODO(glider): this is hacky. Need to wait for the workers instead.
|
|
sleep(1);
|
|
}
|
|
|
|
// Try to trigger abnormal behaviour of dispatch_sync() being unhandled by us.
|
|
void TestGCDReuseWqthreadsSync() {
|
|
dispatch_queue_t queue[4];
|
|
queue[0] = dispatch_get_global_queue(2, 0);
|
|
queue[1] = dispatch_get_global_queue(0, 0);
|
|
queue[2] = dispatch_get_global_queue(-2, 0);
|
|
queue[3] = dispatch_queue_create("my_queue", NULL);
|
|
dispatch_block_t block_alloc = ^{ worker_do_alloc(1024); };
|
|
dispatch_block_t block_crash = ^{ worker_do_crash(1024); };
|
|
for (int i = 0; i < 1000; i++) {
|
|
dispatch_sync(queue[i % 4], block_alloc);
|
|
}
|
|
dispatch_sync(queue[3], block_crash);
|
|
// TODO(glider): this is hacky. Need to wait for the workers instead.
|
|
sleep(1);
|
|
}
|
|
|
|
void TestGCDDispatchAfter() {
|
|
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
|
|
dispatch_block_t block_crash = ^{ worker_do_crash(1024); };
|
|
// Schedule the event one second from the current time.
|
|
dispatch_time_t milestone =
|
|
dispatch_time(DISPATCH_TIME_NOW, 1LL * NSEC_PER_SEC);
|
|
dispatch_after(milestone, queue, block_crash);
|
|
// Let's wait for a bit longer now.
|
|
// TODO(glider): this is still hacky.
|
|
sleep(2);
|
|
}
|
|
|
|
void worker_do_deallocate(void *ptr) {
|
|
free(ptr);
|
|
}
|
|
|
|
void CallFreeOnWorkqueue(void *tsd) {
|
|
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
|
|
dispatch_block_t block_dealloc = ^{ worker_do_deallocate(tsd); };
|
|
dispatch_async(queue, block_dealloc);
|
|
// Do not wait for the worker to free the memory -- nobody is going to touch
|
|
// it.
|
|
}
|
|
|
|
void TestGCDSourceEvent() {
|
|
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
|
|
dispatch_source_t timer =
|
|
dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
|
|
// Schedule the timer one second from the current time.
|
|
dispatch_time_t milestone =
|
|
dispatch_time(DISPATCH_TIME_NOW, 1LL * NSEC_PER_SEC);
|
|
|
|
dispatch_source_set_timer(timer, milestone, DISPATCH_TIME_FOREVER, 0);
|
|
char * volatile mem = malloc(10);
|
|
dispatch_source_set_event_handler(timer, ^{
|
|
access_memory(&mem[10]);
|
|
});
|
|
dispatch_resume(timer);
|
|
sleep(2);
|
|
}
|
|
|
|
void TestGCDSourceCancel() {
|
|
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
|
|
dispatch_source_t timer =
|
|
dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
|
|
// Schedule the timer one second from the current time.
|
|
dispatch_time_t milestone =
|
|
dispatch_time(DISPATCH_TIME_NOW, 1LL * NSEC_PER_SEC);
|
|
|
|
dispatch_source_set_timer(timer, milestone, DISPATCH_TIME_FOREVER, 0);
|
|
char * volatile mem = malloc(10);
|
|
// Both dispatch_source_set_cancel_handler() and
|
|
// dispatch_source_set_event_handler() use dispatch_barrier_async_f().
|
|
// It's tricky to test dispatch_source_set_cancel_handler() separately,
|
|
// so we test both here.
|
|
dispatch_source_set_event_handler(timer, ^{
|
|
dispatch_source_cancel(timer);
|
|
});
|
|
dispatch_source_set_cancel_handler(timer, ^{
|
|
access_memory(&mem[10]);
|
|
});
|
|
dispatch_resume(timer);
|
|
sleep(2);
|
|
}
|
|
|
|
void TestGCDGroupAsync() {
|
|
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
|
|
dispatch_group_t group = dispatch_group_create();
|
|
char * volatile mem = malloc(10);
|
|
dispatch_group_async(group, queue, ^{
|
|
access_memory(&mem[10]);
|
|
});
|
|
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
|
|
}
|