2#include "pedigree/kernel/Atomic.h"
3#include "pedigree/kernel/Log.h"
4#include "pedigree/kernel/process/PerProcessorScheduler.h"
5#include "pedigree/kernel/process/Process.h"
6#include "pedigree/kernel/process/Scheduler.h"
7#include "pedigree/kernel/process/Thread.h"
8#include "pedigree/kernel/processor/Processor.h"
9#include "pedigree/kernel/processor/ProcessorInformation.h"
10#include "pedigree/kernel/time/Time.h"
13class HostedAccountingProcess :
public Process {
15 explicit HostedAccountingProcess(
Thread* driver,
size_t interest = ~
size_t(0))
16 :
Process(DeferredPublication(), driver->getParent()),
23 description() +=
"hosted deferred accounting probe";
27 ~HostedAccountingProcess()
override {
31 void setReportInterest(
size_t interest,
bool enabled) {
42 void reportTimesUpdated(Time::Timestamp userTotal, Time::Timestamp profileTotal)
override {
48 current == m_Driver || !processCount || userTotal < user || profileTotal < profile) {
51 user =
static_cast<size_t>(userTotal);
52 profile =
static_cast<size_t>(profileTotal);
57struct AccountingThreadContext {
58 AccountingThreadContext(
Process* process, Time::Timestamp kernelBefore)
59 : process(process), kernelBefore(kernelBefore), ran(0), firstSliceAccounted(0) {}
62 Time::Timestamp kernelBefore;
67int accountedKernelThread(
void* parameter) {
68 AccountingThreadContext* context =
reinterpret_cast<AccountingThreadContext*
>(parameter);
69#if PEDIGREE_TIME_ACCOUNTING && PEDIGREE_SAMPLED_TIME_ACCOUNTING
70 const Time::Timestamp deadline = Time::getTicks() + 2 * Time::Multiplier::Second;
71 while (context->process->getKernelTime() == context->kernelBefore &&
72 Time::getTicks() < deadline) {
78 context->firstSliceAccounted = context->process->getKernelTime() > context->kernelBefore;
85bool runHostedAccountingRegressions() {
89 const Time::Timestamp threadUser = driver->
getUserTime();
90 const Time::Timestamp threadKernel = driver->getKernelTime();
92 const Time::Timestamp processKernel = driver->
getParent()->getKernelTime();
93 driver->publishTimeAccountingForHostedTest(13, 7);
94 driver->publishTimeAccountingForHostedTest(0, 0);
97 driver->getKernelTime() == threadKernel + 7 &&
99 driver->
getParent()->getKernelTime() == processKernel + 7;
100 bool sampledTickAccounting =
true;
101#if PEDIGREE_TIME_ACCOUNTING && PEDIGREE_SAMPLED_TIME_ACCOUNTING
105 driver->
transitionTime(CpuTimeMode::User, CpuTimeMode::Kernel,
true);
109 sampledTickAccounting &=
111 driver->
getUserTime() == threadUser + 13 && driver->getKernelTime() == threadKernel + 7 &&
113 driver->
getParent()->getKernelTime() == processKernel + 7;
119 driver->getKernelTime() == threadKernel + 7 &&
121 driver->
getParent()->getKernelTime() == processKernel + 7;
126 sampledTickAccounting &=
128 driver->
getUserTime() == threadUser + 30 && driver->getKernelTime() == threadKernel + 30 &&
130 driver->
getParent()->getKernelTime() == processKernel + 30;
135 const bool loadRequestPassed =
Scheduler::instance().runHostedLoadAverageRequestRegression();
136 HostedAccountingProcess* dormant =
new HostedAccountingProcess(driver, 0);
137 dormant->publishTimeAccountingForHostedTest(100, 200);
138 bool interestPassed = !dormant->timeAccountingPendingForHostedTest() &&
139 !dormant->timeAccountingInterestForHostedTest() &&
140 dormant->getUserTime() == 100 && dormant->getKernelTime() == 200;
141 dormant->setReportInterest(1,
true);
142 for (
size_t attempt = 0; !dormant->calls && attempt < 10000; ++attempt) {
143 PerProcessorScheduler::serviceCurrentIrqWorkDoorbellForTest();
147 dormant->calls == 1 && dormant->user == 100 && dormant->profile == 300 && !dormant->failures;
148 dormant->setReportInterest(2,
true);
149 dormant->setReportInterest(1,
false);
150 dormant->publishTimeAccountingForHostedTest(17, 11);
151 interestPassed &= dormant->timeAccountingInterestForHostedTest() == 2;
152 for (
size_t attempt = 0; dormant->calls < 2 && attempt < 10000; ++attempt) {
153 PerProcessorScheduler::serviceCurrentIrqWorkDoorbellForTest();
157 dormant->calls == 2 && dormant->user == 117 && dormant->profile == 328 && !dormant->failures;
158 dormant->setReportInterest(2,
false);
159 dormant->closeTimeAccountingForHostedTest();
160 const size_t dormantCalls = dormant->calls;
161 dormant->publishTimeAccountingForHostedTest(3, 5);
162 interestPassed &= !dormant->timeAccountingInterestForHostedTest() &&
163 !dormant->timeAccountingPendingForHostedTest() &&
164 dormant->calls == dormantCalls && dormant->getUserTime() == 120 &&
165 dormant->getKernelTime() == 216;
168 HostedAccountingProcess* process =
new HostedAccountingProcess(driver);
171 process->publishTimeAccountingForHostedTest(13, 7);
174 constexpr size_t Attempts = 10000;
175 for (
size_t attempt = 0; !process->calls && attempt < Attempts; ++attempt) {
176 PerProcessorScheduler::serviceCurrentIrqWorkDoorbellForTest();
180 const bool exactWorkerBatch =
181 process->calls == 1 && process->user == 13 && process->profile == 20 && !process->failures;
182 process->publishTimeAccountingForHostedTest(0, 0);
183 const bool zeroBatchDiscarded = !process->timeAccountingPendingForHostedTest() &&
184 process->
getUserTime() == 13 && process->getKernelTime() == 7;
186 AccountingThreadContext threadContext(process, process->getKernelTime());
188 new Thread(process, accountedKernelThread, &threadContext,
nullptr,
false,
true,
true);
189 accountedThread->setName(
"hosted accounting first-slice probe");
190 const bool accountedThreadStarted = accountedThread->
start();
191 const bool accountedThreadJoined = accountedThreadStarted && accountedThread->
joinForCompletion();
192 if (!accountedThreadStarted) {
193 delete accountedThread;
196 for (
size_t attempt = 0; process->profile == 20 && attempt < Attempts; ++attempt) {
197 PerProcessorScheduler::serviceCurrentIrqWorkDoorbellForTest();
200 const bool firstKernelSliceAccounted = accountedThreadStarted && accountedThreadJoined &&
201 threadContext.ran && threadContext.firstSliceAccounted &&
202 process->user == 13 && process->profile > 20 &&
204 if (!firstKernelSliceAccounted) {
205 ERROR(
"HOSTED-ACCOUNTING-FIRST: started="
206 << accountedThreadStarted <<
" joined=" << accountedThreadJoined
207 <<
" ran=" <<
static_cast<size_t>(threadContext.ran)
208 <<
" slice=" <<
static_cast<size_t>(threadContext.firstSliceAccounted));
209 ERROR(
"HOSTED-ACCOUNTING-FIRST: user=" <<
static_cast<size_t>(process->user) <<
" profile="
210 <<
static_cast<size_t>(process->profile) <<
" failures="
211 <<
static_cast<size_t>(process->failures));
214 process->closeTimeAccountingForHostedTest();
215 const size_t callsBeforeLatePublication = process->calls;
216 const Time::Timestamp userBeforeLatePublication = process->
getUserTime();
217 const Time::Timestamp kernelBeforeLatePublication = process->getKernelTime();
218 process->publishTimeAccountingForHostedTest(101, 211);
219 for (
size_t attempt = 0; attempt < 32; ++attempt) {
222 const bool latePublicationDiscarded =
223 process->calls == callsBeforeLatePublication &&
224 !process->timeAccountingPendingForHostedTest() &&
225 process->timeAccountingInterestForHostedTest() &&
226 process->
getUserTime() == userBeforeLatePublication + 101 &&
227 process->getKernelTime() == kernelBeforeLatePublication + 211;
230 const bool passed = exactThreadPublication && sampledTickAccounting && loadRequestPassed &&
231 interestPassed && exactWorkerBatch && zeroBatchDiscarded &&
232 firstKernelSliceAccounted && latePublicationDiscarded;
234 ERROR(
"HOSTED-WAIT-TEST: FAIL deferred-time-accounting-worker: exact="
235 << exactThreadPublication <<
" sampled=" << sampledTickAccounting
236 <<
" load=" << loadRequestPassed <<
" interest=" << interestPassed
237 <<
" batch=" << exactWorkerBatch <<
" zero=" << zeroBatchDiscarded
238 <<
" first=" << firstKernelSliceAccounted <<
" late=" << latePublicationDiscarded);
240 NOTICE(
"HOSTED-WAIT-TEST: PASS deferred-time-accounting-worker");
virtual void reportTimesUpdated(Time::Timestamp userTotal, Time::Timestamp total)
LargeStaticString & description()
Time::Timestamp getUserTime() const
void prepareForDestruction()
void setTimeAccountingReportInterest(size_t interest, bool enabled)
void enableTimeAccountingReports(size_t initialInterest=~size_t(0))
static bool getInterrupts()
static ProcessorInformation & information()
static bool inDeviceHardIrq()
static void setInterrupts(bool bEnable)
static Scheduler & instance()
void recordTime(CpuTimeMode mode)
void accountTimerTick(Time::Timestamp delta, bool kernelMode)
CpuTimeMode currentTimeAccountingMode() const
Time::Timestamp getUserTime() const
void trackTime(CpuTimeMode mode)
ALWAYS_INLINE void transitionTimeAtInterruptReturn(CpuTimeMode from, CpuTimeMode to)
Process * getParent() const
void transitionTime(CpuTimeMode from, CpuTimeMode to, bool interruptsAlreadyDisabled=false)