Skip to content
This repository was archived by the owner on Apr 7, 2026. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions environ_base.sh
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export KOS_AR="${KOS_CC_BASE}/bin/${KOS_CC_PREFIX}-gcc-ar"
export KOS_OBJCOPY="${KOS_CC_BASE}/bin/${KOS_CC_PREFIX}-objcopy"
export KOS_OBJDUMP="${KOS_CC_BASE}/bin/${KOS_CC_PREFIX}-objdump"
export KOS_ADDR2LINE="${KOS_CC_BASE}/bin/${KOS_CC_PREFIX}-addr2line"
export KOS_GPROF="${KOS_CC_BASE}/bin/${KOS_CC_PREFIX}-gprof"
export KOS_LD="${KOS_CC_BASE}/bin/${KOS_CC_PREFIX}-ld"
export KOS_RANLIB="${KOS_CC_BASE}/bin/${KOS_CC_PREFIX}-gcc-ranlib"
export KOS_STRIP="${KOS_CC_BASE}/bin/${KOS_CC_PREFIX}-strip"
Expand Down
143 changes: 143 additions & 0 deletions include/sys/gmon.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
/* KallistiOS ##version##

sys/gmon.h
Copyright (C) 2025 Andy Barajas

*/

#ifndef __SYS_GMON_H
#define __SYS_GMON_H

#include <sys/cdefs.h>
__BEGIN_DECLS

#include <stdint.h>
#include <stdbool.h>

/** \defgroup debugging_gprof GPROF
\brief GPROF utilities
\ingroup debugging


This file provides utilities for profiling applications using gprof. Gprof
is a profiling tool that allows you to analyze where your program spent
its time and which functions called which other functions during execution.
This can help you identify performance bottlenecks and optimize your code.

Profiling Steps:

1. Compile your program with profiling flags:

Use the following flags:
```sh
CFLAGS = -pg -fno-omit-frame-pointer -fno-inline
```
= or =
```sh
CFLAGS = -g -finstrument-functions -fno-inline
LDFLAGS = -lgprof
```
These flags enable profiling and prevent function inlining to ensure
accurate profiling data. If you use the -pg flag, the GCC compiler
inserts trapa #33 instructions into your build.

2. Running your program to create gmon.out:

Execute your program normally to completion. This will generate a file
named `gmon.out` in the current directory, which contains the profiling
data.

3. Running gprof:

Use the following command to analyze the profiling data:
```sh
$(KOS_GPROF) $(TARGET) gmon.out > gprof_output.txt
```
Replace `$(TARGET)` with the name of your compiled program. This
command will generate a human-readable report in `gprof_output.txt`.

4. Visualizing profiling data with gprof2dot:

To create a graphical representation of the profiling data, use the
`gprof2dot` tool:
```sh
gprof2dot gprof_output.txt > graph.dot
```
This converts the `gprof` output into a DOT format file, which can be
used to generate various types of graphs.

5. Generating an image from the DOT file:

Use the `dot` command from Graphviz to create a JPEG image from the DOT
file:
```sh
dot -Tjpg graph.dot -o graph.jpg
```
This command will generate a JPEG image (`graph.jpg`) that visually
represents the profiling data.

By following these steps, you can effectively profile your program and
identify performance bottlenecks for optimization.

\author Andy Barajas

@{
*/

/** \brief GPROF Trapa Code

GCC generates this pattern before each profiled function when you compile
your project with the -pg flag:

trapa #33 --- This is a 2-byte instruction
nop --- This is a 2-byte no-op placeholder
*/
#define GPROF_TRAPA_CODE 33

/** \brief Environment variable for setting the gmon output file prefix.

This variable allows you to set a custom prefix for the gmon output file
generated after profiling. The default output filename is gmon.out.
If set, the specified prefix will be appended with the PID of the
running program, which is always 1 for Dreamcast.

Example:
\code
setenv(GMON_OUT_PREFIX, "/pc/test.out", 1);
\endcode
This will generate a file called \c test.out.1 after profiling is complete.
*/
#define GMON_OUT_PREFIX "GMON_OUT_PREFIX"

/** \brief Start gprof profiling for a specified address range.

This function is intended for programs not linked with the `-pg' linker
switch. If `-pg' was used during linking, monstartup is automatically
called by the startup code with arguments covering the entire range of
executable addresses, from the program's entry point to the highest code
segment address. Using this method to initialize gprof will only generate
histogram profiling data, without producing a call graph. Profiling starts
immediately after calling this function.

\param lowpc The lower bound of the address range to profile.
\param highpc The upper bound of the address range to profile.
*/
void monstartup(uintptr_t lowpc, uintptr_t highpc);

/** \brief Restart or stop gprof profiling.

This function restarts or stops gprof profiling. It does NOT start gprof
profiling initially, as gprof profiling starts before the program enters
the main function. You can use this function to stop profiling and then
restart it later when you reach the section of code you want to profile.

\param enable A boolean value to restart (true) or stop (false)
gprof profiling.
*/
void moncontrol(bool enable);

/** @} */

__END_DECLS

#endif /* __SYS_GMON_H */
22 changes: 14 additions & 8 deletions kernel/arch/dreamcast/kernel/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ KOS_INIT_FLAG_WEAK(fs_dclsocket_shutdown, true);
/* Auto-init stuff: override with a non-weak symbol if you don't want all of
this to be linked into your code (and do the same with the
arch_auto_shutdown function too). */
int __weak arch_auto_init(void) {
int __weak arch_auto_init(void) {
/* Initialize memory management */
mm_init();

Expand Down Expand Up @@ -172,7 +172,7 @@ int __weak arch_auto_init(void) {
timer_init(); /* Timers */
hardware_sys_init(); /* DC low-level hardware init */

if (!KOS_PLATFORM_IS_NAOMI)
if(!KOS_PLATFORM_IS_NAOMI)
syscall_sysinfo_init();

/* Initialize our timer */
Expand Down Expand Up @@ -206,7 +206,7 @@ int __weak arch_auto_init(void) {

KOS_INIT_FLAG_CALL(dcload_init);

if (!KOS_PLATFORM_IS_NAOMI)
if(!KOS_PLATFORM_IS_NAOMI)
KOS_INIT_FLAG_CALL(fs_iso9660_init);

KOS_INIT_FLAG_CALL(vmu_fs_init);
Expand All @@ -220,15 +220,15 @@ int __weak arch_auto_init(void) {
KOS_INIT_FLAG_CALL(maple_wait_scan); /* Wait for the maple scan to complete */
}

if (!KOS_PLATFORM_IS_NAOMI)
if(!KOS_PLATFORM_IS_NAOMI)
KOS_INIT_FLAG_CALL(arch_init_net);

return 0;
}

void __weak arch_auto_shutdown(void) {
void __weak arch_auto_shutdown(void) {
KOS_INIT_FLAG_CALL(fs_dclsocket_shutdown);
if (!KOS_PLATFORM_IS_NAOMI)
if(!KOS_PLATFORM_IS_NAOMI)
KOS_INIT_FLAG_CALL(net_shutdown);

snd_shutdown();
Expand All @@ -244,7 +244,7 @@ void __weak arch_auto_shutdown(void) {
library_shutdown();
KOS_INIT_FLAG_CALL(fs_dcload_shutdown);
KOS_INIT_FLAG_CALL(vmu_fs_shutdown);
if (!KOS_PLATFORM_IS_NAOMI)
if(!KOS_PLATFORM_IS_NAOMI)
KOS_INIT_FLAG_CALL(fs_iso9660_shutdown);
#if defined(__NEWLIB__) && !(__NEWLIB__ < 2 && __NEWLIB_MINOR__ < 4)
fs_rnd_shutdown();
Expand All @@ -259,13 +259,16 @@ void __weak arch_auto_shutdown(void) {
rtc_shutdown();
}

/* Strongly defined in gcrt1.s and linked when compiling with -pg */
void __weak gprof_init(void) { }

/* This is the entry point inside the C program */
void arch_main(void) {
uint8 *bss_start = (uint8 *)(&_bss_start);
uint8 *bss_end = (uint8 *)(&end);
int rv;

if (KOS_PLATFORM_IS_NAOMI) {
if(KOS_PLATFORM_IS_NAOMI) {
/* Ugh. I'm really not sure why we have to set up these DMA registers this
way on boot, but failing to do so breaks maple... */
DMAC_SAR2 = 0;
Expand Down Expand Up @@ -296,6 +299,9 @@ void arch_main(void) {
/* Run ctors */
_init();

/* Setup gprof */
gprof_init();

/* Call the user's main function */
rv = main(0, NULL);

Expand Down
33 changes: 31 additions & 2 deletions kernel/arch/dreamcast/kernel/startup.s
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
! KallistiOS ##version##
!
! startup.s
! (c)2000-2001 Megan Potter
! startup.S
! Copyright (C) 2000-2001 Megan Potter
! Copyright (C) 2025 Andress Barajas
!
! This file is added to GCC during the patching stage of toolchain
! compilation. Any changes to this file will not take effect until the
Expand All @@ -21,6 +22,9 @@
.globl __arch_old_stack
.globl __arch_old_fpscr
.globl __arch_mem_top
#ifdef PROFILE
.globl _gprof_init
#endif

.weak _arch_stack_16m
.weak _arch_stack_32m
Expand Down Expand Up @@ -179,8 +183,33 @@ normal_exit:
rts
nop

#ifdef PROFILE

! Call profiler startup code
.align 2
_gprof_init:
mov.l lowpc, r4
mov.l highpc, r5
mov.l monstartup_addr, r2
jmp @r2
nop

! GPROF variables
.align 2

monstartup_addr:
.long __monstartup
lowpc:
.long __executable_start
highpc:
.long __etext

#endif /* PROFILE */


! Misc variables
.align 2

dcload_magic_addr:
.long 0x8c004004
dcload_magic_value:
Expand Down
2 changes: 1 addition & 1 deletion kernel/libc/koslib/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,6 @@ OBJS = abort.o byteorder.o memset2.o memset4.o memcpy2.o memcpy4.o \
creat.o sleep.o rmdir.o rename.o inet_pton.o inet_ntop.o \
inet_ntoa.o inet_aton.o poll.o select.o symlink.o readlink.o \
gethostbyname.o getaddrinfo.o dirfd.o nanosleep.o basename.o dirname.o \
sched_yield.o dup.o dup2.o pipe.o
sched_yield.o dup.o dup2.o pipe.o gmon.o

include $(KOS_BASE)/Makefile.prefab
Loading