-
-
Notifications
You must be signed in to change notification settings - Fork 546
LLVM 19 related updates #1513
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
base: main
Are you sure you want to change the base?
LLVM 19 related updates #1513
Conversation
BenBE
left a comment
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.
Not really happy with some of the changes. Can you provide a set of compiler messages describing why certain changes were made?
| char number[16]; | ||
| xSnprintf(number, 9, "CPU %d", Settings_cpuId(host->settings, i)); | ||
| unsigned cpu_width = 4 + strlen(number); | ||
| unsigned cpu_width = 4 + (unsigned) strlen(number); |
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.
| unsigned cpu_width = 4 + (unsigned) strlen(number); | |
| unsigned cpu_width = 4 + (unsigned)strlen(number); |
| char *endptr; | ||
| errno = 0; | ||
| unsigned long res = strtoul(username, &endptr, 10); | ||
| unsigned castRes = (unsigned) res; |
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.
| unsigned castRes = (unsigned) res; | |
| unsigned castRes = (unsigned)res; |
| errno = 0; | ||
| unsigned long res = strtoul(username, &endptr, 10); | ||
| unsigned castRes = (unsigned) res; | ||
| if (*endptr != '\0' || res == ULONG_MAX || errno != 0 || castRes != res) { |
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 (*endptr != '\0' || res == ULONG_MAX || errno != 0 || castRes != res) { | |
| if (*endptr != '\0' || res == ULONG_MAX || errno != 0 || res > UINT_MAX) { |
or
| if (*endptr != '\0' || res == ULONG_MAX || errno != 0 || castRes != res) { | |
| if (*endptr != '\0' || res > UINT_MAX || errno != 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.
@BenBE I would say res >= UINT_MAX because, AFAIK, (uid_t)-1 is invalid.
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.
That's something the current version didn't check for …
DynamicMeter.c
Outdated
| if (uiName) { | ||
| int len = strlen(uiName); | ||
| size_t len = strlen(uiName); | ||
| assert(len < 32); |
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.
Given this is user-controlled via a config file, this should be a runtime check (DiD).
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.
What does the colon in this uiName string do? And may I replace this strlen call with (size_t)(String_strchrnul(uiName, ':') - uiName) ?
| *pCommStart = (int)(tokenBase - cmdline); | ||
| *pCommEnd = (int)(token - cmdline); |
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.
Given that these both refer to string offsets, we actually should switch to size_t* eventually (DiD).
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 type after the substraction is technically ptrdiff_t. Casting to int would lose upper bits. Also, it's a signed type. (So size_t won't fit, but maybe ssize_t would.)
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.
Technically yes, but since tokenBase > cmdline and token > cmdline, both are positive and a cast to size_t wouldn't cause trouble with the sign bit. I'd have to check re sentinel values, but that's another concern.
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.
This Process.c specific changes for LLVM 19 compatibility may be superseded by #1588.
| continue; | ||
|
|
||
| map_inode = fast_strtoull_dec(&readptr, 0); | ||
| map_inode = (unsigned int) fast_strtoull_dec(&readptr, 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.
| map_inode = (unsigned int) fast_strtoull_dec(&readptr, 0); | |
| map_inode = (unsigned int)fast_strtoull_dec(&readptr, 0); |
| return false; | ||
| } | ||
| continue; | ||
|
|
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.
No blank line here …
| char buffer[PROC_LINE_LENGTH + 1] = {0}; | ||
|
|
||
| ssize_t oomRead = xReadfileat(procFd, "oom_score", buffer, sizeof(buffer)); | ||
| int oomRead = (int) xReadfileat(procFd, "oom_score", buffer, sizeof(buffer)); |
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.
Use ssize_t instead.
| size_t exeLen = process->procExe ? strlen(process->procExe) : 0; | ||
| int exeLen = process->procExe ? (int) strlen(process->procExe) : 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.
What's wrong with size_t here?
| unsigned int n = 0; | ||
| bool Vector_countEquals(const Vector* this, size_t expectedCount) { | ||
| size_t n = 0; | ||
| for (int i = 0; i < this->items; i++) { |
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.
Shouldn't this be updated to size_t here too (I know this would add a cast to this->items).
| errno = 0; | ||
| unsigned long res = strtoul(username, &endptr, 10); | ||
| unsigned castRes = (unsigned) res; | ||
| if (*endptr != '\0' || res == ULONG_MAX || errno != 0 || castRes != res) { |
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.
Before I can suggest a better code for this, I need one behavior clarified:
Is username being an empty string acceptable here?
Because strtoul would not produce an error for the empty string. It would happily return 0 as the result.
| int columns = width; | ||
| RichString_appendnWideColumns(str, attr, content, strlen(content), &columns); | ||
| size_t contentLen = strlen(content); | ||
| RichString_appendnWideColumns(str, attr, content, (int)MINIMUM(contentLen, 1000), &columns); |
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'm not sure I like this construct. If you want the cap the maximum length for a strlen output, then strnlen is there for 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.
Update: I've changed my mind on the use of strnlen here. While strnlen can guarantee it never reads past the specified maxlen, strnlen is (theoretically) less performant than strlen when the input string is well formed already.
That doesn't mean I deny the possibility of using strnlen in htop, but the length clamping, if needed, should be done as early as possible, e.g. in Settings.c when reading the strings from config and not long after the config file was read.
Just using size_t for strlen return values would be slightly cheaper than using strnlen.
| GraphData drawData; | ||
| int h; | ||
| int columnWidthCount; /**< only used internally by the Header */ | ||
| size_t columnWidthCount; /**< only used internally by the Header */ |
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.
Can't understand the reason why expanding the bit width of this columnWidthCount to 64. Would it save code size by saving some cast instructions?
|
|
||
| unsigned long long totalSeconds = totalMicroseconds / 1000000; | ||
| unsigned long microseconds = totalMicroseconds % 1000000; | ||
| unsigned int microseconds = totalMicroseconds % 1000000; |
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.
When I wrote this piece of Row_printNanoseconds code, I did not assume the unsigned int type would have 32 bits for you (for stricter standard compliance). I know what you are doing, but I would like uint32_t be used here instead of unsigned int.
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.
Also in favour of explicit bit width spec.
| fraction /= 10; | ||
| } | ||
| len = xSnprintf(buffer, sizeof(buffer), "%u.%0*lus ", (unsigned int)totalSeconds, width, fraction); | ||
| len = xSnprintf(buffer, sizeof(buffer), "%u.%0*us ", (unsigned int)totalSeconds, width, fraction); |
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 would like this (unsigned long) cast in snprintf function be preserved unless you use PRIu32 format instead.
| char* endp; | ||
| unsigned long int id = strtoul(entry->d_name + 3, &endp, 10); | ||
| if (id == ULONG_MAX || endp == entry->d_name + 3 || *endp != '\0') | ||
| if (id == UINT_MAX || endp == entry->d_name + 3 || *endp != '\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.
| if (id == UINT_MAX || endp == entry->d_name + 3 || *endp != '\0') | |
| if (id >= UINT_MAX || endp == entry->d_name + 3 || *endp != '\0') |
|
|
||
| char* oomPtr = buffer; | ||
| uint64_t oom = fast_strtoull_dec(&oomPtr, oomRead); | ||
| unsigned long oom = fast_strtoul_dec(&oomPtr, oomRead); |
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.
We shouldn't use fast_strou*_dec here, as these "fast" versions come with no overflow check.
By looking at the lines below, it seems like the overflow check is needed.
| char* endptr; | ||
| unsigned long parsedPid = strtoul(name, &endptr, 10); | ||
| if (parsedPid == 0 || parsedPid == ULONG_MAX || *endptr != '\0') | ||
| if (parsedPid == 0 || parsedPid >= INT_MAX || *endptr != '\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.
The logic doesn't look right here. Why would INT_MAX become an invalid PID anyway? If we want to check for overflow, it could be parsedPid > INT_MAX || !errno.
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.
strtoul returns unsigned long, but the result is later assigned to a field of unsigned int. Thus anything greater than UINT_MAX causes an integer overflow. Everything equal to UINT_MAX is the sentinel value for "invalid user". Thus parsedPid >= UINT_MAX to catch both cases at once.
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 This is PID, not UID. Please get the right context. POSIX didn't have a clear limit on the maximum value of PID, or that PID == INT_MAX is always invalid. So I want to play safe here.
Looking at this Stack Exchange question, it look like Linux currently has a hard limit of 4194303 (the maximum PID is one less than the value in /proc/sys/kernel/pid_max). And that no other OS had been using INT_MAX as maximum PID yet, so treating INT_MAX as invalid value may be fine, for now.
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.
By the way, in Unix, pid_t is always a signed type, unlike uid_t. The pid_t is required to be signed as fork(2) returns value in this type and that negative values are reserved for special uses.
|
Since there are changes in this PR that look not trivial. Would you guys mind if I pick some of the easier changes and file a new PR for them? I just hope some of the changes can be merged early. |
| unsigned int pos = x; | ||
| Settings* settings = st->host->settings; | ||
| int s = 2; | ||
| unsigned int s = 2; |
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 think for this algorithm, it's better for s to be in size_t, and use (size_t)x (upcast) for comparisons below. I think I would submit my propsed change in a separate PR.
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.
Oops. When I re-review this function code just now, I discovered that the use of strlen here was wrong at the start. This should have been "terminal width" instead and use a function similar to wcswidth but working with multi-byte string instead.
|
Most of the non-trivial changes in this PR boil down to structural refactorings where many fields that denote size values still use int instead of the more appropriate |
Enabled in LLVM 19
|
I moved this PR and #1588 to 3.5.0 because 3.4.1 is targeted to be a minor bugfix release with little refactoring where possible. As these two PRs both carry quite some code changes that are hard to justify as minor fixes, these will have to wait a bit. Also I'm not really happy with the amount of change that is introduced in these two PRs. Please check if we can manage these fixes with a little bit more structured approach. @Explorer09: Since @cgzones is kinda inactive you may continue the changes of this PR in a superseded implementation, unless there's an objection. If you do, please be mindful to the one who has to review the refactoring. ;-) |
No description provided.