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)
60 kernelBefore(kernelBefore),
64 firstSliceAccounted(0) {}
67 Time::Timestamp kernelBefore;
69 Time::Timestamp kernel;
74int accountedKernelThread(
void* parameter) {
75 AccountingThreadContext* context =
reinterpret_cast<AccountingThreadContext*
>(parameter);
76#if PEDIGREE_TIME_ACCOUNTING && PEDIGREE_SAMPLED_TIME_ACCOUNTING
77 const Time::Timestamp deadline = Time::getTicks() + 2 * Time::Multiplier::Second;
78 while (context->process->getKernelTime() == context->kernelBefore &&
79 Time::getTicks() < deadline) {
85 context->firstSliceAccounted = context->process->getKernelTime() > context->kernelBefore;
87 current->publishTimeAccountingForHostedTest(29, 17);
89 context->kernel = current->getKernelTime();
96bool runHostedAccountingRegressions() {
100 const Time::Timestamp threadUser = driver->
getUserTime();
101 const Time::Timestamp threadKernel = driver->getKernelTime();
103 const Time::Timestamp processKernel = driver->
getParent()->getKernelTime();
104 driver->publishTimeAccountingForHostedTest(13, 7);
105 driver->publishTimeAccountingForHostedTest(0, 0);
108 driver->getKernelTime() == threadKernel + 7 &&
110 driver->
getParent()->getKernelTime() == processKernel + 7;
111 bool sampledTickAccounting =
true;
112#if PEDIGREE_TIME_ACCOUNTING && PEDIGREE_SAMPLED_TIME_ACCOUNTING
116 driver->
transitionTime(CpuTimeMode::User, CpuTimeMode::Kernel,
true);
120 sampledTickAccounting &=
122 driver->
getUserTime() == threadUser + 13 && driver->getKernelTime() == threadKernel + 7 &&
124 driver->
getParent()->getKernelTime() == processKernel + 7;
130 driver->getKernelTime() == threadKernel + 7 &&
132 driver->
getParent()->getKernelTime() == processKernel + 7;
137 sampledTickAccounting &=
139 driver->
getUserTime() == threadUser + 30 && driver->getKernelTime() == threadKernel + 30 &&
141 driver->
getParent()->getKernelTime() == processKernel + 30;
146 const bool loadRequestPassed =
Scheduler::instance().runHostedLoadAverageRequestRegression();
147 HostedAccountingProcess* dormant =
new HostedAccountingProcess(driver, 0);
148 dormant->publishTimeAccountingForHostedTest(100, 200);
149 bool interestPassed = !dormant->timeAccountingPendingForHostedTest() &&
150 !dormant->timeAccountingInterestForHostedTest() &&
151 dormant->getUserTime() == 100 && dormant->getKernelTime() == 200;
152 dormant->setReportInterest(1,
true);
153 for (
size_t attempt = 0; !dormant->calls && attempt < 10000; ++attempt) {
154 PerProcessorScheduler::serviceCurrentIrqWorkDoorbellForTest();
158 dormant->calls == 1 && dormant->user == 100 && dormant->profile == 300 && !dormant->failures;
159 dormant->setReportInterest(2,
true);
160 dormant->setReportInterest(1,
false);
161 dormant->publishTimeAccountingForHostedTest(17, 11);
162 interestPassed &= dormant->timeAccountingInterestForHostedTest() == 2;
163 for (
size_t attempt = 0; dormant->calls < 2 && attempt < 10000; ++attempt) {
164 PerProcessorScheduler::serviceCurrentIrqWorkDoorbellForTest();
168 dormant->calls == 2 && dormant->user == 117 && dormant->profile == 328 && !dormant->failures;
169 dormant->setReportInterest(2,
false);
170 dormant->closeTimeAccountingForHostedTest();
171 const size_t dormantCalls = dormant->calls;
172 dormant->publishTimeAccountingForHostedTest(3, 5);
173 interestPassed &= !dormant->timeAccountingInterestForHostedTest() &&
174 !dormant->timeAccountingPendingForHostedTest() &&
175 dormant->calls == dormantCalls && dormant->getUserTime() == 120 &&
176 dormant->getKernelTime() == 216;
179 HostedAccountingProcess* process =
new HostedAccountingProcess(driver);
182 process->publishTimeAccountingForHostedTest(13, 7);
185 constexpr size_t Attempts = 10000;
186 for (
size_t attempt = 0; !process->calls && attempt < Attempts; ++attempt) {
187 PerProcessorScheduler::serviceCurrentIrqWorkDoorbellForTest();
191 const bool exactWorkerBatch =
192 process->calls == 1 && process->user == 13 && process->profile == 20 && !process->failures;
193 process->publishTimeAccountingForHostedTest(0, 0);
194 const bool zeroBatchDiscarded = !process->timeAccountingPendingForHostedTest() &&
195 process->
getUserTime() == 13 && process->getKernelTime() == 7;
197 AccountingThreadContext threadContext(process, process->getKernelTime());
199 new Thread(process, accountedKernelThread, &threadContext,
nullptr,
false,
true,
true);
200 accountedThread->setName(
"hosted accounting first-slice probe");
201 const bool accountedThreadStarted = accountedThread->
start();
202 const bool accountedThreadJoined = accountedThreadStarted && accountedThread->
joinForCompletion();
203 if (!accountedThreadStarted) {
204 delete accountedThread;
207 for (
size_t attempt = 0; (process->user != 42 || process->profile == 20) && attempt < Attempts;
209 PerProcessorScheduler::serviceCurrentIrqWorkDoorbellForTest();
212 const bool firstKernelSliceAccounted = accountedThreadStarted && accountedThreadJoined &&
213 threadContext.ran && threadContext.firstSliceAccounted &&
214 process->user == 42 && process->profile > 20 &&
216 const bool retiredThreadAccounted = accountedThreadJoined && !process->
getNumThreads() &&
217 process->
getUserTime() == 13 + threadContext.user &&
218 process->getKernelTime() >= 7 + threadContext.kernel;
219 if (!firstKernelSliceAccounted) {
220 ERROR(
"HOSTED-ACCOUNTING-FIRST: started="
221 << accountedThreadStarted <<
" joined=" << accountedThreadJoined
222 <<
" ran=" <<
static_cast<size_t>(threadContext.ran)
223 <<
" slice=" <<
static_cast<size_t>(threadContext.firstSliceAccounted));
224 ERROR(
"HOSTED-ACCOUNTING-FIRST: user=" <<
static_cast<size_t>(process->user) <<
" profile="
225 <<
static_cast<size_t>(process->profile) <<
" failures="
226 <<
static_cast<size_t>(process->failures));
229 process->closeTimeAccountingForHostedTest();
230 const size_t callsBeforeLatePublication = process->calls;
231 const Time::Timestamp userBeforeLatePublication = process->
getUserTime();
232 const Time::Timestamp kernelBeforeLatePublication = process->getKernelTime();
233 process->publishTimeAccountingForHostedTest(101, 211);
234 for (
size_t attempt = 0; attempt < 32; ++attempt) {
237 const bool latePublicationDiscarded =
238 process->calls == callsBeforeLatePublication &&
239 !process->timeAccountingPendingForHostedTest() &&
240 process->timeAccountingInterestForHostedTest() &&
241 process->
getUserTime() == userBeforeLatePublication + 101 &&
242 process->getKernelTime() == kernelBeforeLatePublication + 211;
245 const bool passed = exactThreadPublication && sampledTickAccounting && loadRequestPassed &&
246 interestPassed && exactWorkerBatch && zeroBatchDiscarded &&
247 firstKernelSliceAccounted && retiredThreadAccounted &&
248 latePublicationDiscarded;
250 ERROR(
"HOSTED-WAIT-TEST: FAIL deferred-time-accounting-worker: exact="
251 << exactThreadPublication <<
" sampled=" << sampledTickAccounting <<
" load="
252 << loadRequestPassed <<
" interest=" << interestPassed <<
" batch=" << exactWorkerBatch
253 <<
" zero=" << zeroBatchDiscarded <<
" first=" << firstKernelSliceAccounted
254 <<
" retired=" << retiredThreadAccounted <<
" late=" << latePublicationDiscarded);
256 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)