mirror of
https://github.com/logos-storage/nim-chroprof.git
synced 2026-01-04 22:43:09 +00:00
carve out a better defined external profiler API
This commit is contained in:
parent
aa4801356a
commit
2b3cf84359
@ -1,11 +1,8 @@
|
||||
import chronos/futures
|
||||
import ./[profiler, events]
|
||||
|
||||
export Event, ExtendedFutureState, ProfilerState, MetricsTotals,
|
||||
AggregateMetrics, FutureType, execTimeWithChildren
|
||||
|
||||
type EventCallback* = proc (e: Event) {.nimcall, gcsafe, raises: [].}
|
||||
|
||||
var profilerInstance {.threadvar.}: ProfilerState
|
||||
|
||||
proc getMetrics*(): MetricsTotals =
|
||||
@ -13,13 +10,16 @@ proc getMetrics*(): MetricsTotals =
|
||||
## current thread.
|
||||
result = profilerInstance.metrics
|
||||
|
||||
proc enableEventCallbacks*(callback: EventCallback): void =
|
||||
onBaseFutureEvent = handleBaseFutureEvent
|
||||
onAsyncFutureEvent = handleAsyncFutureEvent
|
||||
handleFutureEvent = callback
|
||||
|
||||
proc enableProfiling*(clientCallback: EventCallback = nil) =
|
||||
proc enableProfiling*(callback: EventCallback = nil) =
|
||||
## Enables profiling for the the event loop running in the current thread.
|
||||
handleFutureEvent = proc (e: Event) {.nimcall.} =
|
||||
profilerInstance.processEvent(e)
|
||||
if not isNil(clientCallback): clientCallback(e)
|
||||
## The client may optionally supply a callback to be notified of `Future`
|
||||
## events.
|
||||
enableMonitoring(
|
||||
if (isNil(callback)):
|
||||
proc(e: Event) {.nimcall.} =
|
||||
profilerInstance.processEvent(e)
|
||||
else:
|
||||
proc (e: Event) {.nimcall.} =
|
||||
profilerInstance.processEvent(e)
|
||||
callback(e)
|
||||
)
|
||||
|
||||
@ -15,7 +15,7 @@ import ./api
|
||||
when defined(metrics):
|
||||
type
|
||||
ChronosProfilerInfo* = ref object of RootObj
|
||||
perfSampler: PerfSampler
|
||||
sampler: MetricsSampler
|
||||
sampleInterval: times.Duration
|
||||
clock: Clock
|
||||
k: int
|
||||
@ -23,7 +23,7 @@ when defined(metrics):
|
||||
lastSample: Time
|
||||
collections*: uint
|
||||
|
||||
PerfSampler = proc (): MetricsTotals {.raises: [].}
|
||||
MetricsSampler = proc (): MetricsTotals {.raises: [].}
|
||||
|
||||
Clock = proc (): Time {.raises: [].}
|
||||
|
||||
@ -76,12 +76,12 @@ when defined(metrics):
|
||||
|
||||
proc newCollector*(
|
||||
ChronosProfilerInfo: typedesc,
|
||||
perfSampler: PerfSampler,
|
||||
sampler: MetricsSampler,
|
||||
clock: Clock,
|
||||
sampleInterval: times.Duration,
|
||||
k: int = 10,
|
||||
): ChronosProfilerInfo = ChronosProfilerInfo(
|
||||
perfSampler: perfSampler,
|
||||
sampler: sampler,
|
||||
clock: clock,
|
||||
k: k,
|
||||
sampleInterval: sampleInterval,
|
||||
@ -137,7 +137,7 @@ when defined(metrics):
|
||||
|
||||
self.collections += 1
|
||||
var currentMetrics = self.
|
||||
perfSampler().
|
||||
sampler().
|
||||
pairs.
|
||||
toSeq.
|
||||
# We don't scoop metrics with 0 exec time as we have a limited number of
|
||||
@ -179,7 +179,7 @@ when defined(metrics):
|
||||
" the one that initialized the metricscolletor module."
|
||||
|
||||
asyncProfilerInfo = ChronosProfilerInfo.newCollector(
|
||||
perfSampler = proc (): MetricsTotals = getMetrics(),
|
||||
sampler = getMetrics,
|
||||
k = k,
|
||||
# We want to collect metrics every 5 seconds.
|
||||
sampleInterval = initDuration(seconds = 5),
|
||||
@ -187,7 +187,7 @@ when defined(metrics):
|
||||
)
|
||||
|
||||
enableProfiling(
|
||||
proc (e: Event) {.nimcall, gcsafe.} =
|
||||
proc (e: Event) {.nimcall, gcsafe, raises: [].} =
|
||||
{.cast(gcsafe).}:
|
||||
if e.newState == ExtendedFutureState.Completed:
|
||||
asyncProfilerInfo.collect()
|
||||
|
||||
@ -17,11 +17,14 @@ type
|
||||
Failed,
|
||||
|
||||
Event* = object
|
||||
## A timestamped event transition in a `Future` state.
|
||||
future: FutureBase
|
||||
newState*: ExtendedFutureState
|
||||
timestamp*: Moment
|
||||
|
||||
EventCallback* = proc (e: Event) {.nimcall, gcsafe, raises: [].}
|
||||
|
||||
var handleFutureEvent* {.threadvar.}: proc (event: Event) {.nimcall, gcsafe, raises: [].}
|
||||
var handleFutureEvent {.threadvar.}: EventCallback
|
||||
|
||||
proc `location`*(self: Event): SrcLoc =
|
||||
self.future.internalLocation[Create][]
|
||||
@ -58,5 +61,16 @@ proc handleAsyncFutureEvent*(future: FutureBase,
|
||||
if not isNil(handleFutureEvent):
|
||||
handleFutureEvent(mkEvent(future, extendedState))
|
||||
|
||||
proc enableMonitoring*(callback: EventCallback) =
|
||||
## Enables monitoring of Chronos `Future` state transitions on the
|
||||
## event loop that runs in the current thread. The provided callback will be
|
||||
## called at every such event.
|
||||
onBaseFutureEvent = handleBaseFutureEvent
|
||||
onAsyncFutureEvent = handleAsyncFutureEvent
|
||||
handleFutureEvent = callback
|
||||
|
||||
proc stopMonitoring*() =
|
||||
onBaseFutureEvent = nil
|
||||
onAsyncFutureEvent = nil
|
||||
handleFutureEvent = nil
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
## This module contains the actual profiler implementation - the piece of code
|
||||
## responsible for computing and aggregating metrics from timestamped event
|
||||
## sequences.
|
||||
##
|
||||
## responsible for computing metrics from sequences of timestamped events and
|
||||
## aggregating them.
|
||||
|
||||
import std/[tables, options, sets]
|
||||
import chronos/[timer, srcloc]
|
||||
|
||||
@ -42,7 +42,7 @@ type
|
||||
callCount*: uint ## Total number of distinct `Future`s observed
|
||||
## for this `FutureType`.
|
||||
|
||||
PartialMetrics* = object
|
||||
PartialMetrics = object
|
||||
state*: ExtendedFutureState
|
||||
created*: Moment
|
||||
lastStarted*: Moment
|
||||
@ -53,7 +53,7 @@ type
|
||||
parent*: Option[uint]
|
||||
pauses*: uint
|
||||
|
||||
MetricsTotals* = Table[SrcLoc, AggregateMetrics]
|
||||
MetricsTotals* = Table[FutureType, AggregateMetrics]
|
||||
|
||||
ProfilerState* = object
|
||||
callStack: seq[uint]
|
||||
@ -152,7 +152,7 @@ proc futureCompleted(self: var ProfilerState, event: Event): void =
|
||||
|
||||
self.partials.del(event.futureId)
|
||||
|
||||
proc processEvent*(self: var ProfilerState, event: Event): void =
|
||||
proc processEvent*(self: var ProfilerState, event: Event): void {.nimcall, gcsafe, raises: []} =
|
||||
case event.newState:
|
||||
of Pending: self.futureCreated(event)
|
||||
of Running: self.futureRunning(event)
|
||||
|
||||
@ -9,11 +9,10 @@ import ./utils
|
||||
suite "event ordering expectations":
|
||||
|
||||
setup:
|
||||
installCallbacks()
|
||||
startRecording()
|
||||
|
||||
teardown:
|
||||
clearRecording()
|
||||
revertCallbacks()
|
||||
stopRecording()
|
||||
|
||||
test "should emit correct events for a simple future":
|
||||
|
||||
|
||||
@ -61,7 +61,7 @@ suite "metrics collector":
|
||||
|
||||
proc setupCollector(k: int = high(int)): void =
|
||||
collector = ChronosProfilerInfo.newCollector(
|
||||
perfSampler = proc (): MetricsTotals = sample,
|
||||
sampler = proc (): MetricsTotals = sample,
|
||||
clock = proc (): Time = wallTime,
|
||||
sampleInterval = times.initDuration(minutes = 5),
|
||||
k = k,
|
||||
|
||||
@ -11,11 +11,10 @@ import ./utils
|
||||
suite "profiler metrics":
|
||||
|
||||
setup:
|
||||
installCallbacks()
|
||||
startRecording()
|
||||
|
||||
teardown:
|
||||
clearRecording()
|
||||
revertCallbacks()
|
||||
stopRecording()
|
||||
resetTime()
|
||||
|
||||
proc recordedMetrics(): MetricsTotals =
|
||||
|
||||
@ -30,19 +30,14 @@ proc recordSegment*(segment: string) =
|
||||
state: ExtendedFutureState.Running
|
||||
))
|
||||
|
||||
proc clearRecording*(): void =
|
||||
proc stopRecording*(): void =
|
||||
recording = @[]
|
||||
rawRecording = @[]
|
||||
stopMonitoring()
|
||||
|
||||
proc installCallbacks*() =
|
||||
assert isNil(handleFutureEvent), "There is a callback already installed"
|
||||
|
||||
enableEventCallbacks(recordEvent)
|
||||
|
||||
proc revertCallbacks*() =
|
||||
assert not isNil(handleFutureEvent), "There are no callbacks installed"
|
||||
|
||||
handleFutureEvent = nil
|
||||
proc startRecording*() =
|
||||
stopRecording()
|
||||
enableMonitoring(recordEvent)
|
||||
|
||||
proc forProc*(self: var MetricsTotals, procedure: string): AggregateMetrics =
|
||||
for (key, aggMetrics) in self.mpairs:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user