Path: blob/master/runtime/exelib/common/memcheck.c
6000 views
/*******************************************************************************1* Copyright (c) 1991, 2021 IBM Corp. and others2*3* This program and the accompanying materials are made available under4* the terms of the Eclipse Public License 2.0 which accompanies this5* distribution and is available at https://www.eclipse.org/legal/epl-2.0/6* or the Apache License, Version 2.0 which accompanies this distribution and7* is available at https://www.apache.org/licenses/LICENSE-2.0.8*9* This Source Code may also be made available under the following10* Secondary Licenses when the conditions for such availability set11* forth in the Eclipse Public License, v. 2.0 are satisfied: GNU12* General Public License, version 2 with the GNU Classpath13* Exception [1] and GNU General Public License, version 2 with the14* OpenJDK Assembly Exception [2].15*16* [1] https://www.gnu.org/software/classpath/license.html17* [2] http://openjdk.java.net/legal/assembly-exception.html18*19* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 OR LicenseRef-GPL-2.0 WITH Assembly-exception20*******************************************************************************/2122#include "j9cfg.h"23#include "exelib_internal.h"24#include "memchknls.h"25#include "memcheck.h"26#ifdef J9ZOS39027#include "atoe.h"28#endif2930/* uncomment this line if you want support the 'simulate' option */31/*#define J9VM_MEMCHK_SIM_SUP32*/3334#ifdef J9VM_MEMCHK_SIM_SUP35#define SIM_TAG "/*MEMSIM*/"36#endif3738#include <stdlib.h>39#include <stdio.h>40#include <string.h>41#include "j9port.h"42#include "j9protos.h"43#include "avl_api.h"44#include "libhlp.h" /* uses main_setNLSCatalog */45#include "omrmutex.h"46#include "ute_core.h"4748#if defined (WIN32) && !defined(BREW)49/* windows.h defined UDATA. Ignore its definition */50#define UDATA UDATA_win32_51#include <windows.h>52#undef UDATA /* this is safe because our UDATA is a typedef, not a macro */53#endif5455/* Padding size for the beginning and end of each block, in bytes. This should be a multiple56of 8 (for alignment purposes). If the padding size is smaller than sizeof(J9MemoryCheckHeader)57it will be increased to that size automatically. */58#define J9_MEMCHECK_PADDING_SIZE 51259#define J9_MEMCHECK_DATA_PADDING_VALUE ((U_32) 0xDEADBEEF )60#define J9_MEMCHECK_DATA_FILL_VALUE ((U_32) 0xE7E7E7E7 )61#define J9_MEMCHECK_DATA_FREED_VALUE ((U_32) 0xBEF0DDED )62#define J9_MEMCHECK_CODE_PADDING_VALUE ((U_32) 0xBAADF00D )63#define J9_MEMCHECK_CODE_FILL_VALUE ((U_32) 0xF7F7F7F7 )64#define J9_MEMCHECK_CODE_FREED_VALUE ((U_32) 0xCCCCCCCC )65/* Must be defined, this limits the number of leaked memory blocks that will be dumped on shutdown.66otherwise, it might print out thousands of blocks... ;) */67#define J9_MEMCHECK_MAX_DUMP_LEAKED_BLOCKS 3268typedef void *(* J9_MEM_ALLOCATE_FUNC)(OMRPortLibrary *, UDATA byteAmount, const char *callsite, U_32 category);69typedef void (* J9_MEM_FREE_FUNC)(OMRPortLibrary *, void *memoryPointer);70typedef void (* J9_MEM_FREE_CODE_FUNC)(OMRPortLibrary *, void *memoryPointer, UDATA size);71typedef I_32 (* J9_SHUTDOWN_FUNC)(OMRPortLibrary *);72typedef void (* J9_SHUTDOWN_AND_EXIT_FUNC)(OMRPortLibrary *, I_32 exitCode);73typedef I_32 (* J9_PORT_CONTROL_FUNC)(OMRPortLibrary *portLib, const char* key, UDATA value);74typedef void (* J9_MEM_SHUTDOWN_FUNC)(OMRPortLibrary *);7576#define J9_MEMCHECK_FREED_SIZE (~(UDATA)0)7778/* Mode flags. */79#define J9_MCMODE_PAD_BLOCKS 0x0000000180#define J9_MCMODE_FULL_SCANS 0x0000000281#define J9_MCMODE_NEVER_FREE 0x0000000482#define J9_MCMODE_FAIL_AT 0x0000000883#define J9_MCMODE_SKIP_TO 0x0000001084#define J9_MCMODE_TOP_DOWN 0x0000002085#define J9_MCMODE_SIMULATE 0x0000004086#define J9_MCMODE_PRINT_CALLSITES 0x0000008087#define J9_MCMODE_PRINT_CALLSITES_SMALL 0x0000010088#define J9_MCMODE_ZERO 0x0000020089#define J9_MCMODE_LIMIT 0x0000040090#define J9_MCMODE_IGNORE_UNKNOWN_BLOCKS 0x0000080091#define J9_MCMODE_SUB_ALLOCATOR 0x0000100092#define J9_MCMODE_MPROTECT 0x0000200093#define J9_MCMODE_NO_SCAN 0x000040009495#define J9_MEMCHECK_SHUTDOWN_NORMAL 096#define J9_MEMCHECK_SHUTDOWN_EXIT 19798static UDATA j9thrDescriptor = 0;99static IDATA (*f_omrthread_attach_ex)(omrthread_t* handle, omrthread_attr_t *attr);100static void (*f_omrthread_detach)(omrthread_t thread);101102static MUTEX mcMutex;103104static UtInterface *uteInterface = NULL;105106/* Internal memorycheck portLibrary */107OMRPortLibrary memCheckPortLibStruct; /* Stack allocated Struct */108OMRPortLibrary *memCheckPortLib; /* Pointer to it */109110/* Memory Protect related variables for working with vmem */111static J9HashTable *vmemIDTable = NULL; /* For MEMORY_PROTECT */112static const UDATA lockMode = 0;113static const UDATA unlockMode = J9PORT_VMEM_MEMORY_MODE_READ | J9PORT_VMEM_MEMORY_MODE_WRITE /*| J9PORT_VMEM_MEMORY_MODE_EXECUTE*/;114static const UDATA allocateMode = J9PORT_VMEM_MEMORY_MODE_READ | J9PORT_VMEM_MEMORY_MODE_WRITE | J9PORT_VMEM_MEMORY_MODE_COMMIT;115static UDATA J9_ALIGN_BOTTOM = 1;116117/* Standard memCheck variables */118static J9MemoryCheckHeader *mostRecentBlock = NULL;119static J9MemoryCheckHeader *mostRecentFreedBlock = NULL;120static J9AVLTree *avl_tree;121static UDATA mode = 0;122static UDATA failAtAlloc = 0;123static UDATA skipToAlloc = 0;124static UDATA callSitePrintCount = 0;125static UDATA limitAlloc = 0;126127static J9_MEM_ALLOCATE_FUNC globalAllocator = NULL;128static J9_MEM_FREE_FUNC globalDeallocator = NULL;129static J9_MEM_FREE_FUNC globalAdviseDeallocator = NULL;130static J9_MEM_ALLOCATE_FUNC globalAllocator32 = NULL;131static J9_MEM_FREE_FUNC globalDeallocator32 = NULL;132133static J9_MEM_ALLOCATE_FUNC old_mem_allocate_memory = NULL;134static J9_MEM_FREE_FUNC old_mem_free_memory = NULL;135static J9_MEM_FREE_FUNC old_mem_advise_and_free_memory = NULL;136static J9_MEM_ALLOCATE_FUNC old_mem_allocate_memory32 = NULL;137static J9_MEM_FREE_FUNC old_mem_free_memory32 = NULL;138static J9_SHUTDOWN_FUNC old_port_shutdown_library = NULL;139static J9_SHUTDOWN_AND_EXIT_FUNC old_shutdown_and_exit = NULL;140static J9_PORT_CONTROL_FUNC old_port_control = NULL;141static J9_MEM_SHUTDOWN_FUNC old_mem_shutdown = NULL;142143static J9MemoryCheckStats memStats;144static char hexd[] = "0123456789ABCDEF";145146static UDATA memoryCheck_get_page_size(OMRPortLibrary *portLib);147static IDATA memoryCheck_lockGuardPages(OMRPortLibrary *portLib, void *memcheckHeader, UDATA size, UDATA mode );148static IDATA memoryCheck_lockWrappedBlock(OMRPortLibrary *portLib, J9MemoryCheckHeader *blockHeader, const UDATA mode);149#define J9_MEMCHECK_PAGE_SIZE memoryCheck_get_page_size(memCheckPortLib)150151/* MEMORY_PROTECT related macros */152#define J9_MEMCHECK_LOCK( ptr )\153((J9MemoryCheckHeader *) ptr)->isLocked = 1; /* Set to Locked */\154((memoryCheck_lockGuardPages( memCheckPortLib, (ptr), J9_MEMCHECK_PAGE_SIZE, lockMode ) == 0) ? \155(void)0 : memCheckPortLib->tty_printf( memCheckPortLib, "LOCK FAIL: (%s)(%d)\n", __FILE__, __LINE__ ) )156157#define J9_MEMCHECK_UNLOCK( ptr )\158((memoryCheck_lockGuardPages( memCheckPortLib, (ptr), J9_MEMCHECK_PAGE_SIZE, unlockMode ) == 0) ? \159(void)0 : memCheckPortLib->tty_printf( memCheckPortLib, "UNLOCK FAIL: (%s)(%d)\n", __FILE__, __LINE__ ) );\160((J9MemoryCheckHeader *)(ptr))->isLocked = 0 /* Set to unlocked */161162#define J9_MEMCHECK_LOCK_BODY( blockHeader )\163memoryCheck_lockWrappedBlock( memCheckPortLib, (blockHeader), lockMode )164165#define J9_MEMCHECK_UNLOCK_BODY( blockHeader )\166memoryCheck_lockWrappedBlock( memCheckPortLib, (blockHeader), unlockMode )167168/*169* Following is for the suballocator170*171* It is based loosely on a Donald Knuth algorithm from his Art of Computer Programming books172* Some of the memory is allocated from a small array of small structures (2400 bytes in this173* version, to keep the "small" * memory from fragmented the heap.174* The size and quantity of this may need to be adjusted depending on the application.175* All references to smallblock could be removed, however, and just the heap manager used to176* provide memory.177*178* If the MEMDEBUG macro is defined, additional printf's (and pool audits, etc.) are179* activated, helping to resolve issues. This will make the allocate run much slower, however.180*/181182/* subAllocator heap size in bytes (Note: heap is initialized in 4 byte blocks) */183static UDATA heapSizeMegaBytes = 0;184#define DEFAULT_HEAP_SIZE_BYTES J9_MEMORY_MAX185static IDATA* j9heap;186187/*188* The following variable is used for aligning mallocs happening in conjunction189* with the subAllocator option. This keeps mallocs on double190* boundaries and forces bottom padding to end on a double word boundary - so191* that the next top padding will start on a double word boundary when using192* the subAllocator. Reference CMVC 119506, related to xscale.193*194* "sizeof double" was chosen because some quick internet research indicated that195* double is the standard basis used for allocating memory.196*/197#define BYTES_FOR_ALIGNMENT(numberOfBytes) (U_32)((sizeof(double) - (((UDATA) numberOfBytes) & (sizeof(double) - 1))) & (sizeof(double) - 1))198199#define STARTIDX 3200201#define start j9heap[STARTIDX-2]202#define heapsize (j9heap[STARTIDX-3])203204/*205* smallest block (in words) allocated,206* plus level at which block is extended to207* absorb next block to prevent fragmentation208*/209#define FRAGTHRESHOLD 6210211#define MAX_SMALL_BLOCK 50212struct smblk {213UDATA data [ FRAGTHRESHOLD ];214} smblk;215static struct smblk smallBlock [MAX_SMALL_BLOCK];216static unsigned char smblkstatus[MAX_SMALL_BLOCK];217static UDATA smblkindex;218static UDATA meminuse;219220/* setting ignoredCallsites to be 128 bytes, hoping we won't try to ignore too many callsites */221#define IGNORECALLSITESTRLENGTH 128222#define MAX_CALLSITE_COUNT 10223#define MAX_CALLSITE_LENGTH 32224225static char ignoreCallSiteStr[IGNORECALLSITESTRLENGTH];226#define CALLSITE_DELIMITER ":"227228#ifdef MEMDEBUG229static UDATA freemem;230static UDATA usedmem;231static UDATA total;232static void *pTop;233static void *pBot;234static UDATA freeCnt;235static UDATA usedCnt;236static UDATA bucket[6];237/* audit masks: */238#define AUDIT 0x0001239#define FREE 0x0002240#define BUCKET 0x0004241#define USED 0x0008242#endif243244245static void memoryCheck_exit_shutdown_and_exit(OMRPortLibrary *portLib, I_32 exitCode );246static void memoryCheck_update_callSites_allocate(OMRPortLibrary *portLib, J9MemoryCheckHeader *header, const char *callSite, UDATA byteAmount);247static void *memoryCheck_wrapper_allocate_memory(OMRPortLibrary *portLib, UDATA byteAmount, char const *operationName, J9_MEM_ALLOCATE_FUNC allocator, U_32 paddingValue, U_32 fillValue, U_32 freedValue, const char *callSite, U_32 category);248static void memoryCheck_dump_callSite_small(OMRPortLibrary *portLibrary, J9AVLTreeNode *node);249static void memoryCheck_dump_bytes(OMRPortLibrary *portLib, void *dumpAddress, UDATA dumpSize);250static void memoryCheck_free_memory(OMRPortLibrary *portLib, void *memoryPointer);251static void memoryCheck_advise_and_free_memory(OMRPortLibrary *portLib, void *memoryPointer);252static void memoryCheck_free_memory32(OMRPortLibrary *portLib, void *memoryPointer);253static void memoryCheck_fill_bytes(OMRPortLibrary *portLib, U_8 *fillAddress, UDATA fillSize, U_32 fillValue, U_8 *blockAddress);254static void memoryCheck_update_callSites_free (J9MEMAVLTreeNode *node, UDATA byteAmount);255static void memoryCheck_null_mem_free_memory(OMRPortLibrary *portLib, void *memoryPointer);256static BOOLEAN memoryCheck_describe_freed_block(OMRPortLibrary *portLib, char const *operationName, J9MemoryCheckHeader *blockHeader);257static void OMRNORETURN memoryCheck_abort(OMRPortLibrary *portLib);258static UDATA memoryCheck_filter_nonVM_unFreed_Blcoks(OMRPortLibrary *portLib);259static void memoryCheck_print_summary(OMRPortLibrary *portLib, I_32 shutdownMode);260static void memoryCheck_shutdown_internal(OMRPortLibrary *portLib, I_32 shutdownMode);261static void memoryCheck_print_stats(OMRPortLibrary *portLib);262static void memoryCheck_dump_callSites_small(OMRPortLibrary *portLibrary, J9AVLTree *tree);263static I_32 memoryCheck_control(OMRPortLibrary *portLib, const char* key, UDATA value);264static BOOLEAN memoryCheck_scan_block(OMRPortLibrary *portLib, J9MemoryCheckHeader *blockHeader);265static void *memoryCheck_wrapper_reallocate_memory(OMRPortLibrary *portLib, void *memoryPointer, UDATA byteAmount, char const *operationName, J9_MEM_ALLOCATE_FUNC allocator, J9_MEM_FREE_FUNC deallocator, U_32 paddingValue, U_32 fillValue, U_32 freedValue, const char *callSite, U_32 category);266static UDATA memoryCheck_verify_backward(OMRPortLibrary *portLib, U_8 *fillAddress, UDATA fillSize, U_32 fillValue, U_8 *blockAddress);267static void memoryCheck_print_stats_callSite(OMRPortLibrary *portLib, J9MEMAVLTreeNode *node);268static void * memoryCheck_reallocate_memory (struct OMRPortLibrary *portLibrary, void *memoryPointer, UDATA byteAmount, const char *callsite, U_32 category);269static void memoryCheck_free_AVLTreeNode(OMRPortLibrary *portLib, J9AVLTreeNode *node);270static I_32 memoryCheck_port_shutdown_library(OMRPortLibrary *portLib);271static void *memoryCheck_allocate_memory(OMRPortLibrary *portLib, UDATA byteAmount, const char *callSite, U_32 category);272static void *memoryCheck_allocate_memory32(OMRPortLibrary *portLib, UDATA byteAmount, const char *callSite, U_32 category);273static void memoryCheck_dump_callSites(OMRPortLibrary *portLibrary, J9AVLTree *tree);274static void memoryCheck_dump_callSite(OMRPortLibrary *portLibrary, J9AVLTreeNode *node);275static IDATA memoryCheck_insertion_Compare(J9AVLTree *tree, J9AVLTreeNode *insertNode, J9AVLTreeNode *walk);276static UDATA memoryCheck_verify_forward(OMRPortLibrary *portLib, U_8 *fillAddress, UDATA fillSize, U_32 fillValue, U_8 *blockAddress);277static BOOLEAN memoryCheck_parseOption(OMRPortLibrary *portLib, char const *option, size_t optionLen);278static IDATA memoryCheck_search_Compare(J9AVLTree *tree, UDATA search, J9AVLTreeNode *walk);279static void memoryCheck_initialize_AVLTree_stats (J9MEMAVLTreeNode *node, UDATA byteAmount);280static void memoryCheck_print_stats_callSite_small(OMRPortLibrary *portLib, J9MEMAVLTreeNode *node);281static void memoryCheck_wrapper_free_memory(OMRPortLibrary *portLib, void *memoryPointer, char const *operationName, J9_MEM_FREE_FUNC deallocator, U_32 paddingValue, U_32 fillValue, U_32 freedValue);282static void memoryCheck_set_AVLTree_prevStats (J9MEMAVLTreeNode *node);283static BOOLEAN memoryCheck_describe_block(OMRPortLibrary *portLib, char const *operationName, J9MemoryCheckHeader *blockHeader);284static BOOLEAN memoryCheck_scan_all_blocks(OMRPortLibrary *portLib);285static void memoryCheck_free_AVLTree(OMRPortLibrary *portLib, J9AVLTree *tree);286static void subAllocator_init_heap(void* memptr, UDATA size);287static void* subAllocator_allocate_memory(OMRPortLibrary *portLib, UDATA size, const char *callSite, U_32 category);288static void subAllocator_free_memory(OMRPortLibrary *portLib, void *ptr);289static UDATA memoryCheck_hashFn (void *vmemId, void *userData);290static UDATA memoryCheck_hashEqualFn (void *leftEntry, void *rightEntry, void *userData);291static UDATA memoryCheck_hashDoFn (void *entry, void *portLib);292293/* we shouldn't need this with a dedicated portlib for memCheck */294#if 0295static void memoryCheck_mem_shutdown(OMRPortLibrary *portLib);296#endif297298static void memoryCheck_lockAllBlocks(OMRPortLibrary *portLib, J9MemoryCheckHeader *listHead, const UDATA lockFlags, const UDATA lockBody);299static void memoryCheck_restore_old_shutdown_functions(OMRPortLibrary *portLib);300301302303#define J9_MEMCHECK_MINIMUM_HEADER_SIZE (sizeof(J9MemoryCheckHeader) + 4*sizeof(U_32))304#define J9_MEMCHECK_ADJUSTED_PADDING \305((((J9_MEMCHECK_PADDING_SIZE > J9_MEMCHECK_MINIMUM_HEADER_SIZE) ? \306J9_MEMCHECK_PADDING_SIZE : J9_MEMCHECK_MINIMUM_HEADER_SIZE) + 7) & ~7)307308IDATA309memoryCheck_initialize(J9PortLibrary *j9portLibrary, char const *modeStr, char **argv)310{311#define DLL_NAME J9_THREAD_DLL_NAME312OMRPortLibrary *portLib = OMRPORT_FROM_J9PORT(j9portLibrary);313if (old_port_shutdown_library) {314/* We were already initialized! */315return 1;316}317318if (portLib->sl_open_shared_library(portLib, DLL_NAME, &j9thrDescriptor, J9PORT_SLOPEN_DECORATE)) {319portLib->nls_printf(portLib, J9NLS_ERROR, J9NLS_MEMCHK_INIT_ERROR);320return -1;321}322323if (portLib->sl_lookup_name(portLib, j9thrDescriptor, "omrthread_attach_ex", (UDATA*)&f_omrthread_attach_ex, "LL")) {324portLib->nls_printf(portLib, J9NLS_ERROR, J9NLS_MEMCHK_INIT_ERROR);325return -1;326}327328if (portLib->sl_lookup_name(portLib, j9thrDescriptor, "omrthread_detach", (UDATA*)&f_omrthread_detach, "L")) {329portLib->nls_printf(portLib, J9NLS_ERROR, J9NLS_MEMCHK_INIT_ERROR);330return -1;331}332333if (!MUTEX_INIT(mcMutex)) {334/* Print warning and fail. */335portLib->nls_printf(portLib, J9NLS_ERROR, J9NLS_MEMCHK_INIT_ERROR);336return -1;337}338339mode = J9_MCMODE_FULL_SCANS | J9_MCMODE_PAD_BLOCKS;340memset(ignoreCallSiteStr, '\0', IGNORECALLSITESTRLENGTH);341while (modeStr && *modeStr) {342char const *q = strchr(modeStr, ',');343344if (!memoryCheck_parseOption(portLib, modeStr, (q ? q-modeStr : strlen(modeStr)))) {345portLib->nls_printf(portLib, J9NLS_ERROR, J9NLS_MEMCHK_UNRECOGNIZED_OPTION, modeStr);346MUTEX_DESTROY(mcMutex);347return 2;348}349350if (q) {351q++;352}353354modeStr = q;355}356357/* check to see if we have legal options */358/* noscan is only supported with callsite, callsitesmall, failat, and zero */359if (0 != (mode & J9_MCMODE_NO_SCAN)) {360/* disable full scan and pad blocks (which are set by default) */361mode = mode & ~(J9_MCMODE_PAD_BLOCKS | J9_MCMODE_FULL_SCANS);362/* check for other options */363if (0 != (mode&(~J9_MCMODE_PRINT_CALLSITES_SMALL)&(~J9_MCMODE_PRINT_CALLSITES)&(~J9_MCMODE_ZERO)364&(~J9_MCMODE_NO_SCAN)&(~J9_MCMODE_SUB_ALLOCATOR)&(~J9_MCMODE_FAIL_AT))) {365/* TODO - this should be an NLS message */366portLib->tty_err_printf(portLib, "-Xcheck:memory:noscan is only supported with 'callsitesmall', 'callsite', 'failat' and 'zero'. Calling exit(3)\n", mode);367exit(3);368}369}370371/* Set up a private portLibrary for the memoryCheck to use */372memCheckPortLib = &memCheckPortLibStruct;373if (0 != portLib->port_init_library(memCheckPortLib, sizeof(OMRPortLibrary))) {374portLib->tty_printf(portLib, "Error creating the private portLibrary for memoryCheck.\n");375return -1;376}377378/* Set up the hashTable to hold the J9PortVmemIdentifiers */379vmemIDTable = hashTableNew( memCheckPortLib, J9_GET_CALLSITE(), 9391, sizeof( J9PortVmemIdentifier *), 0, 0, OMRMEM_CATEGORY_VM, memoryCheck_hashFn, memoryCheck_hashEqualFn, NULL, NULL );380if ( NULL == vmemIDTable ) {381memCheckPortLib->tty_printf( memCheckPortLib, "Error creating vmemID hashTable.\n");382return -1;383}384385/* The hashtable is not allowed to grow or it will cause crashes due to NULL pointers */386hashTableSetFlag( vmemIDTable, J9HASH_TABLE_DO_NOT_GROW );387/* Save original versions of memory allocation and port shutdown functions. */388old_mem_allocate_memory = portLib->mem_allocate_memory;389old_mem_free_memory = portLib->mem_free_memory;390old_mem_advise_and_free_memory = portLib->mem_advise_and_free_memory;391old_mem_allocate_memory32 = portLib->mem_allocate_memory32;392old_mem_free_memory32 = portLib->mem_free_memory32;393old_mem_shutdown = portLib->mem_shutdown;394old_port_shutdown_library = portLib->port_shutdown_library;395old_shutdown_and_exit = portLib->exit_shutdown_and_exit;396old_port_control = portLib->port_control;397398/* allocate the subAllocator's heap, and initialize it */399if (mode & J9_MCMODE_SUB_ALLOCATOR) {400int i = 0;401UDATA heapSizeBytes = heapSizeMegaBytes * 1024 * 1024;402j9heap = memCheckPortLib->mem_allocate_memory(portLib, heapSizeBytes, J9_GET_CALLSITE(), OMRMEM_CATEGORY_VM);403404if (NULL == j9heap) {405/* We failed our first attempt, try something smaller */406/* TODO - we should output how the user can change this */407portLib->tty_printf(portLib, "Initial allocation of subAllocator heap failed. Tried for %i MB\n", heapSizeMegaBytes);408409while (heapSizeBytes >= 1 * 1024 * 1024) {410heapSizeBytes = heapSizeBytes/2;411j9heap = memCheckPortLib->mem_allocate_memory(portLib, heapSizeBytes, J9_GET_CALLSITE(), OMRMEM_CATEGORY_VM);412if (NULL != j9heap) {413break;414}415}416417heapSizeMegaBytes = (heapSizeBytes) / (1024 *1024);418}419420if (NULL == j9heap) {421/* give up */422portLib->tty_printf(portLib, "Unable to allocate subAllocator heap of size %i MB), calling exit(3)\n\n", heapSizeMegaBytes);423exit(3);424}425426portLib->tty_printf(portLib, "Successfully allocated subAllocator heap of size %i MB\n\n", heapSizeMegaBytes);427subAllocator_init_heap(j9heap, heapSizeBytes/sizeof(UDATA)); /* NOTE: the j9heap is an array of UDATA sized blocks */428globalAllocator = subAllocator_allocate_memory;429globalDeallocator = subAllocator_free_memory;430globalAllocator32 = memCheckPortLib->mem_allocate_memory32;431globalDeallocator32 = memCheckPortLib->mem_free_memory32;432} else {433globalAllocator = memCheckPortLib->mem_allocate_memory;434globalDeallocator = memCheckPortLib->mem_free_memory;435globalAdviseDeallocator = memCheckPortLib->mem_advise_and_free_memory;436globalAllocator32 = memCheckPortLib->mem_allocate_memory32;437globalDeallocator32 = memCheckPortLib->mem_free_memory32;438}439440/* Shutdown the portLibrary and reinitialize it so that memorycheck is used from the beginning */441j9portLibrary->port_shutdown_library(j9portLibrary);442443/* Substitute checking versions for the memory allocation444* and port shutdown functions. */445portLib->mem_allocate_memory = memoryCheck_allocate_memory;446portLib->mem_free_memory = memoryCheck_free_memory;447portLib->mem_advise_and_free_memory = memoryCheck_advise_and_free_memory;448449/* MPROTECT mode test will time out if original allocate/free_memory32 is replaced */450if ((mode & J9_MCMODE_MPROTECT) == 0) {451portLib->mem_allocate_memory32 = memoryCheck_allocate_memory32;452portLib->mem_free_memory32 = memoryCheck_free_memory32;453}454455portLib->mem_reallocate_memory = memoryCheck_reallocate_memory;456457/* TODO we can consider to remove this given the two separate port libraries */458# if 0459portLib->mem_shutdown = memoryCheck_mem_shutdown;460#endif461462portLib->port_control = memoryCheck_control;463portLib->port_shutdown_library = memoryCheck_port_shutdown_library;464portLib->exit_shutdown_and_exit = memoryCheck_exit_shutdown_and_exit;465466/* Restart the portLibrary */467if(j9portLibrary->port_startup_library(j9portLibrary)) {468/* If the port library should fail to (re)start, we're going to469* crash trying to continue the normal VM shutdown sequence since470* we have pre-maturely shutdown the portlibrary a few lines up.471*472* Exit with 'magic' exit code to indicate this condition473*/474exit( 1 );475}476477/* Create AVL Tree to store callSite information. This information is useful for describing blocks478* even if callsite is not passed as a parameter to memcheck479*/480avl_tree = old_mem_allocate_memory(memCheckPortLib, sizeof(J9AVLTree), J9_GET_CALLSITE(), OMRMEM_CATEGORY_VM);481if(avl_tree) {482memset(avl_tree, 0, sizeof(J9AVLTree));483avl_tree->insertionComparator = memoryCheck_insertion_Compare;484avl_tree->searchComparator = memoryCheck_search_Compare;485avl_tree->genericActionHook = NULL;486avl_tree->rootNode = NULL;487} else {488memCheckPortLib->nls_printf(memCheckPortLib, J9NLS_ERROR, J9NLS_MEMCHK_AVL_ERROR);489}490491/* Reset up NLS - this must be after the setup of the AVL tree */492if ( argv != NULL ) {493/* j9vm/jvm.c currently calls this function with a null argv, but it has also already setup494* and NLS catalog. */495main_setNLSCatalog(j9portLibrary, argv);496}497498return 0;499}500501static BOOLEAN memoryCheck_parseOption(OMRPortLibrary *portLib, char const *option, size_t optionLen)502{503char const *optStrAll = "all";504size_t optLenAll = strlen(optStrAll);505char const *optStrQuick = "quick";506size_t optLenQuick = strlen(optStrQuick);507char const *optStrNofree = "nofree";508size_t optLenNofree = strlen(optStrNofree);509char const *optStrFailat = "failat=";510size_t optLenFailat = strlen(optStrFailat);511char const *optStrSkipto = "skipto=";512size_t optLenSkipto = strlen(optStrSkipto);513char const *optTopDown = "topdown";514size_t optLenTopDown = strlen(optTopDown);515char const *optStrCallPrint = "callsite=";516size_t optLenCallPrint = strlen(optStrCallPrint);517char const *optStrCallPrintSmall = "callsitesmall=";518size_t optLenCallPrintSmall = strlen(optStrCallPrintSmall);519char const *optStrZero = "zero";520size_t optLenZero = strlen(optStrZero);521char const *optStrLimit = "limit=";522size_t optLenLimit = strlen(optStrLimit);523char const *optStrIgnoreUnknownBlocks = "ignoreUnknownBlocks";524size_t optLenIgnoreUnknownBlocks = strlen(optStrIgnoreUnknownBlocks);525char const *optStrSubAllocator = "subAllocator";526size_t optLenSubAllocator = strlen(optStrSubAllocator);527528char const *optStrMProtect = "mprotect=";529size_t optLenMProtect = strlen(optStrMProtect);530char const *optStrAlignTop = "top";531size_t optLenAlignTop = strlen(optStrAlignTop);532char const *optStrAlignBottom = "bottom";533size_t optLenAlignBottom = strlen(optStrAlignBottom);534535char const *optStrNoScan = "noscan";536size_t optLenNoScan = strlen(optStrNoScan);537538/* undocumented. used to ignore unfreed blocks from specified callsites539* the format is -Xcheck:memory:ignoreUnfreedCallsite=[site1]:[site2]:...540*541* Note, each site string is meant to be a prefix match to call site info.542* if string is 'zip/', it will ignore all unfreed blocks with callsite string with 'zip/'543* Conversely, if a call site is abc/de.c, specifying a string of de.c will not be a match */544char const *optStrIgnoreUnfreedCallsite = "ignoreUnfreedCallsite=";545size_t optLenIgnoreUnfreedCallsite = strlen(optStrIgnoreUnfreedCallsite);546547548#ifdef J9VM_MEMCHK_SIM_SUP549char const *optStrSimulate = "simulate";550size_t optLenSimulate = strlen(optStrSimulate);551552if ((optionLen == optLenSimulate) && !strncmp(option, optStrSimulate, optLenSimulate)) {553mode |= J9_MCMODE_SIMULATE;554return TRUE;555}556#endif557558if ((optionLen > optLenIgnoreUnfreedCallsite) && !strncmp(option, optStrIgnoreUnfreedCallsite, optLenIgnoreUnfreedCallsite)) {559if (strlen (&(option[optLenIgnoreUnfreedCallsite])) < IGNORECALLSITESTRLENGTH) {560strcpy(ignoreCallSiteStr, &(option[optLenIgnoreUnfreedCallsite]));561} else {562portLib->tty_printf(portLib,"WARNING : IgnoreUnfreedCallsite option too long for internal buffer. Ignoring\n");563564}565return TRUE;566}567568if ((optionLen > optLenMProtect) && !strncmp(option, optStrMProtect, optLenMProtect)) {569char temp[20];570IDATA tempSize = optionLen - optLenMProtect;571572mode |= J9_MCMODE_PAD_BLOCKS;573if (tempSize > 19) {574tempSize = 19;575}576strncpy(temp, option + optLenMProtect, tempSize);577temp[tempSize] = '\0';578if (!strncmp(temp, optStrAlignTop, optLenAlignTop) ) {579mode |= J9_MCMODE_MPROTECT;580J9_ALIGN_BOTTOM = 0;581return TRUE;582}583else if (!strncmp(temp, optStrAlignBottom, optLenAlignBottom)) {584mode |= J9_MCMODE_MPROTECT;585J9_ALIGN_BOTTOM = 1;586return TRUE;587}588return FALSE;589}590591if ((optionLen == optLenAll) && !strncmp(option, optStrAll, optLenAll)) {592mode |= J9_MCMODE_PAD_BLOCKS | J9_MCMODE_FULL_SCANS;593return TRUE;594}595if ((optionLen == optLenQuick) && !strncmp(option, optStrQuick, optLenQuick)) {596mode |= J9_MCMODE_PAD_BLOCKS;597mode &= ~J9_MCMODE_FULL_SCANS;598return TRUE;599}600if ((optionLen == optLenNofree) && !strncmp(option, optStrNofree, optLenNofree)) {601mode |= J9_MCMODE_NEVER_FREE;602return TRUE;603}604if ((optionLen == optLenTopDown) && !strncmp(option, optTopDown, optLenTopDown)) {605mode |= J9_MCMODE_TOP_DOWN;606mode &= ~J9_MCMODE_SUB_ALLOCATOR;607return TRUE;608}609if ((optionLen > optLenCallPrint) && !strncmp(option, optStrCallPrint, optLenCallPrint)) {610char temp[20];611IDATA tempSize = optionLen - optLenCallPrint;612if (tempSize > 19)613tempSize = 19;614strncpy(temp, option + optLenCallPrint, tempSize);615temp[tempSize] = '\0';616if (!(callSitePrintCount = atoi(temp)))617return FALSE;618mode |= J9_MCMODE_PRINT_CALLSITES;619/* remove the bits for J9_MCMODE_PRINT_CALLSITES_SMALL since J9_MCMODE_PRINT_CALLSITES was defined after */620mode &= ~J9_MCMODE_PRINT_CALLSITES_SMALL;621return TRUE;622}623if ((optionLen > optLenCallPrintSmall) && !strncmp(option, optStrCallPrintSmall, optLenCallPrintSmall)) {624char temp[20];625IDATA tempSize = optionLen - optLenCallPrintSmall;626if (tempSize > 19)627tempSize = 19;628strncpy(temp, option + optLenCallPrintSmall, tempSize);629temp[tempSize] = '\0';630if (!(callSitePrintCount = atoi(temp)))631return FALSE;632mode |= J9_MCMODE_PRINT_CALLSITES_SMALL;633/* remove the bits for J9_MCMODE_PRINT_CALLSITES since J9_MCMODE_PRINT_CALLSITES_SMALL was defined after */634mode &= ~J9_MCMODE_PRINT_CALLSITES;635return TRUE;636}637if ((optionLen == optLenZero) && !strncmp(option, optStrZero, optLenZero)) {638mode |= J9_MCMODE_ZERO;639return TRUE;640}641if ((optionLen > optLenSkipto) && (!strncmp(option, optStrSkipto, optLenSkipto))) {642char temp[20];643IDATA tempSize = optionLen - optLenSkipto;644if (tempSize > 19)645tempSize = 19;646strncpy(temp, option + optLenSkipto, tempSize);647temp[tempSize] = '\0';648if (!(skipToAlloc = atoi(temp)))649return FALSE;650mode |= J9_MCMODE_SKIP_TO;651return TRUE;652}653if ((optionLen > optLenFailat) && (!strncmp(option, optStrFailat, optLenFailat))) {654char temp[20];655IDATA tempSize = optionLen - optLenFailat;656if (tempSize > 19)657tempSize = 19;658strncpy(temp, option + optLenFailat, tempSize);659temp[tempSize] = '\0';660if (!(failAtAlloc = atoi(temp)))661return FALSE;662mode |= J9_MCMODE_FAIL_AT;663return TRUE;664}665if ((optionLen > optLenLimit) && (!strncmp(option, optStrLimit, optLenLimit))) {666char temp[20];667IDATA tempSize = optionLen - optLenLimit;668if (tempSize > 19)669tempSize = 19;670strncpy(temp, option + optLenLimit, tempSize);671temp[tempSize] = '\0';672if (!(limitAlloc = atoi(temp)))673return FALSE;674mode |= J9_MCMODE_LIMIT;675return TRUE;676}677if ((optionLen == optLenIgnoreUnknownBlocks) && !strncmp(option, optStrIgnoreUnknownBlocks, optLenIgnoreUnknownBlocks)) {678mode |= J9_MCMODE_IGNORE_UNKNOWN_BLOCKS;679return TRUE;680}681/* The heap sub allocator cannot be used with top down */682if ((optionLen >= optLenSubAllocator) && !strncmp(option, optStrSubAllocator, optLenSubAllocator)) {683char temp[20];684IDATA tempSize = optionLen - optLenSubAllocator;685if (tempSize > 19) {686tempSize = 19;687}688strncpy(temp, option + optLenSubAllocator + 1, tempSize);689temp[tempSize] = '\0';690if (tempSize == 0) {691heapSizeMegaBytes = DEFAULT_HEAP_SIZE_BYTES / (1024 * 1024);692} else if (!(heapSizeMegaBytes = atoi(temp))) {693return FALSE;694}695mode &= ~J9_MCMODE_TOP_DOWN;696mode |= J9_MCMODE_SUB_ALLOCATOR;697return TRUE;698}699700if ((optionLen == optLenNoScan) && !strncmp(option, optStrNoScan, optLenNoScan)) {701/* This is only supported with Callsite or Zero702* We will check for other options in memoryCHeck initialize */703mode |= J9_MCMODE_NO_SCAN;704return TRUE;705}706707return FALSE;708}709710711static void OMRNORETURN memoryCheck_abort(OMRPortLibrary *portLib)712{713714if ( mode & J9_MCMODE_MPROTECT ) {715/* Unlock ALL blocks */716memoryCheck_lockAllBlocks( memCheckPortLib, mostRecentBlock, unlockMode, 0 );717memoryCheck_lockAllBlocks( memCheckPortLib, mostRecentFreedBlock, unlockMode, 1 );718}719720memoryCheck_print_stats(portLib);721722if ( mode & J9_MCMODE_MPROTECT ) {723/* free everything from the hashTable*/724/* TODO - why are we freeing anything if we just crashed */725hashTableForEachDo( vmemIDTable, memoryCheck_hashDoFn, memCheckPortLib );726hashTableFree( vmemIDTable );727}728729memCheckPortLib->tty_printf( memCheckPortLib, "Memory error(s) discovered, calling exit(3)\n");730memCheckPortLib->exit_shutdown_and_exit(memCheckPortLib, 3);731732dontreturn: goto dontreturn; /* avoid warnings */733}734735736/* This function passes memoryCheck's private portLibrary memCheckPortLibrary on, NOT the portlib passed into it. */737static void *memoryCheck_allocate_memory(OMRPortLibrary *portLib, UDATA byteAmount, const char *callSite, U_32 category)738{739740#ifdef DEBUG741memCheckPortLib->tty_printf( memCheckPortLib, "allocate_memory(%d)\n", byteAmount);742#endif743744return memoryCheck_wrapper_allocate_memory(memCheckPortLib, byteAmount, "allocate_memory", globalAllocator,745J9_MEMCHECK_DATA_PADDING_VALUE, J9_MEMCHECK_DATA_FILL_VALUE, J9_MEMCHECK_DATA_FREED_VALUE, callSite, category);746}747748static void *memoryCheck_allocate_memory32(OMRPortLibrary *portLib, UDATA byteAmount, const char *callSite, U_32 category)749{750751#ifdef DEBUG752memCheckPortLib->tty_printf( memCheckPortLib, "allocate_memory32(%d)\n", byteAmount);753#endif754755return memoryCheck_wrapper_allocate_memory(memCheckPortLib, byteAmount, "allocate_memory", globalAllocator32,756J9_MEMCHECK_DATA_PADDING_VALUE, J9_MEMCHECK_DATA_FILL_VALUE, J9_MEMCHECK_DATA_FREED_VALUE, callSite, category);757}758759760/* This method prints whatever information as it can gather about a block from the block and its immediate neighbors (if any).761If the block looks like a correct block, TRUE is returned. If anything bogus is discovered about the block, FALSE is returned. */762/* @precondition: If #defined MEMORY_PROTECT, this function must be passed UNLOCK'ed blocks */763/* @internal The memCheckPortLib is the one that should always be passed to this function */764765static BOOLEAN memoryCheck_describe_block(OMRPortLibrary *portLib, char const *operationName,766J9MemoryCheckHeader *blockHeader)767{768BOOLEAN everythingOkay = TRUE;769UDATA topPaddingSize = 0;770UDATA bottomPaddingSize = 0;771U_32 paddingValue;772UDATA topPaddingSmashed, bottomPaddingSmashed;773774U_8 *topPadding = ((U_8 *) blockHeader) + sizeof(J9MemoryCheckHeader);775U_8 *wrappedBlock = NULL;776U_8 *bottomPadding = NULL;777U_8 *dumpPos = topPadding;778779if ( !(mode & J9_MCMODE_MPROTECT) ) {780topPaddingSize = J9_MEMCHECK_ADJUSTED_PADDING - sizeof(J9MemoryCheckHeader);781782/* Adjust bottomPadding to force it to end on a double boundary */783bottomPaddingSize = J9_MEMCHECK_ADJUSTED_PADDING + BYTES_FOR_ALIGNMENT (blockHeader->wrappedBlockSize);784wrappedBlock = ((U_8 *) blockHeader) + J9_MEMCHECK_ADJUSTED_PADDING;785} else {786wrappedBlock = blockHeader->wrappedBlock;787if (J9_ALIGN_BOTTOM) {788topPaddingSize = wrappedBlock - (((U_8 *)blockHeader) + sizeof(J9MemoryCheckHeader));789} else {790topPaddingSize = J9_MEMCHECK_PAGE_SIZE - sizeof(J9MemoryCheckHeader);791}792793/* Adjust bottomPadding to force it to end on a double boundary */794bottomPaddingSize = J9_MEMCHECK_PAGE_SIZE + BYTES_FOR_ALIGNMENT (blockHeader->wrappedBlockSize);795}796797/* TODO - this doesn't take into account the offset area */798bottomPadding = wrappedBlock + blockHeader->wrappedBlockSize;799800portLib->tty_printf(portLib, "%s describing block at %p (header at %p):\n", operationName, wrappedBlock,801blockHeader);802803if ( mode & J9_MCMODE_MPROTECT ) {804if ( J9_ALIGN_BOTTOM ) {805U_32 offsetArea = BYTES_FOR_ALIGNMENT (blockHeader->wrappedBlockSize);806wrappedBlock = blockHeader->bottomPage - blockHeader->wrappedBlockSize - offsetArea;807bottomPadding = wrappedBlock + blockHeader->wrappedBlockSize;808}809}810811/* Make a guess at what the block should look like by peeking right after the header.. */812813if (!memoryCheck_verify_forward(portLib, topPadding, 8, J9_MEMCHECK_DATA_PADDING_VALUE, wrappedBlock)) {814paddingValue = J9_MEMCHECK_DATA_PADDING_VALUE;815} else if (!memoryCheck_verify_forward(portLib, topPadding, 8, J9_MEMCHECK_CODE_PADDING_VALUE, wrappedBlock)) {816paddingValue = J9_MEMCHECK_CODE_PADDING_VALUE;817} else {818/* TODO: check for legal padding with wrong wrappedBlock bits here? */819820/* Test for alignment off by 4 */821if ( (UDATA)topPadding%8 == 4 ) {822if (!memoryCheck_verify_forward(portLib, topPadding+4, 8, J9_MEMCHECK_DATA_PADDING_VALUE, wrappedBlock)) {823paddingValue = J9_MEMCHECK_DATA_PADDING_VALUE;824} else if (!memoryCheck_verify_forward(portLib, topPadding +4, 8, J9_MEMCHECK_CODE_PADDING_VALUE, wrappedBlock)) {825paddingValue = J9_MEMCHECK_CODE_PADDING_VALUE;826}827828portLib->tty_printf(portLib, "Block has unrecognized padding %08x %08x (header is probably trashed)!\n",829((U_32 *) topPadding)[0], ((U_32 *) topPadding)[1]);830everythingOkay = FALSE;831goto dump_header_only;832} else {833portLib->tty_printf(portLib, "Block has unrecognized padding %08x %08x (header is probably trashed)!\n",834((U_32 *) topPadding)[0], ((U_32 *) topPadding)[1]);835everythingOkay = FALSE;836goto dump_header_only;837}838}839840topPaddingSmashed = memoryCheck_verify_forward(portLib, topPadding, topPaddingSize, paddingValue, wrappedBlock);841if (topPaddingSmashed) {842portLib->tty_printf(portLib, "Last %d bytes of top padding are damaged\n", topPaddingSmashed);843everythingOkay = FALSE;844}845846/* Do additional checks to see if header is sane. If not, we won't trust its length field */847/* and we won't worry about the bottom padding. */848849if ( mode & J9_MCMODE_MPROTECT ) {850const UDATA pageSize = J9_MEMCHECK_PAGE_SIZE;851UDATA checkSize = pageSize;852853if (J9_ALIGN_BOTTOM) {854855/* Size to check on the bottom is the bottomPage and the alignment bytes. */856/* Possible enhancement: don't check the bottom page as it can't be touched anyway */857checkSize += BYTES_FOR_ALIGNMENT (blockHeader->wrappedBlockSize);858}859else {860UDATA addMod = blockHeader->wrappedBlockSize % pageSize;861862if ( addMod ) {863checkSize += pageSize - (blockHeader->wrappedBlockSize % pageSize);864}865}866bottomPaddingSmashed = memoryCheck_verify_backward(portLib, bottomPadding, checkSize , paddingValue, wrappedBlock);867} else {868bottomPaddingSmashed = memoryCheck_verify_backward(portLib, bottomPadding, bottomPaddingSize, paddingValue, wrappedBlock);869}870871if (bottomPaddingSmashed) {872portLib->tty_printf(portLib, "First %d bytes of bottom padding are damaged\n", bottomPaddingSmashed);873everythingOkay = FALSE;874}875876portLib->tty_printf(portLib, "Wrapped block size is %d, allocation number is %d\n",877blockHeader->wrappedBlockSize, blockHeader->allocationNumber);878879if (blockHeader->node) {880portLib->tty_printf(portLib, "Block was allocated by %s\n", blockHeader->node->callSite);881}882883if (everythingOkay) {884UDATA size = blockHeader->wrappedBlockSize < 32 ? blockHeader->wrappedBlockSize : 32;885portLib->tty_printf(portLib, "First %d bytes:\n", size);886memoryCheck_dump_bytes(portLib, wrappedBlock, size);887888return TRUE;889}890891if ( mode & J9_MCMODE_MPROTECT ) {892portLib->tty_printf(portLib, "Extra Top Padding:\n");893memoryCheck_dump_bytes(portLib, blockHeader->self, blockHeader->topPage - blockHeader->self);894}895896portLib->tty_printf(portLib, "Block header:\n");897memoryCheck_dump_bytes(portLib, blockHeader, sizeof(*blockHeader));898portLib->tty_printf(portLib, "Top padding:\n");899memoryCheck_dump_bytes(portLib, topPadding, topPaddingSize);900portLib->tty_printf(portLib, "Block contents:\n");901memoryCheck_dump_bytes(portLib, wrappedBlock, blockHeader->wrappedBlockSize);902portLib->tty_printf(portLib, "Bottom padding:\n");903if ( mode & J9_MCMODE_MPROTECT ) {904memoryCheck_dump_bytes(portLib, bottomPadding, blockHeader->totalAllocation - (bottomPadding - blockHeader->self));905} else {906memoryCheck_dump_bytes(portLib, bottomPadding, bottomPaddingSize);907}908return everythingOkay;909910dump_header_only:911portLib->tty_printf(portLib, "(only top padding + first 64 bytes of user data will be printed here)\n");912if ( mode & J9_MCMODE_MPROTECT ) {913portLib->tty_printf(portLib, "Extra Top Padding:\n");914memoryCheck_dump_bytes(portLib, blockHeader->self, blockHeader->topPage - blockHeader->self);915}916portLib->tty_printf(portLib, "Block header:\n");917memoryCheck_dump_bytes(portLib, blockHeader, sizeof(*blockHeader));918portLib->tty_printf(portLib, "Top padding:\n");919memoryCheck_dump_bytes(portLib, topPadding, topPaddingSize);920portLib->tty_printf(portLib, "First 64 bytes at block contents:\n");921memoryCheck_dump_bytes(portLib, wrappedBlock, 64);922return everythingOkay;923}924925926927/* This method fills fillSize bytes at fillAddress with copies of a 64-bit value formed by splitting fillValue into 2 16-bit values and inserting928the low 32 bits of the block address in between. Each instance of fillValue is aligned on an 8-byte boundary, so if fillAddress and/or929fillAddress+fillSize is not 8-byte aligned, a partial copy will appear at the start and/or end. */930931static void memoryCheck_fill_bytes(OMRPortLibrary *portLib, U_8 *fillAddress, UDATA fillSize, U_32 fillValue, U_8 *blockAddress)932{933U_8 fillWith[8];934I_32 blockAddressInt = (I_32)(IDATA)blockAddress;935936U_8 *c;937UDATA index = ((UDATA)fillAddress) & 7;938939memcpy(fillWith, &fillValue, 2);940memcpy(fillWith+2, &blockAddressInt, 4);941memcpy(fillWith+6, ((U_8 *)(&fillValue)) + 2, 2);942943for (c = fillAddress; c < fillAddress+fillSize; c++) {944*c = fillWith[index];945index++;946index &= 7;947}948}949950951952/* @internal The portLibrary passed on to other functions must be the memCheckPortLib. */953static void954memoryCheck_free_memory(OMRPortLibrary *portLib, void *memoryPointer)955{956#ifdef DEBUG957memCheckPortLib->tty_printf( memCheckPortLib, "free_memory(%p)\n", memoryPointer);958#endif959960memoryCheck_wrapper_free_memory( memCheckPortLib, memoryPointer, "free_memory", globalDeallocator,961J9_MEMCHECK_DATA_PADDING_VALUE, J9_MEMCHECK_DATA_FILL_VALUE, J9_MEMCHECK_DATA_FREED_VALUE);962}963964static void965memoryCheck_advise_and_free_memory(OMRPortLibrary *portLib, void *memoryPointer)966{967#ifdef DEBUG968memCheckPortLib->tty_printf( memCheckPortLib, "advise_and_free_memory(%p)\n", memoryPointer);969#endif970971memoryCheck_wrapper_free_memory( memCheckPortLib, memoryPointer, "advise_and_free_memory", globalAdviseDeallocator,972J9_MEMCHECK_DATA_PADDING_VALUE, J9_MEMCHECK_DATA_FILL_VALUE, J9_MEMCHECK_DATA_FREED_VALUE);973}974975static void976memoryCheck_free_memory32(OMRPortLibrary *portLib, void *memoryPointer)977{978#ifdef DEBUG979memCheckPortLib->tty_printf( memCheckPortLib, "free_memory(%p)\n", memoryPointer);980#endif981982memoryCheck_wrapper_free_memory( memCheckPortLib, memoryPointer, "free_memory", globalDeallocator32,983J9_MEMCHECK_DATA_PADDING_VALUE, J9_MEMCHECK_DATA_FILL_VALUE, J9_MEMCHECK_DATA_FREED_VALUE);984}985986/* Designed to override the original port library shut down routine987*988* Shuts down the passed in portLibrary (which should be the user's)989* using the original shut down routines, then shut's down memcheck */990static I_32991memoryCheck_port_shutdown_library(OMRPortLibrary *portLib)992{993I_32 rc = 0;994995memoryCheck_restore_old_shutdown_functions(portLib);996997rc = portLib->port_shutdown_library(portLib);998999memoryCheck_shutdown_internal( memCheckPortLib, J9_MEMCHECK_SHUTDOWN_NORMAL );10001001return rc;1002}10031004/**1005* Returns the page size to be used in the memorycheck calls.1006* The page size must always be large enough to hold the J9MemoryCheckHeader struct1007* as well as some scratch padding. If the default pagesize is less then this size,1008* then a multiple of the page size will be returned which is at least1009* J9_MEMCHECK_ADJUSTED_PADDING in size.1010*/1011static UDATA memoryCheck_get_page_size(OMRPortLibrary *portLib)1012{1013UDATA pageSize;10141015pageSize = (portLib->vmem_supported_page_sizes( portLib )) [0];1016if ( pageSize < J9_MEMCHECK_ADJUSTED_PADDING ) {1017UDATA systemPageSize = portLib->vmem_supported_page_sizes( portLib )[0];10181019pageSize = (J9_MEMCHECK_ADJUSTED_PADDING / systemPageSize)*systemPageSize +1020(J9_MEMCHECK_ADJUSTED_PADDING % systemPageSize ? systemPageSize : 0 );1021}1022return pageSize;1023}10241025/**1026* Used by the hashTableForEachDo iterator to call1027* omrvmem_free_memory() for each reserved section of virtual memory1028* and to free the J9PortVmemIdentifier struct.1029*1030* @param[in] entry The hashTable node.1031* @param[in] userData A pointer to an initialized OMRPortLibrary; The "memCheckPortLibrary" must be the one passed in.1032* This is the portLibrary with its original functions1033* @return Always returns TRUE so that the nodes in the hash table will be deallocated.1034*/1035static UDATA1036memoryCheck_hashDoFn( void *entry, void *portLib )1037{1038if ( entry && portLib ) {1039J9PortVmemIdentifier **vmemID = (J9PortVmemIdentifier **)entry;1040OMRPORT_ACCESS_FROM_OMRPORT(portLib);10411042/* Ensure the node was allocated by us, as every node will have an address which is a multiple of J9_MEMCHECK_PAGE_SIZE */1043/* TODO - consider removing if no one else could have allocated it?? */1044if ( *vmemID && (UDATA)(*vmemID)->address % J9_MEMCHECK_PAGE_SIZE == 0 ) {1045/* Release the virtual mem and free the struct holding the vmemIdentifier */1046omrvmem_free_memory((*vmemID)->address, (*vmemID)->size, *vmemID);1047omrmem_free_memory(*vmemID);1048*vmemID = NULL;1049}1050}1051return TRUE;1052}1053/**1054* This is the function used to determine if two hashed items are the same item or not. This is1055* done by determining they have the same address in the J9PortVmemIdentifier structs's address1056* field. As Each address in the address space should only be mapped into the table once for1057* each allocation, this should always be a unique way to determine if two items are equal.1058*1059* @param[in] leftEntry A valid J9PortVmemIdentifier1060* @param[in] rightEntry A valid J9PortVmemIdentifier1061* @param[in] userData An ignored parameter required by the format used by the hashTable fcn pointer.1062* @return A UDATA with 1 if equal, 0 if not.1063*/1064static UDATA1065memoryCheck_hashEqualFn(void *leftEntry, void *rightEntry, void *userData)1066{1067if ( leftEntry && rightEntry && *(J9PortVmemIdentifier **)leftEntry && *(J9PortVmemIdentifier **)rightEntry ) {1068return ( *(J9PortVmemIdentifier **) leftEntry)->address == ( *(J9PortVmemIdentifier **) rightEntry)->address;1069} else {1070return 0;1071}1072}10731074/**1075* This is the hash function to be used by the hashTable. The magic constant 2654435761 is the1076* golden ratio of 2^32 and the shift by 3 is because memorycheck assumes 8 byte alignment.1077*1078* @param[in] vmemId A valid J9PortVmemIdentifier struct pointer to reserved and committed memory.1079* @param[in] userData An ignored parameter required by the format used by the hashTable fcn pointer.1080* @return The hashkey that will return the pointer to the J9PortVmemIdentifier struct for later use.1081*/1082static UDATA1083memoryCheck_hashFn(void *vmemId, void *userData)1084{1085UDATA key;1086key = (UDATA) (*(J9PortVmemIdentifier **)vmemId)->address;1087return (key >> 3) * 0x9E3779B1;1088}10891090/**1091* This function unlocks all of the blocks on a given list.1092*1093* @param[in] portLib An initialized OMRPortLibrary with its original functions1094* @param[in] listHead The block to start unlocking the list from.1095* @param[in] lockFlags The LockMode: lockMode to lock, unlockMode to unlock1096* @param[in] lockBody Whether the body of the block needs to be unlocked. It should1097* only be set to true for the mostRecentFreedBlock list.1098*/1099static void1100memoryCheck_lockAllBlocks(OMRPortLibrary *portLib, J9MemoryCheckHeader *listHead, const UDATA lockFlags, const UDATA lockBody)1101{1102if ( mode & J9_MCMODE_MPROTECT ) {1103/* Unlock ALL the blocks */1104if ( listHead ) {1105J9MemoryCheckHeader *blockHeader = listHead;1106while ( blockHeader != NULL ) {1107if ( lockFlags == lockMode ) {1108if ( blockHeader->nextBlock ) {1109if ( lockBody ) {1110J9_MEMCHECK_LOCK_BODY( blockHeader ->nextBlock );1111}1112J9_MEMCHECK_LOCK( blockHeader->nextBlock );1113}1114if ( !blockHeader->previousBlock ) {1115if ( lockBody ) {1116J9_MEMCHECK_LOCK_BODY( blockHeader );1117}1118J9_MEMCHECK_LOCK( blockHeader );1119break;1120}1121} else if ( lockFlags == unlockMode ) {1122J9_MEMCHECK_UNLOCK( blockHeader );1123if ( lockBody ) {1124J9_MEMCHECK_UNLOCK_BODY( blockHeader );1125}1126}1127blockHeader = blockHeader->previousBlock;1128}1129blockHeader = NULL;1130}1131}1132return;1133}11341135/**1136* This function is used to lock and unlock the guardpage above and below the wrappedBlock.1137* It is usually called through one of two macros: UNLOCK( portLib, header ) or LOCK( portLib, header ).1138*1139* @precondition The global variable "J9HashTable vmemIDTable" must be already initialized.1140* @param[in] portLib An initialized OMRPortLibrary structure with its original functions1141* @param[in] memcheckHeader An initialized J9MemoryCheckheader.1142* @param[in] requestedMode The flags to apply to the guardPages. This should be either lockMode or unlockMode;1143* @return 0 on Success, -1 on failure1144*/1145static IDATA1146memoryCheck_lockGuardPages(OMRPortLibrary *portLib, void *memcheckHeader, UDATA size, UDATA requestedMode)1147{1148if ( mode & J9_MCMODE_MPROTECT ) {1149void *ret = NULL;1150U_8 *topPageBoundary = NULL;1151J9PortVmemIdentifier getFromTable;1152J9PortVmemIdentifier *getFromTablePtr;1153J9PortVmemIdentifier **block = NULL;1154J9MemoryCheckHeader *head = (J9MemoryCheckHeader *)memcheckHeader;1155UDATA oldMode;1156OMRPORT_ACCESS_FROM_OMRPORT(portLib);11571158if ( !J9_ALIGN_BOTTOM ) {1159topPageBoundary = (U_8 *)memcheckHeader;1160}1161else {1162#if defined(J9ZOS390)1163topPageBoundary = (U_8 *)head->self;1164#else1165topPageBoundary = (U_8 *)memcheckHeader; /* Pointer to the J9MemoryCheckHeader */1166topPageBoundary = topPageBoundary - (UDATA)topPageBoundary % J9_MEMCHECK_PAGE_SIZE; /* Align it to a page Boundary */1167#endif1168}11691170/* Get the correct struct from the hashTable */1171getFromTable.address = topPageBoundary;1172getFromTablePtr = &getFromTable;1173block = hashTableFind( vmemIDTable, &getFromTablePtr );1174if ( block ) {1175getFromTablePtr = *block;1176oldMode = getFromTablePtr->mode;11771178/* Update it so it has the correct access mode and recommit the mem to allow reading */1179getFromTablePtr->mode = unlockMode;1180ret = omrvmem_commit_memory( getFromTablePtr->address, getFromTablePtr->pageSize, getFromTablePtr );1181if ( NULL == ret ) {1182getFromTablePtr->mode = oldMode;1183return -1;1184}11851186/* Apply user passed in mode to the topPage and bottom page */1187getFromTablePtr->mode = requestedMode;1188ret = omrvmem_commit_memory( head->bottomPage, getFromTablePtr->pageSize, getFromTablePtr );1189if ( NULL == ret ) {1190getFromTablePtr->mode = oldMode;1191return -1;1192}11931194ret = omrvmem_commit_memory( topPageBoundary, getFromTablePtr->pageSize, getFromTablePtr );1195if ( NULL == ret ) {1196getFromTablePtr->mode = oldMode;1197return -1;1198}1199getFromTablePtr->mode = oldMode;12001201return 0;1202}1203return -1;1204}1205return 0;1206}12071208/**1209* This function is used to lock and unlock the wrappedBlock. It is usually called through1210* one of two macros: UNLOCK_BODY( portLib, header ) or LOCK_BODY( portLib, header ).1211*1212* @precondition The blockHeader must be unlocked before calling this function1213* @param[in] portLib An initialized OMRPortLibrary structure with its original functions1214* @param[in] blockHeader An initialized blockHeader which already has its guard pages unlocked.1215* @param[in] requestedMode The flags to apply to the wrappedBlock. This should be either lockMode or unlockMode;1216* @return 0 on Success, -1 on failure1217*/1218static IDATA1219memoryCheck_lockWrappedBlock(OMRPortLibrary *portLib, J9MemoryCheckHeader *blockHeader, const UDATA requestedMode)1220{1221if ( mode & J9_MCMODE_MPROTECT ) {1222void *ret = NULL;1223U_8 *topPageBoundary = NULL;1224J9PortVmemIdentifier getFromTable;1225J9PortVmemIdentifier *getFromTablePtr = NULL;1226J9PortVmemIdentifier **block = NULL;1227J9MemoryCheckHeader *head = (J9MemoryCheckHeader *)blockHeader;1228UDATA userPages = 0;1229UDATA oldMode;1230OMRPORT_ACCESS_FROM_OMRPORT(portLib);12311232if ( !J9_ALIGN_BOTTOM ) {1233topPageBoundary = (U_8 *)blockHeader;1234}1235else {1236topPageBoundary = (U_8 *)blockHeader; /* Pointer to the J9MemoryCheckHeader */1237topPageBoundary = topPageBoundary - (UDATA)topPageBoundary % J9_MEMCHECK_PAGE_SIZE; /* Align it to a page Boundary */1238}12391240/* Get the correct struct from the hashTable */1241getFromTable.address = topPageBoundary;1242getFromTablePtr = &getFromTable;1243block = hashTableFind( vmemIDTable, &getFromTablePtr );1244getFromTablePtr = *block;12451246/* Determine the number of pages that are used to hold the 'body' or user-allocated mem */1247userPages = blockHeader->wrappedBlockSize/J9_MEMCHECK_PAGE_SIZE + ( blockHeader->wrappedBlockSize % J9_MEMCHECK_PAGE_SIZE ? 1 : 0 );12481249/* Update and commit struct to lock the userPages */1250oldMode = getFromTablePtr->mode;1251getFromTablePtr->mode = requestedMode;1252ret = omrvmem_commit_memory( topPageBoundary + getFromTablePtr->pageSize, userPages * getFromTablePtr->pageSize, getFromTablePtr );1253getFromTablePtr->mode = oldMode;1254if ( NULL == ret ) {1255return -1;1256}1257}1258return 0;1259}126012611262/* This method scans the entire list of allocated blocks to make sure none of the padding has been scratched.1263We do this on every allocate and every free to make sure we notice trashing as early as possible. If a1264screwed up block is detected, details are printed to the console. If possible, the scan is continued to print1265out other screwed up blocks. Returns FALSE if any blocks are screwed up, or TRUE if everything looks okay. */1266/* @internal The passed in portLib should be the memCheckPortLib with its original functions */12671268static BOOLEAN memoryCheck_scan_all_blocks(OMRPortLibrary *portLib)1269{1270J9MemoryCheckHeader *blockHeader, *prevBlockHeader;1271BOOLEAN everythingOkay = TRUE;1272char const *operationName = "scan_all_blocks";1273UDATA topPaddingSize = 0;1274UDATA bottomPaddingSize;12751276if ( !(mode & J9_MCMODE_MPROTECT) ) {1277/* topPaddingSize is constant with all blocks if MPROTECT is not enabled */1278topPaddingSize = J9_MEMCHECK_ADJUSTED_PADDING - sizeof(J9MemoryCheckHeader);1279}12801281if ( mode & J9_MCMODE_MPROTECT ) {1282/* Unlock ALL blocks */1283memoryCheck_lockAllBlocks( memCheckPortLib, mostRecentBlock, unlockMode, 0 );1284memoryCheck_lockAllBlocks( memCheckPortLib, mostRecentFreedBlock, unlockMode, 1 );1285}12861287for (prevBlockHeader = NULL, blockHeader = mostRecentBlock;1288blockHeader != NULL; prevBlockHeader = blockHeader, blockHeader = blockHeader->previousBlock) {12891290U_8 *topPadding = ((U_8 *) blockHeader) + sizeof(J9MemoryCheckHeader);1291U_8 *wrappedBlock;1292U_8 *bottomPadding;12931294if (mode & J9_MCMODE_MPROTECT) {1295topPaddingSize = blockHeader->wrappedBlock - topPadding;1296wrappedBlock = blockHeader->wrappedBlock;1297} else {1298/* topPaddingSize was already calculated above */1299wrappedBlock = topPadding + topPaddingSize;1300}13011302/* Verify top padding */1303if ( (mode & J9_MCMODE_MPROTECT) && !J9_ALIGN_BOTTOM ) {1304/* all the top padding is write protected, no point in scanning */1305} else {1306if (memoryCheck_verify_forward1307(portLib, topPadding, topPaddingSize, J9_MEMCHECK_DATA_PADDING_VALUE, wrappedBlock)) {1308if (memoryCheck_verify_forward1309(portLib, topPadding, topPaddingSize, J9_MEMCHECK_CODE_PADDING_VALUE, wrappedBlock)) {13101311if (blockHeader->wrappedBlockSize == J9_MEMCHECK_FREED_SIZE) {1312portLib->tty_printf(portLib,1313"ERROR: Found an already freed block %p (header at %p) on the allocated list (should never happen)!\n",1314wrappedBlock, blockHeader);1315goto describe_block_and_fail;1316}13171318/* Block not freed yet, but top padding at least is damaged. If the nextBlock pointer is damaged too1319then we assume the header is useless and we stop scanning (to reduce the chances of a GPF). */13201321if (blockHeader->nextBlock != prevBlockHeader) {1322portLib->tty_printf(portLib,1323"ERROR: Header of block at %p (header at %p) is damaged, scan stopped.\n",1324wrappedBlock, blockHeader);1325goto describe_block_and_fail;1326}13271328everythingOkay = FALSE;1329goto describe_block_and_continue;1330}1331}1332}13331334if (blockHeader->allocationNumber > memStats.totalBlocksAllocated) {1335portLib->tty_printf(portLib, "ERROR: Invalid allocation number %d in block %p (header at %p)\n",1336blockHeader->allocationNumber, wrappedBlock, blockHeader);1337goto describe_block_and_fail; /* If header is screwed then we risk a GPF by continuing.. */1338}13391340if (prevBlockHeader && (blockHeader->allocationNumber >= prevBlockHeader->allocationNumber)) {1341portLib->tty_printf(portLib, "ERROR: Allocation number %d in block %p (header at %p) should be lower than %d in block %p (header at %p)\n",1342blockHeader->allocationNumber, wrappedBlock, blockHeader,1343prevBlockHeader->allocationNumber, ((U_8 *) prevBlockHeader) + J9_MEMCHECK_ADJUSTED_PADDING, prevBlockHeader);1344goto describe_block_and_fail; /* If header is screwed then we risk a GPF by continuing.. */1345}13461347if (blockHeader->nextBlock != prevBlockHeader) {1348portLib->tty_printf(portLib,1349"ERROR: Allocated list links between %p (header at %p) and %p (header at %p) are corrupt\n",1350((U_8 *) prevBlockHeader) + J9_MEMCHECK_ADJUSTED_PADDING, prevBlockHeader, wrappedBlock,1351blockHeader);1352}13531354bottomPadding = wrappedBlock + blockHeader->wrappedBlockSize;13551356if ( mode & J9_MCMODE_MPROTECT ) {13571358/* Adjust bottomPadding to force it to end on a double boundary */1359bottomPaddingSize = blockHeader->totalAllocation - J9_MEMCHECK_PAGE_SIZE - (bottomPadding - blockHeader->topPage);1360} else {13611362/* Adjust bottomPadding to force it to end on a double boundary */1363bottomPaddingSize = J9_MEMCHECK_ADJUSTED_PADDING + BYTES_FOR_ALIGNMENT (blockHeader->wrappedBlockSize);1364}13651366if (memoryCheck_verify_forward1367(portLib, bottomPadding, bottomPaddingSize, J9_MEMCHECK_DATA_PADDING_VALUE, wrappedBlock)) {1368if (memoryCheck_verify_forward1369(portLib, bottomPadding, bottomPaddingSize, J9_MEMCHECK_CODE_PADDING_VALUE, wrappedBlock)) {13701371/* Bottom padding is damaged. */1372goto describe_block_and_continue;13731374}1375}13761377/* This block looked okay, so go back up to top of loop */1378continue;13791380/*1381* Comes here when bottom padding is damaged. But, we still1382* want to continue with other blocks.1383* TODO: Consider refactor code to avoid gotos1384* TODO: Why is return value from memoryCheck_describe_block() ignored?1385*/1386describe_block_and_continue:1387everythingOkay = FALSE;1388memoryCheck_describe_block(portLib, operationName, blockHeader);1389}13901391if (mode & J9_MCMODE_NEVER_FREE) {1392/* Scan all previously freed blocks too */1393for (prevBlockHeader = NULL, blockHeader = mostRecentFreedBlock;1394blockHeader != NULL; prevBlockHeader = blockHeader, blockHeader = blockHeader->previousBlock) {13951396U_8 *topPadding = ((U_8 *) blockHeader) + sizeof(J9MemoryCheckHeader);1397U_8 *bottomPadding, *wrappedBlock;1398UDATA bottomSize; /* this includes the wrappedblock plus the bottom padding */13991400if (mode & J9_MCMODE_MPROTECT) {1401topPaddingSize = blockHeader->wrappedBlock - topPadding;1402wrappedBlock = blockHeader->wrappedBlock;1403} else {1404/* topPaddingSize was already calculated above */1405wrappedBlock = topPadding + topPaddingSize;1406}14071408if ( (mode & J9_MCMODE_MPROTECT) && !J9_ALIGN_BOTTOM ) {1409/* all the top padding is write protected, no point in scanning */1410} else {1411if (memoryCheck_verify_forward1412(portLib, topPadding, topPaddingSize, J9_MEMCHECK_DATA_FREED_VALUE, wrappedBlock)) {1413if (memoryCheck_verify_forward1414(portLib, topPadding, topPaddingSize, J9_MEMCHECK_CODE_FREED_VALUE, wrappedBlock)) {14151416/* Freed block: top padding at least is damaged. If the nextBlock pointer is damaged too1417then we assume the header is useless and we stop scanning (to reduce the chances of a GPF). */14181419if (blockHeader->nextBlock != prevBlockHeader) {1420portLib->tty_printf(portLib,1421"ERROR: Header of previously freed block at %p (header at %p) is damaged, scan stopped.\n",1422wrappedBlock, blockHeader);1423goto describe_freed_block_and_fail;1424}14251426everythingOkay = FALSE;1427goto describe_freed_block_and_continue;1428}1429}1430}14311432if (blockHeader->allocationNumber > memStats.totalBlocksAllocated) {1433portLib->tty_printf(portLib, "ERROR: Invalid allocation number %d in previously freed block %p (header at %p)\n",1434blockHeader->allocationNumber, wrappedBlock, blockHeader);1435goto describe_freed_block_and_fail; /* If header is trashed then we risk a GPF by continuing.. */1436}14371438if (blockHeader->nextBlock != prevBlockHeader) {1439portLib->tty_printf(portLib,1440"ERROR: Previously freed block list links between %p (header at %p) and %p (header at %p) are corrupt\n",1441((U_8 *) prevBlockHeader) + J9_MEMCHECK_ADJUSTED_PADDING, prevBlockHeader, wrappedBlock,1442blockHeader);1443}14441445bottomPadding = wrappedBlock + blockHeader->wrappedBlockSize;14461447if ( mode & J9_MCMODE_MPROTECT ) {1448bottomPaddingSize = blockHeader->totalAllocation - J9_MEMCHECK_PAGE_SIZE - (bottomPadding - blockHeader->topPage);1449} else {14501451/* Adjust bottomPadding to force it to end on a double boundary */1452bottomPaddingSize = J9_MEMCHECK_ADJUSTED_PADDING + BYTES_FOR_ALIGNMENT (blockHeader->wrappedBlockSize);1453}1454bottomSize = bottomPaddingSize + blockHeader->wrappedBlockSize;14551456if (memoryCheck_verify_forward1457(portLib, wrappedBlock, bottomSize, J9_MEMCHECK_DATA_FREED_VALUE, wrappedBlock)) {1458if (memoryCheck_verify_forward1459(portLib, wrappedBlock, bottomSize, J9_MEMCHECK_CODE_FREED_VALUE, wrappedBlock)) {14601461goto describe_freed_block_and_continue;1462}1463}14641465/* this block looked okay */1466continue;14671468/* TODO: Why is return value from memoryCheck_describe_block() ignored? */1469describe_freed_block_and_continue:1470everythingOkay = FALSE;1471memoryCheck_describe_freed_block(portLib, operationName, blockHeader);1472}14731474}14751476if (memStats.totalBlocksFreed > memStats.totalBlocksAllocated) {1477/* This shouldn't happen. In order to cause it, somebody has to free garbage (or free a block twice) without1478this error being caught by any of the previous checks. If global variables were trashed then we would1479hopefully go down in flames before we got here. */14801481portLib->tty_printf(portLib, "ERROR: More blocks freed (%d) than allocated (%d)!\n",1482memStats.totalBlocksFreed, memStats.totalBlocksAllocated);1483everythingOkay = FALSE;1484}14851486if ( mode & J9_MCMODE_MPROTECT ) {1487/* Lock All the Blocks */1488memoryCheck_lockAllBlocks( memCheckPortLib, mostRecentBlock, lockMode, 0 );1489memoryCheck_lockAllBlocks( memCheckPortLib, mostRecentFreedBlock, lockMode, 1 );1490}1491return everythingOkay;14921493describe_block_and_fail:1494memoryCheck_describe_block(portLib, operationName, blockHeader);1495if ( mode & J9_MCMODE_MPROTECT ) {1496/* Lock All the Blocks */1497memoryCheck_lockAllBlocks( memCheckPortLib, mostRecentBlock, lockMode, 0 );1498memoryCheck_lockAllBlocks( memCheckPortLib, mostRecentFreedBlock, lockMode, 1 );1499}1500return FALSE;15011502describe_freed_block_and_fail:1503memoryCheck_describe_freed_block(portLib, operationName, blockHeader);1504if ( mode & J9_MCMODE_MPROTECT ) {1505/* Lock All the Blocks */1506memoryCheck_lockAllBlocks( memCheckPortLib, mostRecentBlock, lockMode, 0 );1507memoryCheck_lockAllBlocks( memCheckPortLib, mostRecentFreedBlock, lockMode, 1 );1508}1509return FALSE;1510}1511151215131514/* @internal The passed in portLib should be the memCheckPortLib */1515static void *1516memoryCheck_wrapper_allocate_memory(OMRPortLibrary *portLib, UDATA byteAmount, char const *operationName,1517J9_MEM_ALLOCATE_FUNC allocator, U_32 paddingValue, U_32 fillValue,1518U_32 freedValue, const char *callSite, U_32 category)1519{1520U_8 *topPadding, *wrappedBlock, *bottomPadding;1521J9MemoryCheckHeader *blockHeader;1522BOOLEAN everythingOkay = TRUE;1523UDATA newAllocationNumber;1524UDATA newTotalBytesAllocated;1525J9PortVmemIdentifier *vmemID = NULL;1526U_8 *topPage = NULL; /* J9_MCMODE_MPROTECT: Points to the first page boundary in the returned data */1527U_8 *bottomPage = NULL; /* J9_MCMODE_MPROTECT: Points to the bottom page boundary in the returned data */1528UDATA topPaddingSize = 0;1529UDATA bottomPaddingSize = 0;1530UDATA allocAmount = 0;15311532MUTEX_ENTER(mcMutex);1533/* Disable trace to avoid deadlock between mcMutex and trace global lock. JTC-JAT 93458 */1534if ((NULL != uteInterface) && (NULL != uteInterface->server)) {1535uteInterface->server->DisableTrace(UT_DISABLE_THREAD);1536}15371538if (0 == (mode & J9_MCMODE_NO_SCAN) ) {15391540if ( mode & J9_MCMODE_MPROTECT ) {1541/* Need only allocate 1 extra page above and below b/c j9vmem always returns page aligned pointers */1542topPaddingSize = J9_MEMCHECK_PAGE_SIZE - sizeof(J9MemoryCheckHeader);1543bottomPaddingSize = J9_MEMCHECK_PAGE_SIZE;1544allocAmount = sizeof(J9MemoryCheckHeader) + topPaddingSize + (byteAmount/J9_MEMCHECK_PAGE_SIZE)*J9_MEMCHECK_PAGE_SIZE +1545(byteAmount%J9_MEMCHECK_PAGE_SIZE ? 1 : 0 )*J9_MEMCHECK_PAGE_SIZE + bottomPaddingSize;1546} else {15471548/* Adjust bottomPadding to force it to end on a double boundary */1549bottomPaddingSize = J9_MEMCHECK_ADJUSTED_PADDING + BYTES_FOR_ALIGNMENT (byteAmount);1550topPaddingSize = J9_MEMCHECK_ADJUSTED_PADDING - sizeof(J9MemoryCheckHeader);1551allocAmount = sizeof(J9MemoryCheckHeader) + topPaddingSize + byteAmount + bottomPaddingSize;1552}15531554newAllocationNumber = memStats.totalBlocksAllocated+1;1555newTotalBytesAllocated = memStats.currentBytesAllocated + byteAmount;15561557if ((mode & J9_MCMODE_SKIP_TO) && (newAllocationNumber < skipToAlloc)) {1558/* We aren't interested in scanning anything yet. */1559} else {1560if (mode & J9_MCMODE_FULL_SCANS) {1561everythingOkay = memoryCheck_scan_all_blocks(portLib);1562} else { /* quick mode - just check most recently allocated block */1563if (mostRecentBlock) {1564J9_MEMCHECK_UNLOCK( mostRecentBlock );1565everythingOkay = memoryCheck_scan_block(portLib, mostRecentBlock);1566J9_MEMCHECK_LOCK( mostRecentBlock );1567}1568}1569}1570if (!everythingOkay) {1571memoryCheck_abort(portLib);1572}15731574if ((mode & J9_MCMODE_FAIL_AT) && (newAllocationNumber >= failAtAlloc)) {1575++memStats.failedAllocs;1576if ((memStats.totalBlocksAllocated+memStats.failedAllocs) == failAtAlloc) {1577portLib->tty_printf(portLib, "WARNING: failat: Returning NULL for allocation attempt %d in %s at %s (%d bytes were requested)\n",1578(memStats.totalBlocksAllocated+memStats.failedAllocs), operationName, callSite, byteAmount);1579portLib->tty_printf(portLib, " All subsequent memory allocation attempt will be failed intentionally\n");1580}15811582if ((NULL != uteInterface) && (NULL != uteInterface->server)) {1583uteInterface->server->EnableTrace(UT_ENABLE_THREAD);1584}1585MUTEX_EXIT(mcMutex);1586return NULL;1587}1588if ((mode & J9_MCMODE_LIMIT) && (newTotalBytesAllocated > limitAlloc)) {1589if (memStats.currentBytesAllocated <= limitAlloc) {1590portLib->tty_printf(portLib, "WARNING: limit: Returning NULL for attempt to allocate %d bytes in %s at %s (%d bytes already allocated)\n",1591byteAmount, operationName, callSite, memStats.currentBytesAllocated);1592portLib->tty_printf(portLib, " All subsequent memory allocation attempt will be failed intentionally\n");1593}1594if ((NULL != uteInterface) && (NULL != uteInterface->server)) {1595uteInterface->server->EnableTrace(UT_ENABLE_THREAD);1596}1597MUTEX_EXIT(mcMutex);1598return NULL;1599}16001601if ( 0 == (mode & J9_MCMODE_MPROTECT) ) {1602#if defined (WIN32) && !defined(BREW)1603if (mode & J9_MCMODE_TOP_DOWN) {1604topPadding = VirtualAlloc(NULL, allocAmount, MEM_COMMIT | MEM_TOP_DOWN, PAGE_EXECUTE_READWRITE);1605} else {1606/* The portLib being used here is the memCheckPortLib, not the user portLib */1607topPadding = allocator(portLib, allocAmount, callSite, category);1608}1609#else1610/* The portLib being used here is the memCheckPortLib, not the user portLib */1611topPadding = allocator(portLib, allocAmount, callSite, category);1612#endif1613if (!topPadding) {1614++memStats.failedAllocs;1615portLib->tty_printf(portLib, "WARNING: Out of memory in %s at %s, returning NULL (%d bytes were requested)\n",1616operationName, callSite, byteAmount);1617if ((NULL != uteInterface) && (NULL != uteInterface->server)) {1618uteInterface->server->EnableTrace(UT_ENABLE_THREAD);1619}1620MUTEX_EXIT(mcMutex);1621return NULL;1622}1623blockHeader = (J9MemoryCheckHeader *) topPadding;1624topPadding += sizeof(J9MemoryCheckHeader);1625wrappedBlock = topPadding + topPaddingSize;1626bottomPadding = wrappedBlock + byteAmount;1627/* zero out the header */1628memset(blockHeader, 0, sizeof(J9MemoryCheckHeader));16291630} else {1631/* we're in mode = J9_MCMODE_MPROTECT */1632/* Allocate the structure.1633* NOTE: memCheckPortLib->mem_allocate_memory is the "allocator" function (because we can't be1634* using the subAllocator in conjunction with mode J9_MCMODE_MPROTECT).1635* making it clear that we are using memCheckPortLib->mem_allocate_memory here because the1636* corresponding free is done using memCheckPortLib->mem_free_memory (and we don't want to pass in1637* a deallocator to memoryCheck_wrapper_allocate_memory1638*/1639vmemID = memCheckPortLib->mem_allocate_memory( memCheckPortLib, sizeof(J9PortVmemIdentifier), callSite, OMRMEM_CATEGORY_VM);1640if (NULL == vmemID) {1641++memStats.failedAllocs;1642portLib->tty_printf(portLib, "WARNING: J9PortVmemIdentifier struct could not be allocated!\n"1643"WARNING: Out of memory in %s at %s, returning NULL (%d bytes were requested)\n",1644operationName, callSite, byteAmount);1645if ((NULL != uteInterface) && (NULL != uteInterface->server)) {1646uteInterface->server->EnableTrace(UT_ENABLE_THREAD);1647}1648MUTEX_EXIT(mcMutex);1649return NULL;1650}1651/* Reserve and Commit the memory - Pointer is page aligned by j9vmem. Do NOT use the J9_MEMCHECK_PAGE_SIZE1652* macro for the pageSize parameter to this function call only! The macro may return a multiple of the system page size1653* due to the default implementation of j9vmem using sizeof(UDATA) as a pagesize.1654*/1655topPage = memCheckPortLib->vmem_reserve_memory( memCheckPortLib, NULL, allocAmount, vmemID, allocateMode,1656memCheckPortLib->vmem_supported_page_sizes( memCheckPortLib )[0], category );16571658if (!topPage) {1659++memStats.failedAllocs;1660portLib->tty_printf(portLib, "WARNING: Out of memory in %s at %s, returning NULL (%d bytes were requested)\n",1661operationName, callSite, byteAmount);1662memCheckPortLib->mem_free_memory( memCheckPortLib, vmemID );1663vmemID = NULL;1664if ((NULL != uteInterface) && (NULL != uteInterface->server)) {1665uteInterface->server->EnableTrace(UT_ENABLE_THREAD);1666}1667MUTEX_EXIT(mcMutex);1668return NULL;1669}16701671/* Add the vmemID to the hashTable */1672if ( !hashTableAdd( vmemIDTable, &vmemID ) ){1673memCheckPortLib->vmem_free_memory( memCheckPortLib, vmemID->address, vmemID->size, vmemID );1674memCheckPortLib->mem_free_memory( memCheckPortLib, vmemID );1675vmemID = NULL;1676++memStats.failedAllocs;1677portLib->tty_printf(portLib, "WARNING: Unable to add vmemID to hashTable! In %s, returning NULL (%d bytes were requested)\n",1678operationName, byteAmount);1679if ((NULL != uteInterface) && (NULL != uteInterface->server)) {1680uteInterface->server->EnableTrace(UT_ENABLE_THREAD);1681}1682MUTEX_EXIT(mcMutex);1683return NULL;1684}16851686blockHeader = (J9MemoryCheckHeader *) topPage;1687blockHeader->self = topPage; /* Grab the malloc'd ptr for calls to free */1688/* TODO - why is the page size not the same as what was used in vmem_reserve_memory ? */1689wrappedBlock = topPage + J9_MEMCHECK_PAGE_SIZE;1690topPadding = (U_8*)blockHeader + sizeof(J9MemoryCheckHeader);1691bottomPadding = wrappedBlock + byteAmount;1692bottomPage = wrappedBlock + (byteAmount/J9_MEMCHECK_PAGE_SIZE)*J9_MEMCHECK_PAGE_SIZE + (byteAmount % J9_MEMCHECK_PAGE_SIZE ? 1 : 0)*J9_MEMCHECK_PAGE_SIZE;16931694/* Set it to unlocked */1695blockHeader->isLocked = 0;1696blockHeader->totalAllocation = allocAmount;1697blockHeader->topPage = topPage;1698blockHeader->wrappedBlock = wrappedBlock;1699blockHeader->bottomPage = bottomPage;1700blockHeader->wrappedBlockSize = byteAmount;17011702/* adjust the user pointer the location of the struct. This adjustment only needs to be made if the1703* data is to be aligned on the bottom page1704*/1705if ( J9_ALIGN_BOTTOM ) {1706U_32 offsetArea;1707/*1708* TOP and BOTTOM locked pages don't move, just adjust the data between them and the1709* other pointers into the memory. The blockHeader will always be 1 J9_MEMCHECK_PAGE_SIZE above1710* the wrappedBlock1711*/1712offsetArea = BYTES_FOR_ALIGNMENT (byteAmount);1713wrappedBlock = bottomPage - byteAmount - offsetArea;1714blockHeader->wrappedBlock = wrappedBlock;1715/* Ensure header fits entirely in topPage */1716if ( wrappedBlock - J9_MEMCHECK_PAGE_SIZE + sizeof(J9MemoryCheckHeader) > topPage + J9_MEMCHECK_PAGE_SIZE ) {1717memmove( wrappedBlock - J9_MEMCHECK_PAGE_SIZE - sizeof(J9MemoryCheckHeader), blockHeader, sizeof(J9MemoryCheckHeader) );1718blockHeader = (J9MemoryCheckHeader *)(wrappedBlock - J9_MEMCHECK_PAGE_SIZE - sizeof(J9MemoryCheckHeader) );1719}1720else {1721memmove( wrappedBlock - J9_MEMCHECK_PAGE_SIZE, blockHeader, sizeof(J9MemoryCheckHeader) );1722blockHeader = (J9MemoryCheckHeader *)(wrappedBlock - J9_MEMCHECK_PAGE_SIZE );1723}1724topPadding = (U_8 *)blockHeader + sizeof(J9MemoryCheckHeader);1725bottomPadding = wrappedBlock + byteAmount;1726}1727} /* J9_MCMODE_MPROTECT */17281729if ( !(mode & J9_MCMODE_MPROTECT) ) {1730if ((mode & J9_MCMODE_SKIP_TO) && (newAllocationNumber < skipToAlloc)) {1731/* We aren't interested in having blocks filled with padding yet. */1732} else {1733memoryCheck_fill_bytes(portLib, topPadding, topPaddingSize, paddingValue, wrappedBlock);1734memoryCheck_fill_bytes(portLib, bottomPadding, bottomPaddingSize, paddingValue, wrappedBlock);1735if (mode & J9_MCMODE_ZERO) {1736memset(wrappedBlock, 0, byteAmount);1737} else {1738memoryCheck_fill_bytes(portLib, wrappedBlock, byteAmount, fillValue, wrappedBlock);1739}1740}1741} else {1742if ((mode & J9_MCMODE_SKIP_TO) && (newAllocationNumber < skipToAlloc)) {1743/* We aren't interested in having blocks filled with padding yet. */1744} else {1745/* Extra padding above */1746if ( blockHeader->self != blockHeader->topPage ) {1747memoryCheck_fill_bytes(portLib, blockHeader->self, blockHeader->topPage - blockHeader->self, paddingValue, wrappedBlock);1748}1749/* Normal top Padding */1750memoryCheck_fill_bytes(portLib, topPadding, wrappedBlock - topPadding, paddingValue, wrappedBlock);1751/* Extra padding below */1752memoryCheck_fill_bytes(portLib, bottomPadding, allocAmount - (bottomPadding - blockHeader->self), paddingValue, wrappedBlock);1753if (mode & J9_MCMODE_ZERO) {1754memset(wrappedBlock, 0, byteAmount );1755} else {1756memoryCheck_fill_bytes(portLib, wrappedBlock, byteAmount, fillValue, wrappedBlock);1757}1758}1759}17601761/* Recompute the allocation number, in case we have been called recursively (due to tracing, for example). */1762newAllocationNumber = memStats.totalBlocksAllocated + 1;17631764#ifdef J9VM_MEMCHK_SIM_SUP1765if (mode & J9_MCMODE_SIMULATE) {1766portLib->tty_printf(portLib, "void * BLOCK_%d; %s\n", newAllocationNumber, SIM_TAG);1767portLib->tty_printf(portLib, "BLOCK_%d = j9mem_allocate_memory(%d); %s\n", newAllocationNumber, byteAmount, SIM_TAG);1768}1769#endif17701771} else {1772/* (mode & J9_MCMODE_NO_SCAN) */1773U_8 * blockStart;17741775newAllocationNumber = memStats.totalBlocksAllocated + 1;1776if ((mode & J9_MCMODE_FAIL_AT) && (newAllocationNumber >= failAtAlloc)) {1777++memStats.failedAllocs;1778if ((memStats.totalBlocksAllocated+memStats.failedAllocs) == failAtAlloc) {1779portLib->tty_printf(portLib, "WARNING: failat: Returning NULL for allocation attempt %d in %s at %s (%d bytes were requested)\n",1780(memStats.totalBlocksAllocated+memStats.failedAllocs), operationName, callSite, byteAmount);1781portLib->tty_printf(portLib, " All subsequent memory allocation attempt will be failed intentionally\n");1782}17831784if ((NULL != uteInterface) && (NULL != uteInterface->server)) {1785uteInterface->server->EnableTrace(UT_ENABLE_THREAD);1786}1787MUTEX_EXIT(mcMutex);1788return NULL;1789}17901791allocAmount = sizeof(J9MemoryCheckHeader) + byteAmount;1792blockStart = allocator(portLib, allocAmount, callSite, category);17931794if (NULL == blockStart) {1795portLib->tty_printf(portLib, "WARNING: Out of memory in %s at %s, returning NULL (%d bytes were requested)\n",1796operationName, callSite, byteAmount);1797if ((NULL != uteInterface) && (NULL != uteInterface->server)) {1798uteInterface->server->EnableTrace(UT_ENABLE_THREAD);1799}1800MUTEX_EXIT(mcMutex);1801return NULL;1802}18031804blockHeader = (J9MemoryCheckHeader *) blockStart;1805/* zero out the header */1806memset(blockHeader, 0, sizeof(J9MemoryCheckHeader));18071808wrappedBlock = blockStart + sizeof(J9MemoryCheckHeader);1809blockHeader->wrappedBlock = wrappedBlock;1810blockHeader->wrappedBlockSize = byteAmount;18111812if (mode & J9_MCMODE_ZERO) {1813memset(wrappedBlock, 0, byteAmount );1814}18151816/* Recompute the allocation number, in case we have been called recursively (due to tracing, for example). */1817newAllocationNumber = memStats.totalBlocksAllocated + 1;18181819} /* (mode & J9_MCMODE_NO_SCAN) */18201821++memStats.totalBlocksAllocated;1822blockHeader->wrappedBlockSize = byteAmount;1823blockHeader->allocationNumber = newAllocationNumber;1824memStats.totalBytesAllocated += byteAmount;1825memStats.currentBlocksAllocated += 1;1826if (memStats.currentBlocksAllocated > memStats.hiWaterBlocksAllocated) {1827memStats.hiWaterBlocksAllocated = memStats.currentBlocksAllocated;1828}1829memStats.currentBytesAllocated += byteAmount;1830if (memStats.currentBytesAllocated > memStats.hiWaterBytesAllocated) {1831memStats.hiWaterBytesAllocated = memStats.currentBytesAllocated;1832}1833if (byteAmount > memStats.largestBlockAllocated) {1834memStats.largestBlockAllocated = byteAmount;1835memStats.largestBlockAllocNum = newAllocationNumber;1836}18371838blockHeader->nextBlock = NULL;1839if ((mode & J9_MCMODE_SKIP_TO) && (newAllocationNumber < skipToAlloc)) {1840/* We don't want this block on the allocated list - no padding or verifying it. */1841blockHeader->previousBlock = NULL;1842} else {1843blockHeader->previousBlock = mostRecentBlock;1844if (mostRecentBlock) {1845J9_MEMCHECK_UNLOCK( mostRecentBlock );1846mostRecentBlock->nextBlock = blockHeader;1847J9_MEMCHECK_LOCK( mostRecentBlock );1848}1849mostRecentBlock = blockHeader;1850}18511852if( avl_tree ) {1853memoryCheck_update_callSites_allocate(portLib, blockHeader, callSite, byteAmount);1854if( (mode & J9_MCMODE_PRINT_CALLSITES) || (mode & J9_MCMODE_PRINT_CALLSITES_SMALL) ) {1855if(memStats.totalBlocksAllocated % callSitePrintCount == 0) {1856if (mode & J9_MCMODE_PRINT_CALLSITES) {1857memoryCheck_dump_callSites(portLib, avl_tree);1858} else if (mode & J9_MCMODE_PRINT_CALLSITES_SMALL) {1859memoryCheck_dump_callSites_small(portLib, avl_tree);1860}1861}1862}1863}18641865J9_MEMCHECK_LOCK( blockHeader );1866if ((NULL != uteInterface) && (NULL != uteInterface->server)) {1867uteInterface->server->EnableTrace(UT_ENABLE_THREAD);1868}1869MUTEX_EXIT(mcMutex);1870return wrappedBlock;1871}187218731874/* @internal The passed in portLib should be the memCheckPortLib */1875static void1876memoryCheck_wrapper_free_memory(OMRPortLibrary *portLib, void *memoryPointer, char const *operationName,1877J9_MEM_FREE_FUNC deallocator, U_32 paddingValue, U_32 fillValue,1878U_32 freedValue)1879{1880J9MemoryCheckHeader *listBlockHeader, *topHeader;1881UDATA topPaddingSize = 0;1882UDATA bottomPaddingSize = 0;1883U_8 *topPadding = NULL;1884U_8 *wrappedBlock;1885BOOLEAN listHeadersAndPaddingOkay = TRUE;1886BOOLEAN currentBlockOkay = TRUE;1887BOOLEAN blockWasSkipped = FALSE;1888J9PortVmemIdentifier getFromTable; /* stack allocated object to index into hashTable */1889J9PortVmemIdentifier *getFromTablePtr = NULL;1890J9PortVmemIdentifier **vmemID = NULL;18911892MUTEX_ENTER(mcMutex);1893/* Disable trace to avoid deadlock between mcMutex and trace global lock. JTC-JAT 93458 */1894if ((NULL != uteInterface) && (NULL != uteInterface->server)) {1895uteInterface->server->DisableTrace(UT_DISABLE_THREAD);1896}18971898if (memoryPointer == NULL) {1899if (0 == (mode & J9_MCMODE_NO_SCAN)) {1900memoryCheck_scan_all_blocks(portLib); /* just in case */1901} else {1902/* J9_MCMODE_NO_SCAN - don't scan! */1903}1904if ((NULL != uteInterface) && (NULL != uteInterface->server)) {1905uteInterface->server->EnableTrace(UT_ENABLE_THREAD);1906}1907MUTEX_EXIT(mcMutex);1908return;1909}19101911if (0 == (mode & J9_MCMODE_NO_SCAN) ) {19121913if ( !(mode & J9_MCMODE_MPROTECT) ) {1914topPaddingSize = J9_MEMCHECK_ADJUSTED_PADDING - sizeof(J9MemoryCheckHeader);1915} else {1916topPaddingSize = J9_MEMCHECK_PAGE_SIZE - sizeof(J9MemoryCheckHeader);1917}19181919wrappedBlock = (U_8 *) memoryPointer;1920topPadding = wrappedBlock - topPaddingSize;1921topHeader = (J9MemoryCheckHeader *) (topPadding - sizeof(J9MemoryCheckHeader));19221923if ( mode & J9_MCMODE_MPROTECT ) {1924/* Find the J9MemoryCheckHeader in the guardPage */1925topHeader = (J9MemoryCheckHeader *)(wrappedBlock - J9_MEMCHECK_PAGE_SIZE);19261927#if defined(J9ZOS390)1928/* if unlocking fails subtract sizeof(J9MemoryCheckHeader) from topHeader and try again*/1929if (memoryCheck_lockGuardPages( memCheckPortLib, topHeader, J9_MEMCHECK_PAGE_SIZE, unlockMode ) != 0) {1930topHeader = (J9MemoryCheckHeader *)((U_8 *)topHeader - sizeof(J9MemoryCheckHeader));1931J9_MEMCHECK_UNLOCK( topHeader );1932} else {1933/* We must set to unlocked here because we did not call the J9_MEMCHECK_UNLOCK() macro */1934topHeader->isLocked = 0;1935}1936topPadding = (U_8 *)topHeader + sizeof(J9MemoryCheckHeader);1937#else1938/* adjust pointer to the header because the header will be stored so that it is entirely in the guard page */1939if ( ( ((UDATA)topHeader & 0XF000) != ((UDATA)((U_8 *)topHeader + sizeof(J9MemoryCheckHeader)) & 0XF000) ) ) {1940if ( (UDATA)((U_8*)topHeader + sizeof(J9MemoryCheckHeader)) % J9_MEMCHECK_PAGE_SIZE != 0 ) {1941topHeader = (J9MemoryCheckHeader *)((U_8 *)topHeader - sizeof(J9MemoryCheckHeader));1942}1943}19441945topPadding = (U_8 *)topHeader + sizeof(J9MemoryCheckHeader);1946J9_MEMCHECK_UNLOCK( topHeader );1947#endif19481949}19501951#ifdef J9VM_MEMCHK_SIM_SUP1952if (mode & J9_MCMODE_SIMULATE) {1953portLib->tty_printf(portLib, "j9mem_free_memory(BLOCK_%d); %s\n", topHeader->allocationNumber, SIM_TAG);1954}1955#endif19561957if (mode & J9_MCMODE_SKIP_TO) {1958/* We must trust the allocation number in the header, to know if the block was1959considered interesting or not when it was allocated. */1960if (topHeader->allocationNumber < skipToAlloc) {1961blockWasSkipped = TRUE;1962goto found_block;1963}1964}19651966currentBlockOkay = memoryCheck_scan_block(portLib, topHeader);19671968if ( (mode & J9_MCMODE_FULL_SCANS) || (!currentBlockOkay) ) {1969/* The current block could be an unknown block, but before we check to see if the block is on the allocate list1970* we have to scan all blocks to make sure the list is not trashed */1971/* scan all blocks expects all blocks to be locked and returns them as all locked */1972J9_MEMCHECK_LOCK( topHeader);1973listHeadersAndPaddingOkay = memoryCheck_scan_all_blocks(portLib);1974J9_MEMCHECK_UNLOCK(topHeader);1975} else { /* quick mode */1976if (mostRecentBlock) {1977/* memoryCheck_scan_block expects the block to be unlocked */1978J9_MEMCHECK_UNLOCK( mostRecentBlock );1979listHeadersAndPaddingOkay = memoryCheck_scan_block(portLib, mostRecentBlock);1980J9_MEMCHECK_LOCK(mostRecentBlock);1981}1982}1983if (!listHeadersAndPaddingOkay) {1984memoryCheck_abort(portLib);1985}19861987if ( (mode & J9_MCMODE_FULL_SCANS) || (!currentBlockOkay && listHeadersAndPaddingOkay) ) {1988/* Walk the allocated list to make sure this block is on it. If not, then we're either freeing random garbage or1989this block has already been freed, or this block was not allocated by the port library */19901991J9_MEMCHECK_LOCK( topHeader );1992listBlockHeader = mostRecentBlock;1993J9_MEMCHECK_UNLOCK( listBlockHeader );19941995while ( listBlockHeader ) {1996if (listBlockHeader == topHeader) {1997J9_MEMCHECK_UNLOCK( topHeader );1998goto found_block;1999}2000if ( listBlockHeader->previousBlock ) {2001J9_MEMCHECK_UNLOCK( listBlockHeader->previousBlock );2002listBlockHeader = listBlockHeader->previousBlock;2003if (listBlockHeader->nextBlock ) {2004J9_MEMCHECK_LOCK( listBlockHeader->nextBlock );2005}2006} else {2007break;2008}2009}20102011J9_MEMCHECK_LOCK( listBlockHeader );2012J9_MEMCHECK_UNLOCK( topHeader );2013if (mode & J9_MCMODE_IGNORE_UNKNOWN_BLOCKS) {2014/* ignore this block and return*/2015portLib->tty_printf(portLib, "WARNING: Tried %s on %p (header at %p), but block is not on the allocated list.\n",2016operationName, wrappedBlock, topHeader);2017memStats.totalUnknownBlocksIgnored++;2018J9_MEMCHECK_LOCK( topHeader );2019if ((NULL != uteInterface) && (NULL != uteInterface->server)) {2020uteInterface->server->EnableTrace(UT_ENABLE_THREAD);2021}2022MUTEX_EXIT(mcMutex);2023return;2024} else {2025portLib->tty_printf(portLib, "ERROR: Tried %s on %p (header at %p), but block is not on the allocated list\n",2026operationName, wrappedBlock, topHeader);2027goto describe_block_and_fail;2028}2029} else {2030/* Quick mode: Don't walk allocated list, just rely on scanning the block to catch freeing errors. */2031J9_MEMCHECK_UNLOCK( topHeader );2032}2033found_block:20342035/* Adjust bottomPadding to force it to end on a double boundary */2036bottomPaddingSize = J9_MEMCHECK_ADJUSTED_PADDING + BYTES_FOR_ALIGNMENT (topHeader->wrappedBlockSize);20372038} else {2039/* we are in J9_MCMODE_NO_SCAN mode */2040wrappedBlock = (U_8 *) memoryPointer;2041topHeader = (J9MemoryCheckHeader *) (wrappedBlock - sizeof(J9MemoryCheckHeader));20422043} /* ( 0 == (mode & J9_MCMODE_NO_SCAN)) */20442045/* So we scanned it before (unless we were in J9_MCMODE_NO_SCAN mode)2046* and it looked okay, unlink the block.. */2047if (mostRecentBlock == topHeader) {2048mostRecentBlock = topHeader->previousBlock;2049}2050if (topHeader->nextBlock) {2051J9_MEMCHECK_UNLOCK( topHeader->nextBlock );2052topHeader->nextBlock->previousBlock = topHeader->previousBlock;2053J9_MEMCHECK_LOCK( topHeader->nextBlock );2054}2055if (topHeader->previousBlock) {2056J9_MEMCHECK_UNLOCK( topHeader->previousBlock );2057topHeader->previousBlock->nextBlock = topHeader->nextBlock;2058J9_MEMCHECK_LOCK( topHeader->previousBlock );2059}2060topHeader->previousBlock = topHeader->nextBlock = NULL;20612062if (!blockWasSkipped) {2063if ( mode & J9_MCMODE_MPROTECT ) {2064if ( J9_ALIGN_BOTTOM ) {2065if (topHeader->self != topHeader->topPage ) {2066memoryCheck_fill_bytes(portLib, topHeader->self, topHeader->topPage - topHeader->self, freedValue, wrappedBlock);2067}2068}2069memoryCheck_fill_bytes(portLib, topPadding, topHeader->totalAllocation - ((UDATA)topPadding - (UDATA)topHeader->self),2070freedValue, wrappedBlock);2071} else if (0 == (mode & J9_MCMODE_NO_SCAN)) {2072memoryCheck_fill_bytes(portLib, topPadding, topPaddingSize + topHeader->wrappedBlockSize + bottomPaddingSize,2073freedValue, wrappedBlock);2074}2075}20762077memStats.totalBlocksFreed += 1;2078memStats.totalBytesFreed += topHeader->wrappedBlockSize;2079memStats.currentBytesAllocated -= topHeader->wrappedBlockSize;2080memStats.currentBlocksAllocated -= 1;20812082/* update the J9MEMAVLTreeNode responsible for this memory block */2083memoryCheck_update_callSites_free(topHeader->node, topHeader->wrappedBlockSize);20842085if (!blockWasSkipped && (mode & J9_MCMODE_NEVER_FREE)) {2086/* Block was painted with recognizable contents above. */2087/* Put block on freed list. */2088topHeader->previousBlock = mostRecentFreedBlock;2089if (mostRecentFreedBlock) {2090J9_MEMCHECK_UNLOCK( mostRecentFreedBlock);2091mostRecentFreedBlock->nextBlock = topHeader;2092J9_MEMCHECK_LOCK( mostRecentFreedBlock);2093}2094mostRecentFreedBlock = topHeader;20952096/* Lock the data area of the block */2097J9_MEMCHECK_UNLOCK( topHeader);2098J9_MEMCHECK_LOCK_BODY( topHeader );2099J9_MEMCHECK_LOCK( topHeader);2100} else {2101topHeader->wrappedBlockSize = J9_MEMCHECK_FREED_SIZE;2102if ( !(mode & J9_MCMODE_MPROTECT) ) {2103#if defined (WIN32) && !defined(BREW)2104if (mode & J9_MCMODE_TOP_DOWN) {2105VirtualFree(topHeader, 0, MEM_RELEASE);2106} else {2107/* The portLib being used here is the memCheckPortLib, not the user portLib */2108deallocator(portLib, topHeader);2109}2110#else2111/* The portLib being used here is the memCheckPortLib, not the user portLib */2112deallocator(portLib, topHeader);2113#endif2114} else { /* J9_MCMODE_MPROTECT */2115/* Create a J9PortVmemIdentifier with the correct address, that can be used to get the right object from the hashTable */2116getFromTable.address = topHeader->self;2117getFromTablePtr = &getFromTable;21182119/* Remove from the vmemIDTable and release the vmem */2120vmemID = (J9PortVmemIdentifier **)hashTableFind( vmemIDTable, &getFromTablePtr );2121if ( vmemID ) {2122J9PortVmemIdentifier *realVmemID = *vmemID;21232124hashTableRemove( vmemIDTable, &getFromTablePtr );2125memCheckPortLib->vmem_free_memory( memCheckPortLib, realVmemID->address, realVmemID->size, realVmemID );2126memCheckPortLib->mem_free_memory( memCheckPortLib, realVmemID );2127}2128}2129}21302131if ((NULL != uteInterface) && (NULL != uteInterface->server)) {2132uteInterface->server->EnableTrace(UT_ENABLE_THREAD);2133}2134MUTEX_EXIT(mcMutex);2135return;21362137describe_block_and_fail:2138memoryCheck_describe_block(portLib, operationName, topHeader);2139memoryCheck_abort(portLib);2140}2141214221432144/* This method formats and prints the contents of the indicated region to the console.2145*2146* @internal The portlibrary passed in should always be the memCheckPortLib2147*/2148static void2149memoryCheck_dump_bytes(OMRPortLibrary *portLib, void *dumpAddress, UDATA dumpSize)2150{2151U_8 *c;2152UDATA digit;2153UDATA column = 0;2154char buffer[128];2155char *bufp, *asciip;2156UDATA address;2157UDATA skipCols = ((UDATA) dumpAddress) & 15;21582159for (c = (U_8 *) dumpAddress; c < ((U_8 *) dumpAddress) + dumpSize; c++) {2160if (column == 0) {2161memset(buffer, ' ', sizeof(buffer));2162address = (UDATA) c & ~15;2163digit = sizeof(UDATA) * 2;2164bufp = buffer + digit;2165asciip = buffer + digit + (16 * 3) + 3;2166*bufp++ = ':';2167while (digit--) {2168buffer[digit] = hexd[address & 15];2169address >>= 4;2170}2171}2172while (skipCols) {2173bufp++;2174*bufp++ = '-';2175bufp++;2176asciip++;2177column++;2178skipCols--;2179}2180*bufp++ = ' ';2181*bufp++ = hexd[((*c) >> 4) & 15];2182*bufp++ = hexd[(*c) & 15];2183*asciip++ = (*c >= ' ' && *c <= '~') ? *c : '.';21842185if (++column >= 16) {2186column = 0;2187*asciip++ = '\n';2188*asciip++ = '\0';2189portLib->tty_printf(portLib, "%s", buffer);2190}2191}2192if (column != 0) {2193*asciip++ = '\n';2194*asciip = '\0';2195/*increment asciip here if it needs to be used further*/2196portLib->tty_printf(portLib, "%s", buffer);2197}2198}219922002201/**2202* @param portLib The "memCheckPortLib" with its original functions is the one that should be used here (I think that should read:2203* ""memCheckPortLib" should be passed into this function "2204*/2205static void2206memoryCheck_print_stats(OMRPortLibrary *portLib)2207{2208portLib->tty_printf(portLib, "Memory checker statistics:\n");2209portLib->tty_printf(portLib, "Total blocks allocated = %u ( = most recent allocationNumber)\n", memStats.totalBlocksAllocated);2210portLib->tty_printf(portLib, "Total blocks freed = %u\n", memStats.totalBlocksFreed);2211portLib->tty_printf(portLib, "Total bytes allocated = %llu\n", memStats.totalBytesAllocated);2212portLib->tty_printf(portLib, "Total bytes freed = %llu\n", memStats.totalBytesFreed);2213portLib->tty_printf(portLib, "Total unknown blocks ignored = %u\n", memStats.totalUnknownBlocksIgnored);2214portLib->tty_printf(portLib, "High water blocks allocated = %u\n", memStats.hiWaterBlocksAllocated);2215portLib->tty_printf(portLib, "High water bytes allocated = %u\n", memStats.hiWaterBytesAllocated);2216portLib->tty_printf(portLib, "Largest block ever allocated = size %u, allocation number %u\n",2217memStats.largestBlockAllocated, memStats.largestBlockAllocNum);2218portLib->tty_printf(portLib, "Failed allocation attempts = %u\n", memStats.failedAllocs);2219}22202221/* This method scans a single block and returns FALSE if it is damaged or TRUE if everything looks okay.2222This method does not check links to/from neighboring blocks. If any damage is detected in the block, then2223memoryCheck_scan_all_blocks will be run to detect and print out as much damage as it can, and then FALSE2224will be returned. Otherwise, this method will return TRUE. */2225/*@precondition: The block must be UNLOCKED2226* @internal The portLibrary used must be the memCheckPortLib2227*/2228static BOOLEAN2229memoryCheck_scan_block(OMRPortLibrary *portLib, J9MemoryCheckHeader *blockHeader)2230{2231BOOLEAN everythingOkay = TRUE;2232char const *operationName = "scan_block";2233UDATA topPaddingSize = 0;2234UDATA bottomPaddingSize;2235U_8 *wrappedBlock;22362237U_8 *topPadding = ((U_8 *) blockHeader) + sizeof(J9MemoryCheckHeader);2238U_8 *bottomPadding;22392240if (mode & J9_MCMODE_MPROTECT) {2241topPaddingSize = blockHeader->wrappedBlock - topPadding;2242wrappedBlock = blockHeader->wrappedBlock;2243} else {2244topPaddingSize = J9_MEMCHECK_ADJUSTED_PADDING - sizeof(J9MemoryCheckHeader);2245wrappedBlock = topPadding + topPaddingSize;22462247}22482249/* Verify top padding */2250if ( (mode & J9_MCMODE_MPROTECT) && !J9_ALIGN_BOTTOM ) {2251/* all the top padding is write protected, no point in scanning */2252} else {2253if (memoryCheck_verify_forward(2254portLib, topPadding, topPaddingSize, J9_MEMCHECK_DATA_PADDING_VALUE, wrappedBlock)) {2255if (memoryCheck_verify_forward(2256portLib, topPadding, topPaddingSize, J9_MEMCHECK_CODE_PADDING_VALUE, wrappedBlock)) {22572258/* Either block was already freed, or top padding is damaged. */2259goto scan_heap_and_fail;2260}2261}2262}22632264if (blockHeader->allocationNumber > memStats.totalBlocksAllocated) {2265/* Allocation number is clearly invalid. */2266goto scan_heap_and_fail;2267}22682269bottomPadding = wrappedBlock + blockHeader->wrappedBlockSize;22702271if ( mode & J9_MCMODE_MPROTECT ) {2272bottomPaddingSize = blockHeader->totalAllocation - J9_MEMCHECK_PAGE_SIZE - (bottomPadding - blockHeader->topPage);2273} else {22742275/* Adjust bottomPadding to force it to end on a double boundary */2276bottomPaddingSize = J9_MEMCHECK_ADJUSTED_PADDING + BYTES_FOR_ALIGNMENT (blockHeader->wrappedBlockSize);2277}22782279/* Verify bottom padding */2280if (memoryCheck_verify_forward2281(portLib, bottomPadding, bottomPaddingSize, J9_MEMCHECK_DATA_PADDING_VALUE, wrappedBlock)) {2282if (memoryCheck_verify_forward2283(portLib, bottomPadding, bottomPaddingSize, J9_MEMCHECK_CODE_PADDING_VALUE, wrappedBlock)) {2284/* Bottom padding looks trashed. */2285goto scan_heap_and_fail;2286}2287}22882289if (memStats.totalBlocksFreed > memStats.totalBlocksAllocated) {2290/* This shouldn't happen. In order to cause it, somebody has to free garbage (or free a block twice) without2291this error being caught by any of the previous checks. If global variables were trashed then we would2292hopefully go down in flames before we got here. */2293goto scan_heap_and_fail;2294}2295return TRUE;22962297scan_heap_and_fail:2298memoryCheck_scan_all_blocks(portLib);2299return FALSE;2300}23012302/**2303* This method prints whatever information as it can gather about a block from the block and its immediate neighbors (if any).2304* If the block looks like an uncorrupted freed block, TRUE is returned. If detectable corruption of the block is discovered, FALSE2305* is returned.2306*2307* The function will Lock and Unlock the body of the wrappedBlocked, but the top and bottom page must be unlocked when the2308* block is passed in2309*2310* @internal The portLibrary passed in should be the memCheckPortLibrary, not the one from the user2311*/23122313static BOOLEAN memoryCheck_describe_freed_block(OMRPortLibrary *portLib, char const *operationName,2314J9MemoryCheckHeader *blockHeader)2315{2316BOOLEAN everythingOkay = TRUE;2317UDATA topPaddingSize = 0;2318UDATA bottomPaddingSize = 0;2319U_32 paddingValue;2320UDATA topPaddingSmashed, bottomPaddingSmashed;23212322U_8 *topPadding = ((U_8 *) blockHeader) + sizeof(J9MemoryCheckHeader);2323U_8 *wrappedBlock = NULL;2324U_8 *bottomPadding = NULL;2325UDATA dumpSize = 0;2326U_8 *dumpPos = topPadding;23272328if ( !(mode & J9_MCMODE_MPROTECT) ) {2329topPaddingSize = J9_MEMCHECK_ADJUSTED_PADDING - sizeof(J9MemoryCheckHeader);2330bottomPaddingSize = J9_MEMCHECK_ADJUSTED_PADDING + BYTES_FOR_ALIGNMENT (blockHeader->wrappedBlockSize);2331wrappedBlock = ((U_8 *) blockHeader) + J9_MEMCHECK_ADJUSTED_PADDING;2332} else {2333topPaddingSize = J9_MEMCHECK_PAGE_SIZE - sizeof(J9MemoryCheckHeader);23342335/* Adjust bottomPadding to force it to end on a double boundary */2336bottomPaddingSize = J9_MEMCHECK_PAGE_SIZE + BYTES_FOR_ALIGNMENT (blockHeader->wrappedBlockSize);2337wrappedBlock = ((U_8 *) blockHeader) + J9_MEMCHECK_PAGE_SIZE;2338}2339bottomPadding = wrappedBlock + blockHeader->wrappedBlockSize;2340dumpSize = topPaddingSize + blockHeader->wrappedBlockSize + bottomPaddingSize;23412342if ( mode & J9_MCMODE_MPROTECT ) {2343if ( J9_ALIGN_BOTTOM ) {2344U_32 offsetArea = BYTES_FOR_ALIGNMENT (blockHeader->wrappedBlockSize);2345wrappedBlock = blockHeader->bottomPage - blockHeader->wrappedBlockSize - offsetArea;2346}2347bottomPadding = wrappedBlock + blockHeader->wrappedBlockSize;2348}23492350portLib->tty_printf(portLib, "%s describing previously freed block at %p (header at %p):\n", operationName, wrappedBlock,2351blockHeader);23522353if (!memoryCheck_verify_forward(portLib, topPadding, 8, J9_MEMCHECK_DATA_FREED_VALUE, wrappedBlock)) {2354paddingValue = J9_MEMCHECK_DATA_FREED_VALUE;2355} else if (!memoryCheck_verify_forward(portLib, topPadding, 8, J9_MEMCHECK_CODE_FREED_VALUE, wrappedBlock)) {2356paddingValue = J9_MEMCHECK_CODE_FREED_VALUE;2357} else {2358/* TODO: check for legal padding with wrong wrappedBlock bits here? */23592360/* Test if alignment is off by 4 */2361if ( (UDATA)topPadding%8 == 4 ) {2362if (!memoryCheck_verify_forward(portLib, topPadding+4, 8, J9_MEMCHECK_DATA_FREED_VALUE, wrappedBlock)) {2363paddingValue = J9_MEMCHECK_DATA_FREED_VALUE;2364} else if (!memoryCheck_verify_forward(portLib, topPadding +4, 8, J9_MEMCHECK_CODE_FREED_VALUE, wrappedBlock)) {2365paddingValue = J9_MEMCHECK_CODE_FREED_VALUE;2366}2367portLib->tty_printf(portLib, "Previously freed block has unrecognized padding %08x %08x (header is probably trashed)!\n",2368((U_32 *) topPadding)[0], ((U_32 *) topPadding)[1]);2369everythingOkay = FALSE;2370goto dump_header_only;2371} else {2372portLib->tty_printf(portLib, "Previously freed block has unrecognized padding %08x %08x (header is probably trashed)!\n",2373((U_32 *) topPadding)[0], ((U_32 *) topPadding)[1]);2374everythingOkay = FALSE;2375goto dump_header_only;2376}2377}23782379if ( mode & J9_MCMODE_MPROTECT ) {2380/* Check if extra top smashed */2381if (blockHeader->self != blockHeader->topPage ) {2382topPaddingSmashed = memoryCheck_verify_forward(portLib, blockHeader->self,2383blockHeader->topPage - blockHeader->self, paddingValue, wrappedBlock);2384if (topPaddingSmashed) {2385portLib->tty_printf(portLib, "Last %d bytes of extra top padding are damaged\n", topPaddingSmashed);2386everythingOkay = FALSE;2387}2388}2389}23902391topPaddingSmashed = memoryCheck_verify_forward(portLib, topPadding, topPaddingSize, paddingValue, wrappedBlock);2392if (topPaddingSmashed) {2393portLib->tty_printf(portLib, "Last %d bytes of top padding have been corrupted\n", topPaddingSmashed);2394everythingOkay = FALSE;2395}23962397J9_MEMCHECK_UNLOCK_BODY( blockHeader );23982399if (memoryCheck_verify_forward(portLib, wrappedBlock, blockHeader->wrappedBlockSize, paddingValue, wrappedBlock))2400{2401portLib->tty_printf(portLib, "Some bytes of wrapped block have been corrupted\n");2402everythingOkay = FALSE;2403}24042405if ( mode & J9_MCMODE_MPROTECT ) {2406const UDATA pageSize = J9_MEMCHECK_PAGE_SIZE;2407UDATA checkSize = pageSize;24082409if (J9_ALIGN_BOTTOM) {24102411/* Size to check on the bottom is the bottomPage and the alignment bytes. */2412checkSize += BYTES_FOR_ALIGNMENT (blockHeader->wrappedBlockSize);2413} else {2414UDATA addMod = blockHeader->wrappedBlockSize % pageSize;2415if ( addMod ) {2416checkSize += pageSize - (blockHeader->wrappedBlockSize % pageSize);2417}2418}2419bottomPaddingSmashed = memoryCheck_verify_backward(portLib, bottomPadding, checkSize , paddingValue, wrappedBlock);2420} else {2421bottomPaddingSmashed = memoryCheck_verify_backward(portLib, bottomPadding, bottomPaddingSize, paddingValue, wrappedBlock);2422}24232424if (bottomPaddingSmashed) {2425portLib->tty_printf(portLib, "First %d bytes of bottom padding have been corrupted\n", bottomPaddingSmashed);2426everythingOkay = FALSE;2427}24282429portLib->tty_printf(portLib, "Wrapped block size was %d, allocation number was %d\n",2430blockHeader->wrappedBlockSize, blockHeader->allocationNumber);24312432if (everythingOkay) {2433J9_MEMCHECK_LOCK_BODY( blockHeader );2434return TRUE;2435}24362437dump_block:2438memoryCheck_dump_bytes(portLib, blockHeader, sizeof(blockHeader));2439memoryCheck_dump_bytes(portLib, dumpPos, dumpSize);24402441J9_MEMCHECK_LOCK_BODY( blockHeader );2442return everythingOkay;24432444dump_header_only:24452446portLib->tty_printf(portLib, "(only top padding + first 64 bytes of wrapped block will be printed here)\n");2447dumpSize = topPaddingSize + 64;2448goto dump_block;24492450}245124522453/**2454* This function scans the region of fillSize bytes at fillAddress, from lower to higher addresses. It determines the number of correct bytes of padding in2455* the scan direction before the first incorrect byte. If an incorrect byte is found, all remaining bytes are considered incorrect, and the number of incorrect2456* bytes (remaining+1) is returned.2457* @precondition: block must be UNLOCK'ed prior to calling this function2458*/2459static UDATA2460memoryCheck_verify_forward(OMRPortLibrary *portLib, U_8 *fillAddress, UDATA fillSize, U_32 fillValue, U_8 *blockAddress)2461{2462U_8 fillWith[8];2463I_32 blockAddressInt = (I_32)(IDATA)blockAddress;2464UDATA smashedBytes = fillSize;2465U_8 *c;2466UDATA index = ((UDATA) fillAddress) & 7;24672468memcpy(fillWith, &fillValue, 2);2469memcpy(fillWith+2, &blockAddressInt, 4);2470memcpy(fillWith+6, ((U_8 *)(&fillValue)) + 2, 2);24712472for (c = fillAddress; c < fillAddress+fillSize; c++) {2473if (*c != fillWith[index]) break;2474index++;2475index &= 7;2476smashedBytes--;2477}2478return smashedBytes;2479}2480248124822483/**2484* This function scans the region of fillSize bytes at fillAddress, from lower to higher addresses. It determines the number of correct bytes of padding in2485* the scan direction before the first incorrect byte. If an incorrect byte is found, all remaining bytes are considered incorrect, and the number of incorrect2486* bytes (remaining+1) is returned.2487* @precondition: block must be UNLOCK'ed prior to calling this function2488*/2489static UDATA2490memoryCheck_verify_backward(OMRPortLibrary *portLib, U_8 *fillAddress, UDATA fillSize, U_32 fillValue, U_8 *blockAddress)2491{2492U_8 fillWith[8];2493I_32 blockAddressInt = (I_32)(IDATA)blockAddress;2494UDATA smashedBytes = fillSize;24952496U_8 *c;2497UDATA index = ((UDATA) fillAddress + fillSize - 1) & 7;24982499memcpy(fillWith, &fillValue, 2);2500memcpy(fillWith+2, &blockAddressInt, 4);2501memcpy(fillWith+6, ((U_8 *)(&fillValue)) + 2, 2);25022503for (c = fillAddress+fillSize-1; c >= fillAddress; c--) {2504if (*c != fillWith[index]) break;2505index--;2506index &= 7;2507smashedBytes--;2508}2509return smashedBytes;2510}251125122513/*2514* Normal shutdown of memory check2515*/2516void memoryCheck_print_report(J9PortLibrary *j9portLib)2517{2518if (NULL == old_port_shutdown_library) {2519/* We aren't installed */2520return;2521}25222523MUTEX_ENTER(mcMutex);2524/* Disable trace to avoid deadlock between mcMutex and trace global lock. JTC-JAT 93458 */2525if (uteInterface && uteInterface->server) {2526uteInterface->server->DisableTrace(UT_DISABLE_THREAD);2527}2528memoryCheck_print_summary(memCheckPortLib, J9_MEMCHECK_SHUTDOWN_NORMAL);2529if ((NULL != uteInterface) && (NULL != uteInterface->server)) {2530uteInterface->server->EnableTrace(UT_ENABLE_THREAD);2531}2532MUTEX_EXIT(mcMutex);2533}25342535/*2536* Search for a memorycheck option on the command line. Elements in argv2537* are searched in reverse order. The first element is always ignored (typically2538* the first argv element is the program name, not a command line option).2539*2540* If one is found, return its index, otherwise return 0.2541*2542* This should happen before anybody allocates memory! Otherwise, shutdown will2543* not work properly.2544*/2545IDATA2546memoryCheck_parseCmdLine(J9PortLibrary *j9portLibrary, UDATA lastLegalArg, char **argv)2547{2548OMRPortLibrary *portLibrary = OMRPORT_FROM_J9PORT(j9portLibrary);2549UDATA i;25502551for (i = lastLegalArg; i >= 1; i--) {2552/* new style -Xcheck:memory options */2553if (0 == strcmp("-Xcheck", argv[i])) {2554memoryCheck_initialize(j9portLibrary, "all", argv);2555return i;2556} else if ( (0 == strcmp("-Xcheck:none", argv[i])) ||2557(0 == strcmp("-Xcheck:memory:none", argv[i])) ||2558(0 == strcmp("-Xcheck:help", argv[i])) ) {2559/* stop looking at -Xcheck options */2560return -1;2561break;2562} else if ( (0 == strcmp("-Xcheck:memory:help", argv[i])) ) {2563/* print out the -Xcheck:memory:help text */2564portLibrary->tty_printf( portLibrary, "\nUsage: Xcheck:memory[:<option>]\n\n");2565portLibrary->tty_printf( portLibrary, "options (default is all):\n");2566portLibrary->tty_printf( portLibrary, " all\n");2567portLibrary->tty_printf( portLibrary, " quick\n");2568portLibrary->tty_printf( portLibrary, " nofree\n");2569portLibrary->tty_printf( portLibrary, " failat\n");2570portLibrary->tty_printf( portLibrary, " skipto\n");2571portLibrary->tty_printf( portLibrary, " callsite\n");2572portLibrary->tty_printf( portLibrary, " zero\n\n");2573return -1;2574break;2575} else if (0 == strcmp("-Xcheck:memory",argv[i])) {2576memoryCheck_initialize(j9portLibrary, "all", argv);2577return i;2578} else if (0 == strncmp("-Xcheck:memory:", argv[i], sizeof("-Xcheck:memory:") - 1 )) {2579memoryCheck_initialize(j9portLibrary, argv[i] + sizeof("-Xcheck:memory:") - 1, argv );2580return i;2581}2582}25832584return 0;2585}2586258725882589/* The port library only shuts down components that absolutely need to be.2590* EG., very few or none of the port library components are shut down.2591* Therefore, shutdown memorycheck, restore user's original portlib shutdown2592* functions and call the original exit_shutdown_and_exit.2593*2594*/2595static void2596memoryCheck_exit_shutdown_and_exit(OMRPortLibrary *portLib, I_32 exitCode)2597{2598if (!old_shutdown_and_exit) {2599/* We aren't installed */2600return;2601}260226032604memoryCheck_shutdown_internal( memCheckPortLib, J9_MEMCHECK_SHUTDOWN_EXIT );26052606memoryCheck_restore_old_shutdown_functions( portLib );26072608portLib->exit_shutdown_and_exit( portLib, exitCode );2609}26102611/* remove all unfreedBlocks that is was not allocated by VM2612* The list of ignored callsites are defined in ignoredCallsites2613*2614* This function assumes mcMutex is acquired2615*2616*2617* returns the number of unfreed blocks that was removed from the link list2618*/26192620static UDATA2621memoryCheck_filter_nonVM_unFreed_Blcoks(OMRPortLibrary *portLib)2622{2623UDATA result = 0;2624J9MemoryCheckHeader *blockHeader, *previous, *next;2625UDATA unfreedBlocks = memStats.totalBlocksAllocated - memStats.totalBlocksFreed;2626UDATA i;2627char ignoredCallsites[MAX_CALLSITE_COUNT][MAX_CALLSITE_LENGTH], *strPtr;2628UDATA ignoredCallsitesSize = 0;2629#ifdef J9ZOS3902630char *ebcdic_callsite;2631#endif26322633memset(ignoredCallsites, '\0', MAX_CALLSITE_COUNT * MAX_CALLSITE_LENGTH* sizeof(char));26342635strPtr = strtok( ignoreCallSiteStr, CALLSITE_DELIMITER );26362637while (NULL != strPtr) {2638if (ignoredCallsitesSize >= MAX_CALLSITE_COUNT) {2639portLib->tty_printf(portLib, "internal buffer full, ignoredCallSite %s discarded\n", strPtr);2640strPtr = strtok( NULL, CALLSITE_DELIMITER );2641continue;2642}2643if (strlen(strPtr) >= MAX_CALLSITE_LENGTH) {2644portLib->tty_printf(portLib, "ignoredCallSite %s length exceeds internal buffer size. Callsite discarded\n", strPtr);2645strPtr = strtok( NULL, CALLSITE_DELIMITER );2646continue;2647}2648strcpy(ignoredCallsites[ignoredCallsitesSize++], strPtr);2649strPtr = strtok( NULL, CALLSITE_DELIMITER );2650}26512652for (blockHeader = mostRecentBlock; (NULL != blockHeader) && (0 != unfreedBlocks); unfreedBlocks--) {2653if (blockHeader->node) {2654previous = blockHeader->previousBlock;2655next = blockHeader->nextBlock;2656#ifdef J9ZOS3902657ebcdic_callsite = e2a_string(blockHeader->node->callSite);2658#endif2659for (i = 0; i < ignoredCallsitesSize; i++) {2660if ((0 == strncmp(blockHeader->node->callSite, ignoredCallsites[i], strlen(ignoredCallsites[i])))2661#ifdef J9ZOS3902662|| (0 == strncmp(ebcdic_callsite, ignoredCallsites[i], strlen(ignoredCallsites[i])))26632664#endif2665) {2666#if 02667portLib->tty_printf(portLib, "WARNING: unfreed block #%d allocated by %s is being ignored from memcheck\n",2668blockHeader->allocationNumber,blockHeader->node->callSite);2669#endif2670if (previous) {2671previous->nextBlock = next;2672}26732674if (next) {2675next->previousBlock = previous;2676}26772678if (blockHeader == mostRecentBlock) {2679mostRecentBlock = previous;2680}2681globalDeallocator(portLib, blockHeader);2682result++;2683#ifdef J9ZOS3902684free(ebcdic_callsite);2685#endif2686break;2687}2688}2689#ifdef J9ZOS3902690free(ebcdic_callsite);2691#endif2692}2693blockHeader = previous;2694}26952696portLib->tty_printf(portLib, "WARNING: %d blocks were ignored per ignoredCallsite parameter\n", result);2697return result;26982699}27002701/* Does a final scan and prints the final summary/stats. Please note that this2702* function expects the caller to have already locked mcMutex.2703*2704* @internal The portLib should be the memCheckPortLib2705*/2706static void2707memoryCheck_print_summary(OMRPortLibrary *portLib, I_32 shutdownMode)2708{2709J9MemoryCheckHeader *blockHeader;2710UDATA ignored = 0;27112712if (0 == (mode & J9_MCMODE_NO_SCAN) ) {2713memoryCheck_scan_all_blocks(portLib); /* One last check for trashing. */2714}27152716/* don't report memory leaks when terminating with exit_shutdown_and_exit */2717if ( (shutdownMode == J9_MEMCHECK_SHUTDOWN_NORMAL) && (mostRecentBlock)) {2718UDATA unfreedBlocks = memStats.totalBlocksAllocated - memStats.totalBlocksFreed;2719portLib->tty_printf(portLib, "WARNING: %d unfreed blocks remaining at shutdown!\n", unfreedBlocks);2720ignored = memoryCheck_filter_nonVM_unFreed_Blcoks(portLib);2721if (unfreedBlocks > J9_MEMCHECK_MAX_DUMP_LEAKED_BLOCKS) {2722unfreedBlocks = J9_MEMCHECK_MAX_DUMP_LEAKED_BLOCKS;2723portLib->tty_printf(portLib, "WARNING: only %d most recent leaked blocks will be described\n",2724unfreedBlocks);2725}2726for (blockHeader = mostRecentBlock; blockHeader && unfreedBlocks; unfreedBlocks--) {2727if (0 == (mode & J9_MCMODE_NO_SCAN ) ) {2728memoryCheck_describe_block(portLib, "port_shutdown_library", blockHeader);2729} else {2730UDATA size = blockHeader->wrappedBlockSize < 32 ? blockHeader->wrappedBlockSize : 32;2731portLib->tty_printf(portLib, "Wrapped block size is %d, allocation number is %d\n",2732blockHeader->wrappedBlockSize, blockHeader->allocationNumber);27332734if (blockHeader->node) {2735portLib->tty_printf(portLib, "Block was allocated by %s\n", blockHeader->node->callSite);2736}2737portLib->tty_printf(portLib, "First %d bytes:\n", size);2738memoryCheck_dump_bytes(portLib, blockHeader->wrappedBlock, size);2739}2740blockHeader = blockHeader->previousBlock;2741}2742}27432744/* If an AVL Tree was created free the memory for the tree */2745if(avl_tree) {2746if (mode & J9_MCMODE_PRINT_CALLSITES) {2747/* Report callsite informtation */2748memoryCheck_dump_callSites(portLib, avl_tree);2749} else if (mode & J9_MCMODE_PRINT_CALLSITES_SMALL) {2750/* Report callsite informtation */2751memoryCheck_dump_callSites_small(portLib, avl_tree);2752}2753/* Free the avl tree and nodes*/2754memoryCheck_free_AVLTree(portLib, avl_tree);2755}27562757memoryCheck_print_stats(portLib);27582759if (memStats.totalBlocksAllocated != (memStats.totalBlocksFreed + ignored)) {2760portLib->tty_printf(portLib, "%d allocated blocks totaling %llu bytes were not freed before shutdown!\n",2761memStats.totalBlocksAllocated - memStats.totalBlocksFreed,2762memStats.totalBytesAllocated - memStats.totalBytesFreed);2763if( shutdownMode == J9_MEMCHECK_SHUTDOWN_EXIT ) {2764portLib->tty_printf( portLib, "The VM terminated due to exit() so unfreed blocks are expected.\n" );2765}2766} else {2767portLib->tty_printf(portLib, "All allocated blocks were freed.\n");2768}27692770return;2771}27722773/* Does a final scan (if it was not called by memoryCheck_exit_shutdown_and_exit2774* and prints the stats.2775* Finally, it shuts down the passed in portlibrary (which should be the memCheckPortLib)2776*2777* @internal The portLib should be the memCheckPortLib */2778static void2779memoryCheck_shutdown_internal(OMRPortLibrary *portLib, I_32 shutdownMode)2780{2781omrthread_t thread = NULL;27822783/* Make sure we are attached, so that we can enter/exit/destroy the mutex */2784if (f_omrthread_attach_ex(&thread, J9THREAD_ATTR_DEFAULT)) {2785return;2786}27872788MUTEX_ENTER(mcMutex);2789/* Disable trace to avoid deadlock between mcMutex and trace global lock. JTC-JAT 93458 */2790if ((NULL != uteInterface) && (NULL != uteInterface->server)) {2791uteInterface->server->DisableTrace(UT_DISABLE_THREAD);2792}27932794if ( mode & J9_MCMODE_MPROTECT ) {2795/* Unlock ALL the blocks */2796memoryCheck_lockAllBlocks( memCheckPortLib, mostRecentBlock, unlockMode, 0 );2797memoryCheck_lockAllBlocks( memCheckPortLib, mostRecentFreedBlock, unlockMode, 1 );2798}27992800memoryCheck_print_summary(portLib, shutdownMode);28012802/* If the VM is terminating due to exit, the trace engine and other bits of2803* the VM may still be operating - so we can't tear their storage out from2804* under them.2805*/2806if ( shutdownMode != J9_MEMCHECK_SHUTDOWN_EXIT && mode & J9_MCMODE_MPROTECT ) {2807/* Free all the blocks remaining in the hashTable and then free the Table itself*/2808hashTableForEachDo( vmemIDTable, memoryCheck_hashDoFn, memCheckPortLib );2809hashTableFree( vmemIDTable );2810}28112812memCheckPortLib->port_shutdown_library(memCheckPortLib);28132814/* Port library has shut down, don't attempt to re-enable trace at this point */2815MUTEX_EXIT(mcMutex);2816MUTEX_DESTROY(mcMutex);28172818f_omrthread_detach(thread);2819}282028212822/* Restore the original shutdown functions */2823static void2824memoryCheck_restore_old_shutdown_functions(OMRPortLibrary *portLib)2825{28262827portLib->port_shutdown_library = old_port_shutdown_library;2828portLib->exit_shutdown_and_exit = old_shutdown_and_exit;2829}283028312832static I_322833memoryCheck_control(OMRPortLibrary *portLib, const char* key, UDATA value)2834{2835if (0 == strcmp(key, "MEMCHECK")) {2836UDATA everythingOkay;2837MUTEX_ENTER(mcMutex);2838/* Disable trace to avoid deadlock between mcMutex and trace global lock. JTC-JAT 93458 */2839if ((NULL != uteInterface) && (NULL != uteInterface->server)) {2840uteInterface->server->DisableTrace(UT_DISABLE_THREAD);2841}28422843everythingOkay = memoryCheck_scan_all_blocks(memCheckPortLib);28442845if (!everythingOkay) {2846memoryCheck_abort(memCheckPortLib);2847}28482849if ((NULL != uteInterface) && (NULL != uteInterface->server)) {2850uteInterface->server->EnableTrace(UT_ENABLE_THREAD);2851}2852MUTEX_EXIT(mcMutex);28532854return 1;2855} else {2856if (0 == strcmp(key, J9PORT_CTLDATA_TRACE_START) && value) {2857MUTEX_ENTER(mcMutex);2858uteInterface = (UtInterface*) value;2859MUTEX_EXIT(mcMutex);2860} else if (0 == strcmp(key, J9PORT_CTLDATA_TRACE_STOP)) {2861MUTEX_ENTER(mcMutex);2862uteInterface = NULL;2863MUTEX_EXIT(mcMutex);2864}28652866return old_port_control(portLib, key, value);2867}2868}2869287028712872/* This function will print the stats for the given node and then2873* recurse over its children.2874*2875* @param portLibrary2876* @param node J9MEMAVLTreeNode to print callSite information for2877* @internal The portLibrary passed in should be the memCheckPortLib->2878*2879*/2880static void2881memoryCheck_dump_callSite(OMRPortLibrary *portLibrary, J9AVLTreeNode *node)2882{2883J9AVLTreeNode *nodePtr = AVL_GETNODE(node);28842885if (nodePtr == NULL) {2886return;2887}28882889memoryCheck_print_stats_callSite(portLibrary, ((J9MEMAVLTreeNode *)nodePtr));2890memoryCheck_set_AVLTree_prevStats(((J9MEMAVLTreeNode *)nodePtr));28912892memoryCheck_dump_callSite(portLibrary, J9AVLTREENODE_LEFTCHILD(nodePtr));2893memoryCheck_dump_callSite(portLibrary, J9AVLTREENODE_RIGHTCHILD(nodePtr));2894}289528962897/* This function will print the header for the callSite information and2898* call memoryCheck_dump_callSite on the root node of the tree2899*2900* @param portLibrary2901* @param tree J9AVLTree storing the callSite information2902*2903*/2904static void2905memoryCheck_dump_callSites(OMRPortLibrary *portLibrary, J9AVLTree *tree)2906{2907if( !(tree) || !(tree->rootNode)) {2908return;2909}29102911portLibrary->tty_printf(portLibrary, " total alloc | total freed | delta alloc | delta freed | high water | largest\n");2912portLibrary->tty_printf(portLibrary, " blocks| bytes | blocks| bytes | blocks| bytes | blocks| bytes | blocks| bytes | bytes | num | callsite\n");2913portLibrary->tty_printf(portLibrary,2914"-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+------------\n");29152916memoryCheck_dump_callSite(portLibrary, tree->rootNode);2917portLibrary->tty_printf(portLibrary,2918"-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+------------\n");2919}292029212922/*2923* This function will start the recursive freeing of the nodes and the free2924* the memory for the AVL Tree2925*2926* @parm portLib OMRPortLibrary used to access the memory functions2927* @param tree J9AVLTree storing the callSite information2928* @internal The portLibrary passed in should be the memCheckPortLib.2929*/2930static void2931memoryCheck_free_AVLTree(OMRPortLibrary *portLib, J9AVLTree *tree)2932{2933if(!tree) {2934return;2935}29362937memoryCheck_free_AVLTreeNode(portLib, tree->rootNode);29382939portLib->mem_free_memory( portLib, tree );2940}294129422943/*2944* This function will recurse over the left and right child of the given2945* node and then free the memory for itself2946*2947* @parm portLib OMRPortLibrary used to access the memory functions2948* @param tree J9AVLTreeNode the node to free2949* @internal The portLibrary passed in should be the memCheckPortLib.2950*/2951static void2952memoryCheck_free_AVLTreeNode(OMRPortLibrary *portLib, J9AVLTreeNode *node)2953{2954J9AVLTreeNode *nodePtr = AVL_GETNODE(node);29552956if (nodePtr == NULL) {2957return;2958}29592960memoryCheck_free_AVLTreeNode(portLib, J9AVLTREENODE_LEFTCHILD(nodePtr));2961memoryCheck_free_AVLTreeNode(portLib, J9AVLTREENODE_RIGHTCHILD(nodePtr));29622963portLib->mem_free_memory( portLib, nodePtr );2964}296529662967/*2968* This function is the insertion comparator for the J9AVLTree that stores J9MEMAVLTreeNodes2969* It will compare the char * of both nodes.2970*2971* @param tree J9ALVTree storing the callSite information2972* @param insertNode The node which will be inserted into the tree2973* @param walk The current search position in the tree2974*2975* @return IDATA the difference between the two strings alphabetically2976*2977*/2978static IDATA2979memoryCheck_insertion_Compare(J9AVLTree *tree, J9AVLTreeNode *insertNode, J9AVLTreeNode *walk)2980{2981return strcmp(((J9MEMAVLTreeNode *)insertNode)->callSite, ((J9MEMAVLTreeNode *)walk)->callSite);2982}298329842985/*2986* This function prints the deatils of the callSite2987*2988* @param portLib OMRPortLibrary used to handle printing2989* @param node The node to print the stats for2990*2991*/2992static void2993memoryCheck_print_stats_callSite(OMRPortLibrary *portLib, J9MEMAVLTreeNode *node)2994{2995portLib->tty_printf(portLib, "%7u %7llu %7u %7llu %7u %7lld %7u %7lld %7u %7u %7u %7u %s\n",2996node->stats->totalBlocksAllocated,2997node->stats->totalBytesAllocated,2998node->stats->totalBlocksFreed,2999node->stats->totalBytesFreed,3000node->stats->totalBlocksAllocated - node->prevStats->totalBlocksAllocated,3001node->stats->totalBytesAllocated - node->prevStats->totalBytesAllocated,3002node->stats->totalBlocksFreed - node->prevStats->totalBlocksFreed,3003node->stats->totalBytesFreed - node->prevStats->totalBytesFreed,3004node->stats->hiWaterBlocksAllocated,3005node->stats->hiWaterBytesAllocated,3006node->stats->largestBlockAllocated,3007node->stats->largestBlockAllocNum,3008node->callSite);3009}3010301130123013/*3014* This function is the search comparator for the J9AVLTree that stores J9MEMAVLTreeNodes3015* It will compare the char * with the node callSite.3016*3017* @param tree J9ALVTree storing the callSite information3018* @param search The char * to search for3019* @param walk The current search position in the tree3020*3021* @return IDATA the difference between the two strings alphabetically3022*3023*/3024static IDATA3025memoryCheck_search_Compare(J9AVLTree *tree, UDATA search, J9AVLTreeNode *walk)3026{3027return strcmp((char *)search, ((J9MEMAVLTreeNode *)walk)->callSite);3028}302930303031/*3032* This function initializes both node->stats and node->prevStats. All3033* values for prevStats should be initialized to 0 since at this point there have3034* been no previous stats.3035*3036* @param node J9MEMAVLTreeNode to initialize stats for3037* @param byteAmount used to initialize bytesAllocated for node->stats only3038*3039*/3040static void3041memoryCheck_initialize_AVLTree_stats(J9MEMAVLTreeNode *node, UDATA byteAmount)3042{3043node->stats->totalBlocksAllocated = 1;3044node->stats->totalBlocksFreed = 0;3045node->stats->totalBytesAllocated = byteAmount;3046node->stats->totalBytesFreed = 0;3047node->stats->totalUnknownBlocksIgnored = 0;3048node->stats->largestBlockAllocated = byteAmount;3049node->stats->largestBlockAllocNum = 1;3050node->stats->currentBlocksAllocated = 1;3051node->stats->hiWaterBlocksAllocated = 1;3052node->stats->currentBytesAllocated = byteAmount;3053node->stats->hiWaterBytesAllocated = byteAmount;3054node->stats->failedAllocs = 0;30553056node->prevStats->totalBlocksAllocated = 0;3057node->prevStats->totalBlocksFreed = 0;3058node->prevStats->totalBytesAllocated = 0;3059node->prevStats->totalBytesFreed = 0;3060node->prevStats->totalUnknownBlocksIgnored = 0;3061node->prevStats->largestBlockAllocated = 0;3062node->prevStats->largestBlockAllocNum = 0;3063node->prevStats->currentBlocksAllocated = 0;3064node->prevStats->hiWaterBlocksAllocated = 0;3065node->prevStats->currentBytesAllocated = 0;3066node->prevStats->hiWaterBytesAllocated = 0;3067node->prevStats->failedAllocs = 0;3068}3069307030713072/*3073* This function inserts or updates the node pertaining to the newly allocated memory block.3074*3075* @param portLib OMRPortLibrary used for memory functions3076* @param header Memory block header which will point to the node3077* @param callSite Char * to the location that called the allocation function3078* @param byteAmount Number of bytes allocated for the memory block3079*3080*/3081static void3082memoryCheck_update_callSites_allocate(OMRPortLibrary *portLib, J9MemoryCheckHeader *header, const char *callSite, UDATA byteAmount)3083{3084J9MEMAVLTreeNode *node;30853086node = (J9MEMAVLTreeNode *)avl_search(avl_tree, (UDATA)callSite);3087if(node) {3088node->stats->totalBlocksAllocated++;3089node->stats->totalBytesAllocated += byteAmount;3090node->stats->currentBlocksAllocated += 1;3091if (node->stats->currentBlocksAllocated > node->stats->hiWaterBlocksAllocated) {3092node->stats->hiWaterBlocksAllocated = node->stats->currentBlocksAllocated;3093}3094node->stats->currentBytesAllocated += byteAmount;3095if (node->stats->currentBytesAllocated > node->stats->hiWaterBytesAllocated) {3096node->stats->hiWaterBytesAllocated = node->stats->currentBytesAllocated;3097}3098if (byteAmount > node->stats->largestBlockAllocated) {3099node->stats->largestBlockAllocated = byteAmount;3100node->stats->largestBlockAllocNum = node->stats->totalBlocksAllocated;3101}3102header->node = node;3103} else {3104/* allocate enough room for the node, char *, and two J9MemoryCheckStats */3105node = old_mem_allocate_memory(portLib, sizeof(J9MEMAVLTreeNode) + strlen(callSite) + 1 +3106sizeof(J9MemoryCheckStats) + sizeof(J9MemoryCheckStats), J9_GET_CALLSITE(), OMRMEM_CATEGORY_VM);3107if(node) {3108node->parentAVLTreeNode.leftChild = 0;3109node->parentAVLTreeNode.rightChild = 0;3110node->stats = (J9MemoryCheckStats *)(node + 1);3111node->prevStats = (J9MemoryCheckStats *)((UDATA)(node->stats) + sizeof(J9MemoryCheckStats));31123113memcpy((char *)((UDATA)(node->prevStats) + sizeof(J9MemoryCheckStats)), callSite, strlen(callSite) + 1);3114node->callSite = (char *)((UDATA)(node->prevStats) + sizeof(J9MemoryCheckStats));31153116memoryCheck_initialize_AVLTree_stats(node, byteAmount);31173118header->node = node;31193120avl_insert(avl_tree, (J9AVLTreeNode *)node);3121}3122}3123}312431253126/*3127* This function sets the node->prevStats to the current value of3128* node->stats3129*3130* @param node J9MEMAVLTreeNode to initialize stats for3131*3132*/3133static void3134memoryCheck_set_AVLTree_prevStats(J9MEMAVLTreeNode *node)3135{3136node->prevStats->totalBlocksAllocated = node->stats->totalBlocksAllocated;3137node->prevStats->totalBlocksFreed = node->stats->totalBlocksFreed;3138node->prevStats->totalBytesAllocated = node->stats->totalBytesAllocated;3139node->prevStats->totalBytesFreed = node->stats->totalBytesFreed;3140node->prevStats->largestBlockAllocated = node->stats->largestBlockAllocated;3141node->prevStats->largestBlockAllocNum = node->stats->largestBlockAllocNum;3142node->prevStats->currentBlocksAllocated = node->stats->currentBlocksAllocated;3143node->prevStats->hiWaterBlocksAllocated = node->stats->hiWaterBlocksAllocated;3144node->prevStats->currentBytesAllocated = node->stats->currentBytesAllocated;3145node->prevStats->hiWaterBytesAllocated = node->stats->hiWaterBytesAllocated;3146}314731483149/*3150* This function prints the deatils of the callSite. This only3151* prints the total/delta alloc/free information.3152*3153* @param portLib OMRPortLibrary used to handle printing3154* @param node The node to print the stats for3155*3156*/3157static void3158memoryCheck_print_stats_callSite_small(OMRPortLibrary *portLib, J9MEMAVLTreeNode *node)3159{3160portLib->tty_printf(portLib, "%7u %7llu %7u %7llu %7u %7llu %7u %7llu %s\n",3161node->stats->totalBlocksAllocated,3162node->stats->totalBytesAllocated,3163node->stats->totalBlocksFreed,3164node->stats->totalBytesFreed,3165node->stats->totalBlocksAllocated - node->prevStats->totalBlocksAllocated,3166node->stats->totalBytesAllocated - node->prevStats->totalBytesAllocated,3167node->stats->totalBlocksFreed - node->prevStats->totalBlocksFreed,3168node->stats->totalBytesFreed - node->prevStats->totalBytesFreed,3169node->callSite);3170}317131723173/* This function will print the header for the callSite information and3174* call memoryCheck_dump_callSite on the root node of the tree3175*3176* @param portLibrary3177* @param tree J9AVLTree storing the callSite information3178* @internal The portLibrary passed in should be the memCheckPortLib->3179*3180*/3181static void3182memoryCheck_dump_callSites_small(OMRPortLibrary *portLibrary, J9AVLTree *tree)3183{3184if(!(tree) || !(tree->rootNode)) {3185return;3186}31873188portLibrary->tty_printf(portLibrary, " total alloc | total freed | delta alloc | delta freed\n");3189portLibrary->tty_printf(portLibrary, " blocks| bytes | blocks| bytes | blocks| bytes | blocks| bytes | callsite\n");3190portLibrary->tty_printf(portLibrary,3191"-------+-------+-------+-------+-------+-------+-------+-------+-----------\n");31923193memoryCheck_dump_callSite_small(portLibrary, tree->rootNode);3194portLibrary->tty_printf(portLibrary,3195"-------+-------+-------+-------+-------+-------+-------+-------+-----------\n");3196}3197319831993200/* This function will print the stats (only the total/delta for alloc/free) for the given node and then3201* recurse over its children.3202*3203* @param portLibrary3204* @param node J9MEMAVLTreeNode to print callSite information for3205* @internal The portLibrary passed in should be the memCheckPortLib.3206*/3207static void3208memoryCheck_dump_callSite_small(OMRPortLibrary *portLibrary, J9AVLTreeNode *node)3209{3210J9AVLTreeNode *nodePtr = AVL_GETNODE(node);32113212if (nodePtr == NULL) {3213return;3214}32153216memoryCheck_print_stats_callSite_small(portLibrary, ((J9MEMAVLTreeNode *)nodePtr));3217memoryCheck_set_AVLTree_prevStats(((J9MEMAVLTreeNode *)nodePtr));32183219memoryCheck_dump_callSite_small(portLibrary, J9AVLTREENODE_LEFTCHILD(nodePtr));3220memoryCheck_dump_callSite_small(portLibrary, J9AVLTREENODE_RIGHTCHILD(nodePtr));3221}322232233224/*3225* This function will update the node to account for the memory block3226* which was just freed.3227*3228* @param node J9MEMAVLTreeNode that will be updated3229* @param byteAmount Number of bytes allocated for the memory block3230*3231*/3232static void3233memoryCheck_update_callSites_free(J9MEMAVLTreeNode *node, UDATA byteAmount)3234{3235if(node) {3236node->stats->totalBlocksFreed++;3237node->stats->totalBytesFreed+=byteAmount;3238node->stats->currentBlocksAllocated--;3239node->stats->currentBytesAllocated-=byteAmount;3240}3241}324232433244/**3245* @internal The portLibrary passed on will be the memCheckPortLibrary with its original functions3246*/3247static void *3248memoryCheck_reallocate_memory(struct OMRPortLibrary *portLibrary, void *memoryPointer, UDATA byteAmount, const char * callsite, U_32 category)3249{3250#ifdef DEBUG3251memCheckPortLib->tty_printf( memCheckPortLib, "reallocate_memory(%p, %d)\n", memoryPointer, byteAmount);3252#endif3253return memoryCheck_wrapper_reallocate_memory(memCheckPortLib, memoryPointer, byteAmount, "reallocate_memory", globalAllocator, globalDeallocator,3254J9_MEMCHECK_DATA_PADDING_VALUE, J9_MEMCHECK_DATA_FILL_VALUE, J9_MEMCHECK_DATA_FREED_VALUE, NULL == callsite ? "unknown" : callsite, category);3255}3256325732583259/* @internal The portLib passed in should be the memCheckPortLib */3260static void *3261memoryCheck_wrapper_reallocate_memory(OMRPortLibrary *portLib, void *memoryPointer, UDATA byteAmount, char const *operationName,3262J9_MEM_ALLOCATE_FUNC allocator, J9_MEM_FREE_FUNC deallocator, U_32 paddingValue, U_32 fillValue,3263U_32 freedValue, const char *callSite, U_32 category)3264{32653266/* It doesn't really make sense to use realloc with memoryCheck since it would try to re-use the3267* pointer which would hide most realloc bugs so we will just emulate its behaviour so we get3268* the rigorous memoryCheck verifications.3269*/3270void *newBlock = NULL;32713272if (memoryPointer == NULL) {3273newBlock = memoryCheck_wrapper_allocate_memory(portLib, byteAmount, operationName, allocator, paddingValue, fillValue, freedValue, "unknown", category);3274} else if (byteAmount == 0) {3275memoryCheck_wrapper_free_memory(portLib, memoryPointer, operationName, deallocator, paddingValue, fillValue, freedValue);3276newBlock = NULL;3277} else {3278newBlock = memoryCheck_wrapper_allocate_memory(portLib, byteAmount, operationName, allocator, paddingValue, fillValue, freedValue, "unknown", category);32793280if (newBlock != NULL) {3281U_8 *wrappedBlock = NULL;3282J9MemoryCheckHeader *topHeader = NULL;3283UDATA oldByteAmount = 0;3284UDATA bytesToCopy = 0;32853286/* Sniff out the size of the actual wrapped block */3287wrappedBlock = (U_8 *) memoryPointer;3288if (0 == (mode & J9_MCMODE_NO_SCAN) ) {3289if ( mode & J9_MCMODE_MPROTECT ) {3290topHeader = (J9MemoryCheckHeader *)(wrappedBlock - J9_MEMCHECK_PAGE_SIZE);3291if ( ( ((UDATA)topHeader & 0XF000) != ((UDATA)((U_8 *)topHeader + sizeof(J9MemoryCheckHeader)) & 0XF000) ) ) {3292if ( (UDATA)((U_8*)topHeader + sizeof(J9MemoryCheckHeader)) % J9_MEMCHECK_PAGE_SIZE != 0 ) {3293topHeader = (J9MemoryCheckHeader *)((U_8 *)topHeader - sizeof(J9MemoryCheckHeader));3294}3295}3296} else {3297UDATA topPaddingSize = J9_MEMCHECK_ADJUSTED_PADDING - sizeof(J9MemoryCheckHeader);3298topHeader = (J9MemoryCheckHeader *) (wrappedBlock - topPaddingSize - sizeof(J9MemoryCheckHeader));3299}3300} else {3301topHeader = (J9MemoryCheckHeader *) (wrappedBlock - sizeof(J9MemoryCheckHeader));3302}33033304J9_MEMCHECK_UNLOCK( topHeader);3305oldByteAmount = topHeader->wrappedBlockSize;3306J9_MEMCHECK_LOCK( topHeader);33073308/* Copy the existing data to the new block */3309bytesToCopy = (oldByteAmount <= byteAmount) ? oldByteAmount : byteAmount;3310memcpy(newBlock, memoryPointer, bytesToCopy);33113312/* Free the old block */3313memoryCheck_wrapper_free_memory(portLib, memoryPointer, operationName, deallocator, paddingValue, fillValue, freedValue);3314}3315}3316/* Return the new pointer */3317return newBlock;3318}33193320332133223323/*3324* This function is used after memcheck has shut down to prevent crashes3325*/3326static void3327memoryCheck_null_mem_free_memory(OMRPortLibrary *portLib, void *memoryPointer)3328{3329return;3330}33313332#ifdef MEMDEBUG3333static void3334subAllocator_audit(OMRPortLibrary *portLib, UDATA mask)3335{3336UDATA top = STARTIDX;3337UDATA bottom;3338UDATA i;33393340OMRPORT_ACCESS_FROM_OMRPORT(portLib);33413342freemem = 0;3343usedmem = 0;3344total = 0;3345freeCnt = 0;3346usedCnt = 0;33473348bucket[0] = bucket[1] = bucket[2] = bucket[3] = bucket[4] = bucket[5] = 0;33493350while (top < heapsize-2) {33513352bottom = top + abs(j9heap[top]);3353pTop = &j9heap[top];3354pBot = &j9heap[bottom];33553356if (j9heap[top] != j9heap[bottom]) {3357total = freemem + usedmem;3358break;3359}33603361if (j9heap[top] > 0) {3362freemem += (j9heap[top] + 1);3363if (mask & FREE) {3364j9tty_printf(NULL, "FR: %7d @ %6d ", j9heap[top], top);3365}3366freeCnt++;3367} else if (j9heap[top] < 0) {3368usedmem += (-j9heap[top] + 1);3369if (mask & USED) {3370j9tty_printf(NULL, "US: %7d @ %6d ", -j9heap[top], top);3371}3372/* arbitrary values to gauge memory usage; change as needed */3373if (-j9heap[top] < 10)3374bucket[1]++;3375else if (-j9heap[top] < 32)3376bucket[2]++;3377else if (-j9heap[top] < 256)3378bucket[3]++;3379else if (-j9heap[top] < 2048)3380bucket[4]++;3381else3382bucket[5]++;33833384usedCnt++;3385}33863387top = abs(j9heap[top]) + top + 1;3388}3389total = freemem + usedmem;33903391for (i=0; i<MAX_SMALL_BLOCK; i++) {3392if (smblkstatus[i] != 0)3393bucket[0]++;3394}33953396if (mask & AUDIT)3397j9tty_printf(NULL, "AUDIT: F %d U %d FC %d UC %d", freemem, usedmem, freeCnt, usedCnt);33983399if (mask & BUCKET) {3400j9tty_printf(NULL, "BUCKET: [%d], %d, %d, %d, %d %d",3401bucket[0], bucket[1], bucket[2], bucket[3], bucket[4], bucket[5]);3402}3403}3404#endif34053406/**3407* Initialize the heap, providng a pointer to the heap space and the size of the heap3408* These two values can be hard coded at this point, if desired.3409* @param[in] void* memptr -- pointer to block of memory used as a heap, or arbitrary size3410* @param[in] UDATA size -- size of heap block, in number of words3411*/3412static void3413subAllocator_init_heap(void* memptr, UDATA size)3414{3415UDATA *j9heap_local = NULL;34163417j9heap_local = (UDATA *) memptr;3418memset(j9heap_local, 0, sizeof(UDATA) * size);3419heapsize = size;34203421/* "Top", at index=2 is 0 */3422j9heap_local[STARTIDX-1] = 0;34233424/* Indicate the size of the initial block, at index = 3*/3425j9heap_local[STARTIDX] = heapsize-(STARTIDX+2);34263427/* Indicate the size of the initial starting block at the second last slot of j9heap */3428j9heap_local[heapsize-2] = heapsize-(STARTIDX+2);34293430/* Place a '0' at the end of the heap */3431j9heap_local[heapsize-1] = 0;3432start = STARTIDX;34333434meminuse = 0;34353436memset(smallBlock, 0, sizeof(smallBlock));3437memset(smblkstatus, 0, sizeof(smblkstatus));3438smblkindex = 0;3439}344034413442static UDATA3443subAllocator_findFirstFreeBlock(UDATA index, IDATA *j9heap)3444{3445IDATA val = j9heap[index];3446UDATA maxIndex = heapsize-2;34473448/* Logically: abs(j9heap[index]), but using IDATA width types */3449if (val < 0) {3450val *= -1;3451}34523453index += (val + 1);34543455while (index < maxIndex) {34563457if (j9heap[index] > 0) {3458return index;3459}3460index = -j9heap[index] + index + 1;3461}3462return STARTIDX;3463}34643465/*3466Release the memory block (if valid). If part of the smallBlock array, return it3467If in heap, coalesce with adjacent free blocks as necessary3468*/34693470static void3471subAllocator_free_memory(OMRPortLibrary *portLib, void *ptr)3472{3473UDATA top;3474UDATA newtop;3475UDATA bottom;3476UDATA newval;34773478OMRPORT_ACCESS_FROM_OMRPORT(portLib);34793480if ((ptr < (void*) &j9heap[0]) || (ptr > (void*) &j9heap[heapsize-2])) {3481if ((ptr < (void*) &smallBlock[0]) || (ptr > (void*) &smallBlock[MAX_SMALL_BLOCK - 1])) {3482return;3483}3484else3485{3486top = (((UDATA) ptr - (UDATA) &smallBlock[0]) / sizeof(smallBlock[0]));3487if (smblkstatus[top]) {3488smblkstatus[top] = 0;3489smblkindex = top;3490/*MSG_FATAL("SMBLK- %d @ %x", top, ptr, 0);*/3491return;3492}3493else3494/*error*/;3495}3496}3497top = (((UDATA) ptr - (UDATA) &j9heap[0]) / sizeof(UDATA)) - 1;34983499/* if (-(j9heap[top]) > 256)3500MSG_FATAL("j9 free %7d bytes @ %8X", (-j9heap[top]-1)*4, &j9heap[top+1], 0);*/3501#ifdef MEMDEBUG3502pTop = &j9heap[top];3503# endif3504if (j9heap[top] < 0) {3505bottom = top + -(j9heap[top]);35063507#ifdef MEMDEBUG3508pBot = &j9heap[bottom];3509#endif3510if (j9heap[top] == j9heap[bottom]) {3511j9heap[top] = -j9heap[top];3512j9heap[bottom] = -j9heap[bottom];35133514/* start is a signed quantity */3515if ((IDATA)top < start)3516start = top;35173518meminuse -= ((j9heap[top]+1)*sizeof(UDATA));35193520if (j9heap[top-1] > 0) {3521/* merge blocks */3522newtop = top - j9heap[top-1] - 1;3523j9heap[newtop] = j9heap[top] + j9heap[top-1] + 1;3524j9heap[bottom] = j9heap[newtop];35253526/* start is a signed quantity */3527if ((IDATA)newtop < start)3528start = newtop;3529}35303531if (j9heap[bottom+1] > 0) {3532newval = j9heap[bottom] + j9heap[bottom+1] + 1;3533j9heap[bottom + j9heap[bottom + 1] + 1 ] = newval;3534newtop = bottom - j9heap[bottom];3535j9heap[ newtop ] = newval;35363537/* start is a signed quantity */3538if ((IDATA)newtop < start)3539start = newtop;3540}3541#ifdef MEMDEBUG3542subAllocator_audit(portLib, j9heap, AUDIT|BUCKET);3543#endif3544return;3545}3546}3547start = STARTIDX;3548}35493550/*3551allocate a block of size bytes from the heap, using a minimum of FRAGTHRESHOLD words3552*/35533554static void*3555subAllocator_allocate_memory(OMRPortLibrary *portLib, UDATA byteAmount, const char *callSite, U_32 category)3556{3557IDATA size = (IDATA) byteAmount;3558UDATA idx;3559const UDATA maxIndex = heapsize-2;3560UDATA newstart;35613562/* round to even heap slot (32/64 bit amount) */3563size = ((size + (sizeof(UDATA)-1)) & ~(sizeof(UDATA)-1)) / sizeof(UDATA);35643565if (size <= FRAGTHRESHOLD)3566{3567UDATA idx = smblkindex;3568while (idx < MAX_SMALL_BLOCK) {3569if (0 == smblkstatus[idx])3570{3571memset(&smallBlock[idx], 0, sizeof(smallBlock[idx]));3572smblkstatus [idx] = 1;3573/*MSG_FATAL("SMBLK+ %d @ %x", idx, &smallBlock[idx], 0);*/3574return &smallBlock[idx];3575}3576else3577{3578if (++idx >= MAX_SMALL_BLOCK)3579idx = 0;3580else if (idx == smblkindex)3581break;3582}3583}3584}35853586#ifdef MEMDEBUG3587subAllocator_audit(portLib, j9heap, AUDIT|BUCKET);3588#endif35893590idx = start;35913592while (idx < maxIndex)3593{3594if (j9heap [idx] >= size + 1) {35953596if (j9heap[idx] > size + FRAGTHRESHOLD ) {3597j9heap[idx + j9heap[idx]] -= (size + 2);3598newstart = idx + size + 2;3599j9heap[newstart] = j9heap[idx + j9heap[idx]];3600if (start == idx)3601start = newstart;3602}3603else {3604/* current block too small to split, use whole thing */3605size = j9heap[idx] - 1;3606if (start == idx)3607start =subAllocator_findFirstFreeBlock(idx, (IDATA *)j9heap);3608}36093610#ifdef MEMDEBUG3611pTop = &j9heap[idx];3612pBot = &j9heap[idx + size + 1];3613#endif36143615j9heap[idx + size + 1] = -(size + 1);3616j9heap[idx] = -(size + 1);3617memset(&j9heap[idx+1], 0, size*sizeof(UDATA));36183619meminuse += ((size+1)*sizeof(UDATA));36203621#ifdef MEMDEBUG3622if (meminuse > (((heapsize/sizeof(UDATA))*3)*sizeof(UDATA))) {3623subAllocator_audit(portLib, j9heap, AUDIT|BUCKET|FREE);3624}3625#endif3626/* if (size > 256)3627MSG_FATAL("j9 malloc %7d bytes @ %8X", size*4, &j9heap[idx+1], 0);*/3628return &j9heap[idx+1];3629}3630else {3631IDATA absoluteIdx = j9heap[idx];3632if (absoluteIdx < 0) {3633absoluteIdx *= -1;3634}3635idx = absoluteIdx + idx + 1;3636}3637}3638#ifdef MEMDEBUG3639subAllocator_audit(portLib, j9heap, AUDIT|FREE|BUCKET|USED);3640#endif3641/* MSG_FATAL("j9 malloc fail, sz %d", size*4, 0, 0); */3642return 0;3643}364436453646