-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
libc/stdio: Support "long long" type if CONFIG_HAVE_LONG_LONG is enabled #6613
Conversation
This reverts commit b1c72c0.
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 PR seems to be addressing initial concern on size increase and keeping things consistent.
LGTM!
@xiaoxiang781216 @pkarashchenko @ALTracer I think the code should use CONFIG_LIBC_LONG_LONG instead of CONFIG_HAVE_LONG_LONG and we need to fix CONFIG_LIBC_LONG_LONG definition on Kconfig to be enabled by default if compiler has support to long long (CONFIG_HAVE_LONG_LONG) and DEFAULT_SMALL is not used. So CONFIG_HAVE_LONG_LONG shouldn't be an entry on Kconfig, but only defined at include/nuttx/compiler.h We need to fix the Kconfig to:
It will avoid the issues pointed by @pkarashchenko |
The current PR reduces code size by disabling long long support for printf (I will double check scanf) and still keeps support of |
Good point! My fault, in fact CONFIG_HAVE_LONG_LONG is defined by the header file. So, maybe we can get rid of CONFIG_LIBC_HAVE_LONG_LONG and only use CONFIG_HAVE_LONG_LONG, because if the system doesn't have it, it will fallback to LONG. |
The original complaint was that there is a need to perform computation in long long, but disable long long printing in LIBC. |
Yes, and since now it can print correctly even when LIBC_LONG_LONG is not enabled and will increase only 192 bytes, this seems a nice solution! @ALTracer do you agree? I know you spent time implementing other fixes, but this solution is easier and solves the original "df -h" issue. |
Once we move CONFIG_HAVE_LONG_LONG to Kconfig, we can apply @acassis 's suggestion to simplify the default setting. Look like @ALTracer will provide a patch for this. |
This PR is better than #6606, but I still have questions. @xiaoxiang781216 Does printf now parse ull as ul? What if ull argument is > ULONG_MAX? Who should deal with overflows -- calling site or printf (with debug warnings before casting / consuming va_arg)? I'm fine with code size like that. Apparently, it's coming only from libc internals now. |
In light of your recent refactoring of most toolchain flags in Kconfig tree and #6123 bringing LTO support, -- yes, I think I'd like to add Kconfig options for HAVE_LONG_LONG and friends (HAVE_FLOAT, HAVE_DOUBLE, HAVE_LONG_DOUBLE) somewhere under Optimizations (where FRAME_POINTERS are) or under System Type (where LTO is). While it's silly to disable FLOAT, for example, I think it's for user to decide to drop DOUBLE. Like C++ has a principle "You only pay for what you use". |
Yes, it will handle both.
it's simply discard the high 32bit, which mean the output is correct if value is less/equal than ULONG_MAX, but wrong if great than ULONG_MAX.
|
It's not always silly to disable FLOAT. Depends on what you need. Various embedded libc support a choice of printf() that supports "%f" vs a printf() that doesn't support it, to save code size (by choosing which version of the library to link). That's up to each application to decide what it needs. Personally I am in favor of allowing NuttX to be reduced to very minimal sizes for certain things, and I also like that you can turn on all the bells and whistles for bigger embedded systems. |
Yes, the GCC |
Won't this create confusion and weird bugs? E.g., if a value > INT32_MAX is passed in, it will be truncated, printing wrong results? |
@hartmannathan I understand what you're referring to. I personally use newlib-nano without @pkarashchenko I second your opinion in that ull behaviour is funkier. I personally would like to stare at the ull argument and issue an overflow warning before casting the va_arg down to long and printing the lower half anyways. Otherwise we might introduce undefined behaviour in signed overflow. From another point of view, some IDEs for embedded often highlight |
How about?
This has 0 cost in production, and you usually want to run your code with DEBUGASSERT() enabled before releasing, so programmer will be notified about trying to print ll when ll is not supported. Then, if programmer wants to ignore this, responsibility of broken code lies on him - and not on nuttx. This of course forces solution where nuttx simply don't print ll without CONFIG_LIBC_LONG_LONG. If you want to get creative, you could also add trivial With these solutions after error/warning programmer can either enable CONFIG_LIBC_LONG_LONG or adapt code to use only int. This wouldn't be acceptable in Linux world, but in small embedded with rtos? I think it's acceptable - and maybe even desired as it saves space. |
Yes, if you decide to disable CONFIG_LIBC_HAVE_LONG_LONG. It is impossible to get the small code size and the more functionality at the same time, but we give you the choice. |
It isn't good since there are many places which use uint64_t when possible, but the result is seldom larger than ULONG_MAX, or the user may prefer to save the coe space and then accept the wrong printf output(e.g. log or procfs).
But, I add the overflow check before down cast: |
@mlyszczek I like this idea. This (or something similar) could really save developers a lot of frustration. If it works for Another alternative, if CONFIG_LIBC_LONG_LONG is not defined, is to print In my opinion, it is better to print something that obviously indicates an error than to risk printing incorrect information (which might be consumed by other programs leading to very hard-to-find bugs) by truncating a uint64_t and printing just the lower 32 bits. I understand the point @xiaoxiang781216 is making that usually a 64-bit variable would contain a 32-bit number. I would be open to compromising and printing the lower 32 bits IF the upper 32 bits are 0 and printing The point is: Either print CORRECT information or print an obvious error. But don't print incorrect information under any circumstances. |
@hartmannathan the patch already add the debug assert in case the value > ULONG_MAX. |
…_LONG follow the kernel side change: apache/nuttx#6613 Signed-off-by: Xiang Xiao <xiaoxiang@xiaomi.com>
It does, but in unpredictable way. Assert will only trigger if in debug runtime, you try to print something bigger than uint32_t, and this may not trigger in debug, but will malform data on production. Second thing is, "x" is signed, and comparision is to unsigned. I have a little anxiety with these runtime checks. These checks may never trigger in a debug build, but on production variable might suddenly (and we all know it eventually will:)) get bigger than long, and someday this will ruin somebody's day. Note, that these checks are not only for mere printf(), but also for sprintf() which in fact may be used for serious business (but so can fprintf(), ie. to pass big number to different CPU via serial link). Safeset approach would be "you want to print %ll? enable support for it or patch your code to include dangerous stuff yourself". If there are places where long long is used and (s)printfed, but we don't care if they overflow or not, these values should be printed as %l and explicitly casted down to (long). Anything else seems like a time bomb. @hartmannathan just out of curiosity, how do you want to print |
Before you ship the product, you must do the full test. If you don't enable CONFIG_LIBC_LONG_LONG, but print the number larger than 32bits, nobody can help you, either assert or panic can't give your the right answer.
It's easy to fix, please review again.
Debug check just help the people to pinpoint the issue quickly. It's wrong to printf the number > 32bits without CONFIG_LIBC_LONG_LONG == y. The only fix is to modify your config to enable CONFIG_LIBC_LONG_LONG.
This patch doesn't forbid you correct the code at the call site. If you really want to do so, please provide the patch to fix them case by case. But please don't add the warning log any more, because this patch already do the assertion in the wrong case.
|
Yea, there won't be a "-1" bug now, but this will trigger an assert for unsigned ll bigger than
To handle both signed and unsigned cases. |
Done. |
Now I don't understand it. Can you explain how does this assert work? I naively think atm that this assert checks whether 64-bit x is a valid (sign-extended representation of a) 32-bit integer from LONG_MIN to ULONG_MAX inclusively. |
You can't compare signed and unsigned. If you try to compare signed and unsigned, signed value will be converted to unsigned. And so, considering 8bits vars,
0 in that case will be promoted to unsigned long.
|
but just format the low 32bits if CONFIG_LIBC_LONG_LONG isn't enabled to avoid to expand the code space to much. Note: the size will increase 192 bytes on stm32_tiny:nsh. Before the change: text data bss dec hex filename 41444 184 1656 43284 a914 nuttx After the change: text data bss dec hex filename 41636 184 1656 43476 a9d4 nuttx Signed-off-by: Xiang Xiao <xiaoxiang@xiaomi.com>
Thanks for the refresher. The logic is always nice and understandable with small values and intervals.
That is usually right. However, vsprintf flips the sign before passing the x to if (x < 0)
{
x = -x;
flags |= FL_NEGATIVE;
}
There are two asserts against differently typed x: first is
No, because x is |
Yeah, of course long long, I meant that 0 will be promoted to unsigned, didn't pay attention to type:) you're right. I admit I didn't dive into the code that much and did focus only at that single line. If we are sure we are dealing with signed type then yes, you are right, second part is not necessary in that case. |
Ok, I remove the second check from DEBUGASSERT. |
@ALTracer @mlyszczek do you have more concern for these PR(#6613, #6607 and apache/nuttx-apps#1221)? if no, @pkarashchenko you can merge these patch now. |
@xiaoxiang781216 No more questions from me, LGTM on all three PRs. Time will tell, let's merge to master and let bleeding edge users test. |
Yep, it LGTM too, just like @ALTracer said, time will unravel how this plays out and if we get another bug report;) |
@pkarashchenko could you merge all related PR? |
I will review again and merge today. |
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.
LGTM
…_LONG follow the kernel side change: apache/nuttx#6613 Signed-off-by: Xiang Xiao <xiaoxiang@xiaomi.com>
…_LONG follow the kernel side change: apache/nuttx#6613 Signed-off-by: Xiang Xiao <xiaoxiang@xiaomi.com>
…_LONG follow the kernel side change: apache/nuttx#6613 Signed-off-by: Xiang Xiao <xiaoxiang@xiaomi.com>
Summary
but just format the low 32bits if CONFIG_LIBC_LONG_LONG isn't enabled to
avoid to expand the code space to much.
Note: the size will increase 192 bytes on stm32_tiny:nsh.
Before the change:
After the change:
Impact
Fix the complain in #6606
Testing
Pass CI