############################################################################ # # Name: memsum.icn # # Title: Summarize Icon memory management # # Author: Ralph E. Griswold # # Date: March 8, 1990 # ############################################################################ # # This program is a filter for Icon allocation history files (see IPD113). # It tabulates the number of allocations by type and the total amount of # storage (in bytes) by type. # # It takes an Icon allocation history file from standard input and writes to # standard output. # # The command-line options are: # # -t produce tab-separated output for use in spreadsheets (the # default is a formatted report # -d produce debugging output # # Some assumptions are made about where newlines occur -- specifically # that verification commands are on single lines and that refresh and # garbage collection data are on multiple lines. # ############################################################################ # # Links: numbers, options # ############################################################################ global cmds, highlights, lastlen, alloccnt, alloctot, collections global mmunits, diagnose, namemap link numbers, options procedure main(args) local line, region, s, skip, opts opts := options(args,"dt") diagnose := if \opts["d"] then write else 1 display := if \opts["t"] then spread else report cmds := 'cefihLlRrSsTtux"XAF' # command characters highlights := '%$Y' # highlight commands mmunits := 4 # (for most systems) namemap := table("*** undefined ***") namemap["b"] := "large integer" namemap["c"] := "cset" namemap["e"] := "table element tv" namemap["f"] := "file" namemap["h"] := "hash block" namemap["i"] := "large integer" namemap["L"] := "list header" namemap["l"] := "list element" namemap["R"] := "record" namemap["r"] := "real number" namemap["S"] := "set header" namemap["s"] := "set element" namemap["T"] := "table header" namemap["t"] := "table element" namemap["u"] := "substring tv" namemap["x"] := "refresh block" namemap["\""] := "string" namemap["X"] := "co-expression" namemap["A"] := "alien block" namemap["F"] := "free space" lastlen := table() # last size alloccnt := table(0) # count of allocations alloctot := table(0) # total allocation collections := list(4,0) # garbage collection counts every alloccnt[!cmds] := 0 every alloctot[!cmds] := 0 cmds ++:= highlights while line := read() do { # input from MemMon history file line ? { # note: coded for extensions if region := tab(upto('{')) then { # skip refresh sequence collections[region] +:= 1 while line := read() | stop("**** premature eof") do line ? if upto('#!') then break next } case move(1) of { "=": next # skip verification command "#": next # skip comment ";": next # skip pause command "!" | ">": next # resynchronize (edited file) default: { # data to process move(-1) # back off from move(1) above if s := tab(upto('<')) then { mmunits := integer(s) # covers old case with no mmunits while line := read() | stop("**** premature eof") do line ? if upto('#>') then break next } else { repeat { # process allocation tab(many(' ')) # skip blanks (old files) if pos(0) then break next skip := process(tab(upto(cmds) + 1)) | stop("*** unexpected data: ",line) move(skip) } } } } } } display() end # Display a table of allocation data # procedure report() local cnt, cnttotal, i, tot, totalcoll, tottotal static col1, col2, gutter # column widths initial { col1 := 16 # name field col2 := 10 # number field gutter := repl(" ",6) } write(, # write column headings "\n", 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( # write the data left(namemap[get(alloccnt)],col1), # name right(cnt := get(alloccnt),col2), # number of allocations gutter, get(alloctot) & right(tot := get(alloctot),col2), # space allocated gutter, fix(tot,cnt,col2) | repl(" ",col2), gutter, fix(100.0 * tot,tottotal,col2) | repl(" ",col2) ) write( # write totals "\n", 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("\n",left("collections:",col1),right(totalcoll,col2)) if totalcoll > 0 then { write(left(" static region:",col1),right(collections[1],col2)) write(left(" string region:",col1),right(collections[2],col2)) write(left(" block region:",col1),right(collections[3],col2)) write(left(" no region:",col1),right(collections[4],col2)) } return end # Produce tab-separated output for a spreadsheet. # procedure spread() alloccnt := sort(alloccnt,3) # get the data alloctot := sort(alloctot,3) write("*\nname number bytes") while write( # write the data namemap[get(alloccnt)], "\t", get(alloccnt), "\t", get(alloctot) & get(alloctot), ) return end # Process datm # procedure process(s) local cmd, len s ? { tab(upto('+') + 1) # skip address len := tab(many(&digits)) | &null cmd := move(1) if cmd == !highlights then return 2 else { # if given len is nonstring, scale if cmd ~== "\"" then \len *:= mmunits alloccnt[cmd] +:= 1 (/len := lastlen[cmd]) | (lastlen[cmd] := len) diagnose(&errout,"cmd=",cmd,", len=",len) alloctot[cmd] +:= len return 0 } } end