This appendix includes a guide to troubleshooting common memory problems. Please read this information carefully — it addresses many common problems that other SmartHeap users have called our technical support staff about.
BC++ version 3.1 for DOS/Windows and BC++ version 1.0 for OS/2 do not support the placement syntax for allocation of arrays of objects that have destructors. Because the SmartHeap Debug version of operator new is implemented with the placement syntax, you cannot compile modules that use operator new to allocate arrays with the SmartHeap DEFINE_NEW_MACRO or DEBUG_NEW options. See §3.2.4, “Debug SmartHeap and C++ operator new” for a work-around to this problem. This problem is due to a bug in BC++ and is not known to exist with any other C++ compiler (it is fixed in BC++ 4.0 and higher for DOS/Windows and BC++ 1.5 and higher for OS/2).
If you use the statically linked Borland C++ Object Windows Library (OWL), version 1.x, you must link with the SmartHeap SH[D|L]OWLB[D].LIB library. Please read “Using SmartHeap with 16-bit Windows Borland OWL,” for instructions.
If you use the DLL version of the 16-bit Borland C++ Object Windows Library (OWL), BIDS, or Class Library, you must rebuild those Borland DLL(s). If the OWL DLL is not recompiled with SmartHeap, the symptom will be a MEM_BAD_POINTER error when deleting an object that was allocated by OWL. It is important to build both a debug and a non-debug version of each Borland DLL for use with Debug and Runtime SmartHeap, respectively. For more information, see the Getting Started and Platform Guide.
If you link the Borland C++ version of the SmartHeap libraries with the OptLink linker, you may get an OptLink error. Correct this problem by running OptLink’s “FIXLIB” utility.
If you use the “QuickWin” library in Microsoft Visual C++ 1.0 or earlier, you cannot have SmartHeap override the _fmalloc/_ffree functions (it does so by default). The reason is that the QuickWin package reads data from blocks after they have been freed with _ffree. This happens to work (apparently) with the Microsoft C/C++ definition of _ffree, but it does not work in the Debug version of SmartHeap, because Debug SmartHeap fills all blocks with a unique fill value immediately after freeing them (it does this specifically to detect this type of bug).
To work around this problem, you must define the int global variable SmartHeap_far_malloc at file scope in your application. You must then remove the shmalfar object module from the SmartHeap library utility. If you do not, the symptom will be a GP fault during termination of your QuickWin-compiled application.
If you use the SmartHeap libraries with Microsoft C/C++ 7.0, the linker may report duplicate definitions for malloc or _fmalloc. For more information, see the Getting Started and Platform Guide.
If you use the statically linked, Debug version of the Microsoft
Foundation Class Library (MFC), you must link with the appropriate
SmartHeap/MFC integration library:
halmfcm.lib for 16-bit MFC 2.x EXEs;
hadmfcm.lib for 16-bit MFC 2.x extension DLLs;
hamfc32m.lib for 32-bit MFC 3.x EXEs/DLLs.
hamfc4m.lib for 32-bit MFC 4.x EXEs/DLLs.
For more information, see the Getting Started and Platform Guide.
If you use the Debug version of MFC, you should be aware that SmartHeap DEBUG_NEW and DEFINE_NEW_MACRO are, like MFC DEBUG_NEW, incompatible with the MFC macros IMPLEMENT_SERIAL, IMPLEMENT_DYNAMIC, and IMPLEMENT_DYNCREATE. This is because these MFC macros contains calls to operator new that use the placement syntax. To work around this problem, place:
on the line just before the call to IMPLEMENT_XXX, and then:
#define new DEBUG_NEW
on the line following this MFC macro. See §3.2.4, “Debug SmartHeap and C++ operator new,” for an example and explanation of this technique.
If you are using Watcom version 10.0 in a C++ application with SmartHeap 2.1, you will need to upgrade to SmartHeap 2.2 or higher, which contains support for this compiler.
SmartHeap supports statically linking only for the compiler versions listed in the Getting Started and Platform Guide. If you want to statically link SmartHeap with another compiler (or compiler version), you must purchase the SmartHeap source code.
If there is a SmartHeap DLL for your platform, then you can use any C/C++ compiler with the SmartHeap DLL. The other directory of the SmartHeap distribution media contains import libraries for the SmartHeap DLL. You can link directly with this import library to call any SmartHeap function except malloc, _fmalloc/farmalloc, and operator new. The statically linked SmartHeap libraries all override these compiler functions by default. If you want to override these compiler functions with an unsupported compiler, compile all of the files in the source directory, and add the resulting object files to both SmartHeap import libraries. The source files in this directory contain simple interfaces between malloc, etc. and the API defined in the SmartHeap DLL.
The Inmark zApp Class Library defines operator new such that it returns zero-initialized memory. Since SmartHeap, like ANSI, does not zero-initialize memory in operator new by default, some integration is required for SmartHeap to support zApp.
To produce a version of operator new that works with zApp, you must compile the file shnew.cpp in the SmartHeap source directory, supplying the define ZAPP on the compiler command line. For example, the following command line compiles Runtime and Debug versions, respectively, of the SmartHeap C++ module with Microsoft Visual C++:
/c /AL /Gs2 /GA /DZAPP=1 shnew.cpp
cl /c /AL /Gs2 /GA /DZAPP=1 /DMEM_DEBUG=1 shnew.cpp
Then either link the resulting object file into your application, or replace the module shnew in the SmartHeap library with the zApp-compatible replacement.
Programmer errors that cause memory-related bugs are a common problem. The symptoms of such bugs include memory protection violations (also known as access violations, bus errors, segmentation violations, GP faults, or UAEs), hangs, or any other weird behavior. The following questions will help you pin-point the cause of such problems:
If the problem occurs only in the version of SmartHeap you recompiled, look for compiler compatibility or product build problems. Did you use a supported compiler and compiler version?
If not, relink with Debug SmartHeap and try to reproduce the problem. Debug SmartHeap will automatically detect many kinds of memory bugs.
If not, try to reproduce the problem with MEM_SAFETY_DEBUG in effect — if the bug is an overwrite, it may not be caught with a safety level lower than MEM_SAFETY_DEBUG.
MemPoolCheck finds memory overwrites that are not detected automatically. This is a very common type of programmer error that can usually be detected only by validating the pool with MemPoolCheck. If you are allocating memory with malloc or new, you should supply MemDefaultPool as the pool parameter to MemPoolCheck. It is important to test the return value of MemPoolCheck; some types of corruption don’t produce an error-handling callback, but all errors cause MemPoolCheck to return FALSE.
If so, the bug could be an overwrite into memory that is in use elsewhere or a write to a wild pointer. Try using dbgMemSetGuardSize to increase the size of the overwrite guards around blocks. Also try dbgMemDeferFreeing so all writes to free blocks are detected.
One possible cause is dereferencing a pointer read from a previously freed memory block. Data freed in Runtime SmartHeap may still be present in memory, but Debug SmartHeap fills all bytes of freed blocks with the pattern established by dbgMemSetFreeFill (default value 0xDD).
A similar error can occur if you dereference a pointer read from uninitialized memory, or from outside the bounds of an allocation. Debug SmartHeap fills these types of memory with the value established by dbgMemSetInUseFill (default 0xEB) and dbgMemSetGuardFill (default 0xFC), respectively.
If MemPoolCheck detects a memory overwrite, a message box will show you the address of the overwritten block. If you compile all of your modules with MEM_DEBUG, it also reports the file, line, and pass count at the last point when the overwrite was known not to have occurred yet. You can move the call to MemPoolCheck successively closer to the reported file/line/pass count to isolate the cause of the overwrite.
The most likely causes are (1) that your error-handling callback function does not have the correct calling convention (far pascal for 16-bit x86 platforms) or (2) for 16-bit x86 platforms, that the function is not declared with _loadds (DOS) or _export (Windows).
SmartHeap provides a macro, MEM_CALLBACK that defines the calling convention and sets DS up correctly for each platform. You should define your error handler as follows:
MEM_BOOL MEM_CALLBACK MyErrorFn(MEM_ERROR_FN);
If so, try to reproduce the problem while running Dr. Watson or a similar post-mortem debugger. If you call MicroQuill technical support about such a problem, please fax or e-mail us a copy of the Dr. Watson report.
Please add the following settings to your WIN.INI file prior to running Dr. Watson when reproducing your problem:
showinfo=disassembly errorlog paramlog locals
If possible, also produce a .SYM file for your EXEs and DLLs so Dr. Watson will have symbolic information. .SYM files for the SmartHeap DLLs are included in all SmartHeap releases for exactly this purpose. Produce a .SYM file with Microsoft C/C++ by running MAPSYM. For Borland C++ 3.1, use BUILDSYM.
If the memory is allocated from a DLL, see section B.4 below.
If so, this could mean:
The pointer was allocated by a memory manager other than SmartHeap, such as from another DLL or EXE, or from the compiler runtime library. Examine your map file to see that the SmartHeap version of malloc, _fmalloc/farmalloc, or operator new is being linked in.
The pointer is “wild” (uninitialized), is allocated on the stack (local variable), or is otherwise invalid.
The pointer was previously freed. If SmartHeap has freed the page from which the pointer was originally allocated, SmartHeap won’t be able to detect that it’s a double free. However, SmartHeap will report the invalid pointer. Use dbgMemDeferFreeing to catch this type of bug.
The pointer was incremented or decremented since the time of allocation.
For 16-bit x86, the pointer was cast to a near pointer after allocation, in which case the segment portion of the pointer has been lost.
The memory pool from which the pointer was allocated has been freed, or SmartHeap has been unregistered from the task.
The task from which the pointer was allocated has terminated (see section B.4).
If so, this could mean:
The memory pool has been freed, or SmartHeap has been unregistered.
The task from which the pool was allocated has terminated (see section B.4, below).
An invalid parameter is passed as the memory pool parameter.
This section is specific to the 16-bit Windows version of SmartHeap.
Allocating memory from DLLs is a common source of memory problems because of a misunderstanding of DLL vs. task memory ownership and problems with DLL initialization or termination.
Memory allocated from a DLL is normally owned by the task current at the time of the allocation; this memory is implicitly freed when that task terminates. Memory allocated from a shared memory pool, on the other hand, remains accessible until it’s explicitly freed or until the Windows module (that is, every instance of an EXE, or a DLL) from which it was allocated terminates.
Here are some guidelines for allocating memory from DLLs:
To allocate memory from a DLL that is specific to a particular client task, you should create a separate memory pool for each such task, and, in each DLL API, find and allocate from the appropriate pool.
To allocate memory that is for the DLL’s own internal data structures or that is to be accessible from multiple client tasks, you should create one or more shared memory pool(s) and allocate all such memory from such pool(s).
If a multi-instance application is hanging or crashing after one instance terminates, it may be that there are references to memory from more than one task, in which case you should use a shared memory pool to allocate the memory.
If a crash or hang occurs when a DLL terminates, the most likely cause is code in the WEP (or in an onexit routine or a static class destructor, which would implicitly be called from the WEP). You shouldn’t have inter-DLL calls from a DLL WEP, even in Windows 3.1, because Windows may have freed the callee DLL before invoking WEP. Termination code (such as freeing a shared memory pool) should generally be in an unregistration function that is called explicitly from client applications of the DLL.
For more information about using SmartHeap with DLLs, see the “Getting started” guide for your compiler.
If SmartHeap does not seem to be overriding global operator new, make sure that the SmartHeap library appears before the C++ compiler runtime library. If the linker still resolves to the compiler’s operator new, you can force it to link in SmartHeap new by placing a reference to SmartHeap_new inside one of your functions — see the example in §2.3.4, “C++ memory API.”
If you get a linker error saying that malloc is defined more than once, make sure that the SmartHeap library is before the C runtime library on the linker command line. For some linkers, you might also have to specify “no extended dictionaries” (for example, /NOE with Microsoft and IBM C).
For 16-bit x86 versions of SmartHeap, if you are using a memory model other than large, you cannot have SmartHeap override malloc, etc.. See §2.3.3, “If you do not want SmartHeap to override malloc,” for instructions.
If your Mac application is hanging or crashing when you think SmartHeap should be reporting an error, the problem may be that you haven’t initialized the Dialog Manager. The SmartHeap default error handler uses Alerts to report errors. If the Dialog Manager and related ToolBox facilities are not initialized, a crash will occur deep inside the Alert routine that SmartHeap calls.
For an example of the specific initialization calls SmartHeap requires, see the file shtestd.c in the samples::debug folder of the SmartHeap release.