9#include "pedigree/kernel/ActivityDiagnostics.h"
11#if PEDIGREE_ACTIVITY_DIAGNOSTICS
13#include "pedigree/kernel/time/Time.h"
15namespace ActivityDiagnostics {
18uint64_t g_InterruptCount = 0;
19uint64_t g_ExceptionCount = 0;
20uint64_t g_HardwareInterruptCount = 0;
21uint64_t g_OtherInterruptCount = 0;
22uint64_t g_InterruptVectorCounts[InterruptVectorCount] = {};
23uint64_t g_InterruptDurationBuckets[DurationBucketCount] = {};
24uint64_t g_PageFaultDurationBuckets[DurationBucketCount] = {};
25uint64_t g_SchedulerTimerDurationBuckets[DurationBucketCount] = {};
26uint64_t g_HardDispatchCount = 0;
27uint64_t g_HardDurationBuckets[DurationBucketCount] = {};
28uint64_t g_ThreadedDispatchCount = 0;
29uint64_t g_ThreadedDurationBuckets[DurationBucketCount] = {};
30uint64_t g_SchedulerTimerTicks = 0;
31uint64_t g_ScheduleCalls = 0;
32uint64_t g_SameThreadSelections = 0;
33uint64_t g_ContextSwitches = 0;
34uint64_t g_IdleSelections = 0;
35uint64_t g_SchedulerIdleFallbacks = 0;
36uint64_t g_SchedulerIdleFallbackCurrentReady = 0;
37uint64_t g_SchedulerIdleFallbackCurrentPending = 0;
38uint64_t g_SchedulerNoEligibleSelections = 0;
39uint64_t g_ReadyQueueScanEntries = 0;
40uint64_t g_ReadyQueueCandidateVisits = 0;
41uint64_t g_ReadyQueuePredicateRejects = 0;
42uint64_t g_ReadyQueueSelectionSampleCounter = 0;
43uint64_t g_ReadyQueueSelectionSamples = 0;
44uint64_t g_ReadyQueueSelectionDurationBuckets[DurationBucketCount] = {};
45uint64_t g_TimeAccountingSampleCounter = 0;
46uint64_t g_TimeAccountingSamples = 0;
47uint64_t g_TimeAccountingDurationBuckets[DurationBucketCount] = {};
48uint64_t g_IdleHaltEntries = 0;
49uint64_t g_FramebufferFlips = 0;
50uint64_t g_FramebufferCells = 0;
51uint64_t g_FramebufferDurationBuckets[DurationBucketCount] = {};
52uint64_t g_UserReturnStageSamples[UserReturnStageCount] = {};
53uint64_t g_UserReturnStageTotalNanoseconds[UserReturnStageCount] = {};
54uint64_t g_UserReturnStageDurationBuckets[UserReturnStageCount][DurationBucketCount] = {};
55uint64_t g_UserReturnFaultHandledSamples = 0;
56uint64_t g_UserReturnFaultFallbackSamples = 0;
57uint64_t g_UserReturnInterruptAffinityWaitedSamples = 0;
58uint64_t g_UserReturnSyscallAffinityWaitedSamples = 0;
59uint64_t g_UserEntryCaptureSamples = 0;
60uint64_t g_UserEntryRestoreSamples = 0;
61uint64_t g_UserEntryCaptureTscTotal = 0;
62uint64_t g_UserEntryRestoreTscTotal = 0;
63uint64_t g_UserEntryEmptyTscSamples = 0;
64uint64_t g_UserEntryEmptyTscTotal = 0;
65uint64_t g_UserEntryCaptureTscBuckets[DurationBucketCount] = {};
66uint64_t g_UserEntryRestoreTscBuckets[DurationBucketCount] = {};
67uint64_t g_UserEntryEmptyTscBuckets[DurationBucketCount] = {};
69size_t durationBucket(uint64_t duration) {
71 uint64_t limit = 1000;
72 while (bucket + 1 < DurationBucketCount && duration >= limit) {
79void recordDuration(uint64_t* buckets, uint64_t duration) {
80 __atomic_fetch_add(&buckets[durationBucket(duration)],
static_cast<uint64_t
>(1),
84size_t tscDurationBucket(uint64_t duration) {
87 while (bucket + 1 < DurationBucketCount && duration >= limit) {
94void recordTscDuration(uint64_t* buckets, uint64_t duration) {
95 __atomic_fetch_add(&buckets[tscDurationBucket(duration)],
static_cast<uint64_t
>(1),
99uint64_t load(
const uint64_t& value) {
100 return __atomic_load_n(&value, __ATOMIC_ACQUIRE);
105#if PEDIGREE_X64_USER_ENTRY_DIAGNOSTICS
106extern "C" uint64_t pedigree_user_entry_capture_calls;
107extern "C" uint64_t pedigree_user_entry_restore_calls;
110uint64_t timestamp() {
111 return Time::getTicks();
114void snapshot(Snapshot& result) {
115 result.interruptCount = load(g_InterruptCount);
116 result.exceptionCount = load(g_ExceptionCount);
117 result.hardwareInterruptCount = load(g_HardwareInterruptCount);
118 result.otherInterruptCount = load(g_OtherInterruptCount);
119 for (
size_t i = 0; i < InterruptVectorCount; ++i)
120 result.interruptVectorCounts[i] = load(g_InterruptVectorCounts[i]);
121 for (
size_t i = 0; i < DurationBucketCount; ++i)
122 result.interruptDurationBuckets[i] = load(g_InterruptDurationBuckets[i]);
123 for (
size_t i = 0; i < DurationBucketCount; ++i)
124 result.pageFaultDurationBuckets[i] = load(g_PageFaultDurationBuckets[i]);
125 for (
size_t i = 0; i < DurationBucketCount; ++i)
126 result.schedulerTimerDurationBuckets[i] = load(g_SchedulerTimerDurationBuckets[i]);
127 result.hardDispatchCount = load(g_HardDispatchCount);
128 for (
size_t i = 0; i < DurationBucketCount; ++i)
129 result.hardDurationBuckets[i] = load(g_HardDurationBuckets[i]);
130 result.threadedDispatchCount = load(g_ThreadedDispatchCount);
131 for (
size_t i = 0; i < DurationBucketCount; ++i)
132 result.threadedDurationBuckets[i] = load(g_ThreadedDurationBuckets[i]);
133 result.schedulerTimerTicks = load(g_SchedulerTimerTicks);
134 result.scheduleCalls = load(g_ScheduleCalls);
135 result.sameThreadSelections = load(g_SameThreadSelections);
136 result.contextSwitches = load(g_ContextSwitches);
137 result.idleSelections = load(g_IdleSelections);
138 result.schedulerIdleFallbacks = load(g_SchedulerIdleFallbacks);
139 result.schedulerIdleFallbackCurrentReady = load(g_SchedulerIdleFallbackCurrentReady);
140 result.schedulerIdleFallbackCurrentPending = load(g_SchedulerIdleFallbackCurrentPending);
141 result.schedulerNoEligibleSelections = load(g_SchedulerNoEligibleSelections);
142 result.readyQueueScanEntries = load(g_ReadyQueueScanEntries);
143 result.readyQueueCandidateVisits = load(g_ReadyQueueCandidateVisits);
144 result.readyQueuePredicateRejects = load(g_ReadyQueuePredicateRejects);
145 result.readyQueueSelectionSamples = load(g_ReadyQueueSelectionSamples);
146 for (
size_t i = 0; i < DurationBucketCount; ++i)
147 result.readyQueueSelectionDurationBuckets[i] = load(g_ReadyQueueSelectionDurationBuckets[i]);
148 result.timeAccountingSamples = load(g_TimeAccountingSamples);
149 for (
size_t i = 0; i < DurationBucketCount; ++i)
150 result.timeAccountingDurationBuckets[i] = load(g_TimeAccountingDurationBuckets[i]);
151 result.idleHaltEntries = load(g_IdleHaltEntries);
152 result.framebufferFlips = load(g_FramebufferFlips);
153 result.framebufferCells = load(g_FramebufferCells);
154 for (
size_t i = 0; i < DurationBucketCount; ++i)
155 result.framebufferDurationBuckets[i] = load(g_FramebufferDurationBuckets[i]);
156 for (
size_t stage = 0; stage < UserReturnStageCount; ++stage) {
157 result.userReturnStageSamples[stage] = load(g_UserReturnStageSamples[stage]);
158 result.userReturnStageTotalNanoseconds[stage] = load(g_UserReturnStageTotalNanoseconds[stage]);
159 for (
size_t i = 0; i < DurationBucketCount; ++i) {
160 result.userReturnStageDurationBuckets[stage][i] =
161 load(g_UserReturnStageDurationBuckets[stage][i]);
164 result.userReturnFaultHandledSamples = load(g_UserReturnFaultHandledSamples);
165 result.userReturnFaultFallbackSamples = load(g_UserReturnFaultFallbackSamples);
166 result.userReturnInterruptAffinityWaitedSamples =
167 load(g_UserReturnInterruptAffinityWaitedSamples);
168 result.userReturnSyscallAffinityWaitedSamples = load(g_UserReturnSyscallAffinityWaitedSamples);
169#if PEDIGREE_X64_USER_ENTRY_DIAGNOSTICS
170 result.userEntryCaptureCalls = pedigree_user_entry_capture_calls;
171 result.userEntryRestoreCalls = pedigree_user_entry_restore_calls;
173 result.userEntryCaptureCalls = 0;
174 result.userEntryRestoreCalls = 0;
176 result.userEntryCaptureSamples = load(g_UserEntryCaptureSamples);
177 result.userEntryRestoreSamples = load(g_UserEntryRestoreSamples);
178 result.userEntryCaptureTscTotal = load(g_UserEntryCaptureTscTotal);
179 result.userEntryRestoreTscTotal = load(g_UserEntryRestoreTscTotal);
180 result.userEntryEmptyTscSamples = load(g_UserEntryEmptyTscSamples);
181 result.userEntryEmptyTscTotal = load(g_UserEntryEmptyTscTotal);
182 for (
size_t i = 0; i < DurationBucketCount; ++i) {
183 result.userEntryCaptureTscBuckets[i] = load(g_UserEntryCaptureTscBuckets[i]);
184 result.userEntryRestoreTscBuckets[i] = load(g_UserEntryRestoreTscBuckets[i]);
185 result.userEntryEmptyTscBuckets[i] = load(g_UserEntryEmptyTscBuckets[i]);
189void recordInterruptEntry(
size_t vector) {
190 if (vector >= InterruptVectorCount)
192 __atomic_fetch_add(&g_InterruptCount,
static_cast<uint64_t
>(1), __ATOMIC_RELAXED);
193 __atomic_fetch_add(&g_InterruptVectorCounts[vector],
static_cast<uint64_t
>(1), __ATOMIC_RELAXED);
195 __atomic_fetch_add(&g_ExceptionCount,
static_cast<uint64_t
>(1), __ATOMIC_RELAXED);
196 }
else if (vector < 48) {
197 __atomic_fetch_add(&g_HardwareInterruptCount,
static_cast<uint64_t
>(1), __ATOMIC_RELAXED);
199 __atomic_fetch_add(&g_OtherInterruptCount,
static_cast<uint64_t
>(1), __ATOMIC_RELAXED);
203void recordInterruptDuration(
size_t vector, uint64_t duration) {
204 if (vector >= InterruptVectorCount)
206 recordDuration(g_InterruptDurationBuckets, duration);
208 recordDuration(g_PageFaultDurationBuckets, duration);
209 else if (vector == 0xfe)
210 recordDuration(g_SchedulerTimerDurationBuckets, duration);
213void recordHardDispatch(uint8_t irq, uint64_t duration) {
215 __atomic_fetch_add(&g_HardDispatchCount,
static_cast<uint64_t
>(1), __ATOMIC_RELAXED);
216 recordDuration(g_HardDurationBuckets, duration);
219void recordThreadedDispatch(uint8_t irq, uint64_t duration) {
221 __atomic_fetch_add(&g_ThreadedDispatchCount,
static_cast<uint64_t
>(1), __ATOMIC_RELAXED);
222 recordDuration(g_ThreadedDurationBuckets, duration);
225void recordSchedulerTimer() {
226 __atomic_fetch_add(&g_SchedulerTimerTicks,
static_cast<uint64_t
>(1), __ATOMIC_RELAXED);
229void recordScheduleCall() {
230 __atomic_fetch_add(&g_ScheduleCalls,
static_cast<uint64_t
>(1), __ATOMIC_RELAXED);
233void recordSameThreadSelection() {
234 __atomic_fetch_add(&g_SameThreadSelections,
static_cast<uint64_t
>(1), __ATOMIC_RELAXED);
237void recordContextSwitch() {
238 __atomic_fetch_add(&g_ContextSwitches,
static_cast<uint64_t
>(1), __ATOMIC_RELAXED);
241void recordIdleSelection() {
242 __atomic_fetch_add(&g_IdleSelections,
static_cast<uint64_t
>(1), __ATOMIC_RELAXED);
245void recordSchedulerIdleFallback(
bool currentReady,
bool currentPending) {
246 __atomic_fetch_add(&g_SchedulerIdleFallbacks,
static_cast<uint64_t
>(1), __ATOMIC_RELAXED);
248 __atomic_fetch_add(&g_SchedulerIdleFallbackCurrentReady,
static_cast<uint64_t
>(1),
251 __atomic_fetch_add(&g_SchedulerIdleFallbackCurrentPending,
static_cast<uint64_t
>(1),
255void recordSchedulerNoEligibleSelection() {
256 __atomic_fetch_add(&g_SchedulerNoEligibleSelections,
static_cast<uint64_t
>(1), __ATOMIC_RELAXED);
259void recordReadyQueueScanEntry() {
260 __atomic_fetch_add(&g_ReadyQueueScanEntries,
static_cast<uint64_t
>(1), __ATOMIC_RELAXED);
263void recordReadyQueueCandidateVisit() {
264 __atomic_fetch_add(&g_ReadyQueueCandidateVisits,
static_cast<uint64_t
>(1), __ATOMIC_RELAXED);
267void recordReadyQueuePredicateReject() {
268 __atomic_fetch_add(&g_ReadyQueuePredicateRejects,
static_cast<uint64_t
>(1), __ATOMIC_RELAXED);
271bool shouldSampleReadyQueueSelection() {
272 return (__atomic_fetch_add(&g_ReadyQueueSelectionSampleCounter,
static_cast<uint64_t
>(1),
277void recordReadyQueueSelection(uint64_t duration) {
278 __atomic_fetch_add(&g_ReadyQueueSelectionSamples,
static_cast<uint64_t
>(1), __ATOMIC_RELAXED);
279 recordDuration(g_ReadyQueueSelectionDurationBuckets, duration);
282bool shouldSampleTimeAccounting() {
283 return (__atomic_fetch_add(&g_TimeAccountingSampleCounter,
static_cast<uint64_t
>(1),
288void recordTimeAccounting(uint64_t duration) {
289 __atomic_fetch_add(&g_TimeAccountingSamples,
static_cast<uint64_t
>(1), __ATOMIC_RELAXED);
290 recordDuration(g_TimeAccountingDurationBuckets, duration);
293void recordIdleHalt() {
294 __atomic_fetch_add(&g_IdleHaltEntries,
static_cast<uint64_t
>(1), __ATOMIC_RELAXED);
297void recordFramebufferFlip(
size_t cells, uint64_t duration) {
298 __atomic_fetch_add(&g_FramebufferFlips,
static_cast<uint64_t
>(1), __ATOMIC_RELAXED);
299 __atomic_fetch_add(&g_FramebufferCells,
static_cast<uint64_t
>(cells), __ATOMIC_RELAXED);
300 recordDuration(g_FramebufferDurationBuckets, duration);
303void recordUserReturnStage(UserReturnStage stage, uint64_t duration) {
304 const size_t index =
static_cast<size_t>(stage);
305 if (index >= UserReturnStageCount)
307 __atomic_fetch_add(&g_UserReturnStageSamples[index],
static_cast<uint64_t
>(1), __ATOMIC_RELAXED);
308 __atomic_fetch_add(&g_UserReturnStageTotalNanoseconds[index], duration, __ATOMIC_RELAXED);
309 recordDuration(g_UserReturnStageDurationBuckets[index], duration);
312void recordUserReturnFaultOutcome(
bool handled) {
314 handled ? &g_UserReturnFaultHandledSamples : &g_UserReturnFaultFallbackSamples;
315 __atomic_fetch_add(counter,
static_cast<uint64_t
>(1), __ATOMIC_RELAXED);
318void recordUserReturnAffinityWait(
bool syscall) {
319 uint64_t* counter = syscall ? &g_UserReturnSyscallAffinityWaitedSamples
320 : &g_UserReturnInterruptAffinityWaitedSamples;
321 __atomic_fetch_add(counter,
static_cast<uint64_t
>(1), __ATOMIC_RELAXED);
324#if PEDIGREE_X64_USER_ENTRY_DIAGNOSTICS
325extern "C" void pedigree_record_user_entry_capture(uint64_t duration, uint64_t emptyDuration) {
326 __atomic_fetch_add(&g_UserEntryCaptureSamples,
static_cast<uint64_t
>(1), __ATOMIC_RELAXED);
327 __atomic_fetch_add(&g_UserEntryCaptureTscTotal, duration, __ATOMIC_RELAXED);
328 recordTscDuration(g_UserEntryCaptureTscBuckets, duration);
329 __atomic_fetch_add(&g_UserEntryEmptyTscSamples,
static_cast<uint64_t
>(1), __ATOMIC_RELAXED);
330 __atomic_fetch_add(&g_UserEntryEmptyTscTotal, emptyDuration, __ATOMIC_RELAXED);
331 recordTscDuration(g_UserEntryEmptyTscBuckets, emptyDuration);
334extern "C" void pedigree_record_user_entry_restore(uint64_t duration, uint64_t emptyDuration) {
335 __atomic_fetch_add(&g_UserEntryRestoreSamples,
static_cast<uint64_t
>(1), __ATOMIC_RELAXED);
336 __atomic_fetch_add(&g_UserEntryRestoreTscTotal, duration, __ATOMIC_RELAXED);
337 recordTscDuration(g_UserEntryRestoreTscBuckets, duration);
338 __atomic_fetch_add(&g_UserEntryEmptyTscSamples,
static_cast<uint64_t
>(1), __ATOMIC_RELAXED);
339 __atomic_fetch_add(&g_UserEntryEmptyTscTotal, emptyDuration, __ATOMIC_RELAXED);
340 recordTscDuration(g_UserEntryEmptyTscBuckets, emptyDuration);