📄 heapinfo.txt
字号:
This symbol enables an alternate set of functions that
test pointers for validity and report invalid pointers
on stderr. The defined value of the symbol is taken to
be the stream on which error reports should appear. For
example, -D_HEAPCHECK=stdout sends error reports to the
standard output stream.
STDMSC and NDEBUG both override _HEAPCHECK. If either is
defined _HEAPCHECK is ignored.
For further details see section IV.C.
c. _HEAPDEBUG -- debugging heap problems
This symbol enables an alternate set of functions that
diagnose heap problems. The functions that alter the
heap are replace by equivalents that check the heap
consistency with _fheapchk(), perform the standard heap
operation, and then record the transaction for diagnostic
reports triggered by errors.
The defined value of the symbol is taken to be the stream
on which error reports should appear. For example,
-D_HEAPDEBUG=stdout sends error reports to the standard
output stream.
Enabling _HEAPDEBUG also enables a stringent form of
_HEAPCHECK. Instead of a test on the value of the pointer
offset, _HEAPDEBUG checks that a matching entry exists in
the heap (this cheap as _HEAPDEBUG must traverse the heap
to check for errors). Thus _HEAPDEBUG guarantees that no
pointer that happens to have a zero offset can masquerade
as a heap pointer.
STDMSC and NDEBUG both override _HEAPDEBUG. If either is
defined _HEAPCHECK is ignored.
For further details see section IV.D.
d. _HEAPTRACE -- tracing heap activity
This symbol enables an alternate set of functions that
trace heap activity. Functions that alter the heap are
replaced by equivalents that write a transaction record
to the trace log file after every heap access. The
transaction reports give the sequence number, the name
of the function, the value of the parameters, the source
file and line number and the returned result.
The defined value of the symbol is taken to be the stream
on which transaction reports should appear. For example,
-D_HEAPTRACE=stdout sends reports to the standard output file.
Enabling _HEAPTRACE implies that _HEAPDEBUG is enabled as well.
STDMSC and NDEBUG both override _HEAPTRACE. If either is
defined _HEAPCHECK is ignored.
For further details see section IV.E
C. Direct access to the heap data structures.
The heap manager is a layer between two interfaces. On the
bottom is the DOS memory manager that believes in arenas and
paragraph alignment, but handles blocks larger than 64 Kb
easily. On the top is the C run-time library definition that
believes in size_t, word alignment, and other limitations of
32-bit segmented addressing on a 16-bit machine with an 8-bit
oriented architecture.
The two interfaces are represented explicitly. On one hand
there are arenas that match the DOS requirements (typdef _ARENA).
The memory contained in an arena is managed with a list of chunks
where each chunk conforms to the requirements of the C interface
(typedef _CHUNK).
In order to support reallocation and expansion of free'd memory
blocks there is a separate list of chunks that have been free'd
that are potential targets for reuse. Prior to an allocation
request the entries in this list are added to the free list, and
adjacent free blocks are joined. (Thus the name join list from
to-be-joined). This list is usualy empty and
when not empty it is small so the overhead is tiny.
To "walk" the heap you traverse the list of arenas based on
_heaphead, and within each arena traverse the list of chunks based
on the arena header. The free and join lists may also be traveled
based on their headers _freelist and _joinlist, but this is not
actually necessary as all chunks, free or used, appear on their
respective arena-control lists.
D. The heap manager will handle non-DOS memory. If you provide a
block of memory with an initialized arena and chunk header the
heap manager will happily manage the it as part of the memory
pool. For more information see the get-new-arena portion of
_xalloc() in heaputil.c. Be sure to set your _ARENA.isdos to
FALSE to prevent _fheappack() from trying to give it back to DOS.
This feature is especially useful on systems with non-DOS memory
such as EMS or XMS "upper" memory in the BIOS area from C0000 to
FFFFF, or video graphics memory in the A0000-AFFFF region.
The next version of the heap manager will provide functions to
add and delete non-DOS blocks of memory from the heap.
IV. Error handling and debugging
The replacement heap manager provides a heap that is harder to damage,
and easier to debug than the original heap manager. The following
sections describes the salient features.
A. Comprehensize error checking
The heap checking performed by the MSC _fheapchk() is minimal. It
appears to be a simple limit test of heap pointers against the
boundaries of the entire heap area.
The replacement heap manager uses a detailed consistency check.
Every pointer is validated against the individual arena limits.
Further, the nodes in the linked lists are checked for reciprocity
(MSC cannot do this because it does not have a doubly-linked list).
The reciprocity test is an extermely powerful method of heap
validation. It is even possible to repair a damaged heap by
appropriate use of the linked lists. This facility is not
included in this library because its usefulness is questionable,
and it grossly exceeds the fuctionality of the specifications
(the MSC heap manager). The consistency check also verifies
that the safety margins set by _heappad are intact.
The actual validation routines are found in fheapwlk.c. _Fheapchk()
and _fheapset() use _fheapwalk() to traverse the heap.
B. User controlled allocation padding for debugging.
One classic problem with heap management is writing past the end
of a buffer obtained from the heap. This typically produces
extremely hostile effects. As the routines that handle
heap-generated buffers are usually embedded in the lower layers
of software, checking them can be tedious.
One method of testing this mode of failure is to add extra space
at the ends of the allocated areas. If the failures stop, there
is evidence of a heap problem. Of course, changing sizes moves
things around which can turn errors on and off randomly so the
evidence is flimsy. The problem with this debugging technique
is that changing the buffer lengths manually is a time-consuming,
error-prone operation with marginal utility.
The replacement heap manager provides a control for automatically
extending all heap allocation requests. Thus an initialization
parameter is enough to provide evidence for or against heap
problems. Typically the padding is set to one or two times the
size of the structures being manipulated.
Because the padding affects the distance between the data area
and the heap control structures it cannot be changed after the
heap has been initialized. The heap manager takes a copy of
_heappad when the heap is initialized and uses the copy thereafter.
Thus _heappad must be set as early as possible, typically in main().
Note that command-line wild cards processed with setargv.obj use
the heap before main is started. Normal programs without wild card
expansion can use:
extern size_t _heappad; /* declare the global variable */
int main()
{
_heappad = 100; /* set the value */
}
But programs with wild card handling enabled (those linked with the
special version of setargv.obj) must use:
size_t _heappad = 100; /* define the global and its value */
int main()
{
}
This sets the value at compile/link time rather than at run time.
The safety margins are automatically filled when a heap entry
is allocated. The fill pattern is initially alternating bits,
0x55, but it is reset by every call to _fheapset(). _Fheapchk()
compares the two safety margins and reports _HEAPBADPTR if they
differ.
C. Heap pointers are distinctive and are checked for validity.
All heap pointers are aligned on paragraph boundaries, so that
a non-zero offset indicates an invalid pointer. Since all of
the functions receiving pointers as arguments check for invalid
pointers it is hard to corrupt the heap with a non-heap pointer.
When an invalid pointer is detected the functions ignore the
call and, where possible, return a failure indicator:
_expand() returns NULL
_ffree() does nothing because _ffree() is void
_fheapwalk() returns _HEAPBADPTR when _heapinfo._pentry is bad
free() does nothing because free() is void
_frelocate() returns NULL
_hexpand() returns NULL
hrealloc() returns NULL
_hmsize() returns zero
_msize() returns zero
realloc() returns NULL
You may wish to compile your application with -D_HEAPCHECK=<stream>
to get more active error handling. The symbol _HEAPCHECK changes the
standard heap access functions to alternates that test pointers
for validity and report errors on the indicated stream file rather
than silently ignoring them.
The error messages appear on stderr and locate the offending
function call by name, source file and line number. You can
install your own error handler by modifying or replacing
_ptr_chk() in ptrchk.c.
As a matter of fact, the pointer checks are quite simple and
therefore very fast. You may decide to leave the error checks
enabled in working software just to catch the infrequent errors
or the ones that will "never happen".
Note that the paragraph alignment principle imples that a
protected-mode equivalent of the replacement heap manager is
feasible, with each heap entry having it's own selector value
and (potentially) a individual set of permissions.
D. Debugging with _HEAPDEBUG
The symbol _HEAPDEBUG enables an alternate set of heap functions
that aid in debugging. The debug functions keep track of every
heap entry they process, recording the function name, desired size,
source file name and line number responsible for the entry, plus a
transaction (sequence) number. The debug functions also check the
heap status prior to every heap access and print a diagnostic report
if _fheapchk() reports a problem. The value of _HEAPDEBUG is taken
to be the stream on which diagnostic error reports should appear.
Enabling _HEAPDEBUG also enables a stringent form of pointer
validity checking. Instead of a test on the value of the pointer
offset, _HEAPDEBUG checks that a matching entry exists in the heap
(this comes free because _HEAPDEBUG must traverse the heap anyway
to check for errors). Thus _HEAPDEBUG guarantees that no pointer
that happens to have a zero offset can masquerade as a heap pointer.
When a debug function detects an error it writes a diagnostic
report to the indicated stream file and exits. The report includes
the following information:
- the function detecting the error (function name, source
file, and source line number)
- the heap information on the offending entry (actual size,
address, and used/free status)
- a dump of the heap as described in section III.B.3.a
including all of the debugging fields
Debugging with an appropriate (non-zero) value in _heappad
increases the chances of detecting a wild pointer or a pointer
running past an end of a heap entry.
The debugging capability of the replacement heap manager features
the following benefits:
- Mapping multiple heap entries to each debug record
permits debugging of very large heaps by minimizing
the memory overhead of debug mode.
- Variable safety margin (_heappad) allows you to match
the size of application structures.
- No source code changes are necessary to activate the
debugging capability, only recompilation with the
-D_HEAPDEBUG=<stream> definition.
The file dbugdemo.c illustrates the basic techniques.
E. Debugging with _HEAPTRACE
The symbol _HEAPTRACE enables an alternate set of heap functions
that aid in debugging. The debug functions report every heap
transaction, including the sequence number, the name of the
function, the value of the parameters, the source file and line
number, and the returned result. The transaction reports are
sent to the stream defined by -D_HEAPTRACE=<stream>.
Note that the heap transaction sequence number is only a rough
guide to the chronology of events. First, heap operations in
modules compiled without _HEAPTRACE or _HEAPDEBUG do not affect
the sequence number so there may be hidden transactions. Second,
when the transaction is saved in a debug record the sequence
number is stored as well. Because all heap entries related to
an individual function call (and of identical size) share a
debug record they will all appear to have the sequence number
of the most recent member.
Enabling _HEAPTRACE also enables _HEAPDEBUG and _HEAPCHECK.
F. Monitoring heap data
The _heapwatch() function establishes the read-only status of
heap entries. Entries marked read-only are tested by the
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -