[PATCH v2 0/4] Fix several small errors in log time handling
While working on the deduplication of clock_gettime() in the logging code, I noticed a few other minor issues. Changes since v1: * Dropped merged change making logfile_write() static * Handle errors from clock_gettime() * Added fixes for some additional problems I noticed David Gibson (4): util: Some corrections for timespec_diff_us log: Correct formatting of timestamps log: Handle errors from clock_gettime() log: Avoid duplicate calls to logtime() log.c | 70 ++++++++++++++++++++++++++++++++++++++++------------------ util.c | 4 ++-- util.h | 2 +- 3 files changed, 52 insertions(+), 24 deletions(-) -- 2.45.2
The comment for timespec_diff_us() claims it will wrap after 2^64µs. This
is incorrect for two reasons:
* It returns a long long, which is probably 64-bits, but might not be
* It returns a signed value, so even if it is 64 bits it will wrap after
2^63µs
Correct the comment and use an explicitly 64-bit type to avoid that
imprecision.
Signed-off-by: David Gibson
logtime_fmt_and_arg() is a rather odd macro, producing both a format
string and an argument, which can only be used in quite specific printf()
like formulations. It also has a significant bug: it tries to display 4
digits after the decimal point (so down to tenths of milliseconds) using
%04i. But the field width in printf() is always a *minimum* not maximum
field width, so this will not truncate the given value, but will redisplay
the entire tenth-of-milliseconds difference again after the decimal point.
Replace the macro with an snprintf() like function which will format the
timestamp, and use an explicit % to correct the display.
Signed-off-by: David Gibson
On Tue, 6 Aug 2024 16:18:37 +1000
David Gibson
logtime_fmt_and_arg() is a rather odd macro, producing both a format string and an argument, which can only be used in quite specific printf() like formulations. It also has a significant bug: it tries to display 4 digits after the decimal point (so down to tenths of milliseconds) using %04i. But the field width in printf() is always a *minimum* not maximum field width, so this will not truncate the given value, but will redisplay the entire tenth-of-milliseconds difference again after the decimal point.
Weird, not for me (glibc 2.38). But anyway, yes, it's much better this way, and definitely, it's specified as minimum width (I never noticed). Just one nit:
Replace the macro with an snprintf() like function which will format the timestamp, and use an explicit % to correct the display.
Signed-off-by: David Gibson
--- log.c | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/log.c b/log.c index eb3a780a..e60852f7 100644 --- a/log.c +++ b/log.c @@ -46,14 +46,24 @@ int log_trace; /* --trace mode enabled */ bool log_conf_parsed; /* Logging options already parsed */ bool log_runtime; /* Daemonised, or ready in foreground */
+#define LL_STRLEN (sizeof("-9223372036854775808")) +#define LOGTIME_STRLEN (LL_STRLEN + 5) + /** - * logtime_fmt_and_arg() - Build format and arguments to print relative log time - * @x: Current timestamp + * logtime_fmt() - Format timestamp into a string for the log + * @buf: Buffer into which to format the time + * @size: Size of @buf + * @ts: Time to format + * + * Return: number of characters written to @buf (excluding \0) */ -#define logtime_fmt_and_arg(x) \ - "%lli.%04lli", \ - (timespec_diff_us((x), &log_start) / 1000000LL), \ - (timespec_diff_us((x), &log_start) / 100LL) +int logtime_fmt(char *buf, size_t size, const struct timespec *ts)
Shouldn't this be static? The rest of the series looks good to me, so I can also fix this up on merge, as you prefer. -- Stefano
On Tue, Aug 06, 2024 at 09:08:07PM +0200, Stefano Brivio wrote:
On Tue, 6 Aug 2024 16:18:37 +1000 David Gibson
wrote: logtime_fmt_and_arg() is a rather odd macro, producing both a format string and an argument, which can only be used in quite specific printf() like formulations. It also has a significant bug: it tries to display 4 digits after the decimal point (so down to tenths of milliseconds) using %04i. But the field width in printf() is always a *minimum* not maximum field width, so this will not truncate the given value, but will redisplay the entire tenth-of-milliseconds difference again after the decimal point.
Weird, not for me (glibc 2.38). But anyway, yes, it's much better this way, and definitely, it's specified as minimum width (I never noticed).
Just one nit:
Replace the macro with an snprintf() like function which will format the timestamp, and use an explicit % to correct the display.
Signed-off-by: David Gibson
--- log.c | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/log.c b/log.c index eb3a780a..e60852f7 100644 --- a/log.c +++ b/log.c @@ -46,14 +46,24 @@ int log_trace; /* --trace mode enabled */ bool log_conf_parsed; /* Logging options already parsed */ bool log_runtime; /* Daemonised, or ready in foreground */
+#define LL_STRLEN (sizeof("-9223372036854775808")) +#define LOGTIME_STRLEN (LL_STRLEN + 5) + /** - * logtime_fmt_and_arg() - Build format and arguments to print relative log time - * @x: Current timestamp + * logtime_fmt() - Format timestamp into a string for the log + * @buf: Buffer into which to format the time + * @size: Size of @buf + * @ts: Time to format + * + * Return: number of characters written to @buf (excluding \0) */ -#define logtime_fmt_and_arg(x) \ - "%lli.%04lli", \ - (timespec_diff_us((x), &log_start) / 1000000LL), \ - (timespec_diff_us((x), &log_start) / 100LL) +int logtime_fmt(char *buf, size_t size, const struct timespec *ts)
Shouldn't this be static?
Yes, yes it should.
The rest of the series looks good to me, so I can also fix this up on merge, as you prefer.
If you can fix on merge that would be great. -- David Gibson (he or they) | I'll have my music baroque, and my code david AT gibson.dropbear.id.au | minimalist, thank you, not the other way | around. http://www.ozlabs.org/~dgibson
On Wed, 7 Aug 2024 11:06:52 +1000
David Gibson
On Tue, Aug 06, 2024 at 09:08:07PM +0200, Stefano Brivio wrote:
On Tue, 6 Aug 2024 16:18:37 +1000 David Gibson
wrote: logtime_fmt_and_arg() is a rather odd macro, producing both a format string and an argument, which can only be used in quite specific printf() like formulations. It also has a significant bug: it tries to display 4 digits after the decimal point (so down to tenths of milliseconds) using %04i. But the field width in printf() is always a *minimum* not maximum field width, so this will not truncate the given value, but will redisplay the entire tenth-of-milliseconds difference again after the decimal point.
Weird, not for me (glibc 2.38). But anyway, yes, it's much better this way, and definitely, it's specified as minimum width (I never noticed).
Just one nit:
Replace the macro with an snprintf() like function which will format the timestamp, and use an explicit % to correct the display.
Signed-off-by: David Gibson
--- log.c | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/log.c b/log.c index eb3a780a..e60852f7 100644 --- a/log.c +++ b/log.c @@ -46,14 +46,24 @@ int log_trace; /* --trace mode enabled */ bool log_conf_parsed; /* Logging options already parsed */ bool log_runtime; /* Daemonised, or ready in foreground */
+#define LL_STRLEN (sizeof("-9223372036854775808")) +#define LOGTIME_STRLEN (LL_STRLEN + 5) + /** - * logtime_fmt_and_arg() - Build format and arguments to print relative log time - * @x: Current timestamp + * logtime_fmt() - Format timestamp into a string for the log + * @buf: Buffer into which to format the time + * @size: Size of @buf + * @ts: Time to format + * + * Return: number of characters written to @buf (excluding \0) */ -#define logtime_fmt_and_arg(x) \ - "%lli.%04lli", \ - (timespec_diff_us((x), &log_start) / 1000000LL), \ - (timespec_diff_us((x), &log_start) / 100LL) +int logtime_fmt(char *buf, size_t size, const struct timespec *ts)
Shouldn't this be static?
Yes, yes it should.
The rest of the series looks good to me, so I can also fix this up on merge, as you prefer.
If you can fix on merge that would be great.
Done. Series applied. -- Stefano
clock_gettime() can, theoretically, fail, although it probably won't until
2038 on old 32-bit systems. Still, it's possible someone could run with
a wildly out of sync clock, or new errors could be added, or it could fail
due to a bug in libc or the kernel.
We don't handle this well. In the debug_print case in vlogmsg we'll just
ignore the failure, and print a timestamp based on uninitialised garbage.
In logfile_write() we exit early and won't log anything at all, which seems
like a good way to make an already weird situation undebuggable.
Add some helpers to instead handle this by using "<error>" in place of a
timestamp if something goes wrong with clock_gettime().
Signed-off-by: David Gibson
We use logtime() to get a timestamp for the log in two places:
- in vlogmsg(), which is used only for debug_print messages
- in logfile_write() which is only used messages to the log file
These cases are mutually exclusive, so we don't ever print the same message
with different timestamps, but that's not particularly obvious to see.
It's possible future tweaks to logging logic could mean we log to two
different places with different timestamps, which would be confusing.
Refactor to have a single logtime() call in vlogmsg() and use it for all
the places we need it.
Signed-off-by: David Gibson
participants (2)
-
David Gibson
-
Stefano Brivio