PatchworkOS  dbbdc99
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/fs.h>
21#include <sys/list.h>
22#include <sys/math.h>
23
27
37
39{
40 perf_cpu_t* perf = SELF_PTR(pcpu_perf);
41
42 perf->activeClocks = 0;
43 perf->interruptClocks = 0;
44 perf->idleClocks = 0;
45 perf->interruptBegin = 0;
46 perf->interruptEnd = 0;
47 lock_init(&perf->lock);
48}
49
50static size_t perf_cpu_read(file_t* file, void* buffer, size_t count, size_t* offset)
51{
52 UNUSED(file);
53
54 char* string = malloc(256 * (cpu_amount() + 1));
55 if (string == NULL)
56 {
57 errno = ENOMEM;
58 return ERR;
59 }
60
61 strcpy(string, "cpu idle_clocks active_clocks interrupt_clocks");
62
63 cpu_t* cpu;
64 CPU_FOR_EACH(cpu)
65 {
66 sprintf(string + strlen(string), "\n");
67
68 perf_cpu_t* perf = CPU_PTR(cpu->id, pcpu_perf);
69
70 lock_acquire(&perf->lock);
72 clock_t delta = uptime - perf->interruptEnd;
73 if (sched_is_idle(cpu))
74 {
75 perf->idleClocks += delta;
76 }
77 else
78 {
79 perf->activeClocks += delta;
80 }
81 perf->interruptEnd = uptime;
82
83 clock_t volatile activeClocks = perf->activeClocks;
84 clock_t volatile interruptClocks = perf->interruptClocks;
85 clock_t volatile idleClocks = perf->idleClocks;
86 lock_release(&perf->lock);
87
88 int length =
89 sprintf(string + strlen(string), "%lu %lu %lu %lu", cpu->id, idleClocks, activeClocks, interruptClocks);
90 if (length < 0)
91 {
92 free(string);
93 errno = EIO;
94 return ERR;
95 }
96 }
97
98 size_t length = strlen(string);
99 size_t readCount = BUFFER_READ(buffer, count, offset, string, length);
100 free(string);
101 return readCount;
102}
103
106};
107
108static size_t perf_mem_read(file_t* file, void* buffer, size_t count, size_t* offset)
109{
110 UNUSED(file);
111
112 char* string = malloc(256);
113 if (string == NULL)
114 {
115 errno = ENOMEM;
116 return ERR;
117 }
118
119 int length = sprintf(string, "total_pages %lu\nfree_pages %lu\nused_pages %lu", pmm_total_pages(),
121 if (length < 0)
122 {
123 free(string);
124 errno = EIO;
125 return ERR;
126 }
127
128 size_t readCount = BUFFER_READ(buffer, count, offset, string, (uint64_t)length);
129 free(string);
130 return readCount;
131}
132
135};
136
138{
139 atomic_init(&ctx->userClocks, 0);
140 atomic_init(&ctx->kernelClocks, 0);
141 ctx->startTime = clock_uptime();
142}
143
145{
146 ctx->syscallBegin = 0;
147 ctx->syscallEnd = 0;
148}
149
150void perf_init(void)
151{
152 perfDir = devfs_dir_new(NULL, "perf", NULL, NULL);
153 if (perfDir == NULL)
154 {
155 panic(NULL, "Failed to initialize performance directory");
156 }
157
159 if (cpuFile == NULL)
160 {
161 panic(NULL, "Failed to create CPU performance file");
162 }
164 if (memFile == NULL)
165 {
166 panic(NULL, "Failed to create memory performance file");
167 }
168}
169
171{
172 perf_cpu_t* perf = SELF_PTR(pcpu_perf);
173 LOCK_SCOPE(&perf->lock);
174
175 if (perf->interruptEnd < perf->interruptBegin)
176 {
177 LOG_WARN("unexpected call to perf_interrupt_begin()\n");
178 return;
179 }
180
182 clock_t delta = perf->interruptBegin - perf->interruptEnd;
183 if (sched_is_idle(SELF->self))
184 {
185 perf->idleClocks += delta;
186 }
187 else
188 {
189 perf->activeClocks += delta;
190 }
191
193 // Do not count interrupt time as part of syscalls
194 if (thread->perf.syscallEnd < thread->perf.syscallBegin)
195 {
196 clock_t syscallDelta = perf->interruptBegin - thread->perf.syscallBegin;
197 atomic_fetch_add(&thread->process->perf.kernelClocks, syscallDelta);
198 }
199 else
200 {
201 clock_t userDelta = perf->interruptBegin - thread->perf.syscallEnd;
202 atomic_fetch_add(&thread->process->perf.userClocks, userDelta);
203 }
204}
205
207{
208 perf_cpu_t* perf = SELF_PTR(pcpu_perf);
209
210 LOCK_SCOPE(&perf->lock);
211
212 perf->interruptEnd = clock_uptime();
213 perf->interruptClocks += perf->interruptEnd - perf->interruptBegin;
214
216 if (thread->perf.syscallEnd < thread->perf.syscallBegin)
217 {
218 thread->perf.syscallBegin = perf->interruptEnd;
219 }
220 else
221 {
222 thread->perf.syscallEnd = perf->interruptEnd;
223 }
224}
225
227{
228 CLI_SCOPE();
229
231 perf_thread_ctx_t* perf = &thread->perf;
232
234 if (perf->syscallEnd < perf->syscallBegin)
235 {
236 LOG_WARN("unexpected call to perf_syscall_begin()\n");
237 return;
238 }
239
240 if (perf->syscallEnd != 0)
241 {
242 atomic_fetch_add(&thread->process->perf.userClocks, uptime - perf->syscallEnd);
243 }
244
245 perf->syscallBegin = uptime;
246}
247
249{
250 CLI_SCOPE();
251
253 perf_thread_ctx_t* perf = &thread->perf;
254 process_t* process = thread->process;
255
256 perf->syscallEnd = clock_uptime();
257 clock_t delta = perf->syscallEnd - perf->syscallBegin;
258
259 atomic_fetch_add(&process->perf.kernelClocks, delta);
260}
EFI_PHYSICAL_ADDRESS buffer
Definition main.c:237
#define CLI_SCOPE()
Macro to increment CLI depth for the duration of the current scope.
Definition cli.h:56
#define SELF_PTR(ptr)
Macro to get a pointer to a percpu variable on the current CPU.
Definition percpu.h:93
#define SELF
Macro to access data in the current cpu.
Definition percpu.h:85
#define CPU_PTR(id, ptr)
Macro to get a pointer to a percpu variable on a specific CPU.
Definition percpu.h:102
#define PERCPU_DEFINE_CTOR(type, name)
Macro to define a percpu variable with a constructor.
Definition percpu.h:130
static uint16_t cpu_amount(void)
Gets the number of identified CPUs.
Definition cpu.h:168
#define CPU_FOR_EACH(cpu)
Macro to iterate over all CPUs.
Definition cpu.h:214
void perf_interrupt_end(void)
Called at the end of an interrupt to update cpu performance data.
Definition perf.c:206
void perf_interrupt_begin(void)
Called at the beginning of an interrupt to update cpu performance data.
Definition perf.c:170
void perf_syscall_end(void)
Called at the end of a syscall to update process performance data.
Definition perf.c:248
void perf_syscall_begin(void)
Called at the beginning of a syscall to update process performance data.
Definition perf.c:226
void perf_process_ctx_init(perf_process_ctx_t *ctx)
Initializes a per-process performance context.
Definition perf.c:137
void perf_thread_ctx_init(perf_thread_ctx_t *ctx)
Initializes a per-thread performance context.
Definition perf.c:144
void perf_init(void)
Initializes the performance driver.
Definition perf.c:150
dentry_t * devfs_file_new(dentry_t *parent, const char *name, const vnode_ops_t *vnodeOps, const file_ops_t *fileOps, void *data)
Create a new file inside a mounted devfs instance.
Definition devfs.c:121
dentry_t * devfs_dir_new(dentry_t *parent, const char *name, const vnode_ops_t *vnodeOps, void *data)
Create a new directory inside a mounted devfs instance.
Definition devfs.c:82
NORETURN void panic(const interrupt_frame_t *frame, const char *format,...)
Panic the kernel, printing a message and halting.
Definition panic.c:292
#define LOG_WARN(format,...)
Definition log.h:92
size_t pmm_total_pages(void)
Get the total number of physical pages.
Definition pmm.c:387
size_t pmm_used_pages(void)
Get the number of used physical pages.
Definition pmm.c:403
size_t pmm_avail_pages(void)
Get the number of available physical pages.
Definition pmm.c:395
clock_t clock_uptime(void)
Retrieve the time in nanoseconds since boot.
Definition clock.c:99
static thread_t * thread_current_unsafe(void)
Retrieves the currently running thread without disabling interrupts.
Definition thread.h:137
bool sched_is_idle(cpu_t *cpu)
Checks if the CPU is currently idle.
Definition sched.c:618
static void lock_init(lock_t *lock)
Initializes a lock.
Definition lock.h:79
#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:175
static void lock_acquire(lock_t *lock)
Acquires a lock, blocking until it is available.
Definition lock.h:96
#define BUFFER_READ(buffer, count, offset, src, size)
Helper macros for implementing file operations dealing with simple buffers.
Definition vfs.h:209
#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:96
clock_t uptime(void)
System call for retreving the time since boot.
Definition uptime.c:6
#define NULL
Pointer error value.
Definition NULL.h:25
#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
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:50
static dentry_t * memFile
Definition perf.c:26
static dentry_t * perfDir
Definition perf.c:24
static file_ops_t cpuOps
Definition perf.c:104
static size_t perf_mem_read(file_t *file, void *buffer, size_t count, size_t *offset)
Definition perf.c:108
static file_ops_t memOps
Definition perf.c:133
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:84
cpu_id_t id
Definition cpu.h:86
Directory entry structure.
Definition dentry.h:155
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
A simple ticket lock implementation.
Definition lock.h:44
clock_t interruptBegin
Definition perf.c:33
lock_t lock
Definition perf.c:35
clock_t interruptClocks
Definition perf.c:31
clock_t idleClocks
Definition perf.c:32
clock_t interruptEnd
Definition perf.c:34
clock_t activeClocks
Definition perf.c:30
clock_t startTime
The time when the process was started.
Definition perf.h:53
Per-Thread performance context.
Definition perf.h:64
clock_t syscallBegin
The time the current syscall began. Also used to "skip" time spent in interrupts.
Definition perf.h:65
clock_t syscallEnd
Definition perf.h:66
Process structure.
Definition process.h:76
perf_process_ctx_t perf
Definition process.h:90
Thread of execution structure.
Definition thread.h:61
perf_thread_ctx_t perf
Definition thread.h:80
process_t * process
The parent process that the thread executes within.
Definition thread.h:62