2#include "pedigree/kernel/LatencyAccounting.h"
3#include "pedigree/kernel/Metrics.h"
4#include "pedigree/kernel/machine/Disk.h"
5#include "pedigree/kernel/process/PerProcessorScheduler.h"
6#include "pedigree/kernel/process/Scheduler.h"
7#include "pedigree/kernel/process/Thread.h"
8#include "pedigree/kernel/processor/PhysicalMemoryManager.h"
9#include "pedigree/kernel/processor/Processor.h"
10#include "pedigree/kernel/syscallError.h"
11#include "pedigree/kernel/time/Time.h"
12#include "pedigree/kernel/utilities/StaticString.h"
14#include "DevFs-block.h"
15#include "PosixProcess.h"
16#include "PosixSubsystem.h"
18#include "file-metadata.h"
19#include "metadata-abi.h"
20#include "modules/system/vfs/MountView.h"
21#include "modules/system/vfs/VFS.h"
25constexpr uint64_t ClockTicksPerSecond = 100;
26constexpr uint64_t NanosecondsPerClockTick = Time::Multiplier::Second / ClockTicksPerSecond;
35TaskCounts taskCounts();
37class GeneratedFile :
public File {
39 GeneratedFile(
const String& name, uintptr_t inode,
ProcFs& filesystem,
File* parent)
40 :
File(name, 0, 0, 0, inode, &filesystem, 0, parent) {
46 uint64_t
readBytewise(uint64_t location, uint64_t size, uintptr_t buffer,
bool)
override {
48 if (!generate(contents) || location >= contents.length())
50 if (size > contents.length() - location)
51 size = contents.length() - location;
52 MemoryCopy(
reinterpret_cast<void*
>(buffer), contents.cstr() + location, size);
56 uint64_t
writeBytewise(uint64_t, uint64_t, uintptr_t,
bool)
override {
60 size_t getSize()
override {
62 return generate(contents) ? contents.length() : 0;
69 virtual bool generate(
String& contents)
const = 0;
72class MetricsSnapshotFile final :
public File {
74 MetricsSnapshotFile(uintptr_t inode,
ProcFs& filesystem,
File* parent,
String&& contents)
75 :
File(
String(
"metrics"), 0, 0, 0, inode, &filesystem, contents.length(), parent),
76 m_Contents(pedigree_std::move(contents)) {
82 uint64_t
readBytewise(uint64_t location, uint64_t size, uintptr_t buffer,
bool)
override {
83 if (location >= m_Contents.length()) {
86 size = min(size, uint64_t(m_Contents.length() - location));
87 MemoryCopy(
reinterpret_cast<void*
>(buffer), m_Contents.cstr() + location, size);
91 uint64_t
writeBytewise(uint64_t, uint64_t, uintptr_t,
bool)
override {
92 SYSCALL_ERROR(PermissionDenied);
105class MetricsFile final :
public File {
107 MetricsFile(uintptr_t inode,
ProcFs& filesystem,
File* parent)
108 :
File(
String(
"metrics"), 0, 0, 0, inode, &filesystem, 0, parent) {
119 SYSCALL_ERROR(OutOfMemory);
123 for (
size_t cpu = 0; cpu < cpus; ++cpu) {
124 Metrics::snapshot(cpu, snapshots.get()[cpu]);
127 const uint64_t uptime = Time::getTicks();
129 uptimeText.append(uptime / Time::Multiplier::Second);
130 uptimeText.append(
".");
131 uptimeText.append(uptime % Time::Multiplier::Second, 10, 9);
134 "# TYPE pedigree_metrics_enabled gauge\npedigree_metrics_enabled %u\n"
135 "# TYPE pedigree_cpus gauge\npedigree_cpus %lu\n"
136 "# TYPE pedigree_uptime_seconds gauge\npedigree_uptime_seconds %s\n",
137 unsigned(PEDIGREE_METRICS), cpus,
static_cast<const char*
>(uptimeText));
139 const TaskCounts tasks = taskCounts();
143 "# HELP pedigree_memory_managed_bytes Physical memory managed by the page allocator.\n"
144 "# TYPE pedigree_memory_managed_bytes gauge\npedigree_memory_managed_bytes %lu\n",
148 "# HELP pedigree_memory_free_bytes Physical memory currently free in the page allocator.\n"
149 "# TYPE pedigree_memory_free_bytes gauge\npedigree_memory_free_bytes %lu\n",
153 "# HELP pedigree_processes Processes registered with the scheduler.\n"
154 "# TYPE pedigree_processes gauge\npedigree_processes %lu\n",
158 "# HELP pedigree_threads Non-idle threads registered with a process.\n"
159 "# TYPE pedigree_threads gauge\npedigree_threads %lu\n",
163 "# HELP pedigree_runnable_threads Non-idle threads ready or running.\n"
164 "# TYPE pedigree_runnable_threads gauge\npedigree_runnable_threads %lu\n",
170 Metrics::Counter counter;
173 static const Counter counters[] = {
174 {
"pedigree_scheduler_schedules_total", Metrics::Schedule,
"Scheduler selection calls."},
175 {
"pedigree_scheduler_yields_total", Metrics::Yield,
"Explicit scheduler yield calls."},
176 {
"pedigree_scheduler_context_switches_total", Metrics::ContextSwitch,
177 "Context switches to another thread."},
178 {
"pedigree_scheduler_same_thread_selections_total", Metrics::SameThread,
179 "Scheduler selections that kept the current thread."},
180 {
"pedigree_scheduler_idle_selections_total", Metrics::IdleSelection,
181 "Context switches selecting an idle thread."},
182 {
"pedigree_scheduler_timer_callbacks_total", Metrics::Timer,
"Scheduler timer callbacks."},
183 {
"pedigree_scheduler_reschedule_services_total", Metrics::RescheduleService,
184 "Pending scheduling and IRQ work service calls, including calls without pending work."},
185 {
"pedigree_scheduler_worker_wakeups_total", Metrics::WorkerWake,
186 "Successful IRQ worker wakeups."},
188 {
"pedigree_interrupt_entries_total", Metrics::Interrupt,
189 "x86-64 interrupt handler entries for vectors 32 and above."},
190 {
"pedigree_exception_entries_total", Metrics::Exception,
191 "x86-64 exception handler entries for vectors below 32."},
192 {
"pedigree_syscalls_total", Metrics::Syscall,
"x86-64 C++ syscall dispatch entries."},
194 {
"pedigree_process_created_total", Metrics::ProcessCreated,
195 "Completed process constructions."},
196 {
"pedigree_process_exited_total", Metrics::ProcessExited,
197 "Processes published as terminated."},
198 {
"pedigree_process_destroyed_total", Metrics::ProcessDestroyed,
199 "Completed process destructions."},
200 {
"pedigree_thread_created_total", Metrics::ThreadCreated,
201 "Threads registered with a process, including idle threads."},
202 {
"pedigree_thread_exit_started_total", Metrics::ThreadExitStarted,
203 "Threads beginning shutdown for the first time."},
204 {
"pedigree_thread_reapable_total", Metrics::ThreadReapable,
205 "Threads retired from their execution stack."},
206 {
"pedigree_thread_destroyed_total", Metrics::ThreadDestroyed,
207 "Completed thread destructions."},
208 {
"pedigree_wait_queue_waits_total", Metrics::WaitQueueWait,
209 "Waiter enrollments in wait queues."},
210 {
"pedigree_wait_queue_unqueues_total", Metrics::WaitQueueUnqueue,
211 "Waiter removals from wait queues."},
212 {
"pedigree_wait_queue_wakes_total", Metrics::WaitQueueWake,
213 "First successful completion of an enrolled waiter."},
214 {
"pedigree_wait_queue_early_wakes_total", Metrics::WaitQueueEarlyWake,
215 "Waiter completions before the thread entered sleeping state."},
216 {
"pedigree_wait_queue_cancels_total", Metrics::WaitQueueCancel,
217 "Enrolled waiters removed by cancellation."},
218 {
"pedigree_wait_queue_requeues_total", Metrics::WaitQueueRequeue,
219 "Waiter moves to another channel."},
220 {
"pedigree_semaphore_contended_acquires_total", Metrics::SemaphoreContended,
221 "Semaphore acquires that missed the initial fast attempts."},
222 {
"pedigree_physical_page_allocations_total", Metrics::PhysicalPageAlloc,
223 "Successful single-page allocation calls after CPU initialization."},
224 {
"pedigree_physical_page_allocation_failures_total", Metrics::PhysicalPageAllocFailure,
225 "Failed single-page allocation calls after CPU initialization."},
226 {
"pedigree_physical_page_frees_total", Metrics::PhysicalPageFree,
227 "Pages returned to the allocator after their last reference."},
228 {
"pedigree_memory_pressure_passes_total", Metrics::MemoryPressurePass,
229 "Admitted memory pressure compact passes."},
230 {
"pedigree_memory_pressure_successes_total", Metrics::MemoryPressurePassSuccess,
231 "Memory pressure passes with a handler reporting success."},
232 {
"pedigree_memory_pressure_kill_requests_total", Metrics::MemoryPressureKill,
233 "Victim kill calls made under memory pressure."},
234 {
"pedigree_page_fault_entries_total", Metrics::PageFault,
235 "Architecture page-fault handler entries."},
236 {
"pedigree_page_fault_copy_on_write_total", Metrics::PageFaultCopyOnWrite,
237 "Page faults resolved by copy-on-write."},
238 {
"pedigree_page_fault_handled_total", Metrics::PageFaultHandled,
239 "Page faults resolved by a registered trap handler."},
240 {
"pedigree_page_fault_deferred_total", Metrics::PageFaultDeferred,
241 "Page faults deferred to a userspace subsystem."},
242 {
"pedigree_cache_lookup_hits_total", Metrics::CacheLookupHit,
243 "Successful Cache::lookup calls."},
244 {
"pedigree_cache_lookup_misses_total", Metrics::CacheLookupMiss,
245 "Cache::lookup calls without an available page."},
246 {
"pedigree_cache_read_bytes_total", Metrics::CacheReadBytes,
247 "Bytes returned by Cache::read."},
248 {
"pedigree_cache_evicted_pages_total", Metrics::CacheEvictedPages,
249 "Cache pages successfully retired."},
250 {
"pedigree_cache_writeback_pages_total", Metrics::CacheWritebackPages,
251 "Cache pages submitted to backing-store writeback callbacks."},
252 {
"pedigree_cache_writeback_failures_total", Metrics::CacheWritebackFailures,
253 "Cache pages whose backing-store writeback callback failed."},
254 {
"pedigree_network_rx_packets_total", Metrics::NetworkRxAccepted,
255 "Received packets accepted by the network stack."},
256 {
"pedigree_network_rx_bytes_total", Metrics::NetworkRxBytes,
257 "Received bytes accepted by the network stack."},
258 {
"pedigree_network_rx_no_device_total", Metrics::NetworkRxNoDevice,
259 "Received packets dropped without a registered device."},
260 {
"pedigree_network_rx_filtered_total", Metrics::NetworkRxFiltered,
261 "Received packets rejected by the network filter."},
262 {
"pedigree_network_rx_no_buffer_total", Metrics::NetworkRxNoBuffer,
263 "Received packets dropped for lack of a network buffer."},
264 {
"pedigree_network_rx_input_failures_total", Metrics::NetworkRxInputFailed,
265 "Received packets rejected by the network stack input."},
266 {
"pedigree_network_tx_packets_total", Metrics::NetworkTxAccepted,
267 "Transmitted packets accepted by a device send call."},
268 {
"pedigree_network_tx_bytes_total", Metrics::NetworkTxBytes,
269 "Transmitted bytes accepted by a device send call."},
270 {
"pedigree_network_tx_filtered_total", Metrics::NetworkTxFiltered,
271 "Transmitted packets rejected by the network filter."},
272 {
"pedigree_network_tx_send_failures_total", Metrics::NetworkTxSendFailed,
273 "Transmitted packets rejected by a device send call."},
274 {
"pedigree_file_read_calls_total", Metrics::FileReadCalls,
275 "File::read calls, including cached reads through that path."},
276 {
"pedigree_file_cached_read_calls_total", Metrics::FileCachedReadCalls,
277 "Direct File::readCached calls."},
278 {
"pedigree_file_read_bytes_total", Metrics::FileReadBytes,
279 "Bytes returned by file read calls, counted once per path."},
280 {
"pedigree_file_write_calls_total", Metrics::FileWriteCalls,
281 "File write and append calls."},
282 {
"pedigree_file_write_bytes_total", Metrics::FileWriteBytes,
283 "Bytes returned by file write and append calls."},
284 {
"pedigree_vfs_find_calls_total", Metrics::VfsFindCalls,
"VFS::find calls."},
285 {
"pedigree_vfs_find_misses_total", Metrics::VfsFindMisses,
286 "VFS::find calls returning no file."},
287 {
"pedigree_vfs_sync_calls_total", Metrics::VfsSyncCalls,
288 "Filesystem backend sync invocations."},
289 {
"pedigree_vfs_sync_unsupported_total", Metrics::VfsSyncUnsupported,
290 "Filesystem backend sync invocations reporting unsupported."},
291 {
"pedigree_vfs_sync_failures_total", Metrics::VfsSyncFailed,
292 "Filesystem backend sync invocations that failed for another reason."},
294 for (
const Counter& counter : counters) {
295 line.Format(
"# HELP %s %s\n# TYPE %s counter\n", counter.name, counter.help, counter.name);
297 for (
size_t cpu = 0; cpu < cpus; ++cpu) {
299 value.append(snapshots.get()[cpu].values[counter.counter]);
300 line.Format(
"%s{cpu=\"%lu\"} %s\n", counter.name, cpu,
static_cast<const char*
>(value));
304 for (
unsigned contended = 0; contended < 2; ++contended) {
305 const char* name = contended ?
"pedigree_spinlock_contended_acquires_total"
306 :
"pedigree_spinlock_acquires_total";
307 const char* help = contended
308 ?
"Successful acquisitions that paused at least once while waiting."
309 :
"Successful acquisition calls, including recursive acquisitions.";
310 line.Format(
"# HELP %s %s\n# TYPE %s counter\n", name, help, name);
312 for (
size_t cpu = 0; cpu < cpus; ++cpu) {
313 const auto counter = contended ? Metrics::SpinlockContended : Metrics::SpinlockAcquire;
315 value.append(snapshots.get()[cpu].values[counter]);
316 line.Format(
"%s{cpu=\"%lu\",policy=\"no_irq\"} %s\n", name, cpu,
317 static_cast<const char*
>(value));
322 contents +=
String(
"# EOF\n");
323 auto* snapshot =
new MetricsSnapshotFile(getInode(), *
static_cast<ProcFs*
>(getFilesystem()),
324 getParent(), pedigree_std::move(contents));
327 SYSCALL_ERROR(OutOfMemory);
330 owner.adopt(snapshot);
335TaskCounts taskCounts() {
339 result.processes = processCount;
340 for (
size_t index = 0; index < processCount; ++index) {
345 if (process->getType() == Process::Posix && process->
getUserspaceId() > result.lastPid) {
349 for (
size_t threadIndex = 0; threadIndex < threadCount; ++threadIndex) {
355 if (owner && owner->isIdleThread(thread.get())) {
360 if (status == Thread::Ready || status == Thread::Running) {
368class KernelThreadsFile final :
public GeneratedFile {
370 KernelThreadsFile(uintptr_t inode,
ProcFs& filesystem,
File* parent)
371 : GeneratedFile(
String(
"threads"), inode, filesystem, parent) {}
374 bool generate(
String& contents)
const override {
376 contents =
String(
"process_id task_id cpu state priority user_ns system_ns idle name\n");
379 for (
size_t index = 0; index < processCount; ++index) {
381 if (!scheduler.
acquireProcess(process, index) || process->getType() == Process::Posix) {
385 for (
size_t threadIndex = 0; threadIndex < threadCount; ++threadIndex) {
392 const char state = status == Thread::Zombie ?
'Z'
393 : status == Thread::Ready || status == Thread::Running ?
'R'
397 const String& description = thread->getName();
398 const size_t length = min(description.length(),
sizeof(name) - 1);
399 for (
size_t i = 0; i < length; ++i) {
400 const char value = description[i];
401 name[i] = value <
' ' || value == 127 ?
' ' : value;
406 line.Format(
"%20lu %20lu %20lu %c %20lu %20lu %20lu %u ", process->
getId(),
407 thread->getTaskId(), owner ? owner->logicalCpu() : 0, state,
408 thread->getPriority(), thread->getUserTime(), thread->getKernelTime(),
409 owner && owner->isIdleThread(thread.get()) ? 1U : 0U);
411 contents +=
String(length ? name :
"unnamed");
419class LatencyFile final :
public GeneratedFile {
421 LatencyFile(uintptr_t inode,
ProcFs& filesystem,
File* parent)
422 : GeneratedFile(
String(
"latency"), inode, filesystem, parent) {
424 setPermissionsOnly(FILE_UR);
428 bool generate(
String& contents)
const override {
429 contents.Format(
"enabled %u\n",
unsigned(PEDIGREE_LATENCY_ACCOUNTING));
431 "cpu sample_ns valid online_since_ns irq_off_ns irq_off_count irq_off_max_ns "
432 "irq_off_max_end_ns irq_off_max_site irq_off_ge_1ms irq_off_ge_10ms "
433 "irq_off_open_since_ns irq_off_open_site event_deferred_cpu_ns "
434 "event_deferred_thread_ns event_deferred_count event_deferred_max_ns\n");
437 const bool valid = LatencyAccounting::snapshot(cpu, snapshot);
439 line.Format(
"%20lu %20lu %u", cpu, Time::getTicks(),
unsigned(valid));
441 for (
size_t field = 0; field < LatencyAccounting::Count; ++field) {
442 line.Format(
" %20lu", snapshot.values[field]);
451class LoadAverageFile final :
public GeneratedFile {
453 LoadAverageFile(uintptr_t inode,
ProcFs& filesystem,
File* parent)
454 : GeneratedFile(
String(
"loadavg"), inode, filesystem, parent) {}
457 bool generate(
String& contents)
const override {
459 const TaskCounts tasks = taskCounts();
460 uint64_t whole[3], fraction[3];
461 for (
size_t i = 0; i < 3; ++i) {
462 const uint64_t hundredths =
463 (activity.loads[i] * 100 + LoadAverage::Scale / 2) / LoadAverage::Scale;
464 whole[i] = hundredths / 100;
465 fraction[i] = hundredths % 100;
467 contents.Format(
"%lu.%02lu %lu.%02lu %lu.%02lu %lu/%lu %lu\n", whole[0], fraction[0], whole[1],
468 fraction[1], whole[2], fraction[2], tasks.runnable, tasks.total, tasks.lastPid);
473class SystemStatFile final :
public GeneratedFile {
475 SystemStatFile(uintptr_t inode,
ProcFs& filesystem,
File* parent)
476 : GeneratedFile(
String(
"stat"), inode, filesystem, parent) {}
479 bool generate(
String& contents)
const override {
481 const TaskCounts tasks = taskCounts();
482 const uint64_t user = activity.userNanoseconds / NanosecondsPerClockTick;
483 const uint64_t kernel = activity.kernelNanoseconds / NanosecondsPerClockTick;
484 const uint64_t idle = activity.idleNanoseconds / NanosecondsPerClockTick;
485 const uint64_t uptime = Time::getTicks() / Time::Multiplier::Second;
486 const uint64_t realtime = Time::getTime();
487 const uint64_t bootTime = realtime > uptime ? realtime - uptime : 0;
489 "cpu %lu 0 %lu %lu 0 0 0 0 0 0\n"
491 "procs_running %lu\n"
493 user, kernel, idle, bootTime, tasks.runnable);
498class CpuInfoFile final :
public GeneratedFile {
500 CpuInfoFile(uintptr_t inode,
ProcFs& filesystem,
File* parent)
501 : GeneratedFile(
String(
"cpuinfo"), inode, filesystem, parent) {}
504 bool generate(
String& contents)
const override {
509 "vendor_id\t: Pedigree\n"
510 "model name\t: Pedigree virtual processor\n"
519class PartitionsFile final :
public GeneratedFile {
521 PartitionsFile(uintptr_t inode,
ProcFs& filesystem,
File* parent)
522 : GeneratedFile(
String(
"partitions"), inode, filesystem, parent) {}
525 bool generate(
String& contents)
const override {
526 contents =
String(
"major minor #blocks name\n\n");
527 uint32_t ids[DiskEndpoints::Capacity];
528 const size_t count = DiskEndpoints::snapshot(ids, DiskEndpoints::Capacity);
529 for (
size_t index = 0; index < count; ++index) {
531 if (!DiskEndpoints::describe(ids[index], bytes))
534 line.Format(
"%5u %5u %9lu disk%u\n", PosixBlock::PhysicalMajor, ids[index], bytes / 1024,
542class ProcessFile :
public GeneratedFile {
544 ProcessFile(
const String& name, uintptr_t inode,
ProcFs& filesystem,
File* parent,
size_t pid)
545 : GeneratedFile(name, inode, filesystem, parent), m_Pid(pid) {}
550 process, m_Pid,
static_cast<ProcFs*
>(getFilesystem())->pidNamespace().get()) &&
551 process->getType() == Process::Posix;
555 return static_cast<ProcFs*
>(getFilesystem())->pidNamespace().get();
565 for (
size_t i = 0; i < description.length(); ++i)
566 if (description[i] ==
'/')
568 size_t length = description.length() - start;
571 return String(
static_cast<const char*
>(description) + start, length);
588char processState(
Process& process) {
589 const auto state = process.getState();
590 if (state == Process::Suspended)
594 bool sleeping =
false;
596 for (
size_t i = 0; i < count; ++i) {
601 if (status == Thread::Ready || status == Thread::Running)
603 if (status == Thread::Sleeping || status == Thread::AwaitingJoin)
606 return sleeping ?
'S' :
'R';
611 for (
size_t i = 0; i < value.length(); ++i) {
626 result +=
String(value.cstr() + i, 1);
633class ProcessMountInfoFile final :
public ProcessFile {
635 ProcessMountInfoFile(uintptr_t inode,
ProcFs& filesystem,
File* parent,
size_t pid)
636 : ProcessFile(
String(
"mountinfo"), inode, filesystem, parent, pid) {}
639 bool generate(
String& contents)
const override {
641 if (!acquire(process)) {
644 auto context = process->acquireFilesystemContext();
645 auto* view = VfsMountView::fromContext(context);
647 if (!view || !view->snapshotMounts(context, mounts)) {
650 for (
const auto& mount : mounts) {
652 if (!mount.backing.acquire(backing)) {
655 struct stat attributes = {};
656 if (!posix_stat_file(
"", backing.filesystem()->
getRoot(), &attributes)) {
659 const unsigned major = PosixMetadata::deviceMajor(attributes.st_dev);
660 const unsigned minor = PosixMetadata::deviceMinor(attributes.st_dev);
661 String options(mount.flags & VfsMountView::ReadOnly ?
"ro" :
"rw");
662 if (mount.flags & VfsMountView::NoSuid) {
665 if (mount.flags & VfsMountView::NoDev) {
668 if (mount.flags & VfsMountView::NoExec) {
671 const String root = mountinfoEscape(mount.root);
672 const String path = mountinfoEscape(mount.path);
675 line.Format(
"%lu %lu %u:%u %s %s %s - %s none %s\n", mount.id, mount.parentId, major, minor,
676 root.cstr(), path.cstr(),
options.cstr(), kind.cstr(),
677 mount.flags & VfsMountView::ReadOnly ?
"ro" :
"rw");
684class ProcessStatFile final :
public ProcessFile {
686 ProcessStatFile(uintptr_t inode,
ProcFs& filesystem,
File* parent,
size_t pid)
687 : ProcessFile(
String(
"stat"), inode, filesystem, parent, pid) {}
690 bool generate(
String& contents)
const override {
694 auto& process = *
static_cast<PosixProcess*
>(lease.get());
695 size_t processGroup = 0;
696 process.getProcessGroupId(processGroup, pidNamespace());
697 const uint64_t user = process.
getUserTime() / NanosecondsPerClockTick;
698 const uint64_t kernel = process.getKernelTime() / NanosecondsPerClockTick;
699 const uint64_t childrenUser = process.getReapedChildrenUserTime() / NanosecondsPerClockTick;
700 const uint64_t childrenKernel = process.getReapedChildrenKernelTime() / NanosecondsPerClockTick;
701 const uint64_t start = process.getStartTimeTicks() / NanosecondsPerClockTick;
702 const ssize_t virtualPages = process.getVirtualPageCount();
703 const ssize_t residentPages = process.getPhysicalPageCount();
704 const uint64_t virtualBytes = virtualPages > 0 ?
static_cast<uint64_t
>(virtualPages) *
707 const long resident = residentPages > 0 ? residentPages : 0;
710 "%lu (%s) %c %lu %lu %lu 0 0 0 0 0 0 0 %lu %lu %lu %lu 20 0 %lu 0 %lu %lu %ld"
715 process.
getUserspaceId(pidNamespace()), processName(process).cstr(), processState(process),
716 parentId(process, pidNamespace()), processGroup, process.getSessionId(pidNamespace()), user,
717 kernel, childrenUser, childrenKernel, process.
getNumThreads(), start, virtualBytes,
723class ProcessStatmFile final :
public ProcessFile {
725 ProcessStatmFile(uintptr_t inode,
ProcFs& filesystem,
File* parent,
size_t pid)
726 : ProcessFile(
String(
"statm"), inode, filesystem, parent, pid) {}
729 bool generate(
String& contents)
const override {
731 if (!acquire(process))
733 const ssize_t total = process->getVirtualPageCount();
734 const ssize_t resident = process->getPhysicalPageCount();
735 const ssize_t shared = process->getSharedPageCount();
736 contents.Format(
"%ld %ld %ld 0 0 0 0\n", total > 0 ? total : 0, resident > 0 ? resident : 0,
737 shared > 0 ? shared : 0);
742class ProcessStatusFile final :
public ProcessFile {
744 ProcessStatusFile(uintptr_t inode,
ProcFs& filesystem,
File* parent,
size_t pid)
745 : ProcessFile(
String(
"status"), inode, filesystem, parent, pid) {}
748 bool generate(
String& contents)
const override {
752 auto& process = *
static_cast<PosixProcess*
>(lease.get());
753 const auto credentials = process.snapshotCredentials();
754 const ssize_t virtualPages = process.getVirtualPageCount();
755 const ssize_t residentPages = process.getPhysicalPageCount();
763 "Uid:\t%lu\t%lu\t%lu\t%lu\n"
764 "Gid:\t%lu\t%lu\t%lu\t%lu\n"
768 processName(process).cstr(), processState(process), process.
getUserspaceId(pidNamespace()),
769 process.
getUserspaceId(pidNamespace()), parentId(process, pidNamespace()), credentials.ruid,
770 credentials.euid, credentials.suid, credentials.euid, credentials.rgid, credentials.egid,
772 virtualPages > 0 ?
static_cast<uint64_t
>(virtualPages) * kilobytesPerPage : 0,
773 residentPages > 0 ? static_cast<uint64_t>(residentPages) * kilobytesPerPage : 0);
778class ProcessCommandLineFile final :
public ProcessFile {
780 ProcessCommandLineFile(uintptr_t inode,
ProcFs& filesystem,
File* parent,
size_t pid)
781 : ProcessFile(
String(
"cmdline"), inode, filesystem, parent, pid) {}
783 uint64_t readBytewise(uint64_t location, uint64_t size, uintptr_t buffer,
bool)
override {
785 if (!snapshot(arguments))
788 for (
const auto& argument : arguments) {
789 const uint64_t fieldSize = argument.length() + 1;
790 if (location >= fieldSize) {
791 location -= fieldSize;
794 const uint64_t available = fieldSize - location;
795 const uint64_t amount = min(size - copied, available);
796 const char* field = argument.length() ? argument.cstr() :
"";
797 MemoryCopy(
reinterpret_cast<void*
>(buffer + copied), field + location, amount);
806 size_t getSize()
override {
808 if (!snapshot(arguments))
811 for (
const auto& argument : arguments)
812 size += argument.length() + 1;
821 auto* subsystem =
static_cast<PosixSubsystem*
>(lease->getSubsystem());
822 return subsystem && subsystem->commandLine(arguments);
825 bool generate(
String& contents)
const override {
833 auto* loadAverage =
new LoadAverageFile(filesystem.getNextInode(), filesystem, &root);
834 auto* stat =
new SystemStatFile(filesystem.getNextInode(), filesystem, &root);
835 auto* cpuInfo =
new CpuInfoFile(filesystem.getNextInode(), filesystem, &root);
836 auto* partitions =
new PartitionsFile(filesystem.getNextInode(), filesystem, &root);
837 auto* metrics =
new MetricsFile(filesystem.getNextInode(), filesystem, &root);
839 &filesystem, 0, &root);
841 kernel ?
new KernelThreadsFile(filesystem.getNextInode(), filesystem, kernel) : nullptr;
842 auto* latency = kernel ?
new LatencyFile(filesystem.getNextInode(), filesystem, kernel) : nullptr;
843 if (!loadAverage || !stat || !cpuInfo || !partitions || !metrics || !kernel || !threads ||
855 root.addEntry(loadAverage->getName(), loadAverage);
856 root.addEntry(stat->getName(), stat);
857 root.addEntry(cpuInfo->getName(), cpuInfo);
858 root.addEntry(partitions->getName(), partitions);
859 root.addEntry(metrics->getName(), metrics);
860 kernel->setPermissions(FILE_UR | FILE_UX | FILE_GR | FILE_GX | FILE_OR | FILE_OX);
861 kernel->addEntry(threads->getName(), threads);
862 kernel->addEntry(latency->getName(), latency);
863 root.addEntry(kernel->getName(), kernel);
868 auto* stat =
new ProcessStatFile(filesystem.getNextInode(), filesystem, &directory, pid);
869 auto* statm =
new ProcessStatmFile(filesystem.getNextInode(), filesystem, &directory, pid);
870 auto* status =
new ProcessStatusFile(filesystem.getNextInode(), filesystem, &directory, pid);
872 new ProcessCommandLineFile(filesystem.getNextInode(), filesystem, &directory, pid);
874 new ProcessMountInfoFile(filesystem.getNextInode(), filesystem, &directory, pid);
875 if (!stat || !statm || !status || !commandLine || !mountinfo) {
883 directory.addEntry(stat->getName(), stat);
884 directory.addEntry(statm->getName(), statm);
885 directory.addEntry(status->getName(), status);
886 directory.addEntry(commandLine->getName(), commandLine);
887 directory.addEntry(mountinfo->getName(), mountinfo);
virtual uint64_t readBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true)
virtual bool isBytewise() const
void setGidOnly(size_t gid)
void setPermissionsOnly(uint32_t perms)
void setUidOnly(size_t uid)
virtual uint64_t writeBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true)
virtual File * openForDescriptor(RetainedFile &owner)
virtual const String & getVolumeLabel() const =0
virtual File * getRoot() const =0
static PhysicalMemoryManager & instance()
static constexpr size_t getPageSize() PURE
size_t getUserspaceId() const
@ Reaped
Terminal wait status is visible; the owner may still be on-stack.
LargeStaticString & description()
MUST_USE_RESULT bool acquireThread(ThreadLease &lease, size_t n)
Time::Timestamp getUserTime() const
This class manages how processes and threads are scheduled across processors.
static Scheduler & instance()
MUST_USE_RESULT bool acquireProcess(ProcessLease &lease, size_t n)
class PerProcessorScheduler * getScheduler() const
A vector / dynamic array.