New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix and improve NICE on FreeBSD #1461
base: main
Are you sure you want to change the base?
Fix and improve NICE on FreeBSD #1461
Conversation
Very cool! Does this also fix that rtprio tasks were not shown in blue on the CPU usage bar? |
No
Doesn't the niceness indicate that now? |
I think niceness and priority class are orthogonal process. E.g. I can use |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where does this touch the displayed NICE
value?
I don't quite understand what you mean by that. It computes the (now correct) niceness in the switch statement now. |
f1e16fe
to
3a5dca2
Compare
The nice handling on FreeBSD was broken for multiple reasons: - Idletime and Realtime scheduled processes were not properly detected and insane values were reported back - There was no indicator for processes that were scheduled in idletime or realtime This now adds a platform specific field for the scheduling type which one can add to interpret the niceness of a process better. Signed-off-by: Nico Sonack <nsonack@herrhotzenplotz.de>
3a5dca2
to
84cb20b
Compare
Missed that when I had a first quick look at the code. Did some further changes. Apart from this LGTM; didn't test though. Maybe @fraggerfox + @clausecker can take a look&test? IIRC we already have a more or less "global" column for the priority class that could/should be re-used here. Also, is that value different from the one shown by @clausecker For the processes to be shown in blue for the CPU time, they need to have "lower than normal" priority IIRC. If they previously didn't due to this bug, they now should be accounted accordingly. |
@BenBE On FreeBSD, everything in the "idle priority" scheduler class has "lower than normal" priority. Maybe new colours for "idle priority" and "real time priority" could be helpful instead. |
Okay, this would thus require a second change (commit) to update the accounting on the CPU meter. Will need to look up how that code works exactly though … |
Yes that value is different. |
Okay, quick update: The CPU meter takes the |
84cb20b
to
fab1bf7
Compare
@@ -73,22 +74,40 @@ void Process_delete(Object* cast) { | |||
free(this); | |||
} | |||
|
|||
static_assert(MAX_SCHEDCLASS == 5, "The lookup table below isn't in sync with the SCHEDCLASS_ enum. Please update."); | |||
static const char FreeBSD_schedclassChars[MAX_SCHEDCLASS] = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- The static assert is probably unnecessary.
- It's good to give some rationale on how the characters are chosen this way, because this thing is likely added into htop documentation. E.g. is the blank character (
SCHEDCLASS_TIMESHARE
) considered normal for a FreeBSD process, and why isSCHEDCLASS_ITHD
represented as a hyphen?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've put in the static assert for a few reasons:
- leave a compiler hint whenever someone changes the schedclass enum
- and thus prevent access to uninitialised data when such changes happen
Yes, it is unlikely this enum will ever change - this is just being less hostile to whoever works on this code and to prevent potential bugs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@herrhotzenplotz There are other mapping tables from an enum in htop codebase like this, and none of them needs a static assert. It's unlikely you need one here either.
When an array is initialized with a "named index" initializer like this, unspecified indices would have zero value. If you need an assert, do so on the mapping side so that the mapped character isn't '\0'
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are other mapping tables from an enum in htop codebase like this, and none of them needs a static assert.
Well I'd say this would be a problem with those "other mapping tables". Having the static_assert in place does no harm and only improves things. I don't see why anyone would willingly remove this simple assurance.
If you need an assert, do so on the mapping side so that the mapped character isn't '\0'.
I can certainly add this runtime assertion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well I'd say this would be a problem with those "other mapping tables". Having the static_assert in place does no harm and only improves things. I don't see why anyone would willingly remove this simple assurance.
First, it's not my decision, I only request this for the sake of code consistency. Second, having an assertion of MAX_SCHEDCLASS == 5
isn't helpful and it partly defeats the purpose of defining the MAX_SCHEDCLASS
enum value (which is meant to increase over time).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
First, it's not my decision, I only request this for the sake of code consistency.
Sacrificing being prone to bugs for code consistency seems like a very bad thing to me.
Second, having an assertion of MAX_SCHEDCLASS == 5 isn't helpful
Yes it is helpful. The point is to stop compilation, let the developer update the lookup table, correct the static assertion and continue. This is what the assertion message tells. This is a pretty common way to guide someone making changes in large C codebases.
and it partly defeats the purpose of defining the MAX_SCHEDCLASS enum value (which is meant to increase over time).
The reason to define MAX_SCHEDCLASS
is to define the static array of the correct size and to range-check incoming enum values.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@herrhotzenplotz Would you raise this issue to a separate discussion?
IMO, a long term solution is for compiler to introduce an array/struct attribute to reject any implicit initialization of array/struct members.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While I personally don't have any objections against this static_assert
there are two reasons against this in this particular case:
- The
htop
source is targeted to be compile-able by any compliant C99 compiler. Asstatic_assert
was not introduced before C11 (as_Static_assert
) or C23 proper this violates the language standard we are aiming for. - The rest of the code base merely assumes for indexed initializers that if there's a
MAX_FOO
orNUM_FOO
constant in an enum it's the last one (i.e. the one with the highest value). Thus having entries defined implicitly below that maximum value is totally fine and expected. The most prominent example is the set of internal IDs for columns.
If you really want to check, I'm totally fine with the runtime check as suggested by @Explorer09.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@BenBE I am fully aware of the fact that C99 does not have static_assert
. However, your code base has got compatibility code:
Lines 71 to 73 in 4c0e965
# define _Static_assert(expr, msg) \ | |
extern int (*__Static_assert_function (void)) \ | |
[!!sizeof (struct { int __error_if_negative: (expr) ? 2 : -1; })] |
In fact, this static_assert macro was used during my testing.
3c8e03c
to
7b8a4e3
Compare
@@ -73,22 +74,40 @@ void Process_delete(Object* cast) { | |||
free(this); | |||
} | |||
|
|||
static_assert(MAX_SCHEDCLASS == 5, "The lookup table below isn't in sync with the SCHEDCLASS_ enum. Please update."); | |||
static const char FreeBSD_schedclassChars[MAX_SCHEDCLASS] = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While I personally don't have any objections against this static_assert
there are two reasons against this in this particular case:
- The
htop
source is targeted to be compile-able by any compliant C99 compiler. Asstatic_assert
was not introduced before C11 (as_Static_assert
) or C23 proper this violates the language standard we are aiming for. - The rest of the code base merely assumes for indexed initializers that if there's a
MAX_FOO
orNUM_FOO
constant in an enum it's the last one (i.e. the one with the highest value). Thus having entries defined implicitly below that maximum value is totally fine and expected. The most prominent example is the set of internal IDs for columns.
If you really want to check, I'm totally fine with the runtime check as suggested by @Explorer09.
7b8a4e3
to
3107a08
Compare
|
||
default: | ||
fp->sched_class = SCHEDCLASS_UNKNOWN; | ||
proc->nice = kproc->ki_nice - NZERO; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see implementation differences between yours in this PR and the FreeBSD top source code you cited.
Specifically, when the sched_class
is unknown, FreeBSD top
simply display "?
" for the nice value. It doesn't use the kproc->ki_nice - NZERO
as if it were a regular ("time sharing") process.
Is this behavior difference intentional?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see how to represent an unknown nice value since proc->nice
is an integer in htop. Setting it to zero would be wrong.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you have a sentinel value would it be sensible to display N/A
in that case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess that makes sense.
While I have no more problem with the code qualities of this PR, there is one potential issue that I am not sure if it's worth addressing. Since it becomes apparent that FreeBSD doesn't use a simple integer nice value for process priority, the sorting of Then I saw this "FreeBSD process priority to nice value" conversation table that I can't tell of it's accurate (from here https://git.quacker.org/d/freebsd-nq/commit/de916c8b747ae5d875788555f6d39adb3fc91e4a): /*
* normal time -> nice value -20 - +20
* real time 0 - 31 -> nice value -52 - -21
* idle time 0 - 31 -> nice value +21 - +52
*/ For example, "real time" 31 and "idle time" 31 should definitely not be ordered close together when sorted by |
That is a good point.
This link does not seem to be an official src mirror and it also references code from 2006 which is very much not up-to-date. The current top code looks very different (e.g.: https://codeberg.org/FreeBSD/freebsd-src/src/branch/main/usr.bin/top/machine.c) |
Good reference. I have two questions to ask, if you can help answering those for me. From what I've seen, there are sbuf_printf(procbuf, "%3d ", pp->ki_pri.pri_level - PZERO);
sbuf_printf(procbuf, "%4s", format_nice(pp)); According to the
|
Yes, the
It is possible, though I typically use the Also: calling renice on an idletime- or realtime scheduled process has no effect. Relevant syscalls are: |
3107a08
to
d045e1d
Compare
Previously the niceness value on FreeBSD was utterly broken (to my and others annoyance):
This fixes:
I have tested this with other people on a few architectures and different versions of FreeBSD and it seems to be working just fine.
Please tell me if I can improve on this or if something is utterly wrong and I should fix.