From d1f86cbb8bd266068213f77e61787d334a097461 Mon Sep 17 00:00:00 2001 From: Francisco Javier Trujillo Mata Date: Tue, 3 Sep 2024 11:10:18 +0200 Subject: [PATCH 1/2] Allowing custom profiling sessions --- ee/libprofglue/include/ps2prof.h | 41 ++++++++++++++++++ ee/libprofglue/src/prof.c | 72 ++++++++++++++++++++++---------- 2 files changed, 90 insertions(+), 23 deletions(-) create mode 100644 ee/libprofglue/include/ps2prof.h diff --git a/ee/libprofglue/include/ps2prof.h b/ee/libprofglue/include/ps2prof.h new file mode 100644 index 00000000000..856d7a50222 --- /dev/null +++ b/ee/libprofglue/include/ps2prof.h @@ -0,0 +1,41 @@ +/* +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# Copyright 2001-2004, ps2dev - http://www.ps2dev.org +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. +*/ + +#ifndef __PS2PROF_H__ +#define __PS2PROF_H__ + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * Start the profiler. + * If the profiler is already running, this function stop previous one, + * and ignore the result. + * Finally, it initializes a new profiler session. + */ +__attribute__((__no_instrument_function__, __no_profile_instrument_function__)) +void gprof_start(void); +/** + * Stop the profiler. + * If the profiler is not running, this function does nothing. + * @param filename The name of the file to write the profiling data to. + * @param should_dump If 1, the profiling data will be written to the file. + * If 0, the profiling data will be discarded. + */ +__attribute__((__no_instrument_function__, __no_profile_instrument_function__)) +void gprof_stop(const char* filename, int should_dump); + +#ifdef __cplusplus +} +#endif + +#endif /* __PS2PROF_H__ */ diff --git a/ee/libprofglue/src/prof.c b/ee/libprofglue/src/prof.c index f65fb33178d..73e2c8f35a8 100644 --- a/ee/libprofglue/src/prof.c +++ b/ee/libprofglue/src/prof.c @@ -23,6 +23,7 @@ #include #include +#include /** gmon.out file header */ struct gmonhdr @@ -100,7 +101,7 @@ static uint64_t timer_handler(int id, uint64_t scheduled_time, uint64_t actual_t /** Initializes pg library - After calculating the text size, __gprof_initialize() allocates enough + After calculating the text size, __gprof_init() allocates enough memory to allow fastest access to arc structures, and some more for sampling statistics. Note that this also installs a timer that runs at 1000 hert. @@ -137,15 +138,28 @@ void __gprof_init() gp.state = GMON_PROF_ON; gp.timerId = SetTimerAlarm(USec2TimerBusClock(SAMPLE_FREQ), &timer_handler, &gp); + if (gp.timerId < 0) { + free(gp.arcs); + free(gp.samples); + gp.arcs = 0; + gp.samples = 0; + gp.state = GMON_PROF_ERROR; + return; + } } -/** Writes gmon.out dump file and stops profiling +__attribute__((__no_instrument_function__, __no_profile_instrument_function__)) +void gprof_start(void) +{ + // There is already a profiling session running, let's stop it and ignore the result + if (gp.state == GMON_PROF_ON) { + gprof_stop(NULL, 0); + } + __gprof_init(); +} - Called from atexit() handler; will dump out a host:gmon.out file - with all collected information. -*/ __attribute__((__no_instrument_function__, __no_profile_instrument_function__)) -void __gprof_cleanup() +void gprof_stop(const char *filename, int should_dump) { FILE *fp; int i; @@ -161,31 +175,43 @@ void __gprof_cleanup() ReleaseTimerAlarm(gp.timerId); - fp = fopen("gmon.out", "wb"); - hdr.lpc = gp.lowpc; - hdr.hpc = gp.highpc; - hdr.ncnt = sizeof(hdr) + (sizeof(unsigned int) * gp.nsamples); - hdr.version = GMONVERSION; - hdr.profrate = SAMPLE_FREQ; - hdr.resv[0] = 0; - hdr.resv[1] = 0; - hdr.resv[2] = 0; - fwrite(&hdr, 1, sizeof(hdr), fp); - fwrite(gp.samples, gp.nsamples, sizeof(unsigned int), fp); - - for (i = 0; i < gp.narcs; i++) { - if (gp.arcs[i].count > 0) { - fwrite(gp.arcs + i, sizeof(struct rawarc), 1, fp); + if (should_dump) { + fp = fopen(filename, "wb"); + hdr.lpc = gp.lowpc; + hdr.hpc = gp.highpc; + hdr.ncnt = sizeof(hdr) + (sizeof(unsigned int) * gp.nsamples); + hdr.version = GMONVERSION; + hdr.profrate = SAMPLE_FREQ; + hdr.resv[0] = 0; + hdr.resv[1] = 0; + hdr.resv[2] = 0; + fwrite(&hdr, 1, sizeof(hdr), fp); + fwrite(gp.samples, gp.nsamples, sizeof(unsigned int), fp); + + for (i = 0; i < gp.narcs; i++) { + if (gp.arcs[i].count > 0) { + fwrite(gp.arcs + i, sizeof(struct rawarc), 1, fp); + } } - } - fclose(fp); + fclose(fp); + } // free memory free(gp.arcs); free(gp.samples); } +/** Writes gmon.out dump file and stops profiling + Called from _libcglue_deinit() function; will dump out a gmon.out file + at cwd with all collected information. +*/ +__attribute__((__no_instrument_function__, __no_profile_instrument_function__)) +void __gprof_cleanup() +{ + gprof_stop("gmon.out", 1); +} + /** Internal C handler for _mcount() @param frompc pc address of caller @param selfpc pc address of current function From b4cde5f8115d0ca9cefd593927c9439b863a9b95 Mon Sep 17 00:00:00 2001 From: Francisco Javier Trujillo Mata Date: Tue, 3 Sep 2024 11:26:01 +0200 Subject: [PATCH 2/2] Adding gprof examples --- ee/libprofglue/samples/Makefile | 12 +++ ee/libprofglue/samples/basic/Makefile | 12 +++ ee/libprofglue/samples/basic/Makefile.sample | 27 +++++++ ee/libprofglue/samples/basic/README.md | 66 ++++++++++++++++ ee/libprofglue/samples/basic/main.c | 70 +++++++++++++++++ ee/libprofglue/samples/custom/Makefile | 12 +++ ee/libprofglue/samples/custom/Makefile.sample | 27 +++++++ ee/libprofglue/samples/custom/README.md | 57 ++++++++++++++ ee/libprofglue/samples/custom/main.c | 75 +++++++++++++++++++ samples/Makefile_sample | 2 + 10 files changed, 360 insertions(+) create mode 100644 ee/libprofglue/samples/Makefile create mode 100644 ee/libprofglue/samples/basic/Makefile create mode 100644 ee/libprofglue/samples/basic/Makefile.sample create mode 100644 ee/libprofglue/samples/basic/README.md create mode 100644 ee/libprofglue/samples/basic/main.c create mode 100644 ee/libprofglue/samples/custom/Makefile create mode 100644 ee/libprofglue/samples/custom/Makefile.sample create mode 100644 ee/libprofglue/samples/custom/README.md create mode 100644 ee/libprofglue/samples/custom/main.c diff --git a/ee/libprofglue/samples/Makefile b/ee/libprofglue/samples/Makefile new file mode 100644 index 00000000000..5908c6b5b88 --- /dev/null +++ b/ee/libprofglue/samples/Makefile @@ -0,0 +1,12 @@ +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# Copyright 2001-2004, ps2dev - http://www.ps2dev.org +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. + +SUBDIRS = basic custom + +include $(PS2SDKSRC)/Defs.make +include $(PS2SDKSRC)/Rules.make diff --git a/ee/libprofglue/samples/basic/Makefile b/ee/libprofglue/samples/basic/Makefile new file mode 100644 index 00000000000..3d9f9f77fd2 --- /dev/null +++ b/ee/libprofglue/samples/basic/Makefile @@ -0,0 +1,12 @@ +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# Copyright 2001-2004, ps2dev - http://www.ps2dev.org +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. + +SAMPLE_DIR = libprofglue/basic + +include $(PS2SDKSRC)/Defs.make +include $(PS2SDKSRC)/samples/Rules.samples diff --git a/ee/libprofglue/samples/basic/Makefile.sample b/ee/libprofglue/samples/basic/Makefile.sample new file mode 100644 index 00000000000..4b8074356cb --- /dev/null +++ b/ee/libprofglue/samples/basic/Makefile.sample @@ -0,0 +1,27 @@ +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# Copyright 2001-2004, ps2dev - http://www.ps2dev.org +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. + +EE_BIN = gprofbasic.elf +EE_OBJS = main.o + +EE_CFLAGS = -Os -pg -g +EE_LDFLAGS += -pg -g + +all: $(EE_BIN) + +clean: + rm -rf $(EE_BIN) $(EE_OBJS) + +run: $(EE_BIN) + ps2client execee host:$(EE_BIN) + +reset: + ps2client reset + +include $(PS2SDK)/samples/Makefile.pref +include $(PS2SDK)/samples/Makefile.eeglobal diff --git a/ee/libprofglue/samples/basic/README.md b/ee/libprofglue/samples/basic/README.md new file mode 100644 index 00000000000..6792de8f941 --- /dev/null +++ b/ee/libprofglue/samples/basic/README.md @@ -0,0 +1,66 @@ +Sample program to show how to use the `gprof` feature. + +The requiremnts are quite easy, just adding `-g -pg` flags to the `EE_CFLAGS` and `EE_LDFLAGS` is enough to make things to work out of the box. + +Firstly execute your program, then once program ends it will automatically generates a `gmon.out` file at CWD level. + +In order to inspect the content of the generated file you need to use the `mips64r5900el-ps2-elf-gprof` binary. + +For instance, following the next syntax: +``` +mips64r5900el-ps2-elf-gprof -b {binary.elf} gmon.out +``` + +like: +``` +mips64r5900el-ps2-elf-gprof -b gprofbasic.elf gmon.out +``` + + +It will show something like: +``` +Flat profile: + +Each sample counts as 0.001 seconds. + % cumulative self self total + time seconds seconds calls ms/call ms/call name + 83.26 0.19 0.19 104728 0.00 0.00 is_prime + 16.74 0.23 0.04 1 39.00 39.00 dummy_function + 0.00 0.23 0.00 1 0.00 233.00 main + 0.00 0.23 0.00 1 0.00 194.00 sum_of_square_roots + + + Call graph + + +granularity: each sample hit covers 2 byte(s) for 0.43% of 0.23 seconds + +index % time self children called name + 0.00 0.23 1/1 _ftext [2] +[1] 100.0 0.00 0.23 1 main [1] + 0.00 0.19 1/1 sum_of_square_roots [4] + 0.04 0.00 1/1 dummy_function [5] +----------------------------------------------- + +[2] 100.0 0.00 0.23 _ftext [2] + 0.00 0.23 1/1 main [1] +----------------------------------------------- + 0.19 0.00 104728/104728 sum_of_square_roots [4] +[3] 83.3 0.19 0.00 104728 is_prime [3] +----------------------------------------------- + 0.00 0.19 1/1 main [1] +[4] 83.3 0.00 0.19 1 sum_of_square_roots [4] + 0.19 0.00 104728/104728 is_prime [3] +----------------------------------------------- + 0.04 0.00 1/1 main [1] +[5] 16.7 0.04 0.00 1 dummy_function [5] +----------------------------------------------- + + +Index by function name + + [5] dummy_function [1] main + [3] is_prime [4] sum_of_square_roots +``` + +Cheers \ No newline at end of file diff --git a/ee/libprofglue/samples/basic/main.c b/ee/libprofglue/samples/basic/main.c new file mode 100644 index 00000000000..cadaf830a27 --- /dev/null +++ b/ee/libprofglue/samples/basic/main.c @@ -0,0 +1,70 @@ +/* +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# Copyright 2001-2005, ps2dev - http://www.ps2dev.org +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. +# +# A basic example for checking the GProf profiler. +*/ + +#include +#include +#include +#include + +// Function to check if a number is prime +int is_prime(int num) +{ + if (num <= 1) + return 0; + if (num <= 3) + return 1; + if (num % 2 == 0 || num % 3 == 0) + return 0; + for (int i = 5; i * i <= num; i += 6) { + if (num % i == 0 || num % (i + 2) == 0) + return 0; + } + return 1; +} + +// Function to compute the sum of square roots of the first N prime numbers +double sum_of_square_roots(int N) +{ + int count = 0; + int num = 2; + double sum = 0.0; + + while (count < N) { + if (is_prime(num)) { + sum += sqrt(num); + count++; + } + num++; + } + return sum; +} + +int dummy_function() +{ + int i; + for (i = 0; i < 10000; i++) { + printf("."); + } + printf("\n"); + return 0; +} + +int main(int argc, char *argv[]) +{ + printf("Hello, world!\n"); + dummy_function(); + int N = 10000; // Large number of primes to compute + printf("Sum of square roots of the first %d prime numbers is %lf\n", N, sum_of_square_roots(N)); + printf("Goodbye, world!\n"); + + return 0; +} \ No newline at end of file diff --git a/ee/libprofglue/samples/custom/Makefile b/ee/libprofglue/samples/custom/Makefile new file mode 100644 index 00000000000..cd6a0ab2e28 --- /dev/null +++ b/ee/libprofglue/samples/custom/Makefile @@ -0,0 +1,12 @@ +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# Copyright 2001-2004, ps2dev - http://www.ps2dev.org +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. + +SAMPLE_DIR = libprofglue/custom + +include $(PS2SDKSRC)/Defs.make +include $(PS2SDKSRC)/samples/Rules.samples diff --git a/ee/libprofglue/samples/custom/Makefile.sample b/ee/libprofglue/samples/custom/Makefile.sample new file mode 100644 index 00000000000..e35f9960f71 --- /dev/null +++ b/ee/libprofglue/samples/custom/Makefile.sample @@ -0,0 +1,27 @@ +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# Copyright 2001-2004, ps2dev - http://www.ps2dev.org +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. + +EE_BIN = gprofcustom.elf +EE_OBJS = main.o + +EE_CFLAGS = -Os -pg -g +EE_LDFLAGS += -pg -g + +all: $(EE_BIN) + +clean: + rm -rf $(EE_BIN) $(EE_OBJS) + +run: $(EE_BIN) + ps2client execee host:$(EE_BIN) + +reset: + ps2client reset + +include $(PS2SDK)/samples/Makefile.pref +include $(PS2SDK)/samples/Makefile.eeglobal diff --git a/ee/libprofglue/samples/custom/README.md b/ee/libprofglue/samples/custom/README.md new file mode 100644 index 00000000000..b95082a2b82 --- /dev/null +++ b/ee/libprofglue/samples/custom/README.md @@ -0,0 +1,57 @@ +More advance example about how to use the `gprof` feature. + +The requiremnts are quite easy, just adding `-g -pg` flags to the `EE_CFLAGS` and `EE_LDFLAGS` is enough to make things to work out of the box. + +This example shows how to enable profiling just around some specific piece of code. +How `gprof` by default start profiling from the very beginning of the app we must discard that result, this is why we do `gprof_stop(NULL, false);`. +Then we just need to call `gprof_start();` whenever we want to start meassuring our piece of code and `gprof_stop("gmon_custom.out", true);` whenver we want to stop the profiling. + +Firstly execute your program, then once program ends it will automatically generates the output with the given names. + +In order to inspect the content of the generated file you need to use the `mips64r5900el-ps2-elf-gprof` binary. + +For instance, following the next syntax: +``` +mips64r5900el-ps2-elf-gprof -b {binary.elf} {gmon_custom.out} +``` +like: +``` +mips64r5900el-ps2-elf-gprof -b gprofcustom.elf gmon_custom.out +``` + +Output in this example: +``` +Flat profile: + +Each sample counts as 0.001 seconds. + % cumulative self self total + time seconds seconds calls s/call s/call name + 87.37 5.18 5.18 1 5.18 5.18 heavy_operation_3 + 12.63 5.93 0.75 1 0.75 0.75 heavy_operation_2 + + + Call graph + + +granularity: each sample hit covers 2 byte(s) for 0.02% of 5.93 seconds + +index % time self children called name + +[1] 100.0 0.00 5.93 main [1] + 5.18 0.00 1/1 heavy_operation_3 [2] + 0.75 0.00 1/1 heavy_operation_2 [3] +----------------------------------------------- + 5.18 0.00 1/1 main [1] +[2] 87.4 5.18 0.00 1 heavy_operation_3 [2] +----------------------------------------------- + 0.75 0.00 1/1 main [1] +[3] 12.6 0.75 0.00 1 heavy_operation_2 [3] +----------------------------------------------- + + +Index by function name + + [3] heavy_operation_2 [2] heavy_operation_3 +``` + +Cheers. \ No newline at end of file diff --git a/ee/libprofglue/samples/custom/main.c b/ee/libprofglue/samples/custom/main.c new file mode 100644 index 00000000000..5fdf31e1325 --- /dev/null +++ b/ee/libprofglue/samples/custom/main.c @@ -0,0 +1,75 @@ +/* +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| |____ ___| ____| | \ PS2DEV Open Source Project. +#----------------------------------------------------------------------- +# Copyright 2001-2005, ps2dev - http://www.ps2dev.org +# Licenced under Academic Free License version 2.0 +# Review ps2sdk README & LICENSE files for further details. +# +# A more advadced example for checking the GProf profiler. +# This example demonstrates how to start and stop the profiler multiple times +*/ + +#include +#include +#include +#include +#include + +#include + +// Simulates a CPU-bound heavy operation by calculating large numbers of square roots +void heavy_operation_1() { + double result = 0.0; + for (long i = 1; i < 100000; i++) { + result += sqrt((double)i); + } + printf("Result of heavy_operation_1: %f\n", result); +} + +// Simulates a different CPU-bound heavy operation by calculating factorials +void heavy_operation_2() { + unsigned long long result = 1; + for (int i = 1; i < 20; i++) { + result = 1; + for (int j = 1; j <= i * 10000; j++) { + result *= j; + result %= 100000; // Prevent overflow by keeping the result manageable + } + } + printf("Result of heavy_operation_2: %llu\n", result); +} + +// Simulates a mixed heavy operation (CPU + IO-bound) by performing some calculations and sleeping +void heavy_operation_3() { + double result = 1.0; + for (long i = 1; i < 50000; i++) { + result *= log((double)i); + if (i % 10000 == 0) { + printf("Heavy operation 3, part %ld: sleeping for 1 second...\n", i / 1000000); + sleep(1); // Simulate some IO-bound activity + } + } + printf("Result of heavy_operation_3: %f\n", result); +} + +int main() { + // Initial heavy operation (this part will be ignored in profiling) + heavy_operation_1(); + + // Stop profiling (ignore the above operation in gprof output) + gprof_stop(NULL, false); + + // Start profiling again + gprof_start(); + + // Operations to be profiled + heavy_operation_2(); + heavy_operation_3(); + + // Stop profiling and save the output to a custom file + gprof_stop("gmon_custom.out", true); + + return 0; +} \ No newline at end of file diff --git a/samples/Makefile_sample b/samples/Makefile_sample index 6786c02dcc9..f1f78519b79 100644 --- a/samples/Makefile_sample +++ b/samples/Makefile_sample @@ -31,6 +31,8 @@ SUBDIRS += libcglue/rewinddir SUBDIRS += libcglue/timezone SUBDIRS += libgs/doublebuffer SUBDIRS += libgs/draw +SUBDIRS += libprofglue/basic +SUBDIRS += libprofglue/custom #SUBDIRS += mpeg #TODO: not modified for updated newlib SUBDIRS += network/tcpip-basic SUBDIRS += network/tcpip-dhcp