(* Copyright (C) 2003-2006, 2008 Henry Cejtin, Matthew Fluet, Suresh * Jagannathan, and Stephen Weeks. * * MLton is released under a BSD-style license. * See the file MLton-LICENSE for details. *) structure MLtonProfile: MLTON_PROFILE = struct structure P = Primitive.MLton.Profile val gcState = Primitive.MLton.GCState.gcState val isOn = P.isOn structure Data = struct datatype t = T of {isCurrent: bool ref, isFreed: bool ref, raw: P.Data.t} val all: t list ref = ref [] local fun make f (T r) = f r in val isFreed = make #isFreed val raw = make #raw end fun equals (d, d') = isFreed d = isFreed d' fun free (d as T {isCurrent, isFreed, raw, ...}) = if not isOn then () else if !isFreed then raise Fail "free of freed profile data" else if !isCurrent then raise Fail "free of current profile data" else (all := List.foldl (fn (d', ac) => if equals (d, d') then ac else d' :: ac) [] (!all) ; P.Data.free (gcState, raw) ; isFreed := true) fun make (raw: P.Data.t): t = T {isCurrent = ref false, isFreed = ref false, raw = raw} fun malloc (): t = let val array = if isOn then P.Data.malloc gcState else P.Data.dummy val d = make array val _ = all := d :: !all in d end fun write (T {isFreed, raw, ...}, file) = if not isOn then () else if !isFreed then raise Fail "write of freed profile data" else P.Data.write (gcState, raw, Primitive.NullString8.fromString (String.nullTerm file)) end val r: Data.t ref = ref (Data.make P.Data.dummy) fun current () = !r fun setCurrent (d as Data.T {isCurrent, isFreed, raw, ...}) = if not isOn then () else if !isFreed then raise Fail "setCurrent of freed profile data" else let val Data.T {isCurrent = ic, ...} = current () val _ = ic := false val _ = isCurrent := true val _ = r := d val _ = P.setCurrent (gcState, raw) in () end fun withData (d: Data.t, f: unit -> 'a): 'a = let val old = current () val _ = setCurrent d in DynamicWind.wind (f, fn () => setCurrent old) end fun init () = setCurrent (Data.make (P.getCurrent gcState)) val _ = if not isOn then () else let val _ = Cleaner.addNew (Cleaner.atExit, fn () => (P.done gcState ; Data.write (current (), "mlmon.out") ; List.app (fn d => P.Data.free (gcState, Data.raw d)) (!Data.all))) val _ = Cleaner.addNew (Cleaner.atLoadWorld, fn () => ((* In a new world, all of the old profiling data is invalid. *) Data.all := [] ; init ())) in init () end end