MODULE ANNOTATE;

!*****************************************************************************
!*                                                                           *
!*  COPYRIGHT (c) 1990  BY                                                   *
!*  DIGITAL EQUIPMENT CORPORATION, MAYNARD, MASSACHUSETTS.                   *
!*  ALL RIGHTS RESERVED.                                                     *
!*                                                                           *
!*  THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE USED AND COPIED    *
!*  ONLY IN  ACCORDANCE WITH  THE  TERMS  OF  SUCH  LICENSE  AND WITH THE    *
!*  INCLUSION OF THE ABOVE COPYRIGHT NOTICE. THIS SOFTWARE OR  ANY  OTHER    *
!*  COPIES THEREOF MAY NOT BE PROVIDED OR OTHERWISE MADE AVAILABLE TO ANY    *
!*  OTHER PERSON.  NO TITLE TO AND OWNERSHIP OF  THE  SOFTWARE IS  HEREBY    *
!*  TRANSFERRED.                                                             * 
!*                                                                           *
!*  THE INFORMATION IN THIS SOFTWARE IS  SUBJECT TO CHANGE WITHOUT NOTICE    *
!*  AND  SHOULD  NOT  BE  CONSTRUED AS  A COMMITMENT BY DIGITAL EQUIPMENT    *
!*  CORPORATION.                                                             *
!*                                                                           *
!*  DIGITAL ASSUMES NO RESPONSIBILITY FOR THE USE  OR  RELIABILITY OF ITS    *
!*  SOFTWARE ON EQUIPMENT WHICH IS NOT SUPPLIED BY DIGITAL.                  *
!*                                                                           *
!*****************************************************************************

INCLUDE FILE 'Annotate_ImageRec.SCI';

TYPE GlobalRec: RECORD
	AbsoluteAddress:	INTEGER,
	RelativeAddress:	INTEGER,
	GlobalSymbolTreePtr:	TREEPTR(STRING) TO INTEGER,
	Image:			POINTER TO ImageRec,
END RECORD;

EXTERNAL PROCEDURE Read_Map() OF POINTER TO ImageRec;
EXTERNAL PROCEDURE BitBucket(INTEGER, FIXED STRING(132));

EXTERNAL PROCEDURE HexToInteger(STRING) OF INTEGER;
EXTERNAL PROCEDURE IntegerToHex(VALUE INTEGER, REFERENCE FIXED STRING(9));

EXTERNAL PROCEDURE ResetAddressSpace(VALUE INTEGER);
EXTERNAL PROCEDURE DefineAddress(VALUE INTEGER, VALUE POINTER TO GlobalRec);
EXTERNAL PROCEDURE LookupAddress(VALUE INTEGER) OF POINTER TO GlobalRec;

DECLARE images: TREE(STRING) OF POINTER TO ImageRec;

! Have we found a global symbol's address in the log file?

DECLARE changed_a_base_address: BOOLEAN;

! Number of global symbols whose absolute address is known.

DECLARE address_count: INTEGER;

! An 'array' whose index is the absolute address of a global symbol,
! and whose value is a structure, GlobalRec, with the name of the
! symbol, its image, and also its absolute address.

DECLARE addresses: TREE(INTEGER) OF GlobalRec;

PROCEDURE FoundGlobalAddress(
	image: STRING,
	global: STRING,
	hexaddr: STRING) OF BOOLEAN;

	DECLARE addr, reladdr: INTEGER;
	DECLARE ptr: POINTER TO ImageRec;

	addr = HexToInteger(hexaddr);

	IF NOT EXISTS(images(image)) THEN
		RETURN FALSE;
	END IF;

	ptr = images(image);

	reladdr = ptr->.Globals->(global);

	ptr->.BaseAddress = addr - reladdr;

	changed_a_base_address = TRUE;

	RETURN TRUE;
END PROCEDURE;

PROCEDURE BuildAddressSpace;
	DECLARE known_image_count, abs_addr, base_addr: INTEGER;
	DECLARE iptr: TREEPTR(STRING) TO POINTER TO ImageRec;
	DECLARE aptr: TREEPTR(INTEGER) TO GlobalRec;
	DECLARE grptr: POINTER TO GlobalRec;
	DECLARE gptr: TREEPTR(STRING) TO INTEGER;
	DECLARE the_image: POINTER TO ImageRec;

	IF NOT changed_a_base_address THEN
		RETURN;
	END IF;

	PRUNE addresses;

	known_image_count = 0;
	address_count = 0;

	! Loop through the images, finding those whose base address
	! we know.

	iptr = FIRST(images);
	WHILE iptr <> NIL;

		! Is the base address of this image known?

		the_image = VALUE(iptr);
		base_addr = the_image->.BaseAddress;

		IF base_addr <> 0 THEN

			! Yes.  Add all of its global symbols into the
			! address space.

			known_image_count = known_image_count + 1;

			gptr = FIRST(the_image->.Globals->);

			WHILE gptr <> NIL;
				abs_addr = base_addr +
					VALUE(gptr);

				address_count = address_count + 1;

				addresses(abs_addr).AbsoluteAddress =
					abs_addr;

				addresses(abs_addr).RelativeAddress =
					VALUE(gptr);

				addresses(abs_addr).GlobalSymbolTreePtr = gptr;

				addresses(abs_addr).Image = the_image;

				gptr = NEXT(gptr);
			END WHILE;
		END IF;

		iptr = NEXT(iptr);
	END WHILE;

	IF known_image_count = 0 THEN
		RETURN;
	END IF;

	! Now we have the addresses in sorted order (implicit in the
	! TREE(INTEGER) OF ... structure).  But it isn't easy to do
	! a binary search on this structure.  So lets put them into
	! a read array.  But SCAN doesn't support them, so we'll
	! call out to C for this purpose.

	CALL ResetAddressSpace(address_count);

	aptr = FIRST(addresses);
	WHILE aptr <> NIL;
		abs_addr = SUBSCRIPT(aptr);
		grptr = VALUEPTR(aptr);

		CALL DefineAddress(abs_addr, grptr);

		aptr = NEXT(aptr);
	END WHILE;
END PROCEDURE;

! Character set definitions:

SET alpha		('A' .. 'Z');
SET numeric		('0' .. '9');
SET hd			( numeric OR 'A' .. 'F' );

SET init_chars		( alpha OR '$' OR '_' );
SET symbol_chars	( init_chars OR numeric );

SET whitespace		(' ' OR '	' OR s'eol' );

! Token definitions:

TOKEN t_pc		CASELESS
			{ hd [hd [hd [hd [hd [hd [hd [hd]]]]]]] };

TOKEN t_dixmain		CASELESS
			{ 'Dixmain address=' hd... };

TOKEN t_gpx		CASELESS
			{ 'gpx$InitOutput address=' hd... };

TOKEN t_mfb		CASELESS
			{ 'mfb$InitOutput address=' hd... };

TOKEN t_scn		CASELESS
			{ 'scn$InitOutput address=' hd... };

TOKEN t_start_stack_dump CASELESS
			{ 'stack dump follows:' };

TOKEN t_whitespace	IGNORE
			{ whitespace... };

MACRO m_dixmain TRIGGER { s: t_dixmain };
	DECLARE b: BOOLEAN;
	b = FoundGlobalAddress(
		'DECW$SERVER_DIX',			!Image
		'DECW$DIX_MAIN',			!Global
		s[INDEX(s, '=')+1..]);			!Address

	IF NOT b THEN
		WRITE 'Found address information, but the expected map hasn''t been loaded.';
		WRITE '		',s;
		WRITE '';
	END IF;
END MACRO;

MACRO m_gpx TRIGGER { s: t_gpx };
	DECLARE b: BOOLEAN;
	b = FoundGlobalAddress(
		'DECW$SERVER_DDX_GA',			!Image
		'GPX$INITOUTPUT',			!Global
		s[INDEX(s, '=')+1..]);			!Address

	IF NOT b THEN
		WRITE 'Found address information, but the expected map hasn''t been loaded.';
		WRITE '		',s;
		WRITE '';
	END IF;
END MACRO;

MACRO m_mfb TRIGGER { s: t_mfb };
	DECLARE b: BOOLEAN;
	b = FoundGlobalAddress(
		'DECW$SERVER_DDX_GC',			!Image
		'MFB$INITOUTPUT',			!Global
		s[INDEX(s, '=')+1..]);			!Address

	IF NOT b THEN
		WRITE 'Found address information, but the expected map hasn''t been loaded.';
		WRITE '		',s;
		WRITE '';
	END IF;
END MACRO;

MACRO m_scn TRIGGER { s: t_scn };
	DECLARE b: BOOLEAN;
	b = FoundGlobalAddress(
		'DECW$SERVER_DDX_GE',			!Image
		'SCN$INITOUTPUT',			!Global
		s[INDEX(s, '=')+1..]);			!Address

	IF NOT b THEN
		WRITE 'Found address information, but the expected map hasn''t been loaded.';
		WRITE '		',s;
		WRITE '';
	END IF;
END MACRO;

MACRO m_stack_dump TRIGGER { t_start_stack_dump {v:t_pc}... };
	DECLARE ptr: TREEPTR(INTEGER) TO STRING;
	DECLARE gptr: POINTER TO GlobalRec;
	DECLARE s: FIXED STRING(80);
	DECLARE rel_addr, abs_addr: INTEGER;
	DECLARE h: FIXED STRING(9);

	WRITE '';
	WRITE 'Found a stack dump!';
	WRITE '';

	CALL BuildAddressSpace;

	IF address_count = 0 THEN
		WRITE 'There are no known absolute addresses!';
		RETURN;
	END IF;

	WRITE 'AbsAddr  = Routine                          + Rel Addr in Image                ';
	WRITE '--------   --------------------------------   --------    ---------------------';

	ptr = FIRST(v);
	WHILE ptr <> NIL;
		abs_addr = HexToInteger(VALUE(ptr));
		gptr = LookupAddress(abs_addr);

		CALL IntegerToHex(abs_addr, h);

		s = ' ';
		s[1 .. 8] = h[1 .. 8];

		IF gptr <> NIL THEN

			rel_addr = abs_addr - gptr->.AbsoluteAddress;
			CALL IntegerToHex(rel_addr, h);

			s[12 .. 43]	= SUBSCRIPT(gptr->.GlobalSymbolTreePtr);
			s[47 .. 54]	= h[1 .. 8];
			s[59 ..]	= gptr->.Image->.Name->;
		END IF;

		WRITE s;

		ptr = NEXT(ptr);
	END WHILE;

	WRITE '';
END MACRO;

PROCEDURE ANNOTATE MAIN OF INTEGER;
	DECLARE p: POINTER TO ImageRec;
	DECLARE filename, imagename: STRING;
	DECLARE image_count: INTEGER;

	! Read the first map.

	image_count = 0;
	p = Read_Map();

	WHILE p <> NIL;
		! File the ImageRec structure by the name of
		! the image it describes.  This will be useful
		! when we find an address in the image which
		! gives us a clue what the base address is.

		images(p->.Name->) = p;
		image_count = image_count + 1;

		! Read the next map.

		p = Read_Map();
	END WHILE;

	IF image_count = 0 THEN
		WRITE 'No images successfully read!';
		RETURN 4;
	END IF;

	WRITE '';

	changed_a_base_address = TRUE;

	READ PROMPT('Enter name of server log file: ') filename;

	START SCAN
		INPUT FILE filename
		OUTPUT PROCEDURE BitBucket;

	RETURN 1;
END PROCEDURE;

END MODULE;
