home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.cs.arizona.edu
/
ftp.cs.arizona.edu.tar
/
ftp.cs.arizona.edu
/
icon
/
historic
/
v941.tgz
/
icon.v941src.tar
/
icon.v941src
/
ipl
/
progs
/
iprofile.icn
< prev
next >
Wrap
Text File
|
2001-05-02
|
10KB
|
382 lines
############################################################################
#
# File: iprofile.icn
#
# Subject: Program to profile Icon procedure usage
#
# Author: Richard L. Goerwitz
#
# Date: May 2, 2001
#
############################################################################
#
# This file is in the public domain.
#
############################################################################
#
# Version: 1.5
#
############################################################################
#
# This very simple profiler takes a single argument - an Icon program
# compiled with the -t option. Displays stats on which procedures
# were called the most often, and from what lines in what files they
# were called. Use this program to figure out what procedures are
# getting worked the hardest and why. Counts only invocations and
# resumptions; not suspensions, returns, failures.
#
# If you are running a program that reads from a file, be sure to
# protect the redirection symbol from the shell (i.e. "profile
# 'myprog < input'" instead of "profile myprog < input"). If a given
# program normally reads &input, please redirect stdin to read from
# another tty than the one you are running profile from. If you
# forget to do this, the results might be very interesting.... Also,
# don't redirect stderr, as this contains the trace that profile will
# be reading and using to obtain run-time statistics. Profile
# automatically redirects stdout to /dev/null.
#
# Currently runs only under UNIX, but with some tweaking could be
# made to run elsewhere as well.
#
# The display should be pretty much self-explanatory. Filenames and
# procedures get truncated at nineteen characters (if the display
# gets too wide, it can become hard to read). A star is prepended to
# procedures whose statistics have changed since the last screen
# update.
#
############################################################################
#
# Requires: co-expressions, keyboard functions, pipes, UNIX
#
############################################################################
#
# Links: itlib, iscreen
#
############################################################################
link itlib
link iscreen
global CM, LI, CO, CE
procedure main(a)
local whitespace, firstidchars, idchars, usage, in_data,
cmd, line, filename, linenum, procname, t, threshhold
whitespace := '\t '
firstidchars := &letters ++ '_'
idchars := &digits ++ &letters ++ '_'
usage := "usage: profile filename _
(filename = Icon program compiled with -t option)"
#
# If called with a program name as the first argument, open it,
# and pipe the trace output back to this program. Assume the
# user knew enough to compile it with the "-t" option.
#
if *a > 0 then {
if find("UNIX", &features) then {
cmd := ""; every cmd ||:= !a || " "
if find("2>", cmd) then
stop("profile: Please don't redirect stderr!")
in_data := open(cmd || " 2>&1 1> /dev/null", "pr") |
stop("profile: Can't find or execute ", cmd, ".")
} else stop("profile: Your OS is not (yet) supported.")
}
else stop(usage)
# clear screen, set up global variables; initialize table
setup_screen()
t := table()
threshhold := 0
while line := read(in_data) do {
threshhold +:= 1
#
# Break each line down into a file name, line number, and
# procedure name.
#
line ? {
tab(many(whitespace))
match(":") & next
{
filename := trim(tab(find(":"))) &
tab(many(whitespace ++ ':')) &
linenum := tab(many(&digits)) &
tab(many(whitespace ++ '|')) &
procname := tab(any(firstidchars)) || tab(many(idchars))
} | next
tab(many(whitespace))
# Count only invocations and resumptions.
match("suspended"|"failed"|"returned") & next
}
#
# Enter statistics into table.
#
/t[procname] := table()
/t[procname][filename] := table(0)
t[procname][filename][linenum] +:= 1
#
# Display stats interactively.
#
if threshhold > 90 then {
threshhold := 0
display_stats(t)
}
}
display_stats(t)
# Write a nice exit message.
goodbye()
end
#
# display_stats: display the information in t interactively
#
procedure display_stats(t)
local l, input, c
static top, len, firstline
# sets global variables CM, LI, CO, and CE
initial {
top := 1
# The first line we can write data to on the screen.
firstline := 3
len := LI - 4 - firstline
}
#
# Structure the information in t into a list. Note that to obtain
# the number of procedures, one must divide l in half.
#
l := sort_table(t)
#
# Check for user input.
#
while kbhit() do {
iputs(igoto(CM, 1, LI-1))
writes("Press j/k/^/$/p/q: ")
iputs(CE)
writes(input := map(getch()))
case input of {
# Increase or decrease top by 4; don't go beyond 0 or
# *l; no even numbers for top (the 4 also must be even).
"j" : top := (*l > (top+2) | *l-1)
"\r" : top := (*l > (top+2) | *l-1)
"\n" : top := (*l > (top+2) | *l-1)
"k" : top := (0 < (top-2) | 1)
"\x02" : top := (0 < (top-4) | 1)
"\x15": top := (0 < (top-4) | 1)
" " : top := (*l > (top+4) | *l-1)
"\x06" : top := (*l > (top+4) | *l-1)
"\x04" : top := (*l > (top+4) | *l-1)
"^" : top := 1
"$" : top := *l-1
"p" : {
iputs(igoto(CM, 1, LI-1))
writes("Press any key to continue: "); iputs(CE)
until kbhit() & getch() do delay(500)
}
"q" : goodbye()
"\x0C" : setup_screen()
"\x012": setup_screen()
default: {
if any(&digits, input) then {
while c := getche() do {
if c == ("\n"|"\r") then {
if not (input <:= 1) then
input +:= input % 2 - 1
top := (0 < input | 1)
top := (*l > input | *l-1)
break
} else {
if any(&digits, c)
then input ||:= c & next
else break
}
}
}
}
}
iputs(igoto(CM, 1, LI-1))
writes("Press j/k/^/$/p/q: ")
iputs(CE)
}
#
# Display the information contained in table t via list l2.
#
write_list(l, top, len, firstline)
return
end
#
# sort_table: structure the info in t into a list
#
# What a mess. T is a table, keys = procedure names, values =
# another table. These other tables are tables where keys = file
# names and values = yet another table. These yet other tables
# are structured as follows: keys = line numbers, values = number
# of invocations. The idea is to collapse all of these tables
# into sorted lists, and at the same time count up the total
# number of invocations for a given procedure name (going through
# all its invocations at every line in every file). A new table
# is then created where keys = procedure names and values = total
# number of invocations. Yet another sort is done on the basis of
# total number of invocations.
#
procedure sort_table(t)
local t2, total_t, k, total, i, l, l2
static old_totals
initial old_totals := table()
t2 := copy(t)
total_t := table()
every k := key(t2) do {
t2[k] := sort(t2[k], 3)
total := 0
every i := 2 to *t2[k] by 2 do {
every total +:= !t2[k][i]
t2[k][i] := sort(t2[k][i], 3)
}
insert(total_t, k, total)
}
l2 := list(); l := sort(total_t, 4)
every i := 1 to *l-1 by 2 do {
push(l2, t2[l[i]])
if not (total_t[l[i]] <= \old_totals[l[i]]) then
l[i] := "*" || l[i]
push(l2, l[i])
}
old_totals := total_t
return l2
end
#
# write_list: write statistics in the upper part of the screen
#
procedure write_list(l, top, len, firstline)
local i, j, k, z, w
static last_i
#global CM, CE
initial last_i := 2
# Arg1, l, is a sorted table of sorted tables of sorted tables!
# Firstline is the first line on the screen we can write data to.
#
i := firstline
iputs(igoto(CM, 1, i)); iputs(CE)
every j := top to *l by 2 do {
writes(left(l[j], 19, " "))
every k := 1 to *l[j+1]-1 by 2 do {
iputs(igoto(CM, 20, i))
writes(left(l[j+1][k], 19, " "))
every z := 1 to *l[j+1][k+1]-1 by 2 do {
iputs(igoto(CM, 40, i))
writes(left(l[j+1][k+1][z], 7, " "))
iputs(igoto(CM, 48, i))
writes(l[j+1][k+1][z+1])
if (i +:= 1) > (firstline + len) then
break break break
else iputs(igoto(CM, 1, i)) & iputs(CE)
}
}
}
# Clear the remaining lines down to the status line.
#
every w := i to last_i do {
iputs(igoto(CM, 1, w))
iputs(CE)
}
last_i := i
return
end
#
# setup_screen: clear screen, set up status line.
#
procedure setup_screen()
# global CM, LI, CO, CE
initial {
CM := getval("cm") |
stop("setup_screen: No cm capability!")
LI := getval("li")
CO := getval("co")
CE := getval("ce")
# UNIX-specific command to disable character echo.
system("stty -echo")
}
clear()
iputs(igoto(CM, 1, 1))
emphasize()
writes(left(left("procedure name", 19, " ") ||
left("source file", 20, " ") ||
left("line", 8, " ") ||
"number of invocations/resumptions",
CO, " "))
normal()
status_line("- \"Profile,\" by Richard Goerwitz -")
iputs(igoto(CM, 1, LI-1))
writes("J or CR=down; k=up; ^=begin; $=end; p=pause; q=quit: ")
iputs(CE)
return
end
#
# goodbye: exit, say something nice
#
procedure goodbye()
# UNIX-specific command.
system("stty echo")
status_line("- \"Profile,\" by Richard Goerwitz -")
every boldface() | emphasize() | normal() |
boldface() | emphasize() | normal()
do {
delay(50)
iputs(igoto(CM, 1, LI-1))
writes("Hope you enjoyed using profile! ")
normal(); iputs(CE)
}
exit()
end
#
# stop_profile: graceful exit after error
#
procedure stop_profile(s)
# UNIX-specific command.
system("stty echo")
status_line("- \"Profile,\" by Richard Goerwitz -")
iputs(igoto(CM, 1, LI-1))
writes(s); iputs(CE)
iputs(igoto(CM, 1, LI))
stop()
end