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

Now only getting time if pattern_formatter needs it #2246

Merged
merged 5 commits into from
Jan 16, 2022

Conversation

doug1234
Copy link
Contributor

The pattern_formatter class was making unwanted calls to localtime_r or similar functions even when it didn't use the time information. A flag was added which disables the calls when the pattern formatter is not going to use the information.

@gabime
Copy link
Owner

gabime commented Jan 14, 2022

Thanks. Not sure if it worth it considering the fact that vast majority of use cases need time and that it is cached each second.

Also this doesn't pass the tests (the second patter_formatter overload, the one without te pattern, needs to set the flag to true since it uses the default formatter).

@doug1234
Copy link
Contributor Author

Fixed the incorrect initialization. Sorry about that.

Thanks for looking over the pull request. I actually didn't add it for efficiency reasons. I was making some spdlog calls between a fork() and execv(). There was a random crash that was in localtime_r(). It seems that localtime_r() needs to do some stuff with environment variables and likely that wasn't a safe time to check environment variables. So I modified my pattern so as to not have time and was shocked to find it randomly crashing in the same place! After my change the random crashes went away.

@gabime
Copy link
Owner

gabime commented Jan 15, 2022

I was making some spdlog calls between a fork() and execv(). There was a random crash that was in localtime_r().

Could you provide more info and a way to reproduce? I could not find anything online about it.

@doug1234
Copy link
Contributor Author

If you take a look at the fork() man page (https://man7.org/linux/man-pages/man2/fork.2.html), calling a lot of functions is unsafe between fork() and execv() which would seem to include localtime_r(). However the fork() man page is not as explicit as I would like (localtime_r isn't signal safe so it is not guaranteed to be safe to call between fork() and execv() but that doesn't mean it will always be a problem either). You might be able to reproduce the issue by logging between a fork() and execv() but I only saw the crash less than 1 in 10 times that my software ran. However, the fork() man page is enough to convince me that its not a good idea to call localtime_r() between fork() and execv() and removing the call fixed my issue.

@gabime
Copy link
Owner

gabime commented Jan 15, 2022

After a fork() in a multithreaded program, the child can
safely call only async-signal-safe functions (see
signal-safety(7)) until such time as it calls execve(2).

spdlog is not async-signal-safe (see #1607), so the crash can be from anywhere, not just localtime.

@doug1234
Copy link
Contributor Author

While I agree that spdlog can't make the async-signal-safe guarantee, spdlog is close enough to it (when used carefully) for my purposes minus the spurious call to localtime. Overall I don't see a down side to this change other than a small increase in complexity and it should improve efficiency in a corner case.

@@ -1021,6 +1021,7 @@ SPDLOG_INLINE pattern_formatter::pattern_formatter(
, pattern_time_type_(time_type)
, last_log_secs_(0)
, custom_handlers_(std::move(custom_user_flags))
, needs_time_(false)
Copy link
Owner

Choose a reason for hiding this comment

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

please rename to “need_localtime_”
Some of the flags needs time but not localtime.

break;

case ('e'): // milliseconds
formatters_.push_back(details::make_unique<details::e_formatter<Padder>>(padding));
needs_time_ = true;
Copy link
Owner

Choose a reason for hiding this comment

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

not needed

break;

case ('E'): // seconds since epoch
formatters_.push_back(details::make_unique<details::E_formatter<Padder>>(padding));
needs_time_ = true;
Copy link
Owner

Choose a reason for hiding this comment

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

not needed

break;

case ('f'): // microseconds
formatters_.push_back(details::make_unique<details::f_formatter<Padder>>(padding));
needs_time_ = true;
Copy link
Owner

Choose a reason for hiding this comment

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

not needed

break;

case ('F'): // nanoseconds
formatters_.push_back(details::make_unique<details::F_formatter<Padder>>(padding));
needs_time_ = true;
Copy link
Owner

Choose a reason for hiding this comment

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

not needed

@@ -1252,18 +1282,22 @@ SPDLOG_INLINE void pattern_formatter::handle_flag_(char flag, details::padding_i

case ('u'): // elapsed time since last log message in nanos
formatters_.push_back(details::make_unique<details::elapsed_formatter<Padder, std::chrono::nanoseconds>>(padding));
needs_time_ = true;
Copy link
Owner

Choose a reason for hiding this comment

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

not needed

break;

case ('i'): // elapsed time since last log message in micros
formatters_.push_back(details::make_unique<details::elapsed_formatter<Padder, std::chrono::microseconds>>(padding));
needs_time_ = true;
Copy link
Owner

Choose a reason for hiding this comment

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

not needed

break;

case ('o'): // elapsed time since last log message in millis
formatters_.push_back(details::make_unique<details::elapsed_formatter<Padder, std::chrono::milliseconds>>(padding));
needs_time_ = true;
Copy link
Owner

Choose a reason for hiding this comment

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

not needed

break;

case ('O'): // elapsed time since last log message in seconds
formatters_.push_back(details::make_unique<details::elapsed_formatter<Padder, std::chrono::seconds>>(padding));
needs_time_ = true;
Copy link
Owner

Choose a reason for hiding this comment

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

not needed

{
cached_tm_ = get_time_(msg);
last_log_secs_ = secs;
if (needs_time_) {
Copy link
Owner

Choose a reason for hiding this comment

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

Please put the open brace in a new line like rest of the codebase

@gabime gabime merged commit caa0e54 into gabime:v1.x Jan 16, 2022
@gabime
Copy link
Owner

gabime commented Jan 16, 2022

Merged. Thanks @doug1234

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants