(FILECREATED " 7-Oct-84 15:30:41" {ERIS}<LISPCORE>LIBRARY>SPY.;16 26659  

      changes to:  (RECORDS SPYRECORD SPYDATA)
		   (FNS SPY.TOGGLE)

      previous date: " 5-Oct-84 22:47:50" {ERIS}<LISPCORE>LIBRARY>SPY.;15)


(* Copyright (c) 1984 by Xerox Corporation. All rights reserved.)

(PRETTYCOMPRINT SPYCOMS)

(RPAQQ SPYCOMS ((VARS SPY.BORDERS SPY.BUFFER.SIZE SPY.FRAGMENTS SPY.NOMERGEFNS (SPY.HASH)
		      (SPY.GRAPH.MENU)
		      SPY.SHOW.PERCENTAGES SPY.SMALLGHOSTS SPY.ICON)
	(INITVARS (SPY.NEXT 0)
		  (SPY.STREAM)
		  (SPY.BUFFER)
		  (SPY.SHOWCOUNTS T)
		  (SPY.SHOW.THRESHOLD 1)
		  (SPY.MAXLINES 10)
		  (SPY.FREQUENCY 10)
		  (SPY.FONT (QUOTE (GACHA 8)))
		  (SPY.TREE))
	(FNS SPY.FIND.TREE SPY.TOGGLE SPY.TREE SPY.LEGEND SPY.GRAPH.EDITOR SPY.END SPY.MAKEGRAPHNODES 
	     SPY.MAX SPY.MERGE SPY.MERGE1 SPY.MERGETREE SPY.NEXT.TREE SPY.SUM SPY.TITLE SPY.MAKE.TREE 
	     SPY.DELETE SPY.DRAWBOX SPY.BUFFER.ENTRY SPY.BUTTON SPY.END.ENTRY SPY.START SPY.INIT 
	     \SPY.INTERRUPT SPY.DUMP.BUFFER SPY.START.ENTRY SPY.ADD.ENTRY SPY.ORIGINAL SPY.OVERFLOW 
	     SPY.MERGE.CALLEES)
	(GLOBALVARS SPY.OVERFLOWED \PERIODIC.INTERRUPT SPY.STREAM SPY.TREE SPY.BUFFER.SIZE SPY.NEXT 
		    SPY.BUFFER.THRESHOLD SPY.BUFFER SPY.FREQUENCY SPY.SHOW.THRESHOLD SPY.MAXLINES 
		    SPY.FONT)
	(MACROS WITH-SPY WITH.SPY)
	(DECLARE: DONTCOPY (RECORDS SPYRECORD SPYDATA))
	(INITRECORDS SPYRECORD)
	(FILES GRAPHER READNUMBER)))

(RPAQQ SPY.BORDERS ((NORMAL "Normal" 2 -1)
		    (GHOST "Shown elsewhere" 2 8840)
		    (RECURSIVEGHOST "End of recursive chain" 2 0 -1)
		    (MERGED "Includes other branches" 4 42405)
		    (SELFRECURSIVE "Includes self-recursive calls" 2 61375)
		    (RECURSIVE "Head of recursive chain" 4 28086)
		    (ENDOFLINE "exceeded depth limit" 6 64510)))

(RPAQQ SPY.BUFFER.SIZE 5120)

(RPAQQ SPY.FRAGMENTS T)

(RPAQQ SPY.NOMERGEFNS (\INTERPRETER1 ERRORSET \EVAL \EVALFORM APPLY EVAL))

(RPAQQ SPY.HASH NIL)

(RPAQQ SPY.GRAPH.MENU NIL)

(RPAQQ SPY.SHOW.PERCENTAGES T)

(RPAQQ SPY.SMALLGHOSTS T)

(RPAQ SPY.ICON (READBITMAP))
(56 28
"OOOOOOOOOOOOOO@@"
"OOOOOOOOOOOOOO@@"
"L@@@@@@@@@@@@C@@"
"L@@@@@@@@@@@@C@@"
"L@@@@@@@@@@@@C@@"
"L@@@@@@@@COONC@@"
"L@@@@@@@@B@@BC@@"
"L@@@@@@@@B@@BC@@"
"L@GOOOO@@N@@BC@@"
"L@D@@@A@CB@@BC@@"
"L@ENODE@LB@@BC@@"
"L@E@IBIC@COONC@@"
"L@ENOAAL@@@@@C@@"
"L@DBHAAF@COONC@@"
"L@ENHAAAHB@@BC@@"
"L@D@@@A@FB@@BC@@"
"L@GOOOO@AJ@@BC@@"
"L@@@@@@@@F@@BC@@"
"L@@@@@@@@B@@BC@@"
"L@@@@@@@@B@@BC@@"
"L@@@@@@@@B@@BC@@"
"L@@@@@@@@B@@BC@@"
"L@@@@@@@@B@@BC@@"
"L@@@@@@@@COONC@@"
"L@@@@@@@@@@@@C@@"
"L@@@@@@@@@@@@C@@"
"OOOOOOOOOOOOOO@@"
"OOOOOOOOOOOOOO@@")

(RPAQ? SPY.NEXT 0)

(RPAQ? SPY.STREAM )

(RPAQ? SPY.BUFFER )

(RPAQ? SPY.SHOWCOUNTS T)

(RPAQ? SPY.SHOW.THRESHOLD 1)

(RPAQ? SPY.MAXLINES 10)

(RPAQ? SPY.FREQUENCY 10)

(RPAQ? SPY.FONT (QUOTE (GACHA 8)))

(RPAQ? SPY.TREE )
(DEFINEQ

(SPY.FIND.TREE
  [LAMBDA (FN)                                               (* ejs: "27-APR-84 11:37")
    (OR (find X in SPY.TREE suchthat (EQ (fetch NAME of X)
					 FN))
	(CAR (push SPY.TREE (create SPYRECORD
				    NAME ← FN
				    COUNT ← 0])

(SPY.TOGGLE
  [LAMBDA NIL                                                (* lmm " 7-Oct-84 15:29")
    (if (EQ \PERIODIC.INTERRUPT (QUOTE \SPY.INTERRUPT))
	then (SPY.END)
	     (printout PROMPTWINDOW .TAB0 0 "SPYing ended")
	     (RESETFORM (CURSOR WAITINGCURSOR)
			(SPY.TREE 10))
      else (printout PROMPTWINDOW .TAB0 0 "SPYing started.")
	   (SPY.START])

(SPY.TREE
  [LAMBDA (THRESHOLD INDIVIDUALP MERGETYPE DEPTHLIMIT)       (* lmm " 3-Jul-84 10:36")
    (COND
      ((NULL SPY.TREE)
	(QUOTE (no SPY samples have been gathered)))
      (T (PROG ((SPYDATA (create SPYDATA
				 CUMULATIVE ←(NOT INDIVIDUALP)
				 THRESHOLD ←(OR THRESHOLD SPY.SHOW.THRESHOLD)
				 MERGETYPE ←(OR MERGETYPE (COND
						  (INDIVIDUALP (QUOTE ALL))
						  (T T)))
				 DEPTH ← DEPTHLIMIT)))
	       (SPY.MAKE.TREE (SPY.MERGE SPY.TREE SPYDATA)
			      SPYDATA])

(SPY.LEGEND
  [LAMBDA NIL                                                (* lmm "28-Sep-84 21:27")
    (SHOWGRAPH (LAYOUTGRAPH [for X in SPY.BORDERS collect (create GRAPHNODE
								  NODEID ← X
								  NODELABEL ←(CADR X)
								  TONODES ← NIL
								  NODEFONT ← SPY.FONT
								  NODEBORDER ←(CDDR X)
								  NODELABELSHADE ←(CADDR
								    (CDDR X]
			    (REVERSE SPY.BORDERS)
			    NIL SPY.FONT NIL 10)
	       "SPY border interpretation"
	       (QUOTE NILL)
	       (QUOTE NILL])

(SPY.GRAPH.EDITOR
  [LAMBDA (W)                                                (* rmk: "13-Sep-84 17:01")
    (PROG [(TREES (WINDOWPROP W (QUOTE TREES)))
	   (TOPCOUNT (WINDOWPROP W (QUOTE TOPCOUNT)))
	   (WINDOW W)
	   NODE LASTNODE (SPYDATA (WINDOWPROP W (QUOTE SPYDATA]
          (TOTOPW W)
          (do [SETQ NODE (NODELST/AS/MENU (fetch (GRAPH GRAPHNODES) of (WINDOWPROP W (QUOTE GRAPH)))
					  (CONS (LASTMOUSEX W)
						(LASTMOUSEY W]
	      (COND
		((NEQ NODE LASTNODE)
		  (AND LASTNODE (FLIPNODE LASTNODE W))
		  [COND
		    (NODE (FLIPNODE NODE W)
			  (WINDOWPROP W (QUOTE TITLE)
				      (SPY.TITLE (fetch NODEID of NODE)
						 TOPCOUNT SPYDATA]
		  (SETQ LASTNODE NODE)))
	     repeatwhile (MOUSESTATE LEFT))
          (COND
	    ((NOT (LASTMOUSESTATE MIDDLE))
	      (AND NODE (FLIPNODE NODE W))
	      (RETURN)))
          [COND
	    [NODE (SELECTQ (PROG1 [MENU (CONSTANT (create MENU
							  ITEMS ←(QUOTE (SubTree Delete]
				  (FLIPNODE NODE W))
			   (NIL (RETURN))
			   (Delete                           (* remove this node?)
				   (SETQ TREES (SPY.DELETE (fetch NAME of (fetch NODEID of NODE))
							   (SPY.ORIGINAL TREES)))
				   (MAPC TREES (FUNCTION SPY.SUM))
				   (SETQ TREES (SPY.MERGE TREES SPYDATA)))
			   (SubTree (SETQ TREES (SPY.MERGE (SPY.ORIGINAL (LIST (fetch NODEID
										  of NODE)))
							   SPYDATA))
                                                             (* make sure that it creates a new window)
				    (SETQ WINDOW NIL))
			   (RETURN (printout PROMPTWINDOW T "SORRY, FEATURE NOT IMPLEMENTED YET"]
	    (T
	      (SELECTQ
		(PROG1
		  (OR [MENU (OR (fetch SPYMENU of SPYDATA)
				(replace SPYMENU of SPYDATA
				   with (create MENU
						ITEMS ←(CONS
						  (QUOTE SetThreshold)
						  (CONS (COND
							  ((fetch CUMULATIVE of SPYDATA)
							    (QUOTE Individual))
							  (T (QUOTE Cumulative)))
							(SELECTQ (fetch MERGETYPE of SPYDATA)
								 (ALL (QUOTE (MergeDefault MergeNone))
								      )
								 (T (QUOTE (MergeNone MergeAll)))
								 ((NIL NONE)
								   (QUOTE (MergeDefault MergeAll)))
								 (SHOULDNT]
		      (RETURN))
		  (SETQ SPYDATA (create SPYDATA using SPYDATA SPYMENU ← NIL)))
		(SetThreshold                                (* no need to remerge)
			      (replace THRESHOLD of SPYDATA with (RNUMBER "Threshold (percent)" NIL 
									  DEFAULTFONT DEFAULTFONT)))
		(MergeAll (replace MERGETYPE of SPYDATA with (QUOTE ALL))
			  (SETQ TREES (SPY.MERGE (SPY.ORIGINAL TREES)
						 SPYDATA)))
		(MergeNone (replace MERGETYPE of SPYDATA with (QUOTE NONE))
			   (SETQ TREES (SPY.MERGE (SPY.ORIGINAL TREES)
						  SPYDATA)))
		(MergeDefault (replace MERGETYPE of SPYDATA with T)
			      (SETQ TREES (SPY.MERGE (SPY.ORIGINAL TREES)
						     SPYDATA)))
		((Cumulative Individual)
		  [replace MERGETYPE of SPYDATA with (COND
						       ((change (fetch CUMULATIVE of SPYDATA)
								(NOT DATUM))
							 T)
						       (T (QUOTE ALL]
		  (SETQ TREES (SPY.MERGE (SPY.ORIGINAL TREES)
					 SPYDATA)))
		(SHOULDNT]
          (SPY.MAKE.TREE TREES SPYDATA WINDOW])

(SPY.END
  [LAMBDA NIL                                                (* lmm "25-Sep-84 16:36")
    (SETQ \PERIODIC.INTERRUPT)
    (SPY.DUMP.BUFFER)
    (COND
      (SPY.STREAM (CLOSEF SPY.STREAM])

(SPY.MAKEGRAPHNODES
  [LAMBDA (TREE THRESHOLD SPYDATA)                           (* lmm "28-Sep-84 10:18")
                                                             (* RETURNS NODE ID FOR TREE)
    [PROG ((LABEL (fetch NAME of TREE))
	   [COUNT (COND
		    ((fetch CUMULATIVE SPYDATA)
		      (fetch SUM of TREE))
		    (T (fetch COUNT of TREE]
	   (STATUS (fetch STATUS of TREE))
	   HEIGHT BORDER WIDTH NODEBITMAP TOOSMALL)
          [SETQ BORDER (CDDR (OR (ASSOC STATUS SPY.BORDERS)
				 (SHOULDNT]
          (SETQ HEIGHT (IQUOTIENT (ITIMES COUNT SPY.MAXLINES (FONTHEIGHT SPY.FONT))
				  TOPCOUNT))
          [COND
	    ([OR [SETQ TOOSMALL (ILESSP HEIGHT (IPLUS (FONTHEIGHT SPY.FONT)
						      (TIMES 2 (CAR BORDER]
		 (EQ STATUS (QUOTE ENDOFLINE))
		 (AND SPY.SMALLGHOSTS (FMEMB STATUS (QUOTE (GHOST RECURSIVEGHOST]
	      (SETQ HEIGHT (IPLUS (FONTHEIGHT SPY.FONT)
				  (TIMES 2 (CAR BORDER]
          [COND
	    ((SELECTQ SPY.SHOW.PERCENTAGES
		      (T T)
		      (NIL NIL)
		      [NOGHOSTS (NOT (FMEMB STATUS (QUOTE (GHOST RECURSIVEGHOST]
		      TOOSMALL)
	      (SETQ LABEL (CONCAT (IQUOTIENT (ITIMES COUNT 100)
					     TOPCOUNT)
				  " " LABEL]
          [SETQ WIDTH (PLUS (STRINGWIDTH LABEL SPY.FONT)
			    (TIMES 2 (CAR BORDER]
          (push SPY.NODES (create GRAPHNODE
				  NODEID ← TREE
				  NODELABEL ← LABEL
				  TONODES ←(for X in (fetch CALLEES of TREE)
					      when (OR (ZEROP THRESHOLD)
						       (IGEQ (SPY.MAX (LIST X)
								      (NOT (fetch CUMULATIVE SPYDATA))
								      )
							     THRESHOLD))
					      bind VAL do (push VAL (SPY.MAKEGRAPHNODES X THRESHOLD 
											SPYDATA))
					      finally (RETURN VAL))
				  NODEFONT ← SPY.FONT
				  NODEBORDER ← BORDER
				  NODEHEIGHT ←(IPLUS HEIGHT 2)
				  NODELABELSHADE ←(CADDR BORDER]
    TREE])

(SPY.MAX
  [LAMBDA (TREES COUNTP MAX)                                 (* lmm " 1-May-84 13:11")
    [for X in TREES do (SETQ MAX (SPY.MAX (fetch CALLEES of X)
					  COUNTP
					  (IMAX (OR MAX (IMAX))
						(if COUNTP
						    then (fetch COUNT of X)
						  else (fetch SUM of X]
    MAX])

(SPY.MERGE
  [LAMBDA (TREES SPYDATA)                                    (* lmm "27-Sep-84 17:16")
    [COND
      (SPY.HASH (CLRHASH SPY.HASH))
      (T (SETQ SPY.HASH (HASHARRAY 100]
    (for X in TREES do (SPY.SUM X))
    (for NEWNODE in TREES bind VAL Z do [for OLDNODE in VAL when (EQ (fetch NAME of OLDNODE)
								     (fetch NAME of NEWNODE))
					   do (RETURN (SPY.MERGETREE NEWNODE OLDNODE SPYDATA NIL
								     (fetch DEPTH SPYDATA)))
					   finally (AND (SETQ Z (SPY.MERGE1 NEWNODE SPYDATA NIL NIL
									    (fetch DEPTH SPYDATA)))
							(SETQ VAL (NCONC1 VAL Z]
       finally (RETURN VAL])

(SPY.MERGE1
  [LAMBDA (NEWORIGINAL SPYDATA PARENTS CALLER DEPTH)         (* lmm " 5-Oct-84 22:37")
                                                             (* return the "merged" tree for TREE, a copy of the 
							     original)
    (PROG ((MERGEP (SELECTQ (fetch MERGETYPE of SPYDATA)
			    ((NIL NONE)
			      NIL)
			    [T (NOT (OR (FMEMB (fetch NAME of NEWORIGINAL)
					       SPY.NOMERGEFNS)
					(FMEMB (fetch NAME of NEWORIGINAL)
					       OPENFNS]
			    (ALL T)
			    (SHOULDNT)))
	   OLDCOPY NEWCOPY)
          (COND
	    [(AND MERGEP (SETQ OLDCOPY (GETHASH (fetch NAME of NEWORIGINAL)
						SPY.HASH)))
                                                             (* mergeable, and we found one to merge into)
                                                             (* show this node only as a ghost)
	      (SPY.MERGETREE NEWORIGINAL OLDCOPY SPYDATA PARENTS DEPTH)
	      (COND
		((fetch NOGHOSTS of SPYDATA))
		[[AND CALLER (SETQ NEWCOPY (find X in (fetch CALLEES of CALLER)
					      suchthat (EQ (fetch NAME of X)
							   (fetch NAME of NEWORIGINAL]
		  (SELECTQ (fetch STATUS of NEWCOPY)
			   [GHOST (AND (FMEMB OLDCOPY PARENTS)
				       (replace STATUS of NEWCOPY with (QUOTE RECURSIVEGHOST]
			   ((RECURSIVEGHOST ENDOFLINE))
			   (HELP (QUOTE (SPY: never seen this case before]
		(T (SETQ NEWCOPY (create SPYRECORD using NEWORIGINAL CALLEES ← NIL STATUS ←(QUOTE
							   GHOST)
							 TREEFROM ← NEWORIGINAL))
		   (AND CALLER (push (fetch CALLEES of CALLER)
				     NEWCOPY))
		   (RETURN NEWCOPY]
	    (T (SETQ NEWCOPY (create SPYRECORD using NEWORIGINAL CALLEES ← NIL TREEFROM ← NEWORIGINAL)
		 )                                           (* create the copy)
	       (AND MERGEP (PUTHASH (fetch NAME of NEWORIGINAL)
				    NEWCOPY SPY.HASH))       (* remember it if it is mergable)
	       (AND CALLER (push (fetch CALLEES of CALLER)
				 NEWCOPY))
	       (SPY.MERGE.CALLEES NEWORIGINAL NEWCOPY SPYDATA PARENTS DEPTH)
	       (RETURN NEWCOPY])

(SPY.MERGETREE
  [LAMBDA (NEWORIGINAL OLDCOPY SPYDATA PARENTS DEPTH)        (* lmm "27-Sep-84 19:05")
                                                             (* insert call tree from NEWORIGINAL into node starting
							     with OLDCOPY)
                                                             (* this function is only called once we've decided to 
							     merge something after all)
    (PROG ((RECURSIVE (FMEMB OLDCOPY PARENTS)))
          [COND
	    ((NOT RECURSIVE)
	      (add (fetch SUM of OLDCOPY)
		   (fetch SUM of NEWORIGINAL]
          (add (fetch COUNT of OLDCOPY)
	       (fetch COUNT of NEWORIGINAL))
          [if RECURSIVE
	      then (SELECTQ (fetch STATUS of OLDCOPY)
			    ((NORMAL SELFRECURSIVE)
			      (replace TREEFROM of OLDCOPY with (LIST (fetch TREEFROM of OLDCOPY)))
                                                             (* must be a list)
			      (replace STATUS of OLDCOPY with (QUOTE RECURSIVE)))
			    (RECURSIVE)
			    (MERGED (replace STATUS of OLDCOPY with (QUOTE RECURSIVE)))
			    (SHOULDNT))
	    else                                             (* add to TREEFROM)
		 (replace TREEFROM of OLDCOPY with (CONS NEWORIGINAL
							 (SELECTQ (fetch STATUS of OLDCOPY)
								  ((NORMAL SELFRECURSIVE)
								    (replace STATUS of OLDCOPY
								       with (QUOTE MERGED))
								    (LIST (fetch TREEFROM
									     of OLDCOPY)))
								  ((MERGED RECURSIVE ENDOFLINE)
								    (fetch TREEFROM of OLDCOPY))
								  (SHOULDNT]
          (SPY.MERGE.CALLEES NEWORIGINAL OLDCOPY SPYDATA PARENTS DEPTH)
          (RETURN T])

(SPY.NEXT.TREE
  [LAMBDA (TREE FN)                                          (* ejs: "27-APR-84 11:37")
    (for X in (fetch CALLEES of TREE) do (COND
					   ((EQ (fetch NAME of X)
						FN)
					     (RETURN X)))
       finally (push (fetch CALLEES of TREE)
		     (SETQ X (create SPYRECORD
				     NAME ← FN
				     COUNT ← 0)))
	       (RETURN X])

(SPY.SUM
  [LAMBDA (TREE)                                             (* lmm "26-Sep-84 10:24")
    (replace SUM of TREE with (PLUS (fetch COUNT of TREE)
				    (PROG1 (for X in (fetch CALLEES of TREE) sum (SPY.SUM X))
					   (SORT (fetch CALLEES of TREE)
						 (FUNCTION (LAMBDA (X Y)
						     (IGREATERP (fetch SUM of X)
								(fetch SUM of Y])

(SPY.TITLE
  [LAMBDA (X TOPCOUNT SPYDATA)                               (* lmm "20-May-84 22:50")
    (CONCAT (QUOTIENT (TIMES 100 (fetch COUNT of X))
		      TOPCOUNT)
	    "%%, "
	    (QUOTIENT (TIMES 100 (fetch SUM of X))
		      TOPCOUNT)
	    "%% cumulative:"
	    (fetch NAME of X])

(SPY.MAKE.TREE
  [LAMBDA (TREES SPYDATA WINDOW)                             (* lmm "27-Sep-84 17:11")
    (PROG (GRAPH IDS W H THRSH TOPCOUNT)
          (OR (FONTP SPY.FONT)
	      (SETQ SPY.FONT (FONTCREATE SPY.FONT)))
          (SETQ W (STRINGWIDTH (QUOTE A)
			       SPY.FONT))
          (SETQ TOPCOUNT (for X in TREES sum (fetch SUM of X)))
          (SETQ THRSH (QUOTIENT (TIMES TOPCOUNT (fetch THRESHOLD of SPYDATA))
				100))
          (SETQ SPY.NODES)
          (SETQ SPY.TOPNODES (for X in TREES collect (SPY.MAKEGRAPHNODES X THRSH SPYDATA)))
          (SETQ TITLE (SPY.TITLE (CAR SPY.TOPNODES)
				 TOPCOUNT SPYDATA))
          (SETQ SPY.WINDOW (SHOWGRAPH (LAYOUTGRAPH (REVERSE SPY.NODES)
						   SPY.TOPNODES NIL SPY.FONT)
				      (COND
					((WINDOWP WINDOW)
					  (WINDOWPROP WINDOW (QUOTE TITLE)
						      TITLE)
					  WINDOW)
					(T TITLE))
				      NIL NIL NIL NIL T))
          (WINDOWPROP SPY.WINDOW (QUOTE ICON)
		      SPY.ICON)
          (WINDOWPROP SPY.WINDOW (QUOTE BUTTONEVENTFN)
		      (FUNCTION SPY.GRAPH.EDITOR))
          (WINDOWPROP SPY.WINDOW (QUOTE RIGHTBUTTONFN)
		      NIL)
          (WINDOWPROP SPY.WINDOW (QUOTE SPYDATA)
		      SPYDATA)
          (WINDOWPROP SPY.WINDOW (QUOTE TREES)
		      TREES)
          (WINDOWPROP SPY.WINDOW (QUOTE TOPCOUNT)
		      TOPCOUNT])

(SPY.DELETE
  [LAMBDA (NAMES TREES)                                      (* lmm " 2-May-84 15:32")
    (for X in TREES when (NOT (EQMEMB (fetch NAME of X)
				      NAMES))
       collect (create SPYRECORD using X CALLEES ←(SPY.DELETE NAMES (fetch CALLEES of X])

(SPY.DRAWBOX
  [LAMBDA (WIDTH HEIGHT BORDERWIDTH BITMAP TEXTURE)          (* Crystal: "27-APR-84 15:20")
    (BITBLT NIL NIL NIL BITMAP 0 0 BORDERWIDTH HEIGHT (QUOTE TEXTURE)
	    (QUOTE PAINT)
	    TEXTURE)
    (BITBLT NIL NIL NIL BITMAP 0 0 WIDTH BORDERWIDTH (QUOTE TEXTURE)
	    (QUOTE PAINT)
	    TEXTURE)
    (BITBLT NIL NIL NIL BITMAP 0 (DIFFERENCE HEIGHT BORDERWIDTH)
	    WIDTH BORDERWIDTH (QUOTE TEXTURE)
	    (QUOTE PAINT)
	    TEXTURE)
    (BITBLT NIL NIL NIL BITMAP (DIFFERENCE WIDTH BORDERWIDTH)
	    0 BORDERWIDTH HEIGHT (QUOTE TEXTURE)
	    (QUOTE PAINT)
	    TEXTURE])

(SPY.BUFFER.ENTRY
  [LAMBDA (N)                                                (* ejs: "27-APR-84 11:37")
    (AND (ILEQ N SPY.BUFFER.SIZE)
	 (\VAG2 0 (\GETBASE SPY.BUFFER N])

(SPY.BUTTON
  [LAMBDA NIL                                                (* lmm " 5-Oct-84 22:46")
    (MOVEW (ADDMENU (create MENU
			    ITEMS ←[QUOTE ((SPY (SPY.TOGGLE]
			    MENUFONT ← BIGFONT)
		    NIL NIL T])

(SPY.END.ENTRY
  [LAMBDA NIL                                                (* ejs: "27-APR-84 11:37")
    (SPY.ADD.ENTRY NIL])

(SPY.START
  [LAMBDA (FILE)                                             (* ejs: "27-APR-84 11:37")
    (SPY.INIT FILE)
    (SETQ \PERIODIC.INTERRUPT.FREQUENCY (QUOTIENT 74Q SPY.FREQUENCY))
    (SETQ \PERIODIC.INTERRUPT (QUOTE \SPY.INTERRUPT])

(SPY.INIT
  [LAMBDA (FILE)                                             (* ejs: "27-APR-84 11:37")
    [OR SPY.BUFFER (SETQ SPY.BUFFER (\ALLOCBLOCK (FOLDHI SPY.BUFFER.SIZE WORDSPERCELL]
    (SETQ SPY.BUFFER.THRESHOLD (QUOTIENT SPY.BUFFER.SIZE 2))
    (SETQ SPY.NEXT 0)
    (COND
      [FILE (SETQ SPY.STREAM (OR (STREAMP FILE)
				 (OPENSTREAM FILE (QUOTE OUTPUT)
					     (QUOTE NEW]
      (T (SETQ SPY.STREAM)))
    (SETQ SPY.TREE])

(\SPY.INTERRUPT
  [LAMBDA NIL                                                (* lmm " 1-May-84 09:50")
    (SETQ \PERIODIC.INTERRUPT)                               (* turn off sampling while gathering sample)
    [PROG [(FRAME (fetch (FX CLINK)
			 (\MYALINK]
          [COND
	    ((IGEQ SPY.NEXT SPY.BUFFER.THRESHOLD)
	      (COND
		(\INTERRUPTABLE (SPY.DUMP.BUFFER))
		(T                                           (* this sample might overflow;
							     just don't do it)
		   (RETURN]
          (SPY.START.ENTRY)
      SAMPLELOOP
          (SPY.ADD.ENTRY (fetch (FX FRAMENAME)
				FRAME))
          (COND
	    ([NOT (fetch (FX INVALIDP)
			 (SETQ FRAME (fetch (FX CLINK)
					    FRAME]
	      (GO SAMPLELOOP))
	    (T (SPY.END.ENTRY]
    (SETQ \PERIODIC.INTERRUPT (QUOTE \SPY.INTERRUPT])

(SPY.DUMP.BUFFER
  [LAMBDA NIL                                                (* lmm "27-Sep-84 15:34")
    [COND
      (SPY.STREAM (\BOUTS SPY.STREAM SPY.BUFFER 0 (TIMES 2 SPY.NEXT))
                                                             (* dump buffer into stream and reset buffer size)
		  )
      (T (bind (I ← 0)
	       NEXTI while (ILESSP I SPY.NEXT)
	    do (bind [J ←(SETQ NEXTI (for K from I while (SPY.BUFFER.ENTRY K) finally (RETURN K]
		     TREE
		     (NAME ← "NO SUCH NAME") first [SETQ TREE (SPY.FIND.TREE (SPY.BUFFER.ENTRY
									       (add J -1]
		  while (IGREATERP J I) do [COND
					     ([NEQ NAME (SETQ NAME (SPY.BUFFER.ENTRY (add J -1]
					       (SETQ TREE (SPY.NEXT.TREE TREE NAME)))
					     (T (replace STATUS of TREE with (QUOTE SELFRECURSIVE]
		  finally (add (fetch COUNT of TREE)
			       1))
	       (SETQ I (ADD1 NEXTI]
    (SETQ SPY.NEXT 0])

(SPY.START.ENTRY
  [LAMBDA NIL                                                (* ejs: "27-APR-84 11:37")
                                                             (* do nothing at the start of the entry, do this at the 
							     end)
    NIL])

(SPY.ADD.ENTRY
  [LAMBDA (NAME)                                             (* ejs: "27-APR-84 11:37")
    (OR (LITATOM NAME)
	(SETQ NAME (QUOTE *FORM*)))
    (\PUTBASE SPY.BUFFER SPY.NEXT (\LOLOC NAME))
    (COND
      ((IGEQ (add SPY.NEXT 1)
	     SPY.BUFFER.SIZE)
	(SPY.OVERFLOW])

(SPY.ORIGINAL
  [LAMBDA (TREES)                                            (* lmm "27-Sep-84 19:01")
    (for X in TREES join (SELECTQ (fetch STATUS of X)
				  [(RECURSIVE MERGED ENDOFLINE)
				    (APPEND (OR (LISTP (fetch TREEFROM of X))
						(SHOULDNT]
				  ((NORMAL GHOST RECURSIVEGHOST SELFRECURSIVE)
				    (LIST (OR (fetch TREEFROM of X)
					      X)))
				  (SHOULDNT])

(SPY.OVERFLOW
  [LAMBDA NIL                                                (* ejs: "27-APR-84 11:37")
    (add SPY.NEXT -1)
    (SETQ SPY.OVERFLOWED T])

(SPY.MERGE.CALLEES
  [LAMBDA (NEWORIGINAL OLDCOPY SPYDATA PARENTS DEPTH)        (* lmm "27-Sep-84 18:59")
                                                             (* insert copies of the CALLEEs of NEWORIGINAL into 
							     OLDTREE's CALLEEs)
    (for ORIGCALLEE in (fetch CALLEES of NEWORIGINAL)
       do (for COPYCALLEE in (fetch CALLEES of OLDCOPY) when (EQ (fetch NAME of COPYCALLEE)
								 (fetch NAME of ORIGCALLEE))
	     do                                              (* found a "callee" that can merge this one with)
		[RETURN (if (EQ (fetch STATUS of COPYCALLEE)
				(QUOTE ENDOFLINE))
			    then (push (fetch TREEFROM of COPYCALLEE)
				       ORIGCALLEE)
			  else (SPY.MERGETREE ORIGCALLEE
					      (SELECTQ (fetch STATUS of COPYCALLEE)
						       ((NORMAL RECURSIVE SELFRECURSIVE MERGED)
							 COPYCALLEE)
						       ((GHOST RECURSIVEGHOST)
							 (OR (GETHASH (fetch NAME of ORIGCALLEE)
								      SPY.HASH)
							     (SHOULDNT)))
						       (SHOULDNT))
					      SPYDATA
					      (CONS OLDCOPY PARENTS)
					      (AND DEPTH (SUB1 DEPTH]
	     finally                                         (* no old node of same name found)
		     (if (AND DEPTH (ILEQ DEPTH 0))
			 then (push (fetch CALLEES of OLDCOPY)
				    (create SPYRECORD using ORIGCALLEE CALLEES ← NIL STATUS ←(QUOTE
							      ENDOFLINE)
							    TREEFROM ←(LIST NEWORIGINAL)))
		       else (SPY.MERGE1 ORIGCALLEE SPYDATA (CONS OLDCOPY PARENTS)
					OLDCOPY
					(AND DEPTH (SUB1 DEPTH])
)
(DECLARE: DOEVAL@COMPILE DONTCOPY

(GLOBALVARS SPY.OVERFLOWED \PERIODIC.INTERRUPT SPY.STREAM SPY.TREE SPY.BUFFER.SIZE SPY.NEXT 
	    SPY.BUFFER.THRESHOLD SPY.BUFFER SPY.FREQUENCY SPY.SHOW.THRESHOLD SPY.MAXLINES SPY.FONT)
)
(DECLARE: EVAL@COMPILE 

(PUTPROPS WITH-SPY MACRO [(FORM)
			  (PROGN (SPY.START)
				 (PROG1 FORM (SPY.END])

(PUTPROPS WITH.SPY MACRO [(FORM)
			  (PROGN (SPY.START)
				 (PROG1 FORM (SPY.END])
)
(DECLARE: DONTCOPY 
[DECLARE: EVAL@COMPILE 

(DATATYPE SPYRECORD (NAME COUNT SUM CALLEES STATUS TREEFROM)
		    STATUS ←(QUOTE NORMAL))

(PROPRECORD SPYDATA (CUMULATIVE MERGETYPE THRESHOLD SPYMENU DEPTH NOGHOSTS)
		    CUMULATIVE ← T)
]
(/DECLAREDATATYPE (QUOTE SPYRECORD)
		  (QUOTE (POINTER POINTER POINTER POINTER POINTER POINTER)))
)
(/DECLAREDATATYPE (QUOTE SPYRECORD)
		  (QUOTE (POINTER POINTER POINTER POINTER POINTER POINTER)))
(FILESLOAD GRAPHER READNUMBER)
(PUTPROPS SPY COPYRIGHT ("Xerox Corporation" 1984))
(DECLARE: DONTCOPY
  (FILEMAP (NIL (2880 25644 (SPY.FIND.TREE 2890 . 3175) (SPY.TOGGLE 3177 . 3592) (SPY.TREE 3594 . 4103) 
(SPY.LEGEND 4105 . 4665) (SPY.GRAPH.EDITOR 4667 . 8293) (SPY.END 8295 . 8516) (SPY.MAKEGRAPHNODES 8518
 . 10635) (SPY.MAX 10637 . 10989) (SPY.MERGE 10991 . 11743) (SPY.MERGE1 11745 . 14044) (SPY.MERGETREE 
14046 . 15890) (SPY.NEXT.TREE 15892 . 16300) (SPY.SUM 16302 . 16751) (SPY.TITLE 16753 . 17073) (
SPY.MAKE.TREE 17075 . 18594) (SPY.DELETE 18596 . 18910) (SPY.DRAWBOX 18912 . 19504) (SPY.BUFFER.ENTRY 
19506 . 19689) (SPY.BUTTON 19691 . 19935) (SPY.END.ENTRY 19937 . 20076) (SPY.START 20078 . 20332) (
SPY.INIT 20334 . 20776) (\SPY.INTERRUPT 20778 . 21624) (SPY.DUMP.BUFFER 21626 . 22691) (
SPY.START.ENTRY 22693 . 22958) (SPY.ADD.ENTRY 22960 . 23259) (SPY.ORIGINAL 23261 . 23719) (
SPY.OVERFLOW 23721 . 23885) (SPY.MERGE.CALLEES 23887 . 25642)))))
STOP