1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
|
#
#
# 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 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
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
profilingRequestedHook*: proc (): bool {.nimcall, locks: 0, 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, locks: 0, 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.}
|