PatchworkOS  28a9544
A non-POSIX operating system.
Loading...
Searching...
No Matches
perf.c
Go to the documentation of this file.
2
3#include <kernel/cpu/cpu.h>
4#include <kernel/fs/file.h>
5#include <kernel/fs/sysfs.h>
6#include <kernel/fs/vfs.h>
7#include <kernel/log/panic.h>
8#include <kernel/mem/pmm.h>
11#include <kernel/sched/timer.h>
12#include <kernel/sync/lock.h>
13
14#include <stdint.h>
15#include <stdio.h>
16#include <stdlib.h>
17#include <sys/io.h>
18#include <sys/list.h>
19#include <sys/math.h>
20
24
26{
27 (void)file; // Unused
28
29 char* string = malloc(256 * (cpu_amount() + 1));
30 if (string == NULL)
31 {
32 return ERR;
33 }
34
35 strcpy(string, "cpu idle_clocks active_clocks interrupt_clocks");
36
37 cpu_t* cpu;
38 CPU_FOR_EACH(cpu)
39 {
40 sprintf(string + strlen(string), "\n");
41
42 lock_acquire(&cpu->perf.lock);
44 clock_t delta = uptime - cpu->perf.interruptEnd;
45 if (sched_is_idle(cpu))
46 {
47 cpu->perf.idleClocks += delta;
48 }
49 else
50 {
51 cpu->perf.activeClocks += delta;
52 }
54
55 clock_t volatile activeClocks = cpu->perf.activeClocks;
56 clock_t volatile interruptClocks = cpu->perf.interruptClocks;
57 clock_t volatile idleClocks = cpu->perf.idleClocks;
58 lock_release(&cpu->perf.lock);
59
60 int length =
61 sprintf(string + strlen(string), "%lu %lu %lu %lu", cpu->id, idleClocks, activeClocks, interruptClocks);
62 if (length < 0)
63 {
64 free(string);
65 errno = EIO;
66 return ERR;
67 }
68 }
69
70 uint64_t length = strlen(string);
71 uint64_t readCount = BUFFER_READ(buffer, count, offset, string, length);
72 free(string);
73 return readCount;
74}
75
78};
79
81{
82 (void)file; // Unused
83
84 char* string = malloc(256);
85 if (string == NULL)
86 {
87 return ERR;
88 }
89
90 int length = sprintf(string, "total_pages %lu\nfree_pages %lu\nused_pages %lu", pmm_total_amount(),
92 if (length < 0)
93 {
94 free(string);
95 errno = EIO;
96 return ERR;
97 }
98
99 uint64_t readCount = BUFFER_READ(buffer, count, offset, string, (uint64_t)length);
100 free(string);
101 return readCount;
102}
103
106};
107
109{
110 ctx->activeClocks = 0;
111 ctx->interruptClocks = 0;
112 ctx->idleClocks = 0;
113 ctx->interruptBegin = 0;
114 ctx->interruptEnd = 0;
115 lock_init(&ctx->lock);
116}
117
119{
120 atomic_init(&ctx->userClocks, 0);
121 atomic_init(&ctx->kernelClocks, 0);
122 ctx->startTime = sys_time_uptime();
123}
124
126{
127 ctx->syscallBegin = 0;
128 ctx->syscallEnd = 0;
129}
130
131void perf_init(void)
132{
133 perfDir = sysfs_dir_new(NULL, "perf", NULL, NULL);
134 if (perfDir == NULL)
135 {
136 panic(NULL, "Failed to initialize performance directory");
137 }
138
140 if (cpuFile == NULL)
141 {
142 panic(NULL, "Failed to create CPU performance file");
143 }
145 if (memFile == NULL)
146 {
147 panic(NULL, "Failed to create memory performance file");
148 }
149}
150
152{
153 perf_cpu_ctx_t* perf = &self->perf;
154 LOCK_SCOPE(&perf->lock);
155
156 if (perf->interruptEnd < perf->interruptBegin)
157 {
158 panic(NULL, "perf_interrupt_begin called while already in interrupt interuptBegin=%llu interruptEnd=%llu",
159 perf->interruptBegin, perf->interruptEnd);
160 }
161
163 clock_t delta = perf->interruptBegin - perf->interruptEnd;
164 if (sched_is_idle(self))
165 {
166 perf->idleClocks += delta;
167 }
168 else
169 {
170 perf->activeClocks += delta;
171 }
172
173 thread_t* thread = sched_thread_unsafe();
174 // Do not count interrupt time as part of syscalls
175 if (thread->perf.syscallEnd < thread->perf.syscallBegin)
176 {
177 clock_t syscallDelta = perf->interruptBegin - thread->perf.syscallBegin;
178 atomic_fetch_add(&thread->process->perf.kernelClocks, syscallDelta);
179 }
180 else
181 {
182 clock_t userDelta = perf->interruptBegin - thread->perf.syscallEnd;
183 atomic_fetch_add(&thread->process->perf.userClocks, userDelta);
184 }
185}
186
188{
189 LOCK_SCOPE(&self->perf.lock);
190
193
194 thread_t* thread = sched_thread_unsafe();
195 if (thread->perf.syscallEnd < thread->perf.syscallBegin)
196 {
197 thread->perf.syscallBegin = self->perf.interruptEnd;
198 }
199 else
200 {
201 thread->perf.syscallEnd = self->perf.interruptEnd;
202 }
203}
204
206{
207 thread_t* thread = sched_thread_unsafe();
208 perf_thread_ctx_t* perf = &thread->perf;
209
211 if (perf->syscallEnd < perf->syscallBegin)
212 {
213 panic(NULL, "perf_syscall_begin called while already in syscall syscallBegin=%llu syscallEnd=%llu",
214 perf->syscallBegin, perf->syscallEnd);
215 }
216
217 if (perf->syscallEnd != 0)
218 {
219 atomic_fetch_add(&thread->process->perf.userClocks, uptime - perf->syscallEnd);
220 }
221
222 perf->syscallBegin = uptime;
223}
224
226{
227 thread_t* thread = sched_thread_unsafe();
228 perf_thread_ctx_t* perf = &thread->perf;
229 process_t* process = thread->process;
230
231 perf->syscallEnd = sys_time_uptime();
232 clock_t delta = perf->syscallEnd - perf->syscallBegin;
233
234 atomic_fetch_add(&process->perf.kernelClocks, delta);
235}
#define CPU_FOR_EACH(cpu)
Macro to iterate over all CPUs.
Definition cpu.h:226
static uint64_t cpu_amount(void)
Gets the number of identified CPUs.
Definition cpu.h:152
void perf_cpu_ctx_init(perf_cpu_ctx_t *ctx)
Initializes a per-CPU performance context, must be called on the CPU that owns the context.
Definition perf.c:108
void perf_syscall_end(void)
Called at the end of a syscall to update process performance data.
Definition perf.c:225
void perf_interrupt_begin(cpu_t *self)
Called at the beginning of an interrupt to update cpu performance data.
Definition perf.c:151
void perf_interrupt_end(cpu_t *self)
Called at the end of an interrupt to update cpu performance data.
Definition perf.c:187
void perf_syscall_begin(void)
Called at the beginning of a syscall to update process performance data.
Definition perf.c:205
void perf_process_ctx_init(perf_process_ctx_t *ctx)
Initializes a per-process performance context.
Definition perf.c:118
void perf_thread_ctx_init(perf_thread_ctx_t *ctx)
Initializes a per-thread performance context.
Definition perf.c:125
void perf_init(void)
Initializes the performance driver.
Definition perf.c:131
#define BUFFER_READ(buffer, count, offset, src, size)
Helper macros for implementing file operations dealing with simple buffers.
Definition vfs.h:339
NORETURN void panic(const interrupt_frame_t *frame, const char *format,...)
Panic the kernel, printing a message and halting.
Definition panic.c:265
uint64_t pmm_free_amount(void)
Retrieves the amount of free physical memory.
Definition pmm.c:243
uint64_t pmm_used_amount(void)
Retrieves the amount of reserved physical memory.
Definition pmm.c:249
uint64_t pmm_total_amount(void)
Retrieves the total amount of physical memory managed by the PMM.
Definition pmm.c:237
thread_t * sched_thread_unsafe(void)
Retrieves the currently running thread without disabling interrupts.
Definition sched.c:175
bool sched_is_idle(cpu_t *cpu)
Checks if the CPU is idle.
Definition sched.c:149
static void lock_init(lock_t *lock)
Initializes a lock.
Definition lock.h:82
#define LOCK_SCOPE(lock)
Acquires a lock for the reminder of the current scope.
Definition lock.h:57
static void lock_release(lock_t *lock)
Releases a lock.
Definition lock.h:142
static void lock_acquire(lock_t *lock)
Acquires a lock, blocking until it is available.
Definition lock.h:99
clock_t sys_time_uptime(void)
Time since boot.
Definition sys_time.c:106
#define EIO
I/O error.
Definition errno.h:57
#define errno
Error number variable.
Definition errno.h:27
clock_t uptime(void)
System call for retreving the time since boot.
Definition uptime.c:6
#define NULL
Pointer error value.
Definition NULL.h:23
#define ERR
Integer error value.
Definition ERR.h:17
__UINT64_TYPE__ clock_t
A nanosecond time.
Definition clock_t.h:13
static dentry_t * file
Definition log_file.c:17
EFI_PHYSICAL_ADDRESS buffer
Definition mem.c:15
static dentry_t * cpuFile
Definition perf.c:22
static dentry_t * memFile
Definition perf.c:23
static uint64_t perf_mem_read(file_t *file, void *buffer, uint64_t count, uint64_t *offset)
Definition perf.c:80
static uint64_t perf_cpu_read(file_t *file, void *buffer, uint64_t count, uint64_t *offset)
Definition perf.c:25
static dentry_t * perfDir
Definition perf.c:21
static file_ops_t cpuOps
Definition perf.c:76
static file_ops_t memOps
Definition perf.c:104
static atomic_long count
Definition main.c:10
#define atomic_fetch_add(object, operand)
Definition stdatomic.h:283
#define atomic_init(obj, value)
Definition stdatomic.h:75
__UINT64_TYPE__ uint64_t
Definition stdint.h:17
_PUBLIC int sprintf(char *_RESTRICT s, const char *_RESTRICT format,...)
Definition sprintf.c:5
_PUBLIC void * malloc(size_t size)
Definition malloc.c:5
_PUBLIC void free(void *ptr)
Definition free.c:11
_PUBLIC size_t strlen(const char *s)
Definition strlen.c:3
_PUBLIC char * strcpy(char *_RESTRICT s1, const char *_RESTRICT s2)
Definition strcpy.c:3
CPU structure.
Definition cpu.h:63
cpuid_t id
Definition cpu.h:64
perf_cpu_ctx_t perf
Definition cpu.h:69
Directory entry structure.
Definition dentry.h:83
File operations structure.
Definition file.h:57
uint64_t(* read)(file_t *file, void *buffer, uint64_t count, uint64_t *offset)
Definition file.h:61
File structure.
Definition file.h:37
Per-CPU performance context.
Definition perf.h:49
lock_t lock
Definition perf.h:55
clock_t activeClocks
Definition perf.h:50
clock_t interruptEnd
Definition perf.h:54
clock_t idleClocks
Definition perf.h:52
clock_t interruptBegin
Definition perf.h:53
clock_t interruptClocks
Definition perf.h:51
clock_t startTime
The time when the process was started.
Definition perf.h:67
Per-Thread performance context.
Definition perf.h:78
clock_t syscallBegin
The time the current syscall began. Also used to "skip" time spent in interrupts.
Definition perf.h:79
clock_t syscallEnd
Definition perf.h:80
Process structure.
Definition process.h:63
perf_process_ctx_t perf
Definition process.h:73
Thread of execution structure.
Definition thread.h:55
perf_thread_ctx_t perf
Definition thread.h:75
process_t * process
The parent process that the thread executes within.
Definition thread.h:57
dentry_t * sysfs_dir_new(dentry_t *parent, const char *name, const inode_ops_t *inodeOps, void *private)
Create a new directory inside a mounted SysFS instance.
Definition sysfs.c:174
dentry_t * sysfs_file_new(dentry_t *parent, const char *name, const inode_ops_t *inodeOps, const file_ops_t *fileOps, void *private)
Create a new file inside a mounted SysFS instance.
Definition sysfs.c:216