mirror of
https://github.com/nim-lang/Nim.git
synced 2025-12-28 17:04:41 +00:00
104 lines
3.2 KiB
Nim
104 lines
3.2 KiB
Nim
#
|
|
#
|
|
# 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, 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.}
|