示例#1
0
/**
 * Show regions info
 * \param info Pointer to region information data
 */
static void _app_show_regions_info(struct regions_info *info)
{
	uint32_t boot_region = info->boot_region;
	void *app_addr = (void *)(APP_START(boot_region));
	bool app_valid = false;
	app_valid = region_check_valid(app_addr,
			info->length[ boot_region],
			info->signature[ boot_region]);

	dbg_print("==== Regions Information ====\r\n");

	dbg_print(": trigger flag: %s\r\n", trigger_modes_str[info->trigger]);
	dbg_print(": source file : %s\r\n", info->boot_file_name);

	dbg_print(":----------------------------\r\n");
	dbg_print(": region\t%8d\t%8d\r\n", 0, 1);
	dbg_print(": size  \t%8d\t%8d\r\n",
			(int)info->length[0], (int)info->length[1]);
	dbg_print(": sign  \t%8x\t%8x\r\n",
			(unsigned)info->signature[0],
			(unsigned)info->signature[1]);
	dbg_print(": boot  \t       %c\t       %c\r\n",
			boot_region ? ' ' : 'Y',
			boot_region ? 'Y' : ' ');
	dbg_print(": App @ %d, %x - data %x ...\r\n",
			(int)boot_region, (unsigned)app_addr,
			(unsigned)*(uint32_t *)app_addr);
	dbg_print("bl: App valid: %c\r\n", app_valid ? 'Y' : 'N');
}
示例#2
0
/**
 * Bootloader main entry
 */
int main(void)
{
	uint8_t boot_region = 0; /* Real boot region at this time */
#ifdef DBG_USE_USART
	uint8_t load_region = 0; /* Real region to put loaded data */
#endif
	struct regions_info info;
	void *app_addr = NULL;
	uint32_t app_size = 0;
	enum trigger_modes trigger;
	bool app_valid = false;

	wdt_disable(WDT);
	sysclk_init();
	board_init();

	/* First turn on the led to indicate the bootloader run. */
	ioport_set_pin_dir(DBG_LED_PIN, IOPORT_DIR_OUTPUT);
	ioport_set_pin_level(DBG_LED_PIN, DBG_LED_PIN_ON_LEVEL);

	dbg_init();
	dbg_print("\r\n\n----------------------\r\n");
	dbg_print("%s Bootloader\r\n", BOARD_NAME);
	dbg_print("Boot region: %x, size %dK\r\n", BOOT0_START, BOOT_SIZE /
			1024);
	dbg_print("App  region: %x, size %dK\r\n", APP0_START,
			(int)APP_SIZE / 1024);
	dbg_print(" - Code %dK + Info %dK\r\n", (int)APP_CODE_SIZE,
			(int)INFO_SIZE);
	dbg_print("----------------------\r\n");

	/* bootloader initialize */
	dbg_print("bl: init ...\r\n");
	trigger_init();
	memory_init();
	media_init(file_list, MEDIA_FILE_LIST_LEN);
	dbg_print("bl: init done\r\n");

	boot_region = _app_boot_get();
	dbg_print("bl: current boot region %d\r\n", (int)boot_region);

#ifdef MEM_LOCK_BOOT_REGION
	dbg_print("bl: lock boot region ...\r\n");
	memory_lock((void *)BOOT0_START, (void *)BOOT0_END);
	dbg_print("bl: lock boot region done\r\n");
#endif

	/* load regions information
	 * Single flash: from last page
	 * Dual flash (remap) : from last page of last physical flash
	 * Dual flash (mirror): from last page of boot flash
	 */
	dbg_print("bl: read regions info ...\r\n");
	region_info_read((void *)INFO_ADDR(true), &info); /* Read for boot */

	app_addr = (void *)(APP_START(boot_region));
	app_valid = region_check_valid(app_addr,
			info.length[boot_region], info.signature[boot_region]);
	dbg_print("bl: read regions info done\r\n");
	dbg_print("bl: trigger flag %s\r\n", trigger_modes_str[info.trigger]);
	dbg_print("bl: region\t%8d\t%8d\r\n", 0, 1);
	dbg_print("bl: size\t%8d\t%8d\r\n", (int)info.length[0],
			(int)info.length[1]);
	dbg_print("bl: sign\t%8x\t%8x\r\n", (unsigned)info.signature[0],
			(unsigned)info.signature[1]);
	dbg_print("bl: boot\t       %c\t       %c\r\n", boot_region ? ' ' : 'Y',
			boot_region ? 'Y' : ' ');
	dbg_print("bl: App @ %d, %x - data %x ...\r\n", (int)boot_region,
			(unsigned)app_addr, (unsigned)*(uint32_t *)app_addr);
	dbg_print("bl: App valid: %c\r\n", app_valid ? 'Y' : 'N');

#ifdef DBG_USE_INFO_EDIT
	/* InfoEdit */
	_app_info_edit(&info);
#endif

	/* bootloader trigger check */
	dbg_print("bl: trigger ...\r\n");
	trigger = trigger_poll(&info);
	dbg_print("bl: trigger mode %s\r\n", trigger_modes_str[trigger]);
	if (TRIGGER_BOOT == trigger) {
		goto main_run_app_check;
	}

	/* Now any other trigger load file to update application directly */
#ifdef DBG_USE_LED
	_app_led_blink(100, 1);
#endif
	/* Update media file information */
	if (info.boot_file_name[0]) {
		dbg_print("bl: boot file assigned, set it\r\n");
		media_set_file_name(info.boot_file_name);
	}

main_load_app:
	/* load new firmware */
#ifdef DBG_USE_USART
	load_region = boot_region;
	dbg_print("bl: download @ %d ...\r\n", load_region);
#endif
	app_size = _app_load(&info, true);
	if (app_size == 0) {
		_app_led_error();
		dbg_print("bl: download fail, retry\r\n");
		goto main_load_app;
	} else {
		dbg_print("bl: download done, size %d\r\n", (int)app_size);
	}

main_run_app_check:

	/* Is application valid */
	dbg_print("bl: check app @ %d is valid\r\n", (int)info.boot_region);
	app_valid = region_check_valid(app_addr, info.length[info.boot_region],
			info.signature[info.boot_region]);
	if (!app_valid) {
		dbg_print("bl: application is not valid\r\n");
		_app_led_error();
		dbg_print("bl: reload firmware\r\n");
		goto main_load_app;
	}

	dbg_print("bl: application is valid, run\r\n");


	/* Turn off the led before jump to the app. */
	_app_led_off(DBG_LED_PIN);

	/* cleanup */
	dbg_print("bl: cleanup ...\r\n");
	media_cleanup();
	trigger_cleanup();
	memory_cleanup();
	dbg_print("bl: cleanup done\r\n");

	/* load application */
	dbg_print("bl: load application ...\r\n\n");

#ifdef DBG_USE_USART
	delay_ms(50); /* Wait USART lines idle */

	dbg_cleanup();

	delay_ms(50);
#endif

	/* run application */
	_app_exec(app_addr);

	return (int)app_addr;
}
示例#3
0
/**
 * Receive new firmware
 *
 * \param info Regions information struct
 * \param no_partition Use single partition, do not split memory to app + buff
 *
 * \return received firmware size
 */
static uint32_t _app_load(struct regions_info *info, bool no_partition)
{
	void *addr, *info_addr;
	uint32_t *p_sign, *p_len;
	uint32_t rx_size;
	uint32_t target_region;
	uint32_t i = 0, flags = 0xFF;
	if (info->boot_region != 0 && info->boot_region != 1) {
		dbg_print("bl: Boot region information (%d) invalid\r\n",
				(int)info->boot_region);
		return 0;
	}

	/* Wait media connection */
	i = 0;
	while (i < MEDIA_NUM_MAX) {
		media_select((enum media_types)i);
		if (media_connect()) {
			dbg_print("bl: Source media %s is ready\r\n",
					media_get_type_str((enum media_types)i));
			break;
		} else if (flags & (1 << i)) {
			flags &= ~(1 << i);
			dbg_print("bl: Source media %s not ready\r\n",
					media_get_type_str((enum media_types)i));
		}

		i++;
	}
	target_region = no_partition ? info->boot_region : (!info->boot_region);

	addr      = (void *)APP_START(target_region);
	info_addr = (void *)INFO_ADDR(false);
	dbg_print("bl: Load to %x, info @ %x\r\n", (unsigned)addr,
			(unsigned)info_addr);

	dbg_print("bl: Unlock download buffer & info area ...\r\n");
	memory_unlock(addr, (void *)((uint32_t)addr + APP_CODE_SIZE - 1));
	memory_unlock(info_addr, (void *)((uint32_t)info_addr + INFO_SIZE - 1));
	dbg_print("bl: Unlock download buffer & info area done\r\n");

	dbg_print("bl: Erase download buffer & info area ...\r\n");
	memory_erase(     addr, APP_CODE_SIZE);
	memory_erase(info_addr, INFO_SIZE);
	dbg_print("bl: Erase download buffer & info area done\r\n");

#ifdef DBG_USE_LED
	_app_led_blink(50, 4);
	_app_led_on(DBG_LED_PIN);
#endif

	/* Clear SW force boot trigger */
#ifdef TRIGGER_USE_FLAG
	info->trigger = TRIGGER_BOOT;
#endif
	p_sign = &info->signature[target_region];
	p_len  = &info->length[target_region];
	rx_size = media_load_file(addr, APP_SIZE,
			(uint8_t *)app_mem_block_buf, MEM_BLOCK_SIZE,
			_app_save_block);
	if (rx_size) {
		*p_sign = region_signature(addr, rx_size);
		*p_len  = rx_size;
	} else {
		*p_sign = 0;
		*p_len  = 0;
	}

	/* Save region information */
	region_info_write(info_addr, info);

	dbg_print("bl: Lock download buffer & info area ...\r\n");
	memory_lock(addr, (void *)((uint32_t)addr + APP_CODE_SIZE - 1));
	memory_lock(info_addr, (void *)((uint32_t)info_addr + INFO_SIZE - 1));
	dbg_print("bl: Lock download buffer & info area done\r\n");

#ifdef DBG_USE_LED
	_app_led_off(DBG_LED_PIN);
#endif

#if DUMP_ENABLE
	_dump_data("Downloaded APP:\r\n", addr, rx_size, true);
	_dump_data("INFO:\r\n", info_addr, INFO_SIZE, true);
#endif
	return rx_size;
}
示例#4
0
int main(int argc, char **argv)
{
	struct option long_options[] = {
		// These options don't set a flag
		{"help",                      no_argument,       NULL, 'h'},
		{"duration",                  required_argument, NULL, 'd'},
		{"initial-size",              required_argument, NULL, 'i'},
		{"num-threads",               required_argument, NULL, 'n'},
		{"range",                     required_argument, NULL, 'r'},
		{"seed",                      required_argument, NULL, 's'},
		{"update-rate",               required_argument, NULL, 'u'},
		{"move-rate",                 required_argument, NULL, 'm'},
		{"snapshot-rate",             required_argument, NULL, 'a'},
		{"elasticity",                required_argument, NULL, 'x'},
		{NULL, 0, NULL, 0}
	};
	
	avl_intset_t *set;
	int i, j, c, size, tree_size;
	val_t last = 0; 
	val_t val = 0;
	unsigned long reads, effreads, updates, effupds, moves, moved, snapshots, 
	snapshoted, aborts, aborts_locked_read, aborts_locked_write, 
	aborts_validate_read, aborts_validate_write, aborts_validate_commit, 
	aborts_invalid_memory, aborts_double_write,
	max_retries, failures_because_contention;
	pthread_attr_t attr;
	thread_data_t *data;
	maintenance_thread_data_t *maintenance_data;
	pthread_t *threads;
	pthread_t *maintenance_threads;
	barrier_t barrier;
	struct timeval start, end;
	struct timespec timeout;
	int duration = DEFAULT_DURATION;
	int initial = DEFAULT_INITIAL;
	int nb_threads = DEFAULT_NB_THREADS;
	int nb_maintenance_threads = DEFAULT_NB_MAINTENANCE_THREADS;
	long range = DEFAULT_RANGE;
	int seed = DEFAULT_SEED;
	int update = DEFAULT_UPDATE;
	int load_factor = DEFAULT_LOAD;
	int move = DEFAULT_MOVE;
	int snapshot = DEFAULT_SNAPSHOT;
	int unit_tx = DEFAULT_ELASTICITY;
	int alternate = DEFAULT_ALTERNATE;
	int effective = DEFAULT_EFFECTIVE;
	sigset_t block_set;
	
	while(1) {
		i = 0;
		c = getopt_long(argc, argv, "hAf:d:i:n:r:s:u:m:a:l:x:", long_options, &i);
		
		if(c == -1)
			break;
		
		if(c == 0 && long_options[i].flag == 0)
			c = long_options[i].val;
		
		switch(c) {
				case 0:
					// Flag is automatically set 
					break;
				case 'h':
					printf("intset -- STM stress test "
								 "(hash table)\n"
								 "\n"
								 "Usage:\n"
								 "  intset [options...]\n"
								 "\n"
								 "Options:\n"
								 "  -h, --help\n"
								 "        Print this message\n"
								 "  -A, --Alternate\n"
								 "        Consecutive insert/remove target the same value\n"
								 "  -f, --effective <int>\n"
								 "        update txs must effectively write (0=trial, 1=effective, default=" XSTR(DEFAULT_EFFECTIVE) ")\n"
								 "  -d, --duration <int>\n"
								 "        Test duration in milliseconds (0=infinite, default=" XSTR(DEFAULT_DURATION) ")\n"
								 "  -i, --initial-size <int>\n"
								 "        Number of elements to insert before test (default=" XSTR(DEFAULT_INITIAL) ")\n"
								 "  -n, --num-threads <int>\n"
								 "        Number of threads (default=" XSTR(DEFAULT_NB_THREADS) ")\n"
								 "  -r, --range <int>\n"
								 "        Range of integer values inserted in set (default=" XSTR(DEFAULT_RANGE) ")\n"
								 "  -s, --seed <int>\n"
								 "        RNG seed (0=time-based, default=" XSTR(DEFAULT_SEED) ")\n"
								 "  -u, --update-rate <int>\n"
								 "        Percentage of update transactions (default=" XSTR(DEFAULT_UPDATE) ")\n"
								 "  -m , --move-rate <int>\n"
								 "        Percentage of move transactions (default=" XSTR(DEFAULT_MOVE) ")\n"
								 "  -a , --snapshot-rate <int>\n"
								 "        Percentage of snapshot transactions (default=" XSTR(DEFAULT_SNAPSHOT) ")\n"
								 "  -l , --load-factor <int>\n"
								 "        Ratio of keys over buckets (default=" XSTR(DEFAULT_LOAD) ")\n"
								 "  -x, --elasticity (default=4)\n"
								 "        Use elastic transactions\n"
								 "        0 = non-protected,\n"
								 "        1 = normal transaction,\n"
								 "        2 = read elastic-tx,\n"
								 "        3 = read/add elastic-tx,\n"
								 "        4 = read/add/rem elastic-tx,\n"
								 "        5 = elastic-tx w/ optimized move.\n"
								 );
					exit(0);
				case 'A':
					alternate = 1;
					break;
				case 'f':
					effective = atoi(optarg);
					break;
				case 'd':
					duration = atoi(optarg);
					break;
				case 'i':
					initial = atoi(optarg);
					break;
				case 'n':
					nb_threads = atoi(optarg);
					break;
				case 'r':
					range = atol(optarg);
					break;
				case 's':
					seed = atoi(optarg);
					break;
				case 'u':
					update = atoi(optarg);
					break;
				case 'm':
					move = atoi(optarg);
					break;
				case 'a':
					snapshot = atoi(optarg);
					break;
				case 'l':
					load_factor = atoi(optarg);
					break;
				case 'x':
					unit_tx = atoi(optarg);
					break;
				case '?':
					printf("Use -h or --help for help\n");
					exit(0);
				default:
					exit(1);
		}
	}
	
	assert(duration >= 0);
	assert(initial >= 0);
	assert(nb_threads > 0);
	assert(range > 0 && range >= initial);
	assert(update >= 0 && update <= 100);
	assert(move >= 0 && move <= update);
	assert(snapshot >= 0 && snapshot <= (100-update));
	assert(initial < MAXHTLENGTH);
	assert(initial >= load_factor);
	
	printf("Set type     : avl tree\n");
	printf("Duration     : %d\n", duration);
	printf("Initial size : %d\n", initial);
	printf("Nb threads   : %d\n", nb_threads);
	printf("Nb mt threads: %d\n", nb_maintenance_threads);
	printf("Value range  : %ld\n", range);
	printf("Seed         : %d\n", seed);
	printf("Update rate  : %d\n", update);
	printf("Load factor  : %d\n", load_factor);
	printf("Move rate    : %d\n", move);
	printf("Snapshot rate: %d\n", snapshot);
	printf("Elasticity   : %d\n", unit_tx);
	printf("Alternate    : %d\n", alternate);	
	printf("Effective    : %d\n", effective);
	printf("Type sizes   : int=%d/long=%d/ptr=%d/word=%d\n",
				 (int)sizeof(int),
				 (int)sizeof(long),
				 (int)sizeof(void *),
				 (int)sizeof(uintptr_t));
	
	timeout.tv_sec = duration / 1000;
	timeout.tv_nsec = (duration % 1000) * 1000000;
	
	if ((data = (thread_data_t *)malloc(nb_threads * sizeof(thread_data_t))) == NULL) {
		perror("malloc");
		exit(1);
	}
	if ((threads = (pthread_t *)malloc(nb_threads * sizeof(pthread_t))) == NULL) {
		perror("malloc");
		exit(1);
	}


	if ((maintenance_data = (maintenance_thread_data_t *)malloc(nb_maintenance_threads * sizeof(maintenance_thread_data_t))) == NULL) {
		perror("malloc");
		exit(1);
	}
	if ((maintenance_threads = (pthread_t *)malloc(nb_maintenance_threads * sizeof(pthread_t))) == NULL) {
		perror("malloc");
		exit(1);
	}


	
	if (seed == 0)
		srand((int)time(0));
	else
		srand(seed);
	
	maxhtlength = (unsigned int) initial / load_factor;

	set = avl_set_new_alloc(0, nb_threads);	
	//set->stop = &stop;

	stop = 0;
	
	// Init STM 
	printf("Initializing STM\n");
	
	TM_STARTUP();
	
	// Populate set 
	printf("Adding %d entries to set\n", initial);
	i = 0;
	maxhtlength = (int) (initial / load_factor);
	while (i < initial) {
		val = rand_range(range);
		if (avl_add(set, val, 0, 0) > 0) {
		  last = val;
		  i++;			
		}
	}
	size = avl_set_size(set);
	validate_avltree(set, range);

	printf("Set size     : %d\n", size);
	tree_size = avl_tree_size(set);
	printf("Tree size     : %d\n", tree_size);
	
	// Access set from all threads 
	barrier_init(&barrier, nb_threads + nb_maintenance_threads + 1);
	pthread_attr_init(&attr);
	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
	for (i = 0; i < nb_threads; i++) {
		printf("Creating thread %d\n", i);
		data[i].id = i;
		data[i].first = last;
		data[i].range = range;
		data[i].update = update;
		data[i].load_factor = load_factor;
		data[i].move = move;
		data[i].snapshot = snapshot;
		data[i].unit_tx = unit_tx;
		data[i].alternate = alternate;
		data[i].effective = effective;
		data[i].nb_add = 0;
		data[i].nb_added = 0;
		data[i].nb_remove = 0;
		data[i].nb_removed = 0;
		data[i].nb_move = 0;
		data[i].nb_moved = 0;
		data[i].nb_snapshot = 0;
		data[i].nb_snapshoted = 0;
		data[i].nb_contains = 0;
		data[i].nb_found = 0;
		data[i].nb_aborts = 0;
		data[i].nb_aborts_locked_read = 0;
		data[i].nb_aborts_locked_write = 0;
		data[i].nb_aborts_validate_read = 0;
		data[i].nb_aborts_validate_write = 0;
		data[i].nb_aborts_validate_commit = 0;
		data[i].nb_aborts_invalid_memory = 0;
		data[i].nb_aborts_double_write = 0;
		data[i].max_retries = 0;
		data[i].seed = rand();
		data[i].set = set;
		data[i].barrier = &barrier;
		data[i].failures_because_contention = 0;
		if (pthread_create(&threads[i], &attr, test, (void *)(&data[i])) != 0) {
			fprintf(stderr, "Error creating thread\n");
			exit(1);
		}
	}

	for (i = 0; i < nb_maintenance_threads; i++) {
	        maintenance_data[i].nb_maint = nb_maintenance_threads;
		maintenance_data[i].id = i;
		maintenance_data[i].nb_removed = 0;
		maintenance_data[i].nb_rotated = 0;
		maintenance_data[i].nb_suc_rotated = 0;
		maintenance_data[i].nb_propagated = 0;
		maintenance_data[i].nb_suc_propagated = 0;
		maintenance_data[i].nb_aborts = 0;
		maintenance_data[i].nb_aborts_locked_read = 0;
		maintenance_data[i].nb_aborts_locked_write = 0;
		maintenance_data[i].nb_aborts_validate_read = 0;
		maintenance_data[i].nb_aborts_validate_write = 0;
		maintenance_data[i].nb_aborts_validate_commit = 0;
		maintenance_data[i].nb_aborts_invalid_memory = 0;
		maintenance_data[i].nb_aborts_double_write = 0;
		maintenance_data[i].max_retries = 0;
		maintenance_data[i].t_data = data;
		maintenance_data[i].nb_threads = nb_threads;

		maintenance_data[i].set = set;
		maintenance_data[i].barrier = &barrier;

		if ((maintenance_data[i].t_nb_trans = (unsigned long *)malloc(nb_threads * sizeof(unsigned long))) == NULL) {
		  perror("malloc");
		  exit(1);
		}
		for(j = 0; j < nb_threads; j++) {
		  maintenance_data[i].t_nb_trans[j] = 0;
		}
		if ((maintenance_data[i].t_nb_trans_old = (unsigned long *)malloc(nb_threads * sizeof(unsigned long))) == NULL) {
		  perror("malloc");
		  exit(1);
		}
		for(j = 0; j < nb_threads; j++) {
		  maintenance_data[i].t_nb_trans_old[j] = 0;
		}

		printf("Creating maintenance thread %d\n", i);
		if (pthread_create(&maintenance_threads[i], &attr, test_maintenance, (void *)(&maintenance_data[i])) != 0) {
			fprintf(stderr, "Error creating thread\n");
			exit(1);
		}
	}


	pthread_attr_destroy(&attr);

	
	
	APP_START();
        // Start threads 
	barrier_cross(&barrier);
	
	printf("STARTING...\n");
	gettimeofday(&start, NULL);
	if (duration > 0) {
		nanosleep(&timeout, NULL);
	} else {
		sigemptyset(&block_set);
		sigsuspend(&block_set);
	}
	AO_store_full(&stop, 1);
	gettimeofday(&end, NULL);
	printf("STOPPING...\n");
	
	//print_avltree(set);
	
	
	// Wait for thread completion 
	for (i = 0; i < nb_threads; i++) {
		if (pthread_join(threads[i], NULL) != 0) {
			fprintf(stderr, "Error waiting for thread completion\n");
			exit(1);
		}
	}

	// Wait for maintenance thread completion 
	for (i = 0; i < nb_maintenance_threads; i++) {
		if (pthread_join(maintenance_threads[i], NULL) != 0) {
			fprintf(stderr, "Error waiting for maintenance thread completion\n");
			exit(1);
		}
	}
        

        APP_END();

	duration = (end.tv_sec * 1000 + end.tv_usec / 1000) - (start.tv_sec * 1000 + start.tv_usec / 1000);
	aborts = 0;
	aborts_locked_read = 0;
	aborts_locked_write = 0;
	aborts_validate_read = 0;
	aborts_validate_write = 0;
	aborts_validate_commit = 0;
	aborts_invalid_memory = 0;
	aborts_double_write = 0;
	failures_because_contention = 0;
	reads = 0;
	effreads = 0;
	updates = 0;
	effupds = 0;
	moves = 0;
	moved = 0;
	snapshots = 0;
	snapshoted = 0;
	max_retries = 0;
	for (i = 0; i < nb_threads; i++) {
		printf("Thread %d\n", i);
		printf("  #add        : %lu\n", data[i].nb_add);
		printf("    #added    : %lu\n", data[i].nb_added);
		printf("  #remove     : %lu\n", data[i].nb_remove);
		printf("    #removed  : %lu\n", data[i].nb_removed);
		printf("  #contains   : %lu\n", data[i].nb_contains);
		printf("    #found    : %lu\n", data[i].nb_found);
		printf("  #move       : %lu\n", data[i].nb_move);
		printf("  #moved      : %lu\n", data[i].nb_moved);
		printf("  #snapshot   : %lu\n", data[i].nb_snapshot);
		printf("  #snapshoted : %lu\n", data[i].nb_snapshoted);
		printf("  #aborts     : %lu\n", data[i].nb_aborts);
		printf("    #lock-r   : %lu\n", data[i].nb_aborts_locked_read);
		printf("    #lock-w   : %lu\n", data[i].nb_aborts_locked_write);
		printf("    #val-r    : %lu\n", data[i].nb_aborts_validate_read);
		printf("    #val-w    : %lu\n", data[i].nb_aborts_validate_write);
		printf("    #val-c    : %lu\n", data[i].nb_aborts_validate_commit);
		printf("    #inv-mem  : %lu\n", data[i].nb_aborts_invalid_memory);
		printf("    #dup-w  : %lu\n", data[i].nb_aborts_double_write);
		printf("    #failures : %lu\n", data[i].failures_because_contention);
		printf("  Max retries : %lu\n", data[i].max_retries);

		printf("  #set read trans reads: %lu\n", data[i].set_read_reads);
		printf("  #set write trans reads: %lu\n", data[i].set_write_reads);
		printf("  #set write trans writes: %lu\n", data[i].set_write_writes);
		printf("  #set trans reads: %lu\n", data[i].set_reads);
		printf("  #set trans writes: %lu\n", data[i].set_writes);
		printf("  set max reads: %lu\n", data[i].set_max_reads);
		printf("  set max writes: %lu\n", data[i].set_max_writes);

		printf("  #read trans reads: %lu\n", data[i].read_reads);
		printf("  #write trans reads: %lu\n", data[i].write_reads);
		printf("  #write trans writes: %lu\n", data[i].write_writes);
		printf("  #trans reads: %lu\n", data[i].reads);
		printf("  #trans writes: %lu\n", data[i].writes);
		printf("  max reads: %lu\n", data[i].max_reads);
		printf("  max writes: %lu\n", data[i].max_writes);
		aborts += data[i].nb_aborts;
		aborts_locked_read += data[i].nb_aborts_locked_read;
		aborts_locked_write += data[i].nb_aborts_locked_write;
		aborts_validate_read += data[i].nb_aborts_validate_read;
		aborts_validate_write += data[i].nb_aborts_validate_write;
		aborts_validate_commit += data[i].nb_aborts_validate_commit;
		aborts_invalid_memory += data[i].nb_aborts_invalid_memory;
		aborts_double_write += data[i].nb_aborts_double_write;
		failures_because_contention += data[i].failures_because_contention;
		reads += data[i].nb_contains;
		effreads += data[i].nb_contains + 
		(data[i].nb_add - data[i].nb_added) + 
		(data[i].nb_remove - data[i].nb_removed) + 
		(data[i].nb_move - data[i].nb_moved) +
		data[i].nb_snapshoted;
		updates += (data[i].nb_add + data[i].nb_remove + data[i].nb_move);
		effupds += data[i].nb_removed + data[i].nb_added + data[i].nb_moved; 
		moves += data[i].nb_move;
		moved += data[i].nb_moved;
		snapshots += data[i].nb_snapshot;
		snapshoted += data[i].nb_snapshoted;
		size += data[i].nb_added - data[i].nb_removed;
		if (max_retries < data[i].max_retries)
			max_retries = data[i].max_retries;
	}



	for (i = 0; i < nb_maintenance_threads; i++) {
		printf("Maintenance thread %d\n", i);
		printf("  #removed %lu\n", set->nb_removed);
		printf("  #rotated %lu\n", set->nb_rotated);
		printf("  #rotated sucs %lu\n", set->nb_suc_rotated);
		printf("  #propogated %lu\n", set->nb_propogated);
		printf("  #propogated sucs %lu\n", set->nb_suc_propogated);
		printf("  #aborts     : %lu\n", maintenance_data[i].nb_aborts);
		printf("    #lock-r   : %lu\n", maintenance_data[i].nb_aborts_locked_read);
		printf("    #lock-w   : %lu\n", maintenance_data[i].nb_aborts_locked_write);
		printf("    #val-r    : %lu\n", maintenance_data[i].nb_aborts_validate_read);
		printf("    #val-w    : %lu\n", maintenance_data[i].nb_aborts_validate_write);
		printf("    #val-c    : %lu\n", maintenance_data[i].nb_aborts_validate_commit);
		printf("    #inv-mem  : %lu\n", maintenance_data[i].nb_aborts_invalid_memory);
		printf("    #dup-w    : %lu\n", maintenance_data[i].nb_aborts_double_write);
		//printf("    #failures : %lu\n", maintenance_data[i].failures_because_contention);
		printf("  Max retries : %lu\n", maintenance_data[i].max_retries);
	}


	printf("Set size      : %d (expected: %d)\n", avl_set_size(set), size);
	printf("Tree size      : %d\n", avl_tree_size(set));

	validate_avltree(set, range);

	printf("Duration      : %d (ms)\n", duration);
	printf("#txs          : %lu (%f / s)\n", reads + updates + snapshots, (reads + updates + snapshots) * 1000.0 / duration);
	
	printf("#read txs     : ");
	if (effective) {
		printf("%lu (%f / s)\n", effreads, effreads * 1000.0 / duration);
		printf("  #cont/snpsht: %lu (%f / s)\n", reads, reads * 1000.0 / duration);
	} else printf("%lu (%f / s)\n", reads, reads * 1000.0 / duration);
	
	printf("#eff. upd rate: %f \n", 100.0 * effupds / (effupds + effreads));
	
	printf("#update txs   : ");
	if (effective) {
		printf("%lu (%f / s)\n", effupds, effupds * 1000.0 / duration);
		printf("  #upd trials : %lu (%f / s)\n", updates, updates * 1000.0 / 
					 duration);
	} else printf("%lu (%f / s)\n", updates, updates * 1000.0 / duration);
	
	printf("#move txs     : %lu (%f / s)\n", moves, moves * 1000.0 / duration);
	printf("  #moved      : %lu (%f / s)\n", moved, moved * 1000.0 / duration);
	printf("#snapshot txs : %lu (%f / s)\n", snapshots, snapshots * 1000.0 / duration);
	printf("  #snapshoted : %lu (%f / s)\n", snapshoted, snapshoted * 1000.0 / duration);
	printf("#aborts       : %lu (%f / s)\n", aborts, aborts * 1000.0 / duration);
	printf("  #lock-r     : %lu (%f / s)\n", aborts_locked_read, aborts_locked_read * 1000.0 / duration);
	printf("  #lock-w     : %lu (%f / s)\n", aborts_locked_write, aborts_locked_write * 1000.0 / duration);
	printf("  #val-r      : %lu (%f / s)\n", aborts_validate_read, aborts_validate_read * 1000.0 / duration);
	printf("  #val-w      : %lu (%f / s)\n", aborts_validate_write, aborts_validate_write * 1000.0 / duration);
	printf("  #val-c      : %lu (%f / s)\n", aborts_validate_commit, aborts_validate_commit * 1000.0 / duration);
	printf("  #inv-mem    : %lu (%f / s)\n", aborts_invalid_memory, aborts_invalid_memory * 1000.0 / duration);
	printf("  #dup-w      : %lu (%f / s)\n", aborts_double_write, aborts_double_write * 1000.0 / duration);
	printf("  #failures   : %lu\n",  failures_because_contention);
	printf("Max retries   : %lu\n", max_retries);
	
	// Delete set 
	avl_set_delete(set);
	
	// Cleanup STM 
	TM_SHUTDOWN();
	
	free(threads);
	free(data);

	free(maintenance_threads);
	free(maintenance_data);
	
	return 0;
}