From 637aa01364493647d1241fe22603f16733233bb0 Mon Sep 17 00:00:00 2001 From: Evan Burkey Date: Thu, 28 Dec 2023 12:28:13 -0800 Subject: [PATCH] add macOS ProcessData --- CMakeLists.txt | 1 + docs/macos.md | 63 +++++++++++++++++++++++++++++++++++++++++++++++ include/lfmacos.h | 23 +++++++++++++++++ src/macos.c | 43 ++++++++++++++++++++++++++++++++ tests/tests.c | 23 +++++++++++++++++ 5 files changed, 153 insertions(+) create mode 100644 docs/macos.md create mode 100644 include/lfmacos.h create mode 100644 src/macos.c diff --git a/CMakeLists.txt b/CMakeLists.txt index dcbac4b..b6ca64b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,6 +15,7 @@ set(SOURCES src/string.c src/vector.c src/utility.c + src/macos.c ) add_library(flint ${SOURCES}) diff --git a/docs/macos.md b/docs/macos.md new file mode 100644 index 0000000..120588c --- /dev/null +++ b/docs/macos.md @@ -0,0 +1,63 @@ +# macOS + +Platform-specific code for macOS. Everything in this library, including the headers, is wrapped in the following +preprocessor macro + +```c +#if defined(__APPLE__) || defined(__MACH__) +/* +... code ... +*/ +#endif /* defined(__APPLE__) || defined(__MACH__) */ +``` + +## Structs + +### ProcessData + +ProcessData is a struct that tracks cpu and memory usage of a process. It needs to be regularly updated with calls to +`update_process()` in order to provide accurate information + +```c +typedef struct { +double total_user_time; +double total_kernel_time; +double last_total_consumed; +double percent_cpu; + +uint64_t virtual_memory; +uint64_t resident_memory; + +time_t timestamp; +time_t last_timestamp; +} ProcessData; +``` + +## Functions + +## new_ProcessData + +Returns a pointer to a `ProcessData` struct. + +```c +ProcessData *new_ProcessData(); +``` + +## update_process + +Updates a `ProcessData` struct. This should be called in a loop or at specific intervals to measure proper usage +data. An example is below + +```c +int update_process(pid_t pid, ProcessData *proc); + +/* Example */ +pid_t pid = getpid(); +ProcessData *pd = new_ProcessData(); +for (int i = 0; i < 10; i++) { + update_process(pid, pd); + printf("CPU: %.2f\n", pd->percent_cpu); + sleep(1); +} +free(pd); +``` diff --git a/include/lfmacos.h b/include/lfmacos.h new file mode 100644 index 0000000..fe54997 --- /dev/null +++ b/include/lfmacos.h @@ -0,0 +1,23 @@ +#ifndef LIBFLINT_MACOS_H +#define LIBFLINT_MACOS_H + +#if defined(__APPLE__) || defined(__MACH__) + +typedef struct { + double total_user_time; + double total_kernel_time; + double last_total_consumed; + double percent_cpu; + + uint64_t virtual_memory; + uint64_t resident_memory; + + time_t timestamp; + time_t last_timestamp; +} ProcessData; + +ProcessData *new_ProcessData(); +int update_process(pid_t pid, ProcessData *proc); + +#endif /* defined(__APPLE__) || defined(__MACH__) */ +#endif /* LIBFLINT_MACOS_H */ diff --git a/src/macos.c b/src/macos.c new file mode 100644 index 0000000..9a79ed6 --- /dev/null +++ b/src/macos.c @@ -0,0 +1,43 @@ +#include +#include +#include +#include +#include + +#include "lfmacos.h" + +ProcessData *new_ProcessData() { + ProcessData *pd = malloc(sizeof(ProcessData)); + pd->last_total_consumed = -1.0; + return pd; +} + +int update_process(pid_t pid, ProcessData *proc) { + struct proc_taskinfo taskinfo; + const int r = proc_pidinfo(pid, PROC_PIDTASKINFO, 0, &taskinfo, PROC_PIDTASKINFO_SIZE); + if (r != PROC_PIDTASKINFO_SIZE) { + return 1; + } + + mach_timebase_info_data_t info; + mach_timebase_info(&info); + const double ns_per_tick = (double)info.numer / (double)info.denom; + + time(&(proc->timestamp)); + proc->total_kernel_time = (taskinfo.pti_total_system * ns_per_tick) / 1000000000.0; + proc->total_user_time = (taskinfo.pti_total_user * ns_per_tick) / 1000000000.0; + proc->virtual_memory = taskinfo.pti_virtual_size; + proc->resident_memory = taskinfo.pti_resident_size; + + if (proc->last_total_consumed > -1.0) { + proc->percent_cpu = fabs(100.0 * + (proc->total_user_time + proc->total_kernel_time - proc->last_total_consumed) / + (proc->last_timestamp - proc->timestamp)); + } else { + proc->percent_cpu = 0.0; + } + proc->last_timestamp = proc->timestamp; + proc->last_total_consumed = proc->total_kernel_time + proc->total_user_time; + + return 0; +} diff --git a/tests/tests.c b/tests/tests.c index eed95c1..b76cffa 100644 --- a/tests/tests.c +++ b/tests/tests.c @@ -1,6 +1,7 @@ #include #include #include +#include #include "lflinkedlist.h" #include "lfset.h" @@ -10,6 +11,10 @@ #include "lfmath.h" #include "lfstring.h" +#if defined(__APPLE__) || defined(__MACH__) +#include "lfmacos.h" +#endif /* defined(__APPLE__) || defined(__MACH__) */ + void print_ll(List *list) { LL_ITER(list) { printf(" %d", *((int *) node->data)); @@ -299,6 +304,19 @@ void test_string() { printf("Passes all string tests\n"); } +void test_macos() { + printf("\n--- macOS TEST ---\n"); + + pid_t pid = getpid(); + ProcessData *pd = new_ProcessData(); + for (int i = 0; i < 4; i++) { + update_process(pid, pd); + printf("CPU: %.2f\n", pd->percent_cpu); + sleep(1); + } + free(pd); +} + int main() { test_ll(); test_set(); @@ -307,5 +325,10 @@ int main() { test_math(); test_vector(); test_string(); + +#if defined(__APPLE__) || defined(__MACH__) + test_macos(); +#endif + return 0; } \ No newline at end of file