diff --git a/build/config_headers/kernel_debug_config.h b/build/config_headers/kernel_debug_config.h index dee6ff1c9a..1fae90e846 100644 --- a/build/config_headers/kernel_debug_config.h +++ b/build/config_headers/kernel_debug_config.h @@ -1,42 +1,93 @@ #ifndef KERNEL_DEBUG_CONFIG_H #define KERNEL_DEBUG_CONFIG_H +// Master switch: +// 0: Disables all debug code that hasn't been enabled otherwise. +// 1: Enables some lightweight debug code. +// 2: Enables more debug code. Will impact performance. +#define KDEBUG_LEVEL 2 + +#define KDEBUG_LEVEL_2 (KDEBUG_LEVEL >= 2) +#define KDEBUG_LEVEL_1 (KDEBUG_LEVEL >= 1) +#define KDEBUG_LEVEL_0 (KDEBUG_LEVEL >= 0) + // general kernel debugging // Enables kernel ASSERT()s and various checks, locking primitives aren't // benaphore-style. -#define KDEBUG 1 +#define KDEBUG KDEBUG_LEVEL_2 // Enable this to get support for kernel breakpoints. -//#define KERNEL_BREAKPOINTS 1 +#define KERNEL_BREAKPOINTS 0 -// block cache +// block/file cache // Enables debugger commands. -#define DEBUG_BLOCK_CACHE +#define DEBUG_BLOCK_CACHE KDEBUG_LEVEL_1 // Enables checks that non-dirty blocks really aren't changed. Seriously // degrades performance when the block cache is used heavily. -#define BLOCK_CACHE_DEBUG_CHANGED +#define BLOCK_CACHE_DEBUG_CHANGED KDEBUG_LEVEL_2 + +// Enables a global list of file maps and related debugger commands. +#define DEBUG_FILE_MAP KDEBUG_LEVEL_1 + + +// heap + +// Initialize newly allocated memory with something non zero. +#define PARANOID_KERNEL_MALLOC KDEBUG_LEVEL_2 + +// Check for double free, and fill freed memory with 0xdeadbeef. +#define PARANOID_KERNEL_FREE KDEBUG_LEVEL_2 + +// Validate sanity of the heap after each operation (slow!). +#define PARANOID_HEAP_VALIDATION 0 + +// Store size, thread and team info at the end of each allocation block. +// Enables the "allocations*" debugger commands. +#define KERNEL_HEAP_LEAK_CHECK 0 + + +// interrupts + +// Adds statistics and unhandled counter per interrupts. Enables the "ints" +// debugger command. +#define DEBUG_INTERRUPTS KDEBUG_LEVEL_1 + + +// semaphores + +// Enables tracking of the last threads that acquired/released a semaphore. +#define DEBUG_SEM_LAST_ACQUIRER KDEBUG_LEVEL_1 + + +// SMP + +// Enables spinlock caller debugging. When acquiring a spinlock twice on a +// non-SMP machine, this will give a clue who locked it the first time. +// Furthermore (also on SMP machines) the "spinlock" debugger command will be +// available. +#define DEBUG_SPINLOCKS KDEBUG_LEVEL_2 // VM // Enables the vm_page::queue, i.e. it is tracked which queue the page should // be in. -//#define DEBUG_PAGE_QUEUE 1 +#define DEBUG_PAGE_QUEUE 0 // Enables extra debug fields in the vm_page used to track page transitions // between caches. -//#define DEBUG_PAGE_CACHE_TRANSITIONS 1 +#define DEBUG_PAGE_CACHE_TRANSITIONS 0 // Enables a global list of all vm_cache structures. -//#define DEBUG_CACHE_LIST 1 +#define DEBUG_CACHE_LIST KDEBUG_LEVEL_1 // Enables swap support. -#define ENABLE_SWAP_SUPPORT 1 +#define ENABLE_SWAP_SUPPORT 1 // When set limits the amount of available RAM (in MB). //#define LIMIT_AVAILABLE_MEMORY 256 diff --git a/headers/private/kernel/heap.h b/headers/private/kernel/heap.h index 9e514297f6..c0b7980365 100644 --- a/headers/private/kernel/heap.h +++ b/headers/private/kernel/heap.h @@ -10,6 +10,9 @@ #include +#include "kernel_debug_config.h" + + // allocate 16MB initial heap for the kernel #define INITIAL_HEAP_SIZE 16 * 1024 * 1024 // grow by another 4MB each time the heap runs out of memory @@ -19,9 +22,6 @@ // use areas for allocations bigger than 1MB #define HEAP_AREA_USE_THRESHOLD 1 * 1024 * 1024 -// store size, thread and team info at the end of each allocation block -#define KERNEL_HEAP_LEAK_CHECK 0 - typedef struct heap_class_s { const char *name; diff --git a/headers/private/kernel/vm_types.h b/headers/private/kernel/vm_types.h index 4ab232e39f..55f4491c65 100644 --- a/headers/private/kernel/vm_types.h +++ b/headers/private/kernel/vm_types.h @@ -83,11 +83,11 @@ struct vm_page { vm_page_mappings mappings; -#ifdef DEBUG_PAGE_QUEUE +#if DEBUG_PAGE_QUEUE void* queue; #endif -#ifdef DEBUG_PAGE_CACHE_TRANSITIONS +#if DEBUG_PAGE_CACHE_TRANSITIONS uint32 debug_flags; struct vm_page *collided_page; #endif diff --git a/src/system/kernel/cache/block_cache.cpp b/src/system/kernel/cache/block_cache.cpp index 9bb6a7f5f9..18df3486e8 100644 --- a/src/system/kernel/cache/block_cache.cpp +++ b/src/system/kernel/cache/block_cache.cpp @@ -65,7 +65,7 @@ struct cached_block { void *current_data; void *original_data; void *parent_data; -#ifdef BLOCK_CACHE_DEBUG_CHANGED +#if BLOCK_CACHE_DEBUG_CHANGED void *compare; #endif int32 ref_count; @@ -837,7 +837,7 @@ block_cache::FreeBlock(cached_block *block) block->block_number, block->original_data, block->parent_data); } -#ifdef BLOCK_CACHE_DEBUG_CHANGED +#if BLOCK_CACHE_DEBUG_CHANGED Free(block->compare); #endif @@ -909,7 +909,7 @@ block_cache::NewBlock(off_t blockNumber) block->parent_data = NULL; block->is_dirty = false; block->unused = false; -#ifdef BLOCK_CACHE_DEBUG_CHANGED +#if BLOCK_CACHE_DEBUG_CHANGED block->compare = NULL; #endif @@ -1000,7 +1000,7 @@ block_cache::LowMemoryHandler(void *data, uint32 resources, int32 level) static void put_cached_block(block_cache *cache, cached_block *block) { -#ifdef BLOCK_CACHE_DEBUG_CHANGED +#if BLOCK_CACHE_DEBUG_CHANGED if (!block->is_dirty && block->compare != NULL && memcmp(block->current_data, block->compare, cache->block_size)) { dprintf("new block:\n"); @@ -1321,7 +1321,7 @@ write_cached_block(block_cache *cache, cached_block *block, } -#ifdef DEBUG_BLOCK_CACHE +#if DEBUG_BLOCK_CACHE static void dump_block(cached_block *block) @@ -1752,7 +1752,7 @@ block_cache_init(void) if (sNotifierWriterThread >= B_OK) send_signal_etc(sNotifierWriterThread, SIGCONT, B_DO_NOT_RESCHEDULE); -#ifdef DEBUG_BLOCK_CACHE +#if DEBUG_BLOCK_CACHE add_debugger_command_etc("block_caches", &dump_caches, "dumps all block caches", "\n", 0); add_debugger_command_etc("block_cache", &dump_cache, @@ -2519,7 +2519,7 @@ block_cache_get_etc(void *_cache, off_t blockNumber, off_t base, off_t length) if (block == NULL) return NULL; -#ifdef BLOCK_CACHE_DEBUG_CHANGED +#if BLOCK_CACHE_DEBUG_CHANGED if (block->compare == NULL) block->compare = cache->Allocate(); if (block->compare != NULL) diff --git a/src/system/kernel/cache/file_map.cpp b/src/system/kernel/cache/file_map.cpp index 556bae16be..fc39fe0772 100644 --- a/src/system/kernel/cache/file_map.cpp +++ b/src/system/kernel/cache/file_map.cpp @@ -21,6 +21,8 @@ #include #include +#include "kernel_debug_config.h" + //#define TRACE_FILE_MAP #ifdef TRACE_FILE_MAP @@ -34,7 +36,6 @@ // for the whole map. // TODO: it would be nice if we could free a file map in low memory situations. -#define DEBUG_FILE_MAP #define CACHED_FILE_EXTENTS 2 // must be smaller than MAX_FILE_IO_VECS @@ -51,7 +52,7 @@ struct file_extent_array { }; class FileMap -#ifdef DEBUG_FILE_MAP +#if DEBUG_FILE_MAP : public DoublyLinkedListLinkImpl #endif { @@ -94,7 +95,7 @@ private: bool fCacheAll; }; -#ifdef DEBUG_FILE_MAP +#if DEBUG_FILE_MAP typedef DoublyLinkedList FileMapList; static FileMapList sList; @@ -111,7 +112,7 @@ FileMap::FileMap(struct vnode* vnode, off_t size) { mutex_init(&fLock, "file map"); -#ifdef DEBUG_FILE_MAP +#if DEBUG_FILE_MAP MutexLocker _(sLock); sList.Add(this); #endif @@ -123,7 +124,7 @@ FileMap::~FileMap() _Free(); mutex_destroy(&fLock); -#ifdef DEBUG_FILE_MAP +#if DEBUG_FILE_MAP MutexLocker _(sLock); sList.Remove(this); #endif @@ -459,7 +460,7 @@ FileMap::Translate(off_t offset, size_t size, file_io_vec* vecs, size_t* _count, // #pragma mark - -#ifdef DEBUG_FILE_MAP +#if DEBUG_FILE_MAP static int dump_file_map(int argc, char** argv) @@ -553,7 +554,7 @@ dump_file_map_stats(int argc, char** argv) extern "C" status_t file_map_init(void) { -#ifdef DEBUG_FILE_MAP +#if DEBUG_FILE_MAP add_debugger_command_etc("file_map", &dump_file_map, "Dumps the specified file map.", "[-p] \n" diff --git a/src/system/kernel/heap.cpp b/src/system/kernel/heap.cpp index 000593b7e5..b927735e23 100644 --- a/src/system/kernel/heap.cpp +++ b/src/system/kernel/heap.cpp @@ -25,6 +25,7 @@ #include #include + //#define TRACE_HEAP #ifdef TRACE_HEAP # define TRACE(x) dprintf x @@ -32,12 +33,6 @@ # define TRACE(x) ; #endif -// initialize newly allocated memory with something non zero -#define PARANOID_KERNEL_MALLOC 1 -// check for double free, and fill freed memory with 0xdeadbeef -#define PARANOID_KERNEL_FREE 1 -// validate sanity of the heap after each operation (slow!) -#define PARANOID_HEAP_VALIDATION 0 #if KERNEL_HEAP_LEAK_CHECK typedef struct heap_leak_check_info_s { diff --git a/src/system/kernel/int.c b/src/system/kernel/int.c index 0f97dbdfff..d25d8a50b9 100644 --- a/src/system/kernel/int.c +++ b/src/system/kernel/int.c @@ -20,6 +20,8 @@ #include #include +#include "kernel_debug_config.h" + //#define TRACE_INT #ifdef TRACE_INT @@ -28,8 +30,6 @@ # define TRACE(x) ; #endif -#define DEBUG_INT - // adds statistics and unhandled counter struct io_handler { struct io_handler *next; @@ -44,7 +44,7 @@ struct io_vector { spinlock vector_lock; int32 enable_count; bool no_lock_vector; -#ifdef DEBUG_INT +#if DEBUG_INTERRUPTS int64 handled_count; int64 unhandled_count; int trigger_count; @@ -55,7 +55,7 @@ struct io_vector { static struct io_vector sVectors[NUM_IO_VECTORS]; -#ifdef DEBUG_INT +#if DEBUG_INTERRUPTS static int dump_int_statistics(int argc, char **argv) { @@ -128,7 +128,7 @@ int_init_post_vm(kernel_args *args) B_INITIALIZE_SPINLOCK(&sVectors[i].vector_lock); sVectors[i].enable_count = 0; sVectors[i].no_lock_vector = false; -#ifdef DEBUG_INT +#if DEBUG_INTERRUPTS sVectors[i].handled_count = 0; sVectors[i].unhandled_count = 0; sVectors[i].trigger_count = 0; @@ -137,7 +137,7 @@ int_init_post_vm(kernel_args *args) initque(&sVectors[i].handler_list); /* initialize handler queue */ } -#ifdef DEBUG_INT +#if DEBUG_INTERRUPTS add_debugger_command("ints", &dump_int_statistics, "list interrupt statistics"); #endif @@ -199,7 +199,7 @@ int_io_interrupt_handler(int vector, bool levelTriggered) invokeScheduler = true; } -#ifdef DEBUG_INT +#if DEBUG_INTERRUPTS sVectors[vector].trigger_count++; if (status != B_UNHANDLED_INTERRUPT || handled || invokeScheduler) { sVectors[vector].handled_count++; diff --git a/src/system/kernel/sem.cpp b/src/system/kernel/sem.cpp index c6fd0f2844..40475cd528 100644 --- a/src/system/kernel/sem.cpp +++ b/src/system/kernel/sem.cpp @@ -34,6 +34,8 @@ #include #include +#include "kernel_debug_config.h" + //#define TRACE_SEM #ifdef TRACE_SEM @@ -49,7 +51,6 @@ # define KTRACE(x...) do {} while (false) #endif -#define DEBUG_LAST_ACQUIRER struct queued_thread : DoublyLinkedListLinkImpl { queued_thread(struct thread *thread, int32 count) @@ -81,7 +82,7 @@ struct sem_entry { team_id owner; // if set to -1, means owned by a port select_info *select_infos; thread_id last_acquirer; -#ifdef DEBUG_LAST_ACQUIRER +#if DEBUG_SEM_LAST_ACQUIRER int32 last_acquire_count; thread_id last_releaser; int32 last_release_count; @@ -177,7 +178,7 @@ dump_sem(struct sem_entry *sem) set_debug_variable("_semID", sem->id); set_debug_variable("_owner", sem->u.used.owner); -#ifdef DEBUG_LAST_ACQUIRER +#if DEBUG_SEM_LAST_ACQUIRER kprintf("last acquired by: %ld, count: %ld\n", sem->u.used.last_acquirer, sem->u.used.last_acquire_count); kprintf("last released by: %ld, count: %ld\n", sem->u.used.last_releaser, @@ -817,7 +818,7 @@ switch_sem_etc(sem_id semToBeReleased, sem_id id, int32 count, if (acquireStatus >= B_OK) { sSems[slot].u.used.last_acquirer = thread_get_current_thread_id(); -#ifdef DEBUG_LAST_ACQUIRER +#if DEBUG_SEM_LAST_ACQUIRER sSems[slot].u.used.last_acquire_count = count; #endif } @@ -833,7 +834,7 @@ switch_sem_etc(sem_id semToBeReleased, sem_id id, int32 count, } else { sSems[slot].u.used.net_count -= count; sSems[slot].u.used.last_acquirer = thread_get_current_thread_id(); -#ifdef DEBUG_LAST_ACQUIRER +#if DEBUG_SEM_LAST_ACQUIRER sSems[slot].u.used.last_acquire_count = count; #endif } @@ -902,7 +903,7 @@ release_sem_etc(sem_id id, int32 count, uint32 flags) flags); sSems[slot].u.used.last_acquirer = -sSems[slot].u.used.last_acquirer; -#ifdef DEBUG_LAST_ACQUIRER +#if DEBUG_SEM_LAST_ACQUIRER sSems[slot].u.used.last_releaser = thread_get_current_thread_id(); sSems[slot].u.used.last_release_count = count; #endif diff --git a/src/system/kernel/smp.cpp b/src/system/kernel/smp.cpp index 9deece2222..87ec4aecd0 100644 --- a/src/system/kernel/smp.cpp +++ b/src/system/kernel/smp.cpp @@ -24,8 +24,9 @@ #include #include +#include "kernel_debug_config.h" + -#define DEBUG_SPINLOCKS 1 //#define TRACE_SMP #ifdef TRACE_SMP diff --git a/src/system/kernel/vm/VMAnonymousCache.cpp b/src/system/kernel/vm/VMAnonymousCache.cpp index ba13dc35e0..9e6c649e10 100644 --- a/src/system/kernel/vm/VMAnonymousCache.cpp +++ b/src/system/kernel/vm/VMAnonymousCache.cpp @@ -735,7 +735,7 @@ VMAnonymousCache::Merge(VMCache* _source) != SWAP_SLOT_NONE) { page->merge_swap = true; } -#ifdef DEBUG_PAGE_CACHE_TRANSITIONS +#if DEBUG_PAGE_CACHE_TRANSITIONS } else { page->debug_flags = 0; if (consumerPage->state == PAGE_STATE_BUSY) diff --git a/src/system/kernel/vm/vm.cpp b/src/system/kernel/vm/vm.cpp index 93a145d4b3..3a4dbe2af6 100644 --- a/src/system/kernel/vm/vm.cpp +++ b/src/system/kernel/vm/vm.cpp @@ -4565,7 +4565,7 @@ fault_get_page(vm_translation_map *map, vm_cache *topCache, off_t cacheOffset, // If we inserted a dummy page into this cache (i.e. if it is the top // cache), we have to remove it now if (dummyPage.state == PAGE_STATE_BUSY && dummyPage.cache == cache) { -#ifdef DEBUG_PAGE_CACHE_TRANSITIONS +#if DEBUG_PAGE_CACHE_TRANSITIONS page->debug_flags = dummyPage.debug_flags | 0x8; if (dummyPage.collided_page != NULL) { dummyPage.collided_page->collided_page = page; @@ -4579,7 +4579,7 @@ fault_get_page(vm_translation_map *map, vm_cache *topCache, off_t cacheOffset, cache->InsertPage(page, cacheOffset); if (dummyPage.state == PAGE_STATE_BUSY) { -#ifdef DEBUG_PAGE_CACHE_TRANSITIONS +#if DEBUG_PAGE_CACHE_TRANSITIONS page->debug_flags = dummyPage.debug_flags | 0x10; if (dummyPage.collided_page != NULL) { dummyPage.collided_page->collided_page = page; @@ -4756,7 +4756,7 @@ vm_soft_fault(vm_address_space *addressSpace, addr_t originalAddress, dummyPage.state = PAGE_STATE_INACTIVE; dummyPage.type = PAGE_TYPE_DUMMY; dummyPage.wired_count = 0; -#ifdef DEBUG_PAGE_CACHE_TRANSITIONS +#if DEBUG_PAGE_CACHE_TRANSITIONS dummyPage.debug_flags = 0; dummyPage.collided_page = NULL; #endif // DEBUG_PAGE_CACHE_TRANSITIONS diff --git a/src/system/kernel/vm/vm_cache.cpp b/src/system/kernel/vm/vm_cache.cpp index 1b77566ce9..3e7ab7332d 100644 --- a/src/system/kernel/vm/vm_cache.cpp +++ b/src/system/kernel/vm/vm_cache.cpp @@ -923,7 +923,7 @@ VMCache::Merge(VMCache* source) // the page is not yet in the consumer cache - move it upwards source->RemovePage(page); InsertPage(page, (off_t)page->cache_offset << PAGE_SHIFT); -#ifdef DEBUG_PAGE_CACHE_TRANSITIONS +#if DEBUG_PAGE_CACHE_TRANSITIONS } else { page->debug_flags = 0; if (consumerPage->state == PAGE_STATE_BUSY) diff --git a/src/system/kernel/vm/vm_page.cpp b/src/system/kernel/vm/vm_page.cpp index b0cb3a5337..c92a9f92e9 100644 --- a/src/system/kernel/vm/vm_page.cpp +++ b/src/system/kernel/vm/vm_page.cpp @@ -286,7 +286,7 @@ dequeue_page(page_queue *queue) if (page->type != PAGE_TYPE_DUMMY) queue->count--; -#ifdef DEBUG_PAGE_QUEUE +#if DEBUG_PAGE_QUEUE if (page->queue != queue) { panic("dequeue_page(queue: %p): page %p thinks it is in queue " "%p", queue, page, page->queue); @@ -304,7 +304,7 @@ dequeue_page(page_queue *queue) static void enqueue_page(page_queue *queue, vm_page *page) { -#ifdef DEBUG_PAGE_QUEUE +#if DEBUG_PAGE_QUEUE if (page->queue != NULL) { panic("enqueue_page(queue: %p, page: %p): page thinks it is " "already in queue %p", queue, page, page->queue); @@ -321,7 +321,7 @@ enqueue_page(page_queue *queue, vm_page *page) if (page->type != PAGE_TYPE_DUMMY) queue->count++; -#ifdef DEBUG_PAGE_QUEUE +#if DEBUG_PAGE_QUEUE page->queue = queue; #endif } @@ -331,7 +331,7 @@ enqueue_page(page_queue *queue, vm_page *page) static void enqueue_page_to_head(page_queue *queue, vm_page *page) { -#ifdef DEBUG_PAGE_QUEUE +#if DEBUG_PAGE_QUEUE if (page->queue != NULL) { panic("enqueue_page_to_head(queue: %p, page: %p): page thinks it is " "already in queue %p", queue, page, page->queue); @@ -348,7 +348,7 @@ enqueue_page_to_head(page_queue *queue, vm_page *page) if (page->type != PAGE_TYPE_DUMMY) queue->count++; -#ifdef DEBUG_PAGE_QUEUE +#if DEBUG_PAGE_QUEUE page->queue = queue; #endif } @@ -357,7 +357,7 @@ enqueue_page_to_head(page_queue *queue, vm_page *page) static void remove_page_from_queue(page_queue *queue, vm_page *page) { -#ifdef DEBUG_PAGE_QUEUE +#if DEBUG_PAGE_QUEUE if (page->queue != queue) { panic("remove_page_from_queue(queue: %p, page: %p): page thinks it " "is in queue %p", queue, page, page->queue); @@ -377,7 +377,7 @@ remove_page_from_queue(page_queue *queue, vm_page *page) if (page->type != PAGE_TYPE_DUMMY) queue->count--; -#ifdef DEBUG_PAGE_QUEUE +#if DEBUG_PAGE_QUEUE page->queue = NULL; #endif } @@ -400,7 +400,7 @@ move_page_to_queue(page_queue *fromQueue, page_queue *toQueue, vm_page *page) static void insert_page_after(page_queue *queue, vm_page *before, vm_page *page) { -#ifdef DEBUG_PAGE_QUEUE +#if DEBUG_PAGE_QUEUE if (page->queue != NULL) { panic("enqueue_page(queue: %p, page: %p): page thinks it is " "already in queue %p", queue, page, page->queue); @@ -424,7 +424,7 @@ insert_page_after(page_queue *queue, vm_page *before, vm_page *page) if (page->type != PAGE_TYPE_DUMMY) queue->count++; -#ifdef DEBUG_PAGE_QUEUE +#if DEBUG_PAGE_QUEUE page->queue = queue; #endif } @@ -558,10 +558,10 @@ dump_page(int argc, char **argv) kprintf("wired_count: %d\n", page->wired_count); kprintf("usage_count: %d\n", page->usage_count); kprintf("busy_writing: %d\n", page->busy_writing); - #ifdef DEBUG_PAGE_QUEUE + #if DEBUG_PAGE_QUEUE kprintf("queue: %p\n", page->queue); #endif - #ifdef DEBUG_PAGE_CACHE_TRANSITIONS + #if DEBUG_PAGE_CACHE_TRANSITIONS kprintf("debug_flags: 0x%lx\n", page->debug_flags); kprintf("collided page: %p\n", page->collided_page); #endif // DEBUG_PAGE_CACHE_TRANSITIONS @@ -1674,10 +1674,10 @@ vm_page_init(kernel_args *args) sPages[i].busy_writing = false; sPages[i].merge_swap = false; sPages[i].cache = NULL; - #ifdef DEBUG_PAGE_QUEUE + #if DEBUG_PAGE_QUEUE sPages[i].queue = NULL; #endif - #ifdef DEBUG_PAGE_CACHE_TRANSITIONS + #if DEBUG_PAGE_CACHE_TRANSITIONS sPages[i].debug_flags = 0; sPages[i].collided_page = NULL; #endif // DEBUG_PAGE_CACHE_TRANSITIONS @@ -1896,7 +1896,7 @@ vm_page_allocate_page(int pageState, bool reserved) if (reserved || sReservedPages < free_page_queue_count()) { page = dequeue_page(queue); if (page == NULL) { -#ifdef DEBUG_PAGE_QUEUE +#if DEBUG_PAGE_QUEUE if (queue->count != 0) panic("queue %p corrupted, count = %d\n", queue, queue->count); #endif