What is the best approach to hooking a function to an idle task? I’m trying to get a simple measurement for an overall CPU load calculation, so I’d also like to know if measuring the idle time through a counter function hooked to the idle task, then using that to calculate the active load is the best approach when it comes to RTEMS.
What I’m trying to get:
A constantly updating numeric value that updates over some window of time consistently.
Things I have ruled out:
Using CPU usage statistics that RTEMS already provides is a bit roundabout, as they are printer functions, not fit for instrumentation.
This may not even be the best approach, so if there are any other recommendations on approaching the simplest way to get a CPU usage metric, please let me know.
You already noted that the report functions are not ideal for instrumentation. As far as I recall, there is not a function to pass in a thread id and get the cpu time used. There are similar things which should be relatively straightforward to get what you want.
- The POSIX function clock_gettime(CLOCK_THREAD_CPUTIME_ID) can return this information on the calling thread. That isn’t quite what you want though. But it’s headed in the right direction as an example. See cpukit/posix/src/clockgettime.c.
- The POSIX function getrusage(RUSAGE_THREAD) has a helper function getrusage_RUSAGE_THREAD() which is pretty close to what you want but it is hard-coded to the self id. See cpukit/libcsupport/src/getrusage.c. Create a new function which takes an rtems_id as an argument for the thread you are interested in and reuse that function. This would be of interest as a new support function.
If you are not interested in CPU time but knowing when the context switches occur and seeing the event trace, you want the Capture Engine and the built-in events for thread creation, start, restart, switch, and exit. @kiwichris should be able to answer any questions about that.
There may be other potentially useful things lurking in RTEMS that have not yet made it into a public API. If anyone remembers anything else, please speak up.
I added support to EPICS iocStats module in PR 61 to get the usage. The fragment of code is:
#include <sys/resource.h>
static double oldActiveUsage;
static double oldIdleUsage;
/*
* See IEEE Std 1003.1-1988 (“POSIX.1”) for getrusage()
*/
static void cpu_ticks(double *total, double *idle) {
struct rusage stats;
double curActive;
double curIdle;
getrusage(RUSAGE_SELF, &stats);
curActive = (double)stats.ru_utime.tv_sec + stats.ru_utime.tv_usec / 1e6;
curIdle = (double)stats.ru_stime.tv_sec + stats.ru_stime.tv_usec / 1e6;
*idle = curIdle - oldIdleUsage;
*total = *idle + (curActive - oldActiveUsage);
oldActiveUsage = curActive;
oldIdleUsage = curIdle;
}
The hard part replacing IDLE is doing this for SMP and non-SMP systems.
Thanks, I think the second route would work best. If I’m adding a support function, this would just be an addition to getrusage.c, right?
To add on to the fragment of code @kiwichris posted, would using that support function in place of getrusage(RUSAGE_SELF, &stats); whilst passing the thread id of each IDLE thread and somehow iterating through them all be ideal? simple but I can’t think of anything else.
I wouldn’t add it to getrusage() since that’s a POSIX method. Add something like rtems_task_get_cpu_usage(id, ×pec). I do not know if it is worth it, but the Clock directives support multiple time formats. Getting CPU usage in multiple formats may or may not be useful.
For the comment on IDLE above, there is one IDLE thread per core. I think it always has the name IDLE. On a single CPU system, until there is major rework on RTEMS ids, it will be 0x09010001. The upper byte indicates “System Thread” and the lower 16-bits indicates the first instance of this type of object.
In an SMP system with four cores, you would have 4 threads named IDLE with IDs 0x09010001 through 0x09010004.
This would have to be confirmed on a real system but I think idle cores will use 1, 2, 3, and then 4. If you can keep 2 cores always busy, I do not think 3 or 4 will ever run. This should mean that IDLE 0x09010001 on an SMP system has more cpu used than 0x09010002, and so on.
It also means that RTEMS CPU usage in an SMP system will be like Linux where you see more than 100% or the elapsed time. In one second, there are N seconds of CPU time.
Oh yeah, of course, I wouldn’t want to add it to the POSIX function itself, I meant whether it would live in the same file? (not entirely sure about the POSIX standards of having custom functions in the same file as POSIX functions).
Thanks for the info on IDLE threads. I think on the implementation side I could iterate through the threads over a window of time periodically while the program is running indefinitely
It would be better to split out that static function. This needs to end up as an rtems_ function in cpukit/rtems/src with possibly a new score helper.
Historically, we put one function with maybe a local helper static function per file. This worked with the linker to avoid unused functions getting linked in. Now the linker arguments we use avoid that but there is still a separation of rtems_ and POSIX functions.
There is 2 open PR for this.
I havent gotten time to go back and fix mine up.
You might find something helpful in there.
1 Like
Alright, so far what I’ve got is a seperate function under cpukit/rtems/src that takes in a timespec struct parameter & and an rtems_id.
I don’t think it needs any score helpers because I can use _Timestamp_to_timespec instead of _Timestamp_to_timeval.
One more question: since rtems-central is unmaintained, what’s the process for writing yaml configurations to add declarations to the task.h header for directives like this?
@gedare Can you answer the question about rtems-central?
My answer would be to add it to the appropriate header and documentation files and not worry about rtems-central.