3#include "pedigree/kernel/Atomic.h"
4#include "pedigree/kernel/Log.h"
5#include "pedigree/kernel/compiler.h"
6#include "pedigree/kernel/process/Process.h"
7#include "pedigree/kernel/process/Scheduler.h"
8#include "pedigree/kernel/process/Thread.h"
9#include "pedigree/kernel/processor/PhysicalMemoryManager.h"
10#include "pedigree/kernel/processor/Processor.h"
11#include "pedigree/kernel/processor/SyscallManager.h"
12#include "pedigree/kernel/processor/hosted/FunctionProfile.h"
13#include "pedigree/kernel/processor/hosted/smoke.h"
14#include "pedigree/kernel/processor/state.h"
15#include "pedigree/kernel/time/Time.h"
16#include "pedigree/kernel/utilities/utility.h"
23#include "modules/subsys/posix/PosixProcess.h"
24#include "modules/subsys/posix/PosixSubsystem.h"
25#include "modules/subsys/posix/syscalls/translate.h"
28#include "modules/system/vfs/VFS.h"
29#include "system/kernel/core/processor/hosted/SyscallManager.h"
32bool runHostedVasRegressions();
33bool runHostedVmOperationGuardRegressions();
34bool runHostedPostSyscallRegressions();
35bool runHostedAccountingRegressions();
38constexpr size_t QueryCount = 1000000;
39constexpr size_t IoCount = 1000;
40constexpr size_t Repetitions = 3;
41constexpr size_t PayloadSize = 4096;
42constexpr uintptr_t ProfileUid = 123;
47 state.service = linuxCompat;
48 state.number = number;
52 HostedSyscallManager::dispatchStateForTest(state);
56bool checkedCall(uintptr_t number, uintptr_t expected, uintptr_t p1 = 0, uintptr_t p2 = 0,
59 if (state.result != expected || state.error) {
60 ERROR(
"HOSTED-PROFILE: FAIL syscall=" <<
Dec << number
61 <<
" result=" <<
static_cast<int64_t
>(state.result)
62 <<
" expected=" << expected <<
" errno=" << state.error);
68void reportPhase(
const char* phase,
size_t repetition,
size_t count, Time::Timestamp elapsed) {
69 NOTICE(
"HOSTED-PROFILE: phase=" << phase <<
" rep=" <<
Dec << repetition + 1 <<
" count=" << count
70 <<
" elapsed_ns=" << elapsed);
73NEVER_INLINE bool hostedProfileGetuid(
size_t repetition,
size_t count) {
74 hostedFunctionProfileBegin(
"getuid", repetition, count);
75 const Time::Timestamp start = Time::getTicks();
76 for (
size_t i = 0; i < count; ++i) {
77 if (!checkedCall(PedigreeLinuxAmd64Syscall_getuid, ProfileUid)) {
78 hostedFunctionProfileEnd();
82 const Time::Timestamp elapsed = Time::getTicks() - start;
83 const bool captured = hostedFunctionProfileEnd();
84 reportPhase(
"getuid", repetition, count, elapsed);
88NEVER_INLINE bool hostedProfileLseek(
size_t repetition,
size_t count,
int fd) {
89 hostedFunctionProfileBegin(
"lseek", repetition, count);
90 const Time::Timestamp start = Time::getTicks();
91 for (
size_t i = 0; i < count; ++i) {
92 if (!checkedCall(PedigreeLinuxAmd64Syscall_lseek, 0, fd, 0, SEEK_SET)) {
93 hostedFunctionProfileEnd();
97 const Time::Timestamp elapsed = Time::getTicks() - start;
98 const bool captured = hostedFunctionProfileEnd();
99 reportPhase(
"lseek", repetition, count, elapsed);
103NEVER_INLINE bool hostedProfileWritev(
size_t repetition,
size_t count,
int fd,
104 const iovec* vectors) {
105 hostedFunctionProfileBegin(
"lseek-writev", repetition, count);
106 const Time::Timestamp start = Time::getTicks();
107 for (
size_t i = 0; i < count; ++i) {
108 if (!checkedCall(PedigreeLinuxAmd64Syscall_lseek, 0, fd, 0, SEEK_SET) ||
109 !checkedCall(PedigreeLinuxAmd64Syscall_writev, PayloadSize, fd,
110 reinterpret_cast<uintptr_t
>(vectors), 2)) {
111 hostedFunctionProfileEnd();
115 const Time::Timestamp elapsed = Time::getTicks() - start;
116 const bool captured = hostedFunctionProfileEnd();
117 reportPhase(
"lseek-writev", repetition, count, elapsed);
121NEVER_INLINE bool hostedProfileReadv(
size_t repetition,
size_t count,
int fd,
122 const iovec* vectors) {
123 hostedFunctionProfileBegin(
"lseek-readv", repetition, count);
124 const Time::Timestamp start = Time::getTicks();
125 for (
size_t i = 0; i < count; ++i) {
126 if (!checkedCall(PedigreeLinuxAmd64Syscall_lseek, 0, fd, 0, SEEK_SET) ||
127 !checkedCall(PedigreeLinuxAmd64Syscall_readv, PayloadSize, fd,
128 reinterpret_cast<uintptr_t
>(vectors), 2)) {
129 hostedFunctionProfileEnd();
133 const Time::Timestamp elapsed = Time::getTicks() - start;
134 const bool captured = hostedFunctionProfileEnd();
135 reportPhase(
"lseek-readv", repetition, count, elapsed);
139bool verifyPayload(
int fd,
const iovec* vectors,
const uint8_t* expected, uint8_t* actual) {
140 ByteSet(actual, 0, PayloadSize);
141 if (!checkedCall(PedigreeLinuxAmd64Syscall_lseek, 0, fd, 0, SEEK_SET) ||
142 !checkedCall(PedigreeLinuxAmd64Syscall_readv, PayloadSize, fd,
143 reinterpret_cast<uintptr_t
>(vectors), 2)) {
146 for (
size_t i = 0; i < PayloadSize; ++i) {
147 if (actual[i] != expected[i]) {
148 ERROR(
"HOSTED-PROFILE: FAIL payload offset=" <<
Dec << i);
155bool runMappedWork(uintptr_t address,
size_t pageSize,
size_t payloadSpan,
size_t queryCount,
157 auto* writeVectors =
reinterpret_cast<iovec*
>(address);
158 auto* readVectors = writeVectors + 2;
159 char* path =
reinterpret_cast<char*
>(readVectors + 2);
160 constexpr char Path[] =
"/syscall-profile";
161 MemoryCopy(path, Path,
sizeof(Path));
162 auto* source =
reinterpret_cast<uint8_t*
>(address + pageSize);
163 auto* destination = source + payloadSpan;
164 writeVectors[0] = {source, PayloadSize / 2};
165 writeVectors[1] = {source + PayloadSize / 2, PayloadSize / 2};
166 readVectors[0] = {destination, PayloadSize / 2};
167 readVectors[1] = {destination + PayloadSize / 2, PayloadSize / 2};
170 dispatch(PedigreeLinuxAmd64Syscall_open,
reinterpret_cast<uintptr_t
>(path),
171 O_CREAT | O_RDWR | O_TRUNC, 0600);
172 if (opened.error ||
static_cast<int64_t
>(opened.result) < 0) {
173 ERROR(
"HOSTED-PROFILE: FAIL open result=" <<
Dec <<
static_cast<int64_t
>(opened.result)
174 <<
" errno=" << opened.error);
177 const int fd =
static_cast<int>(opened.result);
180 bool passed = checkedCall(PedigreeLinuxAmd64Syscall_writev, PayloadSize, fd,
181 reinterpret_cast<uintptr_t
>(writeVectors), 2) &&
182 verifyPayload(fd, readVectors, source, destination);
183 for (
size_t repetition = 0; repetition < Repetitions && passed; ++repetition) {
184 for (
size_t i = 0; i < PayloadSize; ++i) {
185 source[i] =
static_cast<uint8_t
>(i * 37 + repetition + 1);
187 passed = hostedProfileGetuid(repetition, queryCount) &&
188 hostedProfileLseek(repetition, ioCount, fd) &&
189 hostedProfileWritev(repetition, ioCount, fd, writeVectors) &&
190 verifyPayload(fd, readVectors, source, destination);
192 ByteSet(destination, 0, PayloadSize);
193 passed = hostedProfileReadv(repetition, ioCount, fd, readVectors);
194 for (
size_t i = 0; i < PayloadSize && passed; ++i) {
195 if (source[i] != destination[i]) {
196 ERROR(
"HOSTED-PROFILE: FAIL readv payload offset=" <<
Dec << i);
202 const bool closed = checkedCall(PedigreeLinuxAmd64Syscall_close, 0, fd);
203 return passed && closed;
206struct ProfileContext {
214int profileWorker(
void* parameter) {
215 auto* context =
static_cast<ProfileContext*
>(parameter);
216 if (context->getuidOnly) {
217 context->passed =
true;
218 for (
size_t i = 0; i < 1000 && context->passed; ++i) {
219 context->passed = checkedCall(PedigreeLinuxAmd64Syscall_getuid, ProfileUid);
221 for (
size_t repetition = 0; repetition < Repetitions && context->passed; ++repetition) {
222 context->passed = hostedProfileGetuid(repetition, context->queryCount);
224 context->returned += 1;
225 return context->passed ? 0 : 1;
230 const size_t payloadSpan = (PayloadSize + pageSize - 1) & ~(pageSize - 1);
231 const size_t length = pageSize + payloadSpan * 2;
232 uintptr_t address = 0;
233 if (!process->allocateUserRange(Process::UserRegion::Normal, length, address)) {
234 ERROR(
"HOSTED-PROFILE: FAIL user-range");
235 context->returned += 1;
238 uintptr_t mappedAddress = address;
240 mappedAddress, length, MemoryMappedObject::Read | MemoryMappedObject::Write);
241 if (mapping && mappedAddress == address) {
242 ByteSet(
reinterpret_cast<void*
>(address), 0, length);
245 runMappedWork(address, pageSize, payloadSpan, context->queryCount, context->ioCount);
247 ERROR(
"HOSTED-PROFILE: FAIL user-mapping");
250 process->freeUserRange(Process::UserRegion::Normal, address, length);
251 context->returned += 1;
252 return context->passed ? 0 : 1;
256bool hostedRunSyscallProfile() {
257 const char* phase = getenv(
"PEDIGREE_HOSTED_PROFILE_PHASE");
258 const bool getuidOnly = phase && !strcmp(phase,
"getuid");
259 if (phase && *phase && strcmp(phase,
"all") && !getuidOnly) {
260 ERROR(
"HOSTED-PROFILE: FAIL phase must be all or getuid");
263 if (!runHostedVasRegressions()) {
264 ERROR(
"HOSTED-PROFILE: FAIL address-space regressions");
267 if (!runHostedVmOperationGuardRegressions()) {
268 ERROR(
"HOSTED-PROFILE: FAIL operation-guard regressions");
271 if (!runHostedPostSyscallRegressions()) {
272 ERROR(
"HOSTED-PROFILE: FAIL post-syscall regressions");
276 if (!driver || !driver->runHostedStateCleanupRegression()) {
277 ERROR(
"HOSTED-PROFILE: FAIL state-cleanup regressions");
280 NOTICE(
"HOSTED-PROFILE: PASS state-cleanup regressions");
281 if (!runHostedAccountingRegressions()) {
282 ERROR(
"HOSTED-PROFILE: FAIL accounting regressions");
285 const size_t divisor = hostedSyscallProfileDivisor();
286 size_t queryCount = QueryCount / divisor;
287 size_t ioCount = IoCount / divisor;
292#if PEDIGREE_HOSTED_FUNCTION_PROFILE
293 queryCount = hostedFunctionProfileCount(queryCount);
294 ioCount = hostedFunctionProfileCount(ioCount);
296 NOTICE(
"HOSTED-PROFILE: BEGIN scope=kernel-origin user-entry-return=excluded abi=linux");
297 NOTICE(
"HOSTED-PROFILE: config uid=123 payload_bytes=4096 iov_count=2 repetitions=3 divisor="
298 <<
Dec << divisor <<
" phase=" << (getuidOnly ?
"getuid" :
"all"));
300 if (!kernelProcess) {
301 ERROR(
"HOSTED-PROFILE: FAIL kernel-process");
306 VFS::HostedRootViewScope fixture;
307 if (!filesystem.
initialise(
nullptr) || !fixture.open(&filesystem)) {
308 ERROR(
"HOSTED-PROFILE: FAIL ramfs-root");
311 auto* process =
new PosixProcess(kernelProcess,
true, Process::FilesystemContextMode::Deferred);
313 process->setSubsystem(subsystem);
314 subsystem->setAbi(PosixSubsystem::LinuxAbi);
315 process->setUserId(ProfileUid);
316 process->setEffectiveUserId(ProfileUid);
317 const bool contextInstalled = fixture.installContext(*process);
319 ProfileContext context{queryCount, ioCount, getuidOnly};
320 auto*
worker =
new Thread(process, profileWorker, &context,
nullptr,
false,
true,
true);
321 worker->setName(
"hosted syscall profile");
323 const bool started = contextInstalled &&
worker->start();
324 const bool joined = started &&
worker->joinForCompletion();
327 }
else if (!joined) {
328 FATAL(
"HOSTED-PROFILE: FAIL worker lifetime barrier");
330 const bool passed = started && joined && context.returned == 1 && context.passed;
332 if (!fixture.close()) {
333 FATAL(
"HOSTED-PROFILE: FAIL retained filesystem owners");
336 NOTICE(
"HOSTED-PROFILE: PASS all");
338 ERROR(
"HOSTED-PROFILE: FAIL fixture");
Memory-mapped file interface.
MemoryMappedObject * mapAnon(uintptr_t &address, size_t length, MemoryMappedObject::Permissions perms)
size_t remove(uintptr_t base, size_t length)
static MemoryMapManager & instance()
static constexpr size_t getPageSize() PURE
static ProcessorInformation & information()
virtual bool initialise(Disk *pDisk) override
static Scheduler & instance()
void setErrno(size_t err)
Process * getParent() const