diff options
Diffstat (limited to 'lib/system/profiler.nim')
-rw-r--r--[-rwxr-xr-x] | lib/system/profiler.nim | 142 |
1 files changed, 92 insertions, 50 deletions
diff --git a/lib/system/profiler.nim b/lib/system/profiler.nim index 97c7dcfeb..e7eb6ac82 100755..100644 --- a/lib/system/profiler.nim +++ b/lib/system/profiler.nim @@ -1,61 +1,103 @@ # # -# Nimrod's Runtime Library -# (c) Copyright 2008 Andreas Rumpf +# Nim's Runtime Library +# (c) Copyright 2012 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # -# This file implements the Nimrod profiler. The profiler needs support by the -# code generator. +# This file implements the Nim profiler. The profiler needs support by the +# code generator. The idea is to inject the instruction stream +# with 'nimProfile()' calls. These calls are injected at every loop end +# (except perhaps loops that have no side-effects). At every Nth call a +# stack trace is taken. A stack tace is a list of cstrings. + +when defined(profiler) and defined(memProfiler): + {.error: "profiler and memProfiler cannot be defined at the same time (See Embedded Stack Trace Profiler (ESTP) User Guide) for more details".} + +{.push profiler: off.} + +const + MaxTraceLen = 20 # tracking the last 20 calls is enough type - TProfileData {.compilerproc, final.} = object - procname: cstring - total: float + StackTrace* = object + lines*: array[0..MaxTraceLen-1, cstring] + files*: array[0..MaxTraceLen-1, cstring] + ProfilerHook* = proc (st: StackTrace) {.nimcall.} + +proc `[]`*(st: StackTrace, i: int): cstring = st.lines[i] + +proc captureStackTrace(f: PFrame, st: var StackTrace) = + const + firstCalls = 5 + var + it = f + i = 0 + total = 0 + while it != nil and i <= high(st.lines)-(firstCalls-1): + # the (-1) is for the "..." entry + st.lines[i] = it.procname + st.files[i] = it.filename + inc(i) + inc(total) + it = it.prev + var b = it + while it != nil: + inc(total) + it = it.prev + for j in 1..total-i-(firstCalls-1): + if b != nil: b = b.prev + if total != i: + st.lines[i] = "..." + st.files[i] = "..." + inc(i) + while b != nil and i <= high(st.lines): + st.lines[i] = b.procname + st.files[i] = b.filename + inc(i) + b = b.prev var - profileData {.compilerproc.}: array [0..64*1024-1, TProfileData] - -proc sortProfile(a: var array[0..64*1024-1, TProfileData], N: int) = - # we use shellsort here; fast enough and simple - var h = 1 - while true: - h = 3 * h + 1 - if h > N: break - while true: - h = h div 3 - for i in countup(h, N - 1): - var v = a[i] - var j = i - while a[j-h].total <= v.total: - a[j] = a[j-h] - j = j-h - if j < h: break - a[j] = v - if h == 1: break - -proc writeProfile() {.noconv.} = - const filename = "profile_results" - var i = 0 - var f: TFile - var j = 1 - while openFile(f, filename & $j & ".txt"): - closeFile(f) - inc(j) - if openFile(f, filename & $j & ".txt", fmWrite): - var N = 0 - # we have to compute the actual length of the array: - while profileData[N].procname != nil: inc(N) - sortProfile(profileData, N) - writeln(f, "total running time of each proc" & - " (interpret these numbers relatively)") - while profileData[i].procname != nil: - write(f, profileData[i].procname) - write(f, ": ") - writeln(f, profileData[i].total) - inc(i) - closeFile(f) - -addQuitProc(writeProfile) + profilingRequestedHook*: proc (): bool {.nimcall, gcsafe.} + ## set this variable to provide a procedure that implements a profiler in + ## user space. See the `nimprof` module for a reference implementation. + +when defined(memProfiler): + type + MemProfilerHook* = proc (st: StackTrace, requestedSize: int) {.nimcall, gcsafe.} + + var + profilerHook*: MemProfilerHook + ## set this variable to provide a procedure that implements a profiler in + ## user space. See the `nimprof` module for a reference implementation. + + proc callProfilerHook(hook: MemProfilerHook, requestedSize: int) = + var st: StackTrace + captureStackTrace(framePtr, st) + hook(st, requestedSize) + + proc nimProfile(requestedSize: int) = + if not isNil(profilingRequestedHook) and profilingRequestedHook(): + callProfilerHook(profilerHook, requestedSize) +else: + var + profilerHook*: ProfilerHook + ## set this variable to provide a procedure that implements a profiler in + ## user space. See the `nimprof` module for a reference implementation. + + proc callProfilerHook(hook: ProfilerHook) {.noinline.} = + # 'noinline' so that 'nimProfile' does not perform the stack allocation + # in the common case. + when not defined(nimdoc): + var st: StackTrace + captureStackTrace(framePtr, st) + hook(st) + + proc nimProfile() = + ## This is invoked by the compiler in every loop and on every proc entry! + if not isNil(profilingRequestedHook) and profilingRequestedHook(): + callProfilerHook(profilerHook) + +{.pop.} |