Category : C Source Code
Archive   : HEAPDBG.ZIP
Filename : HEAP.DBG

 
Output of file : HEAP.DBG contained in archive : HEAPDBG.ZIP
*********************************README.1ST**********************************

Here is a list of the files following this introduction:

MAIN.C : A sample program to demonstrate the heap debugging stuff
HEAP.H : The header file that turns on the debugging stuff
HEAP.C : The routines that comprise the heap debugger
SCREEN.C : The routines to dump debugging information to the screen.
This is the code that is machine/operating system dependent.
_HEAP.H : A private header file for the debugging routines.

*********************************MAIN.C*****************************************
/*
* Author: Mark Nelson
*
* Date: October 28, 1989
*
* Description: This is the main() module used to test the heap
* debugger routines. It does a few different
* things that should be detected by the debugger
* routines.
*/

#include
#include
#include "heap.h"

void main( void )
{
char *test_guy_1;
char *test_guy_2;
char *test_guy_3;
char *maverick_pointer;

test_guy_1 = malloc( 1000 );
test_guy_2 = malloc( 2000 );
/*
* This call to strdup creates a maverick pointer that I should detect
* next time I get into my debugger routines.
*/
maverick_pointer = strdup( "This is a test" );
test_guy_3 = malloc( 3000 );
/*
* Here I write one byte past my allocated area. This should create a
* picket error.
*/
test_guy_2[ 2000 ] = 'X';
free( test_guy_1 );
free( maverick_pointer );
free( test_guy_2 );
free( test_guy_3 );
}

*********************************HEAP.H*****************************************

#define malloc(size) my_malloc(__FILE__,__LINE__,(size))
#define calloc(nitems,size) my_calloc(__FILE__,__LINE__,(nitems),(size))
#define free(block) my_free(__FILE__,__LINE__,(block))

void *my_malloc(char *file_name,unsigned int line_number,size_t size);
void *my_calloc(char *file_name,unsigned int line_number,size_t nitems,size_t size);
void my_free(char *file_name,unsigned int line_number, void *block);

/* Prototypes from SCREEN.C */

void draw_boxes_and_titles(char *labels[],int row_count);
void screenputc(int row,int col,char c);
void screenputf(int row,int col,char *format,...);
void screenputs(int row,int col,char *string);
void screenclearline(int row);
void initialize_screen(char *labels[],int widths[]);
void setup_screen(char *labels[],int widths[],int rows);
void restore_screen(void);

/* Prototypes from HEAP.C */

void screen_message(int immediate_return, int tag, char *format,...);
void hide_screen(void);
void my_free(char *file_name,unsigned int line_number, void *block);
void *my_malloc(char *file_name,unsigned int line_number,size_t size);
void initialize_tags(void);
void display_tag_table(int last_tag);
void add_tag_to_table(char far *pointer,char maverick,size_t size,char *file_name,unsigned int line_number);
void delete_tag_from_table(int tag);
void verify_heap(void);
void heap_walk(void);
*********************************HEAP.C*****************************************

/*
* Author: Mark Nelson
*
* Date: October 28, 1989
*
* Description: This module contains the replacement routines used
* to debug heap problems. The routines are used in
* conjunction with the HEAP.H header file.
*/
#include
#include
#include
#include
#include
#include "_heap.h"

/*
* This structure defines all the fields that I use in the tag
* table database.
*/
#define TAG_MAX 100

struct tags {
char far * returned_address;
char far * real_address;
size_t size;
char *file_name;
unsigned int line_number;
char maverick;
char tagged;
} tag_table[TAG_MAX];

int next_free_tag = -1;


/*
* These are some odds and ends I use all around.
*/
char leading_picket[] = "0123456789ABCDE";
char trailing_picket[] = "FEDCBA987654321";
#define PICKET_SIZE sizeof( leading_picket )


/*
* These are the labels and widths for each of the columns in the output
* screen.
*/
char *labels[]={"Tag #","File Name","Line #","Picket","Address","Size","Picket",""};
int widths[]={5,12,6,15,9,6,15};
extern int columns[];
int screen_up=0;
int message_line;


/*
* This is the my_malloc routine that replaces malloc(). Before it does
* anything else, it performs the heap checkout stuff. This will pop up
* a message if anything funny is detected. It then gets the pointer
* for the caller, and adds it to the tag table. The screen is then
* cleared up, and the pointer is returned.
*/

void *my_malloc(char *file_name,unsigned int line_number,size_t size)
{
void *malloc_pointer;
size_t real_size;

verify_heap();
real_size = size + PICKET_SIZE*2;
malloc_pointer = malloc( real_size );
if ( malloc_pointer == NULL ) {
screen_message( 1, 0, "File: %s Line: %u requesting %u bytes",
file_name, line_number, size );
screen_message( 0, 0, "Malloc failed! Null pointer will be returned." );
}
else
add_tag_to_table( malloc_pointer, 0, size, file_name, line_number );
hide_screen();
return( ( char * ) malloc_pointer + PICKET_SIZE );
}


/*
* my_free is set up to replace free(). Just like my_malloc(), it first
* checks out the heap and prints a message if anything funny shows up.
* Before I try to free the block, I have to check and see if it is in
* my tag table. If it is, I free the real pointer, not the one I passed
* back to the caller. If it isn't in the tag table, I print a message
* out to that effect, and return.
*/

void my_free( char *file_name, unsigned int line_number, void *block )
{
int tag;

verify_heap();
for ( tag = 0; tag < TAG_MAX ; tag++ ) {
if ( tag_table[ tag ].returned_address == ( void far * ) block )
break;
}
if ( tag < TAG_MAX ) {
if ( tag_table[ tag ].maverick ) {
screen_message( 1, 0, "File: %s Line: %u freeing block %Fp",
file_name, line_number, ( void far * ) block );
screen_message( 0, 0, "Tag is a maverick entry!" );
free( block );
}
else
free( ( char * ) block - PICKET_SIZE );
delete_tag_from_table( tag );
}
else {
screen_message( 1, 0, "File: %s Line: %u freeing block %Fp",
file_name, line_number, ( void far * ) block );
screen_message( 0, 0, "Tag was not found in tag table! Going to try and free() it." );
free( block );
screen_message( 0, 0, "Heap after freeing anonymous block!" );
}
hide_screen();
}


/*
* I need to initialize the tag table my first time through. This
* routine gets called all the time, but only performs the initialization
* once, when next_free_tag is -1.
*/

void initialize_tags()
{
int i;

if ( next_free_tag == -1 ) {
next_free_tag = 0;
for ( i = 0 ; i < TAG_MAX ; i++ ) {
tag_table[ i ].returned_address = NULL;
tag_table[ i ].file_name = "Not in use";
tag_table[ i ].line_number = 0;
tag_table[ i ].size = 0;
}
}
}


/*
* This is the routine called to display the tag table when something
* has gone wrong. It sits in a loop displaying tag table entries, 15
* at a time. The user can hit the 'u' or 'd' keys to move up or down
* in the table. Any other key breaks the user out.
*/

void display_tag_table( int last_tag )
{
int first_tag;
int offset;
int tag;
char far *picket_pointer;
int key;

if ( last_tag < 16 )
first_tag = 0;
else
first_tag = last_tag - 15;

for ( ; ; ) {
for ( offset = 0 ; offset < 15 ; offset++ ) {
tag = first_tag + offset;
screenputf( offset + 3, columns[ 0 ] + 1, "%-3d", tag );
screenputf( offset + 3, columns[ 1 ] + 1, "%-12s",
tag_table[ tag ].file_name );
screenputf( offset + 3, columns[ 2 ] + 1, "%-5d",
tag_table[ tag ].line_number );
if ( tag_table[ tag ].returned_address != NULL ) {
picket_pointer = tag_table[ tag ].returned_address;
picket_pointer -= PICKET_SIZE ;
if ( tag_table[ tag ].maverick )
screenputf( offset + 3, columns[ 3 ] + 1,"%15s", "***MAVERICK***" );
else
screenputf( offset + 3, columns[ 3 ] + 1, "%15s", picket_pointer );
screenputf( offset + 3, columns[ 4 ] + 1, "%Fp",
tag_table[ tag ].returned_address );
picket_pointer += PICKET_SIZE;
picket_pointer += tag_table[ tag ].size;
if ( tag_table[ tag ].maverick )
screenputf( offset + 3, columns[ 6 ] + 1, "%15s", "***MAVERICK***" );
else
screenputf( offset + 3, columns[ 6 ] + 1, "%15s", picket_pointer );
}
else {
screenputf( offset + 3, columns[ 3 ] + 1, "%15s", "" );
screenputf( offset + 3, columns[ 4 ] + 1, " NULL " );
screenputf( offset + 3, columns[ 6 ] + 1, "%15s", "" );
}
screenputf( offset + 3, columns[ 5 ] + 1, "%-5d", tag_table[ tag ].size );
}
key = getch();
if ( key == 'u' || key == 'U' ) {
first_tag -= 15;
if (first_tag < 0)
first_tag = 0;
}
else if ( key == 'd' || key == 'D' ) {
first_tag += 15;
if ( ( first_tag + 15 ) >= TAG_MAX )
first_tag = TAG_MAX - 15;
}
else
break;
}
}


/*
* This routine is called when a new pointer needs to be added to the tag
* table. It can be a maverick pointer or a regular pointer.
*/

void add_tag_to_table( char far *pointer,
char maverick,
size_t size,
char *file_name,
unsigned int line_number )
{
int i;

if ( next_free_tag >= TAG_MAX )
return;
tag_table[ next_free_tag ].returned_address = pointer;
tag_table[ next_free_tag ].real_address = pointer;
tag_table[ next_free_tag ].size = size;
tag_table[ next_free_tag ].file_name = file_name;
tag_table[ next_free_tag ].line_number = line_number;
tag_table[ next_free_tag ].maverick = maverick;
if ( !maverick ) {
for ( i = 0 ; i < PICKET_SIZE ; i++ )
pointer[ i ] = leading_picket[ i ];
pointer += size;
pointer += PICKET_SIZE ;
for ( i = 0 ; i < PICKET_SIZE ; i++ )
pointer[ i ] = trailing_picket[ i ];
tag_table[ next_free_tag ].returned_address += PICKET_SIZE;
}
next_free_tag++;
}


/*
* This routine is called when a tag needs to be deleted from the table. This
* can happen when a call to free() is made, or when a heapwalk shows that
* one of the pointers is missing.
*/

void delete_tag_from_table( int tag )
{
int i;

next_free_tag--;
for ( i = tag ; i < next_free_tag ; i++ )
tag_table[ i ] = tag_table[ i + 1 ];
tag_table[ i ].returned_address = NULL;
tag_table[ i ].file_name = "Not in use";
tag_table[ i ].line_number = 0;
tag_table[ i ].size = 0;
}


/*
* This is the verify routine that is called on the entry to my_malloc()
* or my_free(). It calls the heap_walk() routine first, to let it do
* its check out of the heap. It then goes through the entire tag table
* and verifies that the pickets are all intact.
*/

void verify_heap()
{
int i;
int tag;
char far *picket_pointer;

initialize_tags();
heap_walk();
for ( tag = 0 ; tag < next_free_tag ; tag++ ) {
if ( tag_table[ tag ].maverick )
continue;
picket_pointer = tag_table[ tag ].returned_address;
picket_pointer -= PICKET_SIZE ;
for ( i = 0 ; i < PICKET_SIZE ; i++ )
if ( picket_pointer[ i ] != leading_picket[ i ] ) {
screen_message( 0, i, "Error in leading picket, tag %3d", tag );
break;
}
picket_pointer += PICKET_SIZE ;
picket_pointer += tag_table[tag].size;
for ( i = 0 ; i < PICKET_SIZE ; i++ )
if ( picket_pointer[ i ] != trailing_picket[ i ] ) {
screen_message( 0, tag, "Error in trailing picket, tag %3d", tag );
break;
}
}
}

/*
* This is the routine that walks through the heap. If an heap entry
* is not found in the tag table, a message is printed, and it is added
* as a maverick. Otherwise, the tag is noted. After walking through
* the whole heap, a check is done to see if any of our tagged entries
* didn't show up, which generates another message.
*
* Remember that this code is MSC-specific. You need to rewrite this
* routine for every different compiler.
*/

void heap_walk()
{
struct _heapinfo hinfo;
int heapstatus;
size_t size;
int i;

hinfo._pentry = NULL;

for ( i = 0 ; i < next_free_tag ; i++ )
tag_table[ i ].tagged = 0;

for ( ; ; ) {
heapstatus = _heapwalk( &hinfo );
if ( heapstatus == _HEAPEMPTY )
break;
if ( heapstatus == _HEAPEND )
break;
if ( heapstatus != _HEAPOK ) {
screen_message( 0, 0, "Heap is corrupted! Going to exit." );
exit( 1 );
}
if ( hinfo._useflag != _USEDENTRY )
continue;
for ( i = 0 ; i < next_free_tag ; i++ ) {
if ( (int far *) tag_table[ i ].real_address == hinfo._pentry ) {
tag_table[ i ].tagged = 1;
break;
}
}
if ( i == next_free_tag ) {
size = hinfo._size;
if ( i < TAG_MAX )
tag_table[ i ].tagged = 1;
add_tag_to_table( (char far *) hinfo._pentry, 1, size, "MAVERICK",0 );
screen_message( 0, i, "Found a maverick heap entry: %Fp", hinfo._pentry );
}
}
/*
* At this point every entry should have been tagged, so I can go through
* the table and look for ones that didn't get tagged.
*/
for ( i = 0 ; i < next_free_tag ; i++ ) {
if ( tag_table[ i ].tagged == 0 ) {
screen_message( 0, i, "Didn't find heap entry in heapwalk, tag %d", i );
delete_tag_from_table( i );
}
}
}


/*
* During the process of checking out the heap, if I see anything worthy
* of a message, I call this routine. If the screen is not already up,
* this guy pulls it up. Then it prints the message.
*/

void screen_message( int immediate_return, int tag, char *format, ... )
{
char message[ 81 ];
va_list args;

if ( screen_up == 0 ) {
screen_up = 1;
message_line = 20;
setup_screen( labels, widths, 15 );
}
va_start( args, format );
vsprintf( message, format, args );
screenputs( message_line, 0, message );
if ( ++message_line >= 25 )
message_line = 20;
if ( !immediate_return )
display_tag_table( tag );
}

/*
* After all the work is done, I have to hide my heap screen so the user
* can see the application output. This is pretty easy to do, a routine
* in SCREEN.C does the whole job for me.
*/
void hide_screen()
{
if ( screen_up != 0 ) {
screen_up = 0;
restore_screen();
}
}



*********************************SCREEN.C***************************************

/*
* Author: Mark Nelson
*
* Date: October 28, 1989
*
* Description: This module contains the screen I/O routines used in
* the heap debugger module. These are very simplified
* screen I/O routines.
*/

#include
#include
#include
#include "_heap.h"
/*
* These are all the line drawing constants defined.
*/
#define UL_CORNER 218
#define UR_CORNER 191
#define LL_CORNER 192
#define LR_CORNER 217
#define UPPER_TEE 194
#define LOWER_TEE 193
#define LEFT_TEE 195
#define RIGHT_TEE 180
#define CENTER_TEE 197
#define HORIZONTAL_LINE 196
#define VERTICAL_LINE 179

/*
* I create a structure so I can write directly to screen as if it were
* a big array. That way the compiler takes care of computing the
* addresses, all I have to do is insert the row and column.
*/
struct video_element {
unsigned char character;
unsigned char attribute;
};
struct video_element (far *physical_screen)[25][80];
struct video_element saved_screen[25][80];

/*
* This routine draws the box I use up on the screen. It is passed a
* list of labels to draw at the head of the columns, plus a count
* of how many rows are to be left open for data entry. It depends
* on some earlier code somewhere to have initialized an array called
* columns[] that tells it where to draw each column on the screen.
* There is a lot of code here to draw the boxes, but it is all
* straightforward.
*/
int columns[10];
int column_count=0;

void draw_boxes_and_titles(char *labels[],int row_count)
{
int col;
int row;
int i;
int j;
int rows[3];
/*
* The three rows I define are the top and bottom of the box, plus the
* line that divides the title lines from the data lines.
*/
rows[0]=0;
rows[1]=2;
rows[2]=3+row_count;
for (col=1;col for (i=0;i<3;i++)
(*physical_screen)[rows[i]][col].character = HORIZONTAL_LINE;
for (i=0;i<=column_count;i++)
for (row=0;row<(row_count+4);row++)
(*physical_screen)[row][columns[i]].character = VERTICAL_LINE;
(*physical_screen)[0][columns[0]].character = UL_CORNER;
(*physical_screen)[row_count+3][columns[0]].character = LL_CORNER;
(*physical_screen)[0][columns[column_count]].character = UR_CORNER;
(*physical_screen)[row_count+3][columns[column_count]].character = LR_CORNER;

(*physical_screen)[rows[1]][columns[0]].character = LEFT_TEE;
(*physical_screen)[rows[1]][columns[column_count]].character = RIGHT_TEE;
for (j=1;j (*physical_screen)[0][columns[j]].character = UPPER_TEE;
for (j=1;j (*physical_screen)[row_count+3][columns[j]].character = LOWER_TEE;

for (j=1;j (*physical_screen)[rows[1]][columns[j]].character = CENTER_TEE;
/*
* Here is where I draw the labels. They need to go in the center of
* their little boxes.
*/
for (i=0;i {
col=columns[i]+1;
col += (columns[i+1]-columns[i]-1)/2;
col -= strlen(labels[i])/2;
screenputs(1,col,labels[i]);
}
}
/*
* This is a general purpose routine to print a formatted string on
* the screen.
*/
void screenputf(int row,int col,char *format,...)
{
char buffer[81];
va_list args;

va_start(args,format);
vsprintf(buffer,format,args);
screenputs(row,col,buffer);
}
/*
* This is a general purpose routine to put an unformatted string
* out to the screen.
*/
void screenputs(int row,int col,char *string)
{
char c;

while (1)
{
c=*string++;
if (c=='\0')
break;
(*physical_screen)[row][col++].character=c;
}
}
/*
* This is a general purpose routine to clear a whole line on the
* screen.
*/
void screenclearline(int row)
{
int col;

for (col=0;col<80;col++)
(*physical_screen)[row][col].character=' ';
}
/*
* This is the screen initialization code. It is a trap door routine that
* gets called all the time, but only executes once. It computes what
* columns the vertical lines are going to go in, based on the widths needed
* for each column, passed as a parameter.
* Note that if you are using a monochrome monitor, you need to change
* the screen pointer to be 0xb0000000L.
* This routine also initializes the tag table
*/
void initialize_screen(char *labels[],int widths[])
{
int row;
int col;
int i;
static int first_time=0;

if (first_time==0)
{
first_time=1;
columns[0]=1;
column_count=0;
while (strlen(labels[column_count]) != 0)
{
columns[column_count+1] = columns[column_count]+widths[column_count]+1;
column_count++;
}
}
physical_screen=(struct video_element (far *)[25][80])0xb8000000L;
for (row=0;row<25;row++)
for (col=0;col<80;col++)
{
saved_screen[row][col]=(*physical_screen)[row][col];
(*physical_screen)[row][col].character=' ';
(*physical_screen)[row][col].attribute=0x1b;
}
}

/*
* Whenever the heap routines decide they need to print a screen message,
* they set up the screen first by calling this guy.
*/
void setup_screen(char *labels[],int widths[],int rows)
{
initialize_screen(labels,widths);
draw_boxes_and_titles(labels,rows);
}

/*
* After the heap routines are done printing debug information, they
* have to restore the screen back to where it was when they got called.
* This routine does that.
*/
void restore_screen()
{
int row;
int col;

for (row=0;row<25;row++)
for (col=0;col<80;col++)
(*physical_screen)[row][col]=saved_screen[row][col];
}

*********************************_HEAP.H****************************************

/* Prototypes from SCREEN.C */

void draw_boxes_and_titles(char *labels[],int row_count);
void screenputc(int row,int col,char c);
void screenputf(int row,int col,char *format,...);
void screenputs(int row,int col,char *string);
void screenclearline(int row);
void initialize_screen(char *labels[],int widths[]);
void setup_screen(char *labels[],int widths[],int rows);
void restore_screen(void);

/* Prototypes from HEAP.C */

void screen_message(int immediate_return, int tag, char *format,...);
void hide_screen(void);
void my_free(char *file_name,unsigned int line_number, void *block);
void *my_malloc(char *file_name,unsigned int line_number,size_t size);
void initialize_tags(void);
void display_tag_table(int last_tag);
void add_tag_to_table(char far *pointer,char maverick,size_t size,char *file_name,unsigned int line_number);
void delete_tag_from_table(int tag);
void verify_heap(void);
void heap_walk(void);


  3 Responses to “Category : C Source Code
Archive   : HEAPDBG.ZIP
Filename : HEAP.DBG

  1. Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!

  2. This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.

  3. But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/