-
Notifications
You must be signed in to change notification settings - Fork 18
/
wall-profiler.ts
126 lines (104 loc) · 2.92 KB
/
wall-profiler.ts
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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
import { time, SourceMapper, LabelSet, TimeProfileNode } from '@datadog/pprof'
import { Profile } from 'pprof-format'
import { ProfileExport } from '../profile-exporter'
import { Profiler } from './profiler'
import debug from 'debug'
const MICROS_PER_SECOND = 1e6
const log = debug('pyroscope::profiler::wall')
export interface WallProfilerStartArgs {
samplingDurationMs: number
samplingIntervalMicros: number
sourceMapper: SourceMapper | undefined
collectCpuTime: boolean
}
export interface GenerateTimeLabelsArgs {
node: TimeProfileNode
context?: TimeProfileNodeContext
}
export interface TimeProfileNodeContext {
context: ProfilerContext
timestamp: bigint
cpuTime: number
}
export interface ProfilerContext {
labels?: LabelSet
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type AnyWrappedCallback = any
export class WallProfiler implements Profiler<WallProfilerStartArgs> {
private lastProfiledAt: Date
private lastContext: ProfilerContext
private lastSamplingIntervalMicros!: number
constructor() {
this.lastContext = {}
this.lastProfiledAt = new Date()
}
public getLabels(): LabelSet {
return this.lastContext.labels ?? {}
}
public profile(): ProfileExport {
log('profile')
return this.innerProfile(true)
}
public wrapWithLabels(
lbls: LabelSet,
fn: () => void,
...args: unknown[]
): void {
const oldLabels = this.getLabels()
this.setLabels({
...oldLabels,
...lbls,
})
;(fn as AnyWrappedCallback)(...args)
this.setLabels({
...oldLabels,
})
}
public setLabels(labels: LabelSet): void {
this.newContext({
labels: labels,
})
}
public start(args: WallProfilerStartArgs): void {
if (!time.isStarted()) {
log('start')
this.lastProfiledAt = new Date()
this.lastSamplingIntervalMicros = args.samplingDurationMs
time.start({
sourceMapper: args.sourceMapper,
durationMillis: args.samplingDurationMs,
intervalMicros: args.samplingIntervalMicros,
withContexts: true,
workaroundV8Bug: true,
collectCpuTime: args.collectCpuTime,
})
this.newContext({})
}
}
public stop(): ProfileExport {
log('stop')
return this.innerProfile(false)
}
private newContext(o: ProfilerContext) {
this.lastContext = o
time.setContext(o)
}
private generateLabels(args: GenerateTimeLabelsArgs): LabelSet {
return { ...(args.context?.context.labels ?? {}) }
}
private innerProfile(restart: boolean): ProfileExport {
this.newContext({})
const profile: Profile = time.stop(restart, this.generateLabels)
const lastProfileStartedAt: Date = this.lastProfiledAt
this.lastProfiledAt = new Date()
return {
profile,
sampleRate: Math.ceil(
MICROS_PER_SECOND / this.lastSamplingIntervalMicros
),
startedAt: lastProfileStartedAt,
stoppedAt: this.lastProfiledAt,
}
}
}