############################################################################
#
#	File:     memsum.icn
#
#	Subject:  Program to tabulate memory allocation
#
#	Author:   Ralph E. Griswold
#
#	Date:     August 17, 1994
#
############################################################################
#
#   This file is in the public domain.
#
############################################################################
#
#  This tool tabulates storage allocation.  It is called as
#
#	memsum prog
#
#  where prog is a program compiled under MT Icon whose events are to
#  be tabulated.
#	
#  The options supported are:
#
#	-o s	write output to file s; default &output.
#
#	-t	record time spent in monitoring.
#
############################################################################
#
#  Requires:  MT Icon and event monitoring.
#
############################################################################
#
#  Links:  evinit, evnames, numbers, options
#
############################################################################
#
#  Includes:  evdefs.icn
#
############################################################################

link evinit
link evnames
link numbers
link options

$include "evdefs.icn"

global highlights, alloccnt, alloctot, collections, output

procedure main(args)
   local opts, itime, mask

   opts := options(args, "to:")
   output := open(\opts["o"], "w") | &output
   if \opts["t"] then itime := &time

   EvInit(args) | stop("*** cannot load program")	# initialize interface

   alloccnt := table(0)			# count of allocations
   alloctot := table(0)			# total allocation
   collections := table(0)		# garbage collection counts

   #  Be sure all allocation types are listed even if there is no allocation
   #  for them.

   every alloccnt[!AllocMask] := 0
   every alloctot[!AllocMask] := 0

   mask := AllocMask ++ E_Collect

   while EvGet(mask) do 
      if &eventcode === E_Collect then collections[&eventvalue] +:= 1
      else {
         alloccnt[&eventcode] +:= 1
         alloctot[&eventcode] +:= &eventvalue
         }

   report()

   write(output, "\nelapsed time: ", &time - \itime, "ms")

end

#  Display a table of allocation data
#
procedure report()
   local i, cnttotal, tottotal, cnt, tot, totalcoll

   static col1, col2, gutter		# column widths

   initial {
      col1 := 20			# name field
      col2 := 10			# number field
      gutter := "  "
      }

   write(output, "\n",			# write column headings
      left("type",col1), right("number",col2), gutter,
      right("bytes",col2), gutter, right("average",col2), gutter,
      right("% bytes",col2), "\n"
      )

   alloccnt := sort(alloccnt, 3)				# get the data
   alloctot := sort(alloctot, 3)

   cnttotal := 0
   tottotal := 0
 
   every i := 2 to *alloccnt by 2 do {
      cnttotal +:= alloccnt[i]
      tottotal +:= alloctot[i]
      }

   while write(output,					# write the data
      left(name(get(alloccnt)), col1),
      right(cnt := get(alloccnt), col2), gutter,
      get(alloctot) & right(tot := get(alloctot), col2), gutter,
      fix(tot, cnt, col2, 2) | right("0.00", col2), gutter,
      fix(100.0 * tot, tottotal, col2, 2) | right("0.00", col2)
      )

   write(output, "\n",					# write totals
      left("total:",col1), right(cnttotal,col2), gutter, right(tottotal,col2),
      gutter, fix(tottotal,cnttotal,col2) | repl(" ",col2)
      )

   totalcoll := 0					# garbage collections
   every totalcoll +:= !collections
   write(output,"\n",left("collections:",col1),right(totalcoll,col2))
   if totalcoll > 0 then {
      write(output,left("  static region:",col1),right(collections[1],col2))
      write(output,left("  string region:",col1),right(collections[2],col2))
      write(output,left("  block region:",col1),right(collections[3],col2))
      write(output,left("  no region:",col1),right(collections[0],col2))
      }

   return
end

#  Produce event name
#
procedure name(code)
   local result

   result := evnames(code)

   result ?:= tab(find(" allocation"))

   result ?:= {
      tab(find("trapped variable")) || "tv"
      }

   return result

end
