Skip to content
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

logging: Prevent multiple arguments evaluation (take 2) #42686

Merged
merged 6 commits into from
Feb 22, 2022

Conversation

nordic-krch
Copy link
Contributor

@nordic-krch nordic-krch commented Feb 10, 2022

This is another attempt to fix #42050. First attempt (#42165) was reverted (#42609) due to #42608. Test was failing because of increased stack usage after changes in logging. Increased stack usage has been seen in CONFIG_NO_OPTIMIZATIONS=y case.

In this take, logging macro path has been changed so that runtime packaging (the one without preprocessor magic and multiple arguments evaluation) is used always when CONFIG_NO_OPTIMIZATIONS=y or CONFIG_LOG_MODE_IMMEDIATE=y. In both cases static packaging is not needed since static packaging is used to get best performance and with those two options enabled it is not a case. Since preprocessor magic is used only when optimization is enabled, compiler handles it correctly, resolving all static values without bloating stack usage.

Additionally, added a test (tests/subsys/logging/log_stack) for checking stack usage when logging is used. Test has been used to characterize stack usage for various architectures (it varies a lot) in 4 configurations:

  • deferred mode
  • deferred mode (no optimization)
  • immediate mode
  • immediate mode (no optimization)

Test should detect any unexpected stack usage increases in logging area.

Fixes #42050.
Fixed #43016.

@nordic-krch nordic-krch requested a review from nashif as a code owner February 10, 2022 10:27
@nordic-krch nordic-krch requested a review from dcpleung February 10, 2022 10:27
@github-actions github-actions bot added area: API Changes to public APIs area: Documentation area: Logging area: Tests Issues related to a particular existing or missing test labels Feb 10, 2022
@nordic-krch nordic-krch added the bug The issue is a bug, or the PR is fixing a bug label Feb 10, 2022
@nashif
Copy link
Member

nashif commented Feb 10, 2022

@nordic-krch we also had issues with the previous attempt on XCC, @dcpleung can provide the details.

@dcpleung
Copy link
Member

Actually, @MaureenHelm found the issue first.

The issue is that XCC complains: #pragma GCC diagnostic not allowed inside functions. If you #ifdef them, XCC complains about _auto_type being unknown since this was introduced in GCC 4.9, but XCC is based on GCC 4.2.

@nordic-krch
Copy link
Contributor Author

The issue is that XCC complains: #pragma GCC diagnostic not allowed inside functions. If you #ifdef them, XCC complains about _auto_type being unknown since this was introduced in GCC 4.9, but XCC is based on GCC 4.2.

Added guard similar to one in cbprintf. For GCC < 4.9 and clang < 3.8 runtime packaging is used always and preprocessor cuts path with __auto_type and _Generic.

@MaureenHelm MaureenHelm self-requested a review February 11, 2022 13:25
#ifndef CONFIG_LOG2_ALWAYS_RUNTIME
#if !defined(__cplusplus) && \
!(((__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) >= 40900) || \
((__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) >= 30800))
Copy link
Collaborator

@SebastianBoe SebastianBoe Feb 15, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe put this in a #define ?

#if condition_for_supporting_autotype
#define AUTO_TYPE_IS_SUPPORTED
#endif

Would make the rest of the logic simpler, and potentially be useful if someone else wanted to know if autotype is supported.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are there any way we could detect if this is supported before build time ?

For example checking compiler version in CMake or testing the compiler (I'm not aware of any compiler flags related to this feature, so this will most likely not be possible).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure about compile time, but i rephrased this define to fixed #42686 (comment) same time.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

placed comment here: #42686 (comment)

comments to comments easily gets lost in history threads.

*/
#define Z_LOG_LOCAL_ARG_NAME(idx, arg) COND_CODE_0(idx, (arg), (_v##idx))

/* Create local variable from input variable (expect first (fmt) argument). */
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/* Create local variable from input variable (expect first (fmt) argument). */
/* Create local variable from input variable (except for the first (fmt) argument). */

COND_CODE_0(idx, (), (auto Z_LOG_LOCAL_ARG_NAME(idx, arg) = (arg) + 0))
#else
#define Z_LOG_LOCAL_ARG_CREATE(idx, arg) \
COND_CODE_0(idx, (), (__auto_type Z_LOG_LOCAL_ARG_NAME(idx, arg) = (arg) + 0))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we have a define that evaluates to either auto or __auto_type so this is simplified ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, Setting Z_AUTO_TYPE define to auto in cpp and __auto_type if c (and supported).


/* First level of processing creates stack variables to be passed for further processing.
* This is done to prevent multiple evaluations of input arguments (in case argument
* evaluation has consequences, e.g. it is a function call).
Copy link
Collaborator

@SebastianBoe SebastianBoe Feb 15, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* evaluation has consequences, e.g. it is a function call).
* evaluation has side effects, e.g. it is a non-pure function call).

@@ -0,0 +1,288 @@
/*
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suffix should be .h, not .inc.

This makes it easier for tools and humans to understand what kind of file it is.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there is a pattern, first started in tests/unit/util to name if test.inc and this is forth test that do that. I would keep it as is for consistency.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fair enough

((__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) >= 30800)
#define Z_AUTO_TYPE __auto_type
#else
#error "Compiler lacks support for __auto_type. Enable CONFIG_LOG2_ALWAYS_RUNTIME."
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be

#ifndef CONFIG_LOG2_ALWAYS_RUNTIME
#error
#endif

right?

I see that below in this file we are checking if this define is set, so I assume it is not always unset.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

my bad. fixed.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"move fast and break things" ;)

looks like a good job overall, at least the parts I was able
to understand.

SebastianBoe
SebastianBoe previously approved these changes Feb 17, 2022
dcpleung
dcpleung previously approved these changes Feb 17, 2022
Copy link
Collaborator

@tejlmand tejlmand left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use the features of CMake instead of hacks like including test.inc in to main files that are named just for the sole purposes of having them compiled with different compilers.

Comment on lines 7 to 9
target_sources_ifdef(CONFIG_CPLUSPLUS app PRIVATE src/maincxx.cxx)

target_sources_ifndef(CONFIG_CPLUSPLUS app PRIVATE src/main.c)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually, because maincxx.cxx and main.c are identical and including the same file, so a much better approach is to request CMake to compile main.c using the C++ compiler, like this:

Suggested change
target_sources_ifdef(CONFIG_CPLUSPLUS app PRIVATE src/maincxx.cxx)
target_sources_ifndef(CONFIG_CPLUSPLUS app PRIVATE src/main.c)
target_sources(app PRIVATE src/main.c)
if(CONFIG_CPLUSPLUS)
# Feel free to add a good comment here.
set_source_files_properties(src/main.c PROPERTIES LANGUAGE CXX)
endif()

and then remove maincxx.cxx as well as test.inc.
Have just a single main.c file.

Cleaning of other places using test.inc can be done in seperate PR.

#define Z_AUTO_TYPE __auto_type
#else
#ifndef CONFIG_LOG2_ALWAYS_RUNTIME
#error "Compiler lacks support for __auto_type. Enable CONFIG_LOG2_ALWAYS_RUNTIME."
Copy link
Collaborator

@tejlmand tejlmand Feb 21, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we check configure time:
#42686 (comment)

For example checking compiler version in CMake

This can be done with: https://cmake.org/cmake/help/latest/variable/CMAKE_LANG_COMPILER_VERSION.html which is supported for gcc and llvm.
That way we may detect this BEFORE compilation.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that way we can inform user earlier that there is a configuration issue that must be fixed.

Always use runtime packaging in immediate mode. Removing attempt to
package on stack if possible. It would be done only to speed up
logging but that is not a requirement in immediate mode where
packaging time is fractional compared to backend processing. Using
runtime packaging also reduces stack usage in the context which calls
log message.

Signed-off-by: Krzysztof Chruscinski <[email protected]>
@nordic-krch nordic-krch dismissed stale reviews from dcpleung and SebastianBoe via 2df7853 February 22, 2022 08:26
@nordic-krch nordic-krch force-pushed the fix/log_multieval3 branch 3 times, most recently from 9f57b20 to c7ed4f7 Compare February 22, 2022 10:10
@nordic-krch
Copy link
Contributor Author

@SebastianBoe @dcpleung approvals have been withdrawn, rebased and applied @tejlmand comments. Please take another look.

@carlescufi carlescufi added the Hotfix Fix for issues blocking development, i.e. CI issues, tests failing in CI, etc. label Feb 22, 2022
SebastianBoe
SebastianBoe previously approved these changes Feb 22, 2022
Copy link
Collaborator

@tejlmand tejlmand left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, only comment left is we should reduce the fatal error to a warning.

if (NOT CONFIG_LOG2_ALWAYS_RUNTIME)
if(CMAKE_C_COMPILER_ID STREQUAL "Clang")
if(CMAKE_C_COMPILER_VERSION VERSION_LESS "3.8.0")
message(FATAL_ERROR "Compiler version requires CONFIG_LOG2_ALWAYS_RUNTIME to be set")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should downgrade this to WARNING.

Suggested change
message(FATAL_ERROR "Compiler version requires CONFIG_LOG2_ALWAYS_RUNTIME to be set")
message(WARNING "Compiler version requires CONFIG_LOG2_ALWAYS_RUNTIME to be set")

Reason, a fatal error will prevent users from running menuconfig and fix the problem, ref #9573

In principle FATAL_ERROR would be optimal here, but as users should be able to fix this using menuconfig, we have to go for less optimal solution.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

endif()
if(CMAKE_C_COMPILER_ID STREQUAL "GNU")
if(CMAKE_C_COMPILER_VERSION VERSION_LESS "4.9.0")
message(FATAL_ERROR "Compiler version requires CONFIG_LOG2_ALWAYS_RUNTIME to be set")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should downgrade this to WARNING.

Suggested change
message(FATAL_ERROR "Compiler version requires CONFIG_LOG2_ALWAYS_RUNTIME to be set")
message(WARNING "Compiler version requires CONFIG_LOG2_ALWAYS_RUNTIME to be set")

Reason, a fatal error will prevent users from running menuconfig and fix the problem, ref #9573

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Logging v2 is utilizing complex preprocessing operations to
prepare message at compile time. Multiple operations are peformed
on log message arguments. However, it is expected that argument
will be evaluated only once (e.g. it can be a call to a function
with side effects). Adding additional layer which creates copies
of user arguments on stack and passes them to further processing.

Updated test for log_msg2 which is using internal macro which
got renamed.

Signed-off-by: Krzysztof Chruscinski <[email protected]>
Added test which checks that log argument is evaluated only
once when log message is created.

Signed-off-by: Krzysztof Chruscinski <[email protected]>
When immediate logging is used and optimization is off then bigger
stack is needed for thread analyzer. Adjusting the value.

Signed-off-by: Krzysztof Chruscinski <[email protected]>
Added test which estimates stack usage of few types of logging
messages in various modes, with and without optimization. Test
was used to determine usage for various architectures and is expected
to be used to detect stack usage increase since it fails when
stack usage in given configuration goes beyond the hardcoded limit.

Signed-off-by: Krzysztof Chruscinski <[email protected]>
Add information about logging stack usage.

Signed-off-by: Krzysztof Chruscinski <[email protected]>
Copy link
Collaborator

@tejlmand tejlmand left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: API Changes to public APIs area: Build System area: Debugging area: Documentation area: Logging area: Tests Issues related to a particular existing or missing test bug The issue is a bug, or the PR is fixing a bug Hotfix Fix for issues blocking development, i.e. CI issues, tests failing in CI, etc.
Projects
None yet
7 participants