summary refs log tree commit diff stats
path: root/lib/pure/nimprof.nim
diff options
context:
space:
mode:
Diffstat (limited to 'lib/pure/nimprof.nim')
-rw-r--r--lib/pure/nimprof.nim120
1 files changed, 74 insertions, 46 deletions
diff --git a/lib/pure/nimprof.nim b/lib/pure/nimprof.nim
index cce2a20ae..bf8367d1d 100644
--- a/lib/pure/nimprof.nim
+++ b/lib/pure/nimprof.nim
@@ -1,23 +1,29 @@
 #
 #
 #            Nim's Runtime Library
-#        (c) Copyright 2012 Andreas Rumpf
+#        (c) Copyright 2015 Andreas Rumpf
 #
 #    See the file "copying.txt", included in this
 #    distribution, for details about the copyright.
 #
 
 ## Profiling support for Nim. This is an embedded profiler that requires
-## ``--profiler:on``. You only need to import this module to get a profiling
-## report at program exit.
+## `--profiler:on`. You only need to import this module to get a profiling
+## report at program exit. See `Embedded Stack Trace Profiler <estp.html>`_
+## for usage.
 
 when not defined(profiler) and not defined(memProfiler):
-  {.warning: "Profiling support is turned off!".}
+  {.error: "Profiling support is turned off! Enable profiling by passing `--profiler:on --stackTrace:on` to the compiler (see the Nim Compiler User Guide for more options).".}
+
+{.used.}
 
 # We don't want to profile the profiling code ...
 {.push profiler: off.}
 
-import hashes, algorithm, strutils, tables, sets
+import std/[hashes, algorithm, strutils, tables, sets]
+
+when defined(nimPreviewSlimSystem):
+  import std/[syncio, sysatomics]
 
 when not defined(memProfiler):
   include "system/timers"
@@ -26,31 +32,34 @@ const
   withThreads = compileOption("threads")
   tickCountCorrection = 50_000
 
-when not declared(system.TStackTrace):
-  type TStackTrace = array [0..20, cstring]
+when not declared(system.StackTrace):
+  type StackTrace = object
+    lines: array[0..20, cstring]
+    files: array[0..20, cstring]
+  proc `[]`*(st: StackTrace, i: int): cstring = st.lines[i]
 
 # We use a simple hash table of bounded size to keep track of the stack traces:
 type
-  TProfileEntry = object
+  ProfileEntry = object
     total: int
-    st: TStackTrace
-  TProfileData = array [0..64*1024-1, ptr TProfileEntry]
+    st: StackTrace
+  ProfileData = array[0..64*1024-1, ptr ProfileEntry]
 
-proc `==`(a, b: TStackTrace): bool =
-  for i in 0 .. high(a):
+proc `==`(a, b: StackTrace): bool =
+  for i in 0 .. high(a.lines):
     if a[i] != b[i]: return false
   result = true
 
 # XXX extract this data structure; it is generally useful ;-)
 # However a chain length of over 3000 is suspicious...
 var
-  profileData: TProfileData
+  profileData: ProfileData
   emptySlots = profileData.len * 3 div 2
   maxChainLen = 0
   totalCalls = 0
 
 when not defined(memProfiler):
-  var interval: TNanos = 5_000_000 - tickCountCorrection # 5ms
+  var interval: Nanos = 5_000_000 - tickCountCorrection # 5ms
 
   proc setSamplingFrequency*(intervalInUs: int) =
     ## set this to change the sampling frequency. Default value is 5ms.
@@ -60,17 +69,17 @@ when not defined(memProfiler):
     else: interval = intervalInUs * 1000 - tickCountCorrection
 
 when withThreads:
-  import locks
+  import std/locks
   var
-    profilingLock: TLock
+    profilingLock: Lock
 
   initLock profilingLock
 
-proc hookAux(st: TStackTrace, costs: int) =
+proc hookAux(st: StackTrace, costs: int) =
   # this is quite performance sensitive!
   when withThreads: acquire profilingLock
   inc totalCalls
-  var last = high(st)
+  var last = high(st.lines)
   while last > 0 and isNil(st[last]): dec last
   var h = hash(pointer(st[last])) and high(profileData)
 
@@ -94,8 +103,8 @@ proc hookAux(st: TStackTrace, costs: int) =
     var chain = 0
     while true:
       if profileData[h] == nil:
-        profileData[h] = cast[ptr TProfileEntry](
-                             allocShared0(sizeof(TProfileEntry)))
+        profileData[h] = cast[ptr ProfileEntry](
+                             allocShared0(sizeof(ProfileEntry)))
         profileData[h].total = costs
         profileData[h].st = st
         dec emptySlots
@@ -115,53 +124,68 @@ when defined(memProfiler):
   var
     gTicker {.threadvar.}: int
 
-  proc hook(st: TStackTrace, size: int) {.nimcall.} =
+  proc requestedHook(): bool {.nimcall.} =
     if gTicker == 0:
-      gTicker = -1
-      when defined(ignoreAllocationSize):
-        hookAux(st, 1)
-      else:
-        hookAux(st, size)
       gTicker = SamplingInterval
+      result = true
     dec gTicker
 
+  proc hook(st: StackTrace, size: int) {.nimcall.} =
+    when defined(ignoreAllocationSize):
+      hookAux(st, 1)
+    else:
+      hookAux(st, size)
+
 else:
   var
-    t0 {.threadvar.}: TTicks
-
-  proc hook(st: TStackTrace) {.nimcall.} =
+    t0 {.threadvar.}: Ticks
+    gTicker: int # we use an additional counter to
+                 # avoid calling 'getTicks' too frequently
+
+  proc requestedHook(): bool {.nimcall.} =
+    if interval == 0: result = true
+    elif gTicker == 0:
+      gTicker = 500
+      if getTicks() - t0 > interval:
+        result = true
+    else:
+      dec gTicker
+
+  proc hook(st: StackTrace) {.nimcall.} =
+    #echo "profiling! ", interval
     if interval == 0:
       hookAux(st, 1)
-    elif int64(t0) == 0 or getTicks() - t0 > interval:
+    else:
       hookAux(st, 1)
       t0 = getTicks()
 
-proc getTotal(x: ptr TProfileEntry): int =
+proc getTotal(x: ptr ProfileEntry): int =
   result = if isNil(x): 0 else: x.total
 
-proc cmpEntries(a, b: ptr TProfileEntry): int =
+proc cmpEntries(a, b: ptr ProfileEntry): int =
   result = b.getTotal - a.getTotal
 
 proc `//`(a, b: int): string =
-  result = format("$1/$2 = $3%", a, b, formatFloat(a / b * 100.0, ffDefault, 2))
+  result = format("$1/$2 = $3%", a, b, formatFloat(a / b * 100.0, ffDecimal, 2))
 
 proc writeProfile() {.noconv.} =
-  when declared(system.TStackTrace):
+  system.profilingRequestedHook = nil
+  when declared(system.StackTrace):
     system.profilerHook = nil
   const filename = "profile_results.txt"
   echo "writing " & filename & "..."
   var f: File
   if open(f, filename, fmWrite):
     sort(profileData, cmpEntries)
-    writeln(f, "total executions of each stack trace:")
+    writeLine(f, "total executions of each stack trace:")
     var entries = 0
     for i in 0..high(profileData):
       if profileData[i] != nil: inc entries
 
     var perProc = initCountTable[string]()
     for i in 0..entries-1:
-      var dups = initSet[string]()
-      for ii in 0..high(TStackTrace):
+      var dups = initHashSet[string]()
+      for ii in 0..high(StackTrace.lines):
         let procname = profileData[i].st[ii]
         if isNil(procname): break
         let p = $procname
@@ -173,13 +197,15 @@ proc writeProfile() {.noconv.} =
     for i in 0..min(100, entries-1):
       if profileData[i].total > 1:
         inc sum, profileData[i].total
-        writeln(f, "Entry: ", i+1, "/", entries, " Calls: ",
+        writeLine(f, "Entry: ", i+1, "/", entries, " Calls: ",
           profileData[i].total // totalCalls, " [sum: ", sum, "; ",
           sum // totalCalls, "]")
-        for ii in 0..high(TStackTrace):
+        for ii in 0..high(StackTrace.lines):
           let procname = profileData[i].st[ii]
+          let filename = profileData[i].st.files[ii]
           if isNil(procname): break
-          writeln(f, "  ", procname, " ", perProc[$procname] // totalCalls)
+          writeLine(f, "  ", $filename & ": " & $procname, " ",
+                    perProc[$procname] // totalCalls)
     close(f)
     echo "... done"
   else:
@@ -189,17 +215,19 @@ var
   disabled: int
 
 proc disableProfiling*() =
-  when declared(system.TStackTrace):
+  when declared(system.StackTrace):
     atomicDec disabled
-    system.profilerHook = nil
+    system.profilingRequestedHook = nil
 
 proc enableProfiling*() =
-  when declared(system.TStackTrace):
+  when declared(system.StackTrace):
     if atomicInc(disabled) >= 0:
-      system.profilerHook = hook
+      system.profilingRequestedHook = requestedHook
 
-when declared(system.TStackTrace):
+when declared(system.StackTrace):
+  import std/exitprocs
+  system.profilingRequestedHook = requestedHook
   system.profilerHook = hook
-  addQuitProc(writeProfile)
+  addExitProc(writeProfile)
 
 {.pop.}