PatchworkOS  3984a1d
A non-POSIX operating system.
Loading...
Searching...
No Matches
perf.c
Go to the documentation of this file.
3
4#include <kernel/cpu/cpu.h>
5#include <kernel/fs/devfs.h>
6#include <kernel/fs/file.h>
7#include <kernel/fs/vfs.h>
8#include <kernel/log/log.h>
9#include <kernel/log/panic.h>
10#include <kernel/mem/pmm.h>
11#include <kernel/sched/clock.h>
12#include <kernel/sched/sched.h>
13#include <kernel/sched/timer.h>
14#include <kernel/sync/lock.h>
15#include <kernel/utils/utils.h>
16
17#include <stdint.h>
18#include <stdio.h>
19#include <stdlib.h>
20#include <sys/io.h>
21#include <sys/list.h>
22#include <sys/math.h>
23
27
28static size_t perf_cpu_read(file_t* file, void* buffer, size_t count, size_t* offset)
29{
30 UNUSED(file);
31
32 char* string = malloc(256 * (cpu_amount() + 1));
33 if (string == NULL)
34 {
35 errno = ENOMEM;
36 return ERR;
37 }
38
39 strcpy(string, "cpu idle_clocks active_clocks interrupt_clocks");
40
41 cpu_t* cpu;
42 CPU_FOR_EACH(cpu)
43 {
44 sprintf(string + strlen(string), "\n");
45
46 lock_acquire(&cpu->perf.lock);
48 clock_t delta = uptime - cpu->perf.interruptEnd;
49 if (sched_is_idle(cpu))
50 {
51 cpu->perf.idleClocks += delta;
52 }
53 else
54 {
55 cpu->perf.activeClocks += delta;
56 }
58
59 clock_t volatile activeClocks = cpu->perf.activeClocks;
60 clock_t volatile interruptClocks = cpu->perf.interruptClocks;
61 clock_t volatile idleClocks = cpu->perf.idleClocks;
62 lock_release(&cpu->perf.lock);
63
64 int length =
65 sprintf(string + strlen(string), "%lu %lu %lu %lu", cpu->id, idleClocks, activeClocks, interruptClocks);
66 if (length < 0)
67 {
68 free(string);
69 errno = EIO;
70 return ERR;
71 }
72 }
73
74 size_t length = strlen(string);
75 size_t readCount = BUFFER_READ(buffer, count, offset, string, length);
76 free(string);
77 return readCount;
78}
79
82};
83
84static size_t perf_mem_read(file_t* file, void* buffer, size_t count, size_t* offset)
85{
86 UNUSED(file);
87
88 char* string = malloc(256);
89 if (string == NULL)
90 {
91 errno = ENOMEM;
92 return ERR;
93 }
94
95 int length = sprintf(string, "total_pages %lu\nfree_pages %lu\nused_pages %lu", pmm_total_amount(),
97 if (length < 0)
98 {
99 free(string);
100 errno = EIO;
101 return ERR;
102 }
103
104 size_t readCount = BUFFER_READ(buffer, count, offset, string, (uint64_t)length);
105 free(string);
106 return readCount;
107}
108
111};
112
114{
115 ctx->activeClocks = 0;
116 ctx->interruptClocks = 0;
117 ctx->idleClocks = 0;
118 ctx->interruptBegin = 0;
119 ctx->interruptEnd = 0;
120 lock_init(&ctx->lock);
121}
122
124{
125 atomic_init(&ctx->userClocks, 0);
126 atomic_init(&ctx->kernelClocks, 0);
127 ctx->startTime = clock_uptime();
128}
129
131{
132 ctx->syscallBegin = 0;
133 ctx->syscallEnd = 0;
134}
135
136void perf_init(void)
137{
138 perfDir = devfs_dir_new(NULL, "perf", NULL, NULL);
139 if (perfDir == NULL)
140 {
141 panic(NULL, "Failed to initialize performance directory");
142 }
143
145 if (cpuFile == NULL)
146 {
147 panic(NULL, "Failed to create CPU performance file");
148 }
150 if (memFile == NULL)
151 {
152 panic(NULL, "Failed to create memory performance file");
153 }
154}
155
157{
158 perf_cpu_ctx_t* perf = &self->perf;
159 LOCK_SCOPE(&perf->lock);
160
161 if (perf->interruptEnd < perf->interruptBegin)
162 {
163 LOG_WARN("unexpected call to perf_interrupt_begin()\n");
164 return;
165 }
166
168 clock_t delta = perf->interruptBegin - perf->interruptEnd;
169 if (sched_is_idle(self))
170 {
171 perf->idleClocks += delta;
172 }
173 else
174 {
175 perf->activeClocks += delta;
176 }
177
178 thread_t* thread = sched_thread_unsafe();
179 // Do not count interrupt time as part of syscalls
180 if (thread->perf.syscallEnd < thread->perf.syscallBegin)
181 {
183 atomic_fetch_add(&thread->process->perf.kernelClocks, syscallDelta);
184 }
185 else
186 {
188 atomic_fetch_add(&thread->process->perf.userClocks, userDelta);
189 }
190}
191
193{
194 LOCK_SCOPE(&self->perf.lock);
195
198
199 thread_t* thread = sched_thread_unsafe();
200 if (thread->perf.syscallEnd < thread->perf.syscallBegin)
201 {
202 thread->perf.syscallBegin = self->perf.interruptEnd;
203 }
204 else
205 {
206 thread->perf.syscallEnd = self->perf.interruptEnd;
207 }
208}
209
211{
213
214 thread_t* thread = sched_thread_unsafe();
215 perf_thread_ctx_t* perf = &thread->perf;
216
218 if (perf->syscallEnd < perf->syscallBegin)
219 {
220 LOG_WARN("unexpected call to perf_syscall_begin()\n");
221 return;
222 }
223
224 if (perf->syscallEnd != 0)
225 {
226 atomic_fetch_add(&thread->process->perf.userClocks, uptime - perf->syscallEnd);
227 }
228
229 perf->syscallBegin = uptime;
230
232}
233
235{
237
238 thread_t* thread = sched_thread_unsafe();
239 perf_thread_ctx_t* perf = &thread->perf;
240 process_t* process = thread->process;
241
242 perf->syscallEnd = clock_uptime();
243 clock_t delta = perf->syscallEnd - perf->syscallBegin;
244
245 atomic_fetch_add(&process->perf.kernelClocks, delta);
246
248}
int64_t y
Definition main.c:153
void interrupt_disable(void)
Disable interrupts and increment the disableDepth.
Definition interrupt.c:25
void interrupt_enable(void)
Decrement the CLI depth and enable interrupts if depth reaches zero and interrupts were previously en...
Definition interrupt.c:37
static uint16_t cpu_amount(void)
Gets the number of identified CPUs.
Definition cpu.h:244
#define CPU_FOR_EACH(cpu)
Macro to iterate over all CPUs.
Definition cpu.h:349
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:113
void perf_syscall_end(void)
Called at the end of a syscall to update process performance data.
Definition perf.c:234
void perf_interrupt_begin(cpu_t *self)
Called at the beginning of an interrupt to update cpu performance data.
Definition perf.c:156
void perf_interrupt_end(cpu_t *self)
Called at the end of an interrupt to update cpu performance data.
Definition perf.c:192
void perf_syscall_begin(void)
Called at the beginning of a syscall to update process performance data.
Definition perf.c:210
void perf_process_ctx_init(perf_process_ctx_t *ctx)
Initializes a per-process performance context.
Definition perf.c:123
void perf_thread_ctx_init(perf_thread_ctx_t *ctx)
Initializes a per-thread performance context.
Definition perf.c:130
void perf_init(void)
Initializes the performance driver.
Definition perf.c:136
dentry_t * devfs_dir_new(dentry_t *parent, const char *name, const inode_ops_t *inodeOps, void *private)
Create a new directory inside a mounted devfs instance.
Definition devfs.c:86
dentry_t * devfs_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 devfs instance.
Definition devfs.c:125
NORETURN void panic(const interrupt_frame_t *frame, const char *format,...)
Panic the kernel, printing a message and halting.
Definition panic.c:267
#define LOG_WARN(format,...)
Definition log.h:92
size_t pmm_free_amount(void)
Retrieves the amount of free physical memory.
Definition pmm.c:247
size_t pmm_total_amount(void)
Retrieves the total amount of physical memory managed by the PMM.
Definition pmm.c:241
size_t pmm_used_amount(void)
Retrieves the amount of reserved physical memory.
Definition pmm.c:253
clock_t clock_uptime(void)
Retrieve the time in nanoseconds since boot.
Definition clock.c:99
thread_t * sched_thread_unsafe(void)
Retrieves the currently running thread without disabling interrupts.
Definition sched.c:626
bool sched_is_idle(cpu_t *cpu)
Checks if the CPU is currently idle.
Definition sched.c:604
static void lock_init(lock_t *lock)
Initializes a lock.
Definition lock.h:87
#define LOCK_SCOPE(lock)
Acquires a lock for the reminder of the current scope.
Definition lock.h:58
static void lock_release(lock_t *lock)
Releases a lock.
Definition lock.h:184
static void lock_acquire(lock_t *lock)
Acquires a lock, blocking until it is available.
Definition lock.h:104
#define BUFFER_READ(buffer, count, offset, src, size)
Helper macros for implementing file operations dealing with simple buffers.
Definition vfs.h:206
#define ENOMEM
Out of memory.
Definition errno.h:92
#define EIO
I/O error.
Definition errno.h:57
#define errno
Error number variable.
Definition errno.h:27
#define UNUSED(x)
Mark a variable as unused.
Definition defs.h:100
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 uint64_t offset
Definition screen.c:19
EFI_PHYSICAL_ADDRESS buffer
Definition mem.c:15
static dentry_t * cpuFile
Definition perf.c:25
static size_t perf_cpu_read(file_t *file, void *buffer, size_t count, size_t *offset)
Definition perf.c:28
static dentry_t * memFile
Definition perf.c:26
static dentry_t * perfDir
Definition perf.c:24
static file_ops_t cpuOps
Definition perf.c:80
static size_t perf_mem_read(file_t *file, void *buffer, size_t count, size_t *offset)
Definition perf.c:84
static file_ops_t memOps
Definition perf.c:109
static atomic_long count
Definition main.c:11
#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:122
cpuid_t id
Definition cpu.h:123
perf_cpu_ctx_t perf
Definition cpu.h:131
Directory entry structure.
Definition dentry.h:153
File operations structure.
Definition file.h:54
size_t(* read)(file_t *file, void *buffer, size_t count, size_t *offset)
Definition file.h:58
File structure.
Definition file.h:39
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:72
perf_process_ctx_t perf
Definition process.h:86
Thread of execution structure.
Definition thread.h:56
perf_thread_ctx_t perf
Definition thread.h:75
process_t * process
The parent process that the thread executes within.
Definition thread.h:57