-- TexDebug.mesa

-- last written by Doug Wyatt, December 19, 1979  3:04 AM

DIRECTORY
	TexDebugDefs: FROM "TexDebugDefs",
	TexDefs: FROM "TexDefs",
	TexGlueDefs: FROM "TexGlueDefs",
	TexIODefs: FROM "TexIODefs",
	TexMemDefs: FROM "TexMemDefs",
	TexMem: FROM "TexMem",
	TexNodeDefs: FROM "TexNodeDefs",
	TexNode: FROM "TexNode",
	TexSynDefs: FROM "TexSynDefs";

TexDebug: PROGRAM
IMPORTS TexIODefs,mem:TexMem,TexNodeDefs,node:TexNode,TexSynDefs
EXPORTS TexDebugDefs SHARES TexMem,TexMemDefs,TexNode =
BEGIN OPEN TexDefs,TexGlueDefs,TexNodeDefs,TexMemDefs,TexIODefs;

showMem: BOOLEAN=FALSE;
showNodes: BOOLEAN=FALSE;

-- * * * * * Memory statistics: entering MainControl
-- seglist (x,x,xx,xx) = xxx pages, xxxxx words
-- freesize xxxxx  biglist (xx,xx)
--   size   NodesAlloc NodesUsed  WordsAlloc WordsUsed  WordsFree
--     n      xxxxx      xxxxx      xxxxx      xxxxx      xxxxx
--     n      xxxxx      xxxxx      xxxxx      xxxxx      xxxxx
--     n      xxxxx      xxxxx      xxxxx      xxxxx      xxxxx
--    nn      xxxxx      xxxxx      xxxxx      xxxxx      xxxxx
--    nn      xxxxx      xxxxx      xxxxx      xxxxx      xxxxx
--    nn      xxxxx      xxxxx      xxxxx      xxxxx      xxxxx
--    nn      xxxxx      xxxxx      xxxxx      xxxxx      xxxxx
-- small      xxxxx      xxxxx      xxxxx      xxxxx      xxxxx
--   big      xxxxx      xxxxx      xxxxx      xxxxx      xxxxx
-- total      xxxxx      xxxxx      xxxxx      xxxxx      xxxxx
-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

foo: STRING←"Debugging"; -- for calling ShowMem from debugger

ShowMem: PUBLIC PROCEDURE[string: STRING] =
	BEGIN IF showMem THEN MemStats[string] END;

CheckDEBUG: PROCEDURE[s: STRING] RETURNS[BOOLEAN] =
	BEGIN OPEN TexDebugDefs;
	IF NOT DEBUG THEN
		BEGIN
		Ws["* * * "L]; Ws[s];
		Ws[" unavailable: DEBUG not enabled!"L]; Cr;
		END;
	RETURN[DEBUG];
	END;

MemStats: PROCEDURE[string: STRING] =
	BEGIN OPEN mem,debug;
	seg: SegPtr;
	bptr: BigPtr;
	size: SmallSize; slot: Slot;
	pgs,na,wa,nu,wu,wf: CARDINAL←0;
	sna,snu,swa,swu,swf: CARDINAL←0;
	bna,bwa,bnu,bwu,bnf,bwf: CARDINAL←0;
	IF NOT CheckDEBUG["MemStats"L] THEN RETURN;
	Cr; Ws["* * * * * Memory statistics: "L]; Ws[string]; Cr;
	Ws["seglist ("L];
	IF (seg←seglist)#NIL THEN
		DO
		Wnf[seg.pages,0]; pgs←pgs+seg.pages;
		IF (seg←seg.link)#NIL THEN Wc[',] ELSE EXIT;
		ENDLOOP;
	Ws[") = "L]; Wnf[pgs,0]; Ws[" pages,"L];
	Wnf[WordsForPages[pgs],0]; Ws[" words"L]; Cr;
	Ws["freesize"L]; Wnf[freesize,6]; Ws["  biglist ("L];
	IF (bptr←biglist)#NIL THEN
		DO
		Wnf[bptr.size,0]; bnf←bnf+1; bwf←bwf+bptr.size;
		IF (bptr←bptr.link)#NIL THEN Wc[',] ELSE EXIT;
		ENDLOOP;
	Ws[")"L]; Cr;
	Ws["  size  NodesAlloc NodesUsed  WordsAlloc WordsUsed  WordsFree"L]; Cr;
	FOR slot IN Slot
		DO
		IF (na←slotcount[slot])=0 THEN LOOP; nu←slotNodesUsed[slot];
		size←slotsize[slot]; wa←na*size; wu←slotWordsUsed[slot]; wf←wa-wu;
		sna←sna+na; snu←snu+nu;
		swa←swa+wa; swu←swu+wu; swf←swf+wf;
		Wnf[size,5]; Wnf[na,11]; Wnf[nu,11];
		Wnf[wa,11]; Wnf[wu,11]; Wnf[wf,11]; Cr;
		ENDLOOP;
	Ws["small"L]; Wnf[sna,11]; Wnf[snu,11];
	Wnf[swa,11]; Wnf[swu,11]; Wnf[swf,11]; Cr;
	bna←bnf+(bnu←bigNodesUsed); bwa←bwf+(bwu←bigWordsUsed);
	Ws["  big"L]; Wnf[bna,11]; Wnf[bnu,11];
	Wnf[bwa,11]; Wnf[bwu,11]; Wnf[bwf,11]; Cr;
	Ws["total"L]; Wnf[sna+bna,11]; Wnf[snu+bnu,11];
	Wnf[swa+bwa,11]; Wnf[swu+bwu,11]; Wnf[swf+bwf,11]; Cr;
	Ws[" strings"L]; Wnf[strings,6];
	Ws["     Chars"L]; Wnf[stringChars,6];
	Ws["     Words"L]; Wnf[stringWords,6]; Cr;
	Ws["* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *"L]; Cr;
	END;

-- *** Math mode Zone: pgs xxx  alloc xxxxx  used xxxxx
-- *** seglist (x,x,x)  unused xxxxx

ShowZone: PUBLIC PROCEDURE[zone: ZonePtr, string: STRING] =
	BEGIN
	IF showMem THEN ZoneStats[zone,string];
	END;

ZoneStats: PROCEDURE[zone: ZonePtr, string: STRING] =
	BEGIN OPEN mem,LOOPHOLE[zone,ZPtr],debug;
	seg: ZSegPtr;
	pgs,wa,wu: CARDINAL←0;
	IF NOT CheckDEBUG["ZoneStats"L] THEN RETURN;
	pgs←totalpages; wa←WordsForPages[pgs]; wu←wa-(freesize+unused);
	Cr; Ws["*** "L]; Ws[string]; Ws[" Zone: pgs"L]; Wnf[pgs,4];
	Ws["  alloc"L]; Wnf[wa,6]; Ws["  used"L]; Wnf[wu,6]; Cr;
	IF (seg←seglist)#NIL THEN
		BEGIN
		Ws["*** seglist ("L];
			DO
			Wnf[seg.pages,0]; pgs←pgs+seg.pages;
			IF (seg←seg.link)#NIL THEN Wc[',] ELSE EXIT;
			ENDLOOP;
		Ws[")  unused"L]; Wnf[unused,6]; Cr;
		END;
	END;


Clobbered: ERROR = CODE;

thresh: CARDINAL←1;
indent: CARDINAL←0;
nitems: CARDINAL←5;

Indent: PROCEDURE =
	BEGIN
	Cr; THROUGH [1..indent] DO Wc['.] ENDLOOP;
	END;

DumpNodeList: PROCEDURE[p: NodePtr] =
	BEGIN
	ENABLE Clobbered => BEGIN Ws["***???***"]; CONTINUE END;
	c: CARDINAL←0;
	q: NodePtr;
	IF indent>thresh THEN RETURN;
	FOR q←p,q.link UNTIL q=NIL
		DO
		Indent;
		IF (c←c+1)>nitems THEN BEGIN Ws["etc."]; RETURN END;
		ShowNode[q];
		ENDLOOP;
	END;

ShowNode: PUBLIC PROCEDURE[p: NodePtr] =
	BEGIN OPEN TexIODefs;
	DumpSubList: PROCEDURE[q: NodePtr] =
		BEGIN indent←indent+1; DumpNodeList[q]; indent←indent-1 END;
	WITH pp:p SELECT FROM
		char,disc =>
			BEGIN IF pp.type=disc THEN Ws["\discretionary"]; WFChar[pp.c] END;
		box =>
			BEGIN
			SELECT pp.dir FROM
				hlist => Ws["\hbox"];
				vlist => Ws["\vbox"];
				ENDCASE => ERROR Clobbered;
			Wc['(]; Wn[pp.height]; Ws["+"]; Wn[pp.depth]; Ws[")x"]; Wn[pp.width];
			IF pp.glueset#zeroGlueSet THEN
				BEGIN Ws[", glueset "]; WGlueSet[pp.glueset] END;
			IF pp.shiftamt#0 THEN
				BEGIN Ws[", shifted "]; Wn[pp.shiftamt] END;
			DumpSubList[pp.head];
			END;
		rule =>
			BEGIN
			Wr: PROCEDURE[x: Dimn] =
				BEGIN IF x=nilDimn THEN Wc['*] ELSE Wn[x] END;
			Ws["\rule"];
			Wc['(]; Wr[pp.height]; Ws["+"]; Wr[pp.depth]; Ws[")x"]; Wr[pp.width];
			END;
		glue => BEGIN Ws["\glue "]; WGlue[pp.g] END;
		space => BEGIN Ws["\space "]; Wn[pp.s] END;
		kern => BEGIN Ws["\kern "]; Wn[pp.s] END;
		leader => BEGIN Ws["\leaders"]; DumpSubList[pp.p] END;
		hyph =>
			BEGIN
			Ws["\hyphenation "];
			SELECT pp.auto FROM
				on => Ws["on"];
				off => Ws["off"];
				ENDCASE => ERROR Clobbered;
			END;
		penalty => BEGIN Ws["\penalty "]; Wn[pp.pts] END;
		eject => Ws["\eject"];
		mark =>
			BEGIN
			Ws["\mark"]; Wc['{];
			TexSynDefs.DumpTokens[NIL,pp.t.link];
			END;
		ins =>
			BEGIN
			SELECT pp.where FROM
				top => Ws["\topinsert "];
				bot => Ws["\botinsert "];
				ENDCASE => ERROR Clobbered;
			IF pp.dir=vlist THEN Ws["(can wait) "];
			WGlue[pp.glue]; DumpSubList[pp.vlist];
			END;
		unset => Ws["\unset"]; -- *** more here
		ENDCASE => ERROR Clobbered;
	END;

WGlue: PROCEDURE[g: GluePtr] =
	BEGIN
	f: Flex;
	Wn[g.space];
	IF (f←g.flex[str])#zeroFlex THEN
		BEGIN Ws[" plus "]; WFlex[f] END;
	IF (f←g.flex[shr])#zeroFlex THEN
		BEGIN Ws[" minus "]; WFlex[f] END;
	END;

WFlex: PROCEDURE[f: Flex] =
	BEGIN
	SELECT f.order FROM
		regular => NULL;
		lowerfill => Ws["lowerfill "];
		fill => Ws["fill "];
		ENDCASE => ERROR Clobbered;
	Wn[f.val];
	END;

WGlueSet: PROCEDURE[gs: GlueSet] =
	BEGIN
	SELECT gs.dir FROM
		str => Ws["str "];
		shr => Ws["shr "];
		ENDCASE => ERROR Clobbered;
	SELECT gs.order FROM
		regular => NULL;
		lowerfill => Ws["lowerfill "];
		fill => Ws["fill "];
		ENDCASE => ERROR Clobbered;
	Wn[gs.num]; Wc['/]; Wn[gs.den];
	END;

-- * * * Node statistics: page
--   NodeType   Nodes   Words
--      char      xx      xx
--    string     xxx    xxxx
--       box      xx     xxx
--      glue     xxx    xxxx
-- gluespecs      xx     xxx
--      hyph      xx      xx
--   penalty       x      xx
--     total    xxxx    xxxx
--  Len  Strings  Words
--    1      xxx   xxxx
--    2      xxx   xxxx
--    3       xx    xxx
-- * * * * * * * * * * * * * *

nodetype: ARRAY NodeType OF STRING ←
	["char", "string", "box", "rule", "glue", "space", "kern", "leader",
	"hyph", "penalty", "disc", "eject", "mark", "ins", "unset", "listhead"];

nodes: ARRAY NodeType OF CARDINAL;
sizes: ARRAY NodeType OF CARDINAL;
stringvec: ARRAY StringLength OF CARDINAL;
gluespecs: CARDINAL;

ShowNodeStatistics: PUBLIC PROCEDURE[p: NodePtr, string: STRING] =
	BEGIN IF showNodes THEN NodeStats[p,string] END;

NodeStats: PROCEDURE[p: NodePtr, string: STRING] =
	BEGIN OPEN node;
	t: NodeType;
	tnodes,twords: CARDINAL←0;
	n,w: CARDINAL;
	l: StringLength;
	FOR t IN NodeType DO nodes[t]←sizes[t]←0 ENDLOOP;
	FOR l IN StringLength DO stringvec[l]←0 ENDLOOP;
	gluespecs←0;
	AddNodeList[p];
	Cr; Ws["* * * Node statistics: "L]; Ws[string]; Cr;
	Ws["  NodeType   Nodes   Words"L]; Cr;
	FOR t IN NodeType
		DO
		tnodes←tnodes+(n←nodes[t]); twords←twords+(w←sizes[t]);
		IF n=0 THEN LOOP;
		Wrjs[nodetype[t],9]; Wnf[n,8]; Wnf[w,8]; Cr;
		IF t=glue AND gluespecs>0 THEN
			BEGIN
			n←gluespecs; w←n*SIZE[GlueSpec];
			Wrjs["gluespecs"L,9]; Wnf[n,8]; Wnf[w,8]; Cr;
			END;
		ENDLOOP;
	Wrjs["total"L,9]; Wnf[tnodes,8]; Wnf[twords,8]; Cr;
	StringStats;
	Ws["* * * * * * * * * * * * * *"L]; Cr;
	END;

StringStats: PROCEDURE =
	BEGIN
	l: StringLength;
	lmin,lmax: StringLength;
	n,w: CARDINAL;
	FOR lmin IN StringLength UNTIL stringvec[lmin]>0 DO ENDLOOP;
	FOR lmax DECREASING IN StringLength UNTIL stringvec[lmax]>0 DO ENDLOOP;
	Ws[" Len  Strings  Words"L]; Cr;
	FOR l IN [lmin..lmax]
		DO
		n←stringvec[l]; w←n*StringNodeSize[l];
		Wnf[l,3]; Wnf[n,9]; Wnf[w,7]; Sp;
		THROUGH [0..(n+3)/5) DO Wc['#] ENDLOOP; Cr;
		ENDLOOP;
	END;

-- write right-justified string
Wrjs: PROCEDURE[s: STRING, w: CARDINAL] =
	BEGIN THROUGH [s.length..w) DO Sp ENDLOOP; Ws[s] END;

AddNode: PROCEDURE[p: NodePtr] =
	BEGIN
	t: NodeType←p.type;
	s: CARDINAL←0;
	WITH pp:p SELECT FROM
		char => s←SIZE[char Node];
		disc => s←SIZE[disc Node];
		string =>
			BEGIN
			l: StringLength←pp.length;
			stringvec[l]←stringvec[l]+1;
			s←StringNodeSize[l];
			END;
		box => BEGIN AddNodeList[pp.head]; s←SIZE[box Node] END;
		rule => s←SIZE[rule Node];
		glue =>
			BEGIN
			g: GluePtr←pp.g;
			IF g.refs=1 THEN gluespecs←gluespecs+1;
			s←SIZE[glue Node];
			END;
		space => s←SIZE[space Node];
		kern => s←SIZE[kern Node];
		leader => s←SIZE[leader Node];
		hyph => s←SIZE[hyph Node];
		penalty => s←SIZE[penalty Node];
		eject => s←SIZE[eject Node];
		mark => s←SIZE[mark Node];
		ins => BEGIN AddNodeList[pp.vlist]; s←SIZE[ins Node] END;
		unset => BEGIN AddNode[pp.box]; s←SIZE[unset Node] END;
		listhead => s←SIZE[listhead Node];
		ENDCASE => ERROR;
	nodes[t]←nodes[t]+1; sizes[t]←sizes[t]+s;
	END;

AddNodeList: PROCEDURE[p: NodePtr] =
	BEGIN
	q: NodePtr;
	FOR q←p,q.link UNTIL q=NIL DO AddNode[q] ENDLOOP;
	END;

END.