CVS: sml-dist/src/tools/TraceDebugProf back-trace.cm,NONE,1.1 back-trace.sml,NONE,1.1 coverage.cm,NONE,1.1 coverage.sml,NONE,1.1 install-back-trace.sml,NONE,1.1 install-coverage.sml,NONE,1.1 plugins.cm,NONE,1.1

Matthias Blume <[email protected]>
Newsgroups gmane.comp.lang.sml.smlnj.commits
Message-ID <[email protected]>
Update of /cvsroot/smlnj/sml-dist/src/tools/TraceDebugProf
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv28211/src/tools/TraceDebugProf

Added Files:
	back-trace.cm back-trace.sml coverage.cm coverage.sml 
	install-back-trace.sml install-coverage.sml plugins.cm 
Log Message:
moved TraceDebugProf where it belongs

--- NEW FILE: back-trace.cm ---
(* back-trace.cm
 *
 *   Library that (when loaded via CM.make) causes the test back-trace
 *   plugin to be installed into its core hook.
 *
 * Copyright (c) 2004 by The Fellowship of SML/NJ
 *
 * Author: Matthias Blume ([email protected])
 *)
Library
	structure BackTrace
	structure InstallBackTrace
is
	$smlnj-tdp/plugins.cm
	install-back-trace.sml

--- NEW FILE: back-trace.sml ---
(* back-trace.sml
 *
 *   A plug-in module for back-tracing.  This module hooks itself into
 *   the core environment so that tdp-instrumented code will invoke the
 *   provided functions "enter", "push", "save", and "report".
 *
 *   This module keeps track of the dynamic call-chain of instrumented modules.
 *   Non-tail calls are maintained in a stack-like fashion, and in addition
 *   to this the module will also track tail-calls so that a sequence of
 *   GOTO-like jumps from loop-cluster to loop-cluster can be shown.
 *
 *   This strategy, while certainly costly, has no more than constant-factor
 *   overhead in space and time and will keep tail-recursive code
 *   tail-recursive.
 *
 * Copyright (c) 2004 by The Fellowship of SML/NJ
 *
 * Author: Matthias Blume ([email protected])
 *)
structure BackTrace : sig
    val trigger : unit -> 'a
    val monitor : (unit -> 'a) -> 'a
    val install : unit -> unit
end = struct

    structure M = IntRedBlackMap

    (* Home-cooked set representation:
     *  This relies on two things:
     *   - we don't need a lookup operation
     *   - we only join sets that are known to be disjoint *)
    datatype set =
	EMPTY
      | SINGLETON of int
      | UNION of set * set

    fun fold f i EMPTY = i
      | fold f i (SINGLETON x) = f (x, i)
      | fold f i (UNION (x, y)) = fold f (fold f i y) x

    datatype descr =
	STEP of int
      | LOOP of set

    type stage = { num: int, from: int, descr: descr }

    type frame = { depth: int, map: int M.map, stages: stage list }

    type history = frame * frame list

    datatype state =
	NORMAL of history
      | PENDING of int * history

    val cur : state ref =
	ref (NORMAL ({ depth = 0, map = M.empty, stages = [] }, []))

    val names = ref (M.empty: string M.map)

    fun register (module, _: int, id, s) =
	names := M.insert (!names, module + id, s)

    fun enter (module, fct) = let
	val i = module + fct
	val (from, front, back) =
	    case !cur of
		PENDING (from, (front, back)) => (from, front, back)
	      | NORMAL (front, back) => (~1, front, back)
	val { depth, map, stages } = front
    in
	case M.find (map, i) of
	    SOME num => let
		fun toSet (STEP i) = SINGLETON i
		  | toSet (LOOP s) = s
		fun join (set, d) = UNION (set, toSet d)
		fun finish (stages, from, c, EMPTY) =
		    let val stage = { num = num, from = from,
				      descr = LOOP (toSet c) }
			val front' = { depth = depth,
				       map = map,
				       stages = stage :: stages }
		    in
			cur := NORMAL (front', back)
		    end
		  | finish (stages, from, c, set) =
		    let	val stage = { num = num, from = from,
				      descr = LOOP (join (set, c)) }
			fun ins (i, m) = M.insert (m, i, num)
			val front' = { depth = depth,
				       map = fold ins map set,
				       stages = stage :: stages }
		    in
			cur := NORMAL (front', back)
		    end
		fun loop ([], set) = () (* cannot happen! *)
		  | loop ({ num = n', from, descr = d' } :: t, set) =
		    if num = n' then finish (t, from, d', set)
		    else loop (t, join (set, d'))
	    in
		loop (stages, EMPTY)
	    end
	  | NONE => let
		val num = case stages of
			      [] => 0
			    | s0 :: _ => #num s0 + 1
		val stage = { num = num, from = from, descr = STEP i}
		val front' = { depth = depth,
			       map = M.insert (map, i, num),
			       stages = stage :: stages }
	    in
		cur := NORMAL (front' , back)
	    end
    end

    fun push (module, loc) = let
	val id = module + loc
	val (NORMAL old | PENDING (_, old)) = !cur
	val (front, _) = old
	val front' = { depth = #depth front + 1, map = M.empty, stages = [] }
    in
	cur := PENDING (id, (front', op :: old));
	fn () => cur := NORMAL old
    end

    fun nopush (module, loc) = let
	val id = module + loc
	val (NORMAL old | PENDING (_, old)) = !cur
    in
	cur := PENDING (id, old)
    end

    fun save () = let
	val old = !cur
    in
	fn () => cur := old
    end

    fun report () = let
	val (NORMAL top | PENDING (_, top)) = !cur
	val (front, back) = top
	fun do_report () = let
	    val (NORMAL bot | PENDING (_, bot)) = !cur
	    val (front', _) = bot
	    val bot_depth = #depth front'
	    fun isBot (f: frame) = #depth f = bot_depth
	    fun name (w, pad, from, i) = let
		fun find x = getOpt (M.find (!names, x), "???")
		val n = find i
		val tail = case from of
			       NONE => ["\n"]
			     | SOME j => ["\n          (from: ", find j, ")\n"]
	    in
		concat (w :: pad :: " " :: n :: tail)
	    end
	    fun stage (w, { num, from, descr = STEP i }, a) =
		name (w, "  ", SOME from, i) :: a
	      | stage (w, { num, from, descr = LOOP s }, a) = let
		    fun loop ([], a) = a
		      | loop ([i], a) = name (w, "-\\", SOME from, i) :: a
		      | loop (h :: t, a) =
			loop (t, name ("    ", " |", NONE, h) :: a)
		    fun start ([], a) = a
		      | start ([i], a) = name (w, "-(", SOME from, i) :: a
		      | start (h :: t, a) =
			loop (t, name ("    ", " /", NONE, h) :: a)
		in
		    start (fold (op ::) [] s, a)
		end
	    fun jumps ([], a) = a
	      | jumps ([n], a) = stage ("CALL", n, a)
	      | jumps (h :: t, a) = jumps (t, stage ("GOTO", h, a))
	    fun calls (h, [], a) = jumps (#stages h, a)
	      | calls (h, h' :: t, a) = let
		    val a = jumps (#stages h, a)
		in
		    if isBot h then a else calls (h', t, a)
		end
	in
	    rev (calls (front, back, []))
	end
    in
	do_report
    end

    exception BTraceTriggered of unit -> string list

    fun monitor work =
	let val restore = save ()
	    fun last (x, []) = x
	      | last (_, x :: xs) = last (x, xs)
	    fun emsg e =
		case SMLofNJ.exnHistory e of
		    [] => General.exnMessage e
		  | (h :: t) =>
		      concat [last (h, t), ": ", General.exnMessage e]
	    fun hdl (e, []) =
		  (Control.Print.say (emsg e ^ "\n\n");
		   raise e)
	      | hdl (e, hist) =
		  (Control.Print.say
		       (concat ("\n*** BACK-TRACE ***\n" :: hist));
		   Control.Print.say
		       (concat ["\n", emsg e, "\n\n"]);
		   raise e)
	in
	    work ()
	    handle e as BTraceTriggered do_report =>
		     (restore ();
		      hdl (e, do_report ()))
		 | e =>
		   let val do_report = report ()
		   in
		       restore ();
		       hdl (e, do_report ())
		   end
	end

    val name = "btrace"

    fun install () =
	let val plugin = { name = name, save = save,
			   push = push, nopush = nopush,
			   enter = enter, register = register }
	    val monitor = { name = name, monitor = monitor }
	    fun addto r x = r := x :: !r
	in
	    addto SMLofNJ.Internals.TDP.active_plugins plugin;
	    addto SMLofNJ.Internals.TDP.active_monitors monitor
	end

    fun trigger () = raise BTraceTriggered (report ())
end

--- NEW FILE: coverage.cm ---
(* coverage.cm
 *
 *   Library that (when loaded via CM.make) causes the test coverage
 *   plugin to be installed into its core hook.
 *
 * Copyright (c) 2004 by The Fellowship of SML/NJ
 *
 * Author: Matthias Blume ([email protected])
 *)
Library
	structure Coverage
	structure InstallCoverage
is
	$smlnj-tdp/plugins.cm
	install-coverage.sml

--- NEW FILE: coverage.sml ---
(* coverage.sml
 *
 *   Using the generic trace/debug/profile framework for test coverage.
 *
 * Copyright (c) 2004 by The Fellowship of SML/NJ
 *
 * Author: Matthias Blume ([email protected])
 *)
structure Coverage : sig

    type kind

    val functions:      kind
    val tail_calls:     kind
    val non_tail_calls: kind

    val not_covered : kind list -> unit
    val hot_spots : kind list -> int -> unit

    val install : unit -> unit
end = struct

    structure M = IntRedBlackMap
    structure F = FormatComb

    structure TDP = SMLofNJ.Internals.TDP

    type kind = int
    val functions = TDP.idk_entry_point
    val tail_calls = TDP.idk_tail_call
    val non_tail_calls = TDP.idk_non_tail_call

    type record = { kind : int, descr: string }

    val records = ref (M.empty : record M.map)

    val counters = ref (Array.fromList [0])

    fun count idx = Array.sub (!counters, idx) handle General.Subscript => 0

    fun bump (module, id) =
	let val idx = module + id
	    val a = !counters
	in
	    Array.update (a, idx, Array.sub (a, idx) + 1)
	    handle General.Subscript =>
		   let val olen = Array.length a
		       val nlen = Int.min (idx + 1, olen + olen)
		       fun cp i = if i < olen then Array.sub (a, i)
				  else if i = idx then 1
				  else 0
		   in
		       counters := Array.tabulate (nlen, cp)
		   end
	end

    val enter = bump
    fun push mi = (bump mi; fn () => ())
    val nopush = bump

    fun register (module, kind, id, s) =
	let val idx = module + id
	    val r = { kind = kind, descr = s }
	in
	    records := M.insert (!records, idx, r)
	end

    fun save () () = ()

    val name = "coverage"

    fun install () =
	let val plugin = { name = name, save = save,
			   push = push, nopush = nopush,
			   enter = enter, register = register }
	    fun addto r x = r := x :: !r
	in
	    addto TDP.active_plugins plugin
	end

    fun not_covered kinds =
	let fun zerocnt (idx, r: record) =
		count idx = 0 andalso List.exists (fn k => k = #kind r) kinds
	    val zrecords = M.filteri zerocnt (!records)
	    fun tell { descr, kind } =
		Control.Print.say (descr ^ "\n")
	in
	    M.app tell zrecords
	end

    fun hot_spots kinds n =
	let fun getcount (idx, r: record) =
		if List.exists (fn k => k = #kind r) kinds then
		    SOME (#descr r, count idx)
		else NONE
	    val countmap = M.mapPartiali getcount (!records)
	    val countlist = M.listItems countmap
	    fun lt ((_, c), (_, c')) = c < c'
	    val sortedcountlist = ListMergeSort.sort lt countlist
	    fun loop ([], _) = ()
	      | loop (_, 0) = ()
	      | loop ((descr, count) :: rest, n) =
		  (Control.Print.say (F.format (F.padl 3 F.int o F.sp 1 o F.string o F.nl) count descr);
		   loop (rest, n - 1))
	in
	    loop (sortedcountlist, n)
	end
end

--- NEW FILE: install-back-trace.sml ---
(* install-back-trace.sml
 *
 *   A module that causes (at link time) to have the back-trace
 *   plugin installed into its core hook.
 *
 * Copyright (c) 2004 by The Fellowship of SML/NJ
 *
 * Author: Matthias Blume ([email protected])
 *)
structure InstallBackTrace = struct
    val _ = BackTrace.install ()
end

--- NEW FILE: install-coverage.sml ---
(* install-coverage.sml
 *
 *   A module that causes (at link time) to have the test coverage
 *   plugin installed into its core hook.
 *
 * Copyright (c) 2004 by The Fellowship of SML/NJ
 *
 * Author: Matthias Blume ([email protected])
 *)
structure InstallCoverage = struct
    val _ = Coverage.install ()
end

--- NEW FILE: plugins.cm ---
(* plugins.cm
 *
 *   Library of plug-in modules for tracing, debugging, and profiling.
 *
 * Copyright (c) 2004 by The Fellowship of SML/NJ
 *
 * Author: Matthias Blume ([email protected])
 *)

Library
	structure BackTrace
	structure Coverage
is
	back-trace.sml
	coverage.sml

	$/basis.cm
	$/smlnj-lib.cm
	$smlnj/compiler.cm



-------------------------------------------------------
SF email is sponsored by - The IT Product Guide
Read honest & candid reviews on hundreds of IT Products from real users.
Discover which products truly live up to the hype. Start reading now.
http://ads.osdn.com/?ad_id=6595&alloc_id=14396&op=click
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.