profiler.nim 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. #
  2. #
  3. # Nim's Runtime Library
  4. # (c) Copyright 2012 Andreas Rumpf
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. # This file implements the Nim profiler. The profiler needs support by the
  10. # code generator. The idea is to inject the instruction stream
  11. # with 'nimProfile()' calls. These calls are injected at every loop end
  12. # (except perhaps loops that have no side-effects). At every Nth call a
  13. # stack trace is taken. A stack tace is a list of cstrings.
  14. when defined(profiler) and defined(memProfiler):
  15. {.error: "profiler and memProfiler cannot be defined at the same time (See Embedded Stack Trace Profiler (ESTP) User Guide) for more details".}
  16. {.push profiler: off.}
  17. const
  18. MaxTraceLen = 20 # tracking the last 20 calls is enough
  19. type
  20. StackTrace* = object
  21. lines*: array[0..MaxTraceLen-1, cstring]
  22. files*: array[0..MaxTraceLen-1, cstring]
  23. ProfilerHook* = proc (st: StackTrace) {.nimcall.}
  24. proc `[]`*(st: StackTrace, i: int): cstring = st.lines[i]
  25. proc captureStackTrace(f: PFrame, st: var StackTrace) =
  26. const
  27. firstCalls = 5
  28. var
  29. it = f
  30. i = 0
  31. total = 0
  32. while it != nil and i <= high(st.lines)-(firstCalls-1):
  33. # the (-1) is for the "..." entry
  34. st.lines[i] = it.procname
  35. st.files[i] = it.filename
  36. inc(i)
  37. inc(total)
  38. it = it.prev
  39. var b = it
  40. while it != nil:
  41. inc(total)
  42. it = it.prev
  43. for j in 1..total-i-(firstCalls-1):
  44. if b != nil: b = b.prev
  45. if total != i:
  46. st.lines[i] = "..."
  47. st.files[i] = "..."
  48. inc(i)
  49. while b != nil and i <= high(st.lines):
  50. st.lines[i] = b.procname
  51. st.files[i] = b.filename
  52. inc(i)
  53. b = b.prev
  54. var
  55. profilingRequestedHook*: proc (): bool {.nimcall, gcsafe.}
  56. ## set this variable to provide a procedure that implements a profiler in
  57. ## user space. See the `nimprof` module for a reference implementation.
  58. when defined(memProfiler):
  59. type
  60. MemProfilerHook* = proc (st: StackTrace, requestedSize: int) {.nimcall, gcsafe.}
  61. var
  62. profilerHook*: MemProfilerHook
  63. ## set this variable to provide a procedure that implements a profiler in
  64. ## user space. See the `nimprof` module for a reference implementation.
  65. proc callProfilerHook(hook: MemProfilerHook, requestedSize: int) =
  66. var st: StackTrace
  67. captureStackTrace(framePtr, st)
  68. hook(st, requestedSize)
  69. proc nimProfile(requestedSize: int) =
  70. if not isNil(profilingRequestedHook) and profilingRequestedHook():
  71. callProfilerHook(profilerHook, requestedSize)
  72. else:
  73. var
  74. profilerHook*: ProfilerHook
  75. ## set this variable to provide a procedure that implements a profiler in
  76. ## user space. See the `nimprof` module for a reference implementation.
  77. proc callProfilerHook(hook: ProfilerHook) {.noinline.} =
  78. # 'noinline' so that 'nimProfile' does not perform the stack allocation
  79. # in the common case.
  80. when not defined(nimdoc):
  81. var st: StackTrace
  82. captureStackTrace(framePtr, st)
  83. hook(st)
  84. proc nimProfile() =
  85. ## This is invoked by the compiler in every loop and on every proc entry!
  86. if not isNil(profilingRequestedHook) and profilingRequestedHook():
  87. callProfilerHook(profilerHook)
  88. {.pop.}