diff mbox series

[v2] Improve tracef/tracelog to use the stack for small strings

Message ID 20220801143524.160208-1-norbert.lange@andritz.com
State New
Headers show
Series [v2] Improve tracef/tracelog to use the stack for small strings | expand

Commit Message

Norbert Lange Aug. 1, 2022, 2:35 p.m. UTC
From: Norbert Lange <nolange79 at gmail.com>

Support two common cases, one being that the resulting message is
small enough to fit into a on-stack buffer.
The seconds being the common 'printf("%s", "Message")' scheme.

Unfortunately, iterating a va_list is destructive,
so it has to be copied before calling vprintf.

The implementation was moved to a separate file,
used by both tracef.c and tracelog.c.

Signed-off-by: Norbert Lange <nolange79 at gmail.com>
---
v2 -> v3:

*   Wrap macro in do/while
*   drop loop
*   other changes in response to feedback

v2:
-   move define into src/common/tracer.h
    see https://lists.lttng.org/pipermail/lttng-dev/2021-May/029977.html
-   moved macro magic into common tracelog-internal.h header
-   rebased onto master
---
 src/common/tracer.h                   |  2 +
 src/lib/lttng-ust/tracef.c            | 32 +++------
 src/lib/lttng-ust/tracelog-internal.h | 95 +++++++++++++++++++++++++++
 src/lib/lttng-ust/tracelog.c          | 40 +++--------
 4 files changed, 114 insertions(+), 55 deletions(-)
 create mode 100644 src/lib/lttng-ust/tracelog-internal.h

Comments

Norbert Lange Aug. 1, 2022, 2:44 p.m. UTC | #1
Am Mo., 1. Aug. 2022 um 16:35 Uhr schrieb Norbert Lange <nolange79 at gmail.com>:
>
> From: Norbert Lange <nolange79 at gmail.com>
>
> Support two common cases, one being that the resulting message is
> small enough to fit into a on-stack buffer.
> The seconds being the common 'printf("%s", "Message")' scheme.
>
> Unfortunately, iterating a va_list is destructive,
> so it has to be copied before calling vprintf.
>
> The implementation was moved to a separate file,
> used by both tracef.c and tracelog.c.
>
> Signed-off-by: Norbert Lange <nolange79 at gmail.com>
> ---
> v2 -> v3:
>
> *   Wrap macro in do/while
> *   drop loop
> *   other changes in response to feedback
>
> v2:
> -   move define into src/common/tracer.h
>     see https://lists.lttng.org/pipermail/lttng-dev/2021-May/029977.html
> -   moved macro magic into common tracelog-internal.h header
> -   rebased onto master
> ---
>  src/common/tracer.h                   |  2 +
>  src/lib/lttng-ust/tracef.c            | 32 +++------
>  src/lib/lttng-ust/tracelog-internal.h | 95 +++++++++++++++++++++++++++
>  src/lib/lttng-ust/tracelog.c          | 40 +++--------
>  4 files changed, 114 insertions(+), 55 deletions(-)
>  create mode 100644 src/lib/lttng-ust/tracelog-internal.h
>
> diff --git a/src/common/tracer.h b/src/common/tracer.h
> index 2affd6ab..8e18c9b5 100644
> --- a/src/common/tracer.h
> +++ b/src/common/tracer.h
> @@ -26,6 +26,8 @@
>  #define LTTNG_RFLAG_EXTENDED           RING_BUFFER_RFLAG_END
>  #define LTTNG_RFLAG_END                        (LTTNG_RFLAG_EXTENDED << 1)
>
> +#define LTTNG_TRACE_PRINTF_BUFSIZE     512
> +
>  /*
>   * LTTng client type enumeration. Used by the consumer to map the
>   * callbacks from its own address space.
> diff --git a/src/lib/lttng-ust/tracef.c b/src/lib/lttng-ust/tracef.c
> index c05c7811..92911e1d 100644
> --- a/src/lib/lttng-ust/tracef.c
> +++ b/src/lib/lttng-ust/tracef.c
> @@ -7,6 +7,7 @@
>  #define _LGPL_SOURCE
>  #include <stdio.h>
>  #include "common/macros.h"
> +#include "common/tracer.h"
>
>  /* The tracepoint definition is public, but the provider definition is hidden. */
>  #define LTTNG_UST_TRACEPOINT_PROVIDER_HIDDEN_DEFINITION
> @@ -15,39 +16,22 @@
>  #define LTTNG_UST_TRACEPOINT_DEFINE
>  #include "lttng-ust-tracef-provider.h"
>
> -static inline
> -void lttng_ust___vtracef(const char *fmt, va_list ap)
> -       __attribute__((always_inline, format(printf, 1, 0)));
> -static inline
> -void lttng_ust___vtracef(const char *fmt, va_list ap)
> -{
> -       char *msg;
> -       const int len = vasprintf(&msg, fmt, ap);
> -
> -       /* len does not include the final \0 */
> -       if (len < 0)
> -               goto end;
> -       lttng_ust_tracepoint_cb_lttng_ust_tracef___event(msg, len,
> -               LTTNG_UST_CALLER_IP());
> -       free(msg);
> -end:
> -       return;
> -}
> +#include "tracelog-internal.h"
>
>  void lttng_ust__vtracef(const char *fmt, va_list ap)
>         __attribute__((format(printf, 1, 0)));
>  void lttng_ust__vtracef(const char *fmt, va_list ap)
>  {
> -       lttng_ust___vtracef(fmt, ap);
> +       LTTNG_UST_TRACELOG_VALIST(fmt, ap,
> +               lttng_ust_tracepoint_cb_lttng_ust_tracef___event,
> +               msg, len, LTTNG_UST_CALLER_IP());
>  }
>
>  void lttng_ust__tracef(const char *fmt, ...)
>         __attribute__((format(printf, 1, 2)));
>  void lttng_ust__tracef(const char *fmt, ...)
>  {
> -       va_list ap;
> -
> -       va_start(ap, fmt);
> -       lttng_ust___vtracef(fmt, ap);
> -       va_end(ap);
> +       LTTNG_UST_TRACELOG_VARARG(fmt,
> +               lttng_ust_tracepoint_cb_lttng_ust_tracef___event,
> +               msg, len, LTTNG_UST_CALLER_IP());
>  }
> diff --git a/src/lib/lttng-ust/tracelog-internal.h b/src/lib/lttng-ust/tracelog-internal.h
> new file mode 100644
> index 00000000..34537023
> --- /dev/null
> +++ b/src/lib/lttng-ust/tracelog-internal.h
> @@ -0,0 +1,95 @@
> +/*
> + * SPDX-License-Identifier: MIT
> + *
> + * Copyright (C) 2013-2014 Mathieu Desnoyers <mathieu.desnoyers at efficios.com>
> + * Copyright (C) 2021 Norbert Lange <nolange79 at gmail.com>
> + *
> + * Shared helper macro for tracelog and tracef.
> + */
> +
> +#define LTTNG_UST_TRACELOG_VARARG(fmt, callback, ...) \
> +       do { \
> +               va_list ap; \
> +               char local_buf[LTTNG_TRACE_PRINTF_BUFSIZE]; \
> +               size_t len = 0; \
> +               char *msg = local_buf; \
> +               char *alloc_buff = NULL; \
> +\
> +               if (caa_unlikely(fmt[0] == '%' && fmt[1] == 's' && fmt[2] == '\0')) { \
> +                       va_start(ap, fmt); \
> +                       msg = va_arg(ap, char *); \
> +                       va_end(ap); \
> +                       len = strlen(msg); \
> +               } else { \
> +                       int ret; \
> +                       size_t buflen = sizeof(local_buf); \
> +\
> +                       /* On-stack buffer attempt */ \
> +                       va_start(ap, fmt); \
> +                       ret = vsnprintf(msg, buflen, fmt, ap); \
> +                       va_end(ap); \
> +                       if (caa_unlikely(ret < 0)) \
> +                               break; \
> +                       len = (size_t)ret; \
> +\
> +                       if (caa_unlikely(len >= sizeof(local_buf))) { \
> +                               buflen = len + 1; \
> +                               alloc_buff = (char *)malloc(buflen); \
> +                               if (!alloc_buff) \
> +                                       goto end; \
> +                               msg = alloc_buff; \
> +                               va_start(ap, fmt); \
> +                               ret = vsnprintf(msg, buflen, fmt, ap); \
> +                               va_end(ap); \
> +                               lttng_ust_runtime_bug_on(ret < 0 || (size_t)ret != buflen - 1); \
> +                               len = (size_t)ret; \
> +                       } \
> +               } \
> +\
> +               callback(__VA_ARGS__); \
> +end: \
> +               /* Don't call a potentially instrumented forbidden free needlessly. */ \
> +               if (caa_unlikely(alloc_buff)) \
> +                       free(alloc_buff); \
> +       } while(0)
> +
> +#define LTTNG_UST_TRACELOG_VALIST(fmt, ap, callback, ...) \
> +       do { \
> +               char local_buf[LTTNG_TRACE_PRINTF_BUFSIZE]; \
> +               size_t len = 0; \
> +               char *msg = local_buf; \
> +               char *alloc_buff = NULL; \
> +\
> +               if (caa_unlikely(fmt[0] == '%' && fmt[1] == 's' && fmt[2] == '\0')) { \
> +                       msg = va_arg(ap, char *); \
> +                       len = strlen(msg); \
> +               } else { \
> +                       va_list ap2; \
> +                       int ret; \
> +                       size_t buflen = sizeof(local_buf); \
> +\
> +                       va_copy(ap2, ap); \
> +                       ret = vsnprintf(msg, buflen, fmt, ap2); \
> +                       va_end(ap2); \
> +                       if (caa_unlikely(ret < 0)) \
> +                               break; \
> +                       len = (size_t)ret; \
> +\
> +                       if (caa_unlikely(len >= sizeof(local_buf))) { \
> +                               buflen = len + 1; \
> +                               alloc_buff = (char *)malloc(buflen); \
> +                               if (!alloc_buff) \
> +                                       goto end; \
> +                               msg = alloc_buff; \
> +                               ret = vsnprintf(msg, buflen, fmt, ap); \
> +                               lttng_ust_runtime_bug_on(ret < 0 || (size_t)ret != buflen - 1); \
> +                               len = (size_t)ret; \
> +                       } \
> +               } \
> +\
> +               callback(__VA_ARGS__); \
> +end: \
> +               /* Don't call a potentially instrumented forbidden free needlessly. */ \
> +               if (caa_unlikely(alloc_buff)) \
> +                       free(alloc_buff); \
> +       } while (0)
> diff --git a/src/lib/lttng-ust/tracelog.c b/src/lib/lttng-ust/tracelog.c
> index 8147d7a3..bd38032c 100644
> --- a/src/lib/lttng-ust/tracelog.c
> +++ b/src/lib/lttng-ust/tracelog.c
> @@ -7,6 +7,7 @@
>  #define _LGPL_SOURCE
>  #include <stdio.h>
>  #include "common/macros.h"
> +#include "common/tracer.h"
>
>  /* The tracepoint definition is public, but the provider definition is hidden. */
>  #define LTTNG_UST_TRACEPOINT_PROVIDER_HIDDEN_DEFINITION
> @@ -15,32 +16,9 @@
>  #define LTTNG_UST_TRACEPOINT_DEFINE
>  #include "lttng-ust-tracelog-provider.h"
>
> +#include "tracelog-internal.h"
> +
>  #define LTTNG_UST_TRACELOG_CB(level) \
> -       static inline \
> -       void lttng_ust___vtracelog_##level(const char *file, \
> -                       int line, const char *func, \
> -                       const char *fmt, va_list ap) \
> -               __attribute__((always_inline, format(printf, 4, 0))); \
> -       \
> -       static inline \
> -       void lttng_ust___vtracelog_##level(const char *file, \
> -                       int line, const char *func, \
> -                       const char *fmt, va_list ap) \
> -       { \
> -               char *msg; \
> -               const int len = vasprintf(&msg, fmt, ap); \
> -               \
> -               /* len does not include the final \0 */ \
> -               if (len < 0) \
> -                       goto end; \
> -               lttng_ust_tracepoint_cb_lttng_ust_tracelog___##level(file, \
> -                       line, func, msg, len, \
> -                       LTTNG_UST_CALLER_IP()); \
> -               free(msg); \
> -       end: \
> -               return; \
> -       } \
> -       \
>         void lttng_ust__vtracelog_##level(const char *file, \
>                         int line, const char *func, \
>                         const char *fmt, va_list ap) \
> @@ -53,7 +31,9 @@
>                         int line, const char *func, \
>                         const char *fmt, va_list ap) \
>         { \
> -               lttng_ust___vtracelog_##level(file, line, func, fmt, ap); \
> +               LTTNG_UST_TRACELOG_VALIST(fmt, ap, \
> +                       lttng_ust_tracepoint_cb_lttng_ust_tracelog___##level, \
> +                       file, line, func, msg, len, LTTNG_UST_CALLER_IP()); \
>         } \
>         \
>         void lttng_ust__tracelog_##level(const char *file, \
> @@ -68,11 +48,9 @@
>                         int line, const char *func, \
>                         const char *fmt, ...) \
>         { \
> -               va_list ap; \
> -               \
> -               va_start(ap, fmt); \
> -               lttng_ust___vtracelog_##level(file, line, func, fmt, ap); \
> -               va_end(ap); \
> +               LTTNG_UST_TRACELOG_VARARG(fmt, \
> +                       lttng_ust_tracepoint_cb_lttng_ust_tracelog___##level, \
> +                       file, line, func, msg, len, LTTNG_UST_CALLER_IP()); \
>         }
>
>  LTTNG_UST_TRACELOG_CB(LTTNG_UST_TRACEPOINT_LOGLEVEL_EMERG)
> --
> 2.35.1
>

Any chance of not having to link liblttng-ust for the tracelog/tracef functions,
like all other tracepoints (only hard dependency on liblttng-ust-tracepoint)?
My involvement is limited to "complain-only" for that one, but think of
how technically great that would be!

Norbert
Mathieu Desnoyers Aug. 1, 2022, 3:12 p.m. UTC | #2
----- On Aug 1, 2022, at 10:35 AM, Norbert Lange via lttng-dev lttng-dev at lists.lttng.org wrote:

> From: Norbert Lange <nolange79 at gmail.com>
> 
> Support two common cases, one being that the resulting message is
> small enough to fit into a on-stack buffer.
> The seconds being the common 'printf("%s", "Message")' scheme.
> 
> Unfortunately, iterating a va_list is destructive,
> so it has to be copied before calling vprintf.
> 
> The implementation was moved to a separate file,
> used by both tracef.c and tracelog.c.
> 
> Signed-off-by: Norbert Lange <nolange79 at gmail.com>

Merged, thanks!

Mathieu

> ---
> v2 -> v3:
> 
> *   Wrap macro in do/while
> *   drop loop
> *   other changes in response to feedback
> 
> v2:
> -   move define into src/common/tracer.h
>    see https://lists.lttng.org/pipermail/lttng-dev/2021-May/029977.html
> -   moved macro magic into common tracelog-internal.h header
> -   rebased onto master
> ---
> src/common/tracer.h                   |  2 +
> src/lib/lttng-ust/tracef.c            | 32 +++------
> src/lib/lttng-ust/tracelog-internal.h | 95 +++++++++++++++++++++++++++
> src/lib/lttng-ust/tracelog.c          | 40 +++--------
> 4 files changed, 114 insertions(+), 55 deletions(-)
> create mode 100644 src/lib/lttng-ust/tracelog-internal.h
> 
> diff --git a/src/common/tracer.h b/src/common/tracer.h
> index 2affd6ab..8e18c9b5 100644
> --- a/src/common/tracer.h
> +++ b/src/common/tracer.h
> @@ -26,6 +26,8 @@
> #define LTTNG_RFLAG_EXTENDED		RING_BUFFER_RFLAG_END
> #define LTTNG_RFLAG_END			(LTTNG_RFLAG_EXTENDED << 1)
> 
> +#define LTTNG_TRACE_PRINTF_BUFSIZE	512
> +
> /*
>  * LTTng client type enumeration. Used by the consumer to map the
>  * callbacks from its own address space.
> diff --git a/src/lib/lttng-ust/tracef.c b/src/lib/lttng-ust/tracef.c
> index c05c7811..92911e1d 100644
> --- a/src/lib/lttng-ust/tracef.c
> +++ b/src/lib/lttng-ust/tracef.c
> @@ -7,6 +7,7 @@
> #define _LGPL_SOURCE
> #include <stdio.h>
> #include "common/macros.h"
> +#include "common/tracer.h"
> 
> /* The tracepoint definition is public, but the provider definition is hidden.
> */
> #define LTTNG_UST_TRACEPOINT_PROVIDER_HIDDEN_DEFINITION
> @@ -15,39 +16,22 @@
> #define LTTNG_UST_TRACEPOINT_DEFINE
> #include "lttng-ust-tracef-provider.h"
> 
> -static inline
> -void lttng_ust___vtracef(const char *fmt, va_list ap)
> -	__attribute__((always_inline, format(printf, 1, 0)));
> -static inline
> -void lttng_ust___vtracef(const char *fmt, va_list ap)
> -{
> -	char *msg;
> -	const int len = vasprintf(&msg, fmt, ap);
> -
> -	/* len does not include the final \0 */
> -	if (len < 0)
> -		goto end;
> -	lttng_ust_tracepoint_cb_lttng_ust_tracef___event(msg, len,
> -		LTTNG_UST_CALLER_IP());
> -	free(msg);
> -end:
> -	return;
> -}
> +#include "tracelog-internal.h"
> 
> void lttng_ust__vtracef(const char *fmt, va_list ap)
> 	__attribute__((format(printf, 1, 0)));
> void lttng_ust__vtracef(const char *fmt, va_list ap)
> {
> -	lttng_ust___vtracef(fmt, ap);
> +	LTTNG_UST_TRACELOG_VALIST(fmt, ap,
> +		lttng_ust_tracepoint_cb_lttng_ust_tracef___event,
> +		msg, len, LTTNG_UST_CALLER_IP());
> }
> 
> void lttng_ust__tracef(const char *fmt, ...)
> 	__attribute__((format(printf, 1, 2)));
> void lttng_ust__tracef(const char *fmt, ...)
> {
> -	va_list ap;
> -
> -	va_start(ap, fmt);
> -	lttng_ust___vtracef(fmt, ap);
> -	va_end(ap);
> +	LTTNG_UST_TRACELOG_VARARG(fmt,
> +		lttng_ust_tracepoint_cb_lttng_ust_tracef___event,
> +		msg, len, LTTNG_UST_CALLER_IP());
> }
> diff --git a/src/lib/lttng-ust/tracelog-internal.h
> b/src/lib/lttng-ust/tracelog-internal.h
> new file mode 100644
> index 00000000..34537023
> --- /dev/null
> +++ b/src/lib/lttng-ust/tracelog-internal.h
> @@ -0,0 +1,95 @@
> +/*
> + * SPDX-License-Identifier: MIT
> + *
> + * Copyright (C) 2013-2014 Mathieu Desnoyers <mathieu.desnoyers at efficios.com>
> + * Copyright (C) 2021 Norbert Lange <nolange79 at gmail.com>
> + *
> + * Shared helper macro for tracelog and tracef.
> + */
> +
> +#define LTTNG_UST_TRACELOG_VARARG(fmt, callback, ...) \
> +	do { \
> +		va_list ap; \
> +		char local_buf[LTTNG_TRACE_PRINTF_BUFSIZE]; \
> +		size_t len = 0; \
> +		char *msg = local_buf; \
> +		char *alloc_buff = NULL; \
> +\
> +		if (caa_unlikely(fmt[0] == '%' && fmt[1] == 's' && fmt[2] == '\0')) { \
> +			va_start(ap, fmt); \
> +			msg = va_arg(ap, char *); \
> +			va_end(ap); \
> +			len = strlen(msg); \
> +		} else { \
> +			int ret; \
> +			size_t buflen = sizeof(local_buf); \
> +\
> +			/* On-stack buffer attempt */ \
> +			va_start(ap, fmt); \
> +			ret = vsnprintf(msg, buflen, fmt, ap); \
> +			va_end(ap); \
> +			if (caa_unlikely(ret < 0)) \
> +				break; \
> +			len = (size_t)ret; \
> +\
> +			if (caa_unlikely(len >= sizeof(local_buf))) { \
> +				buflen = len + 1; \
> +				alloc_buff = (char *)malloc(buflen); \
> +				if (!alloc_buff) \
> +					goto end; \
> +				msg = alloc_buff; \
> +				va_start(ap, fmt); \
> +				ret = vsnprintf(msg, buflen, fmt, ap); \
> +				va_end(ap); \
> +				lttng_ust_runtime_bug_on(ret < 0 || (size_t)ret != buflen - 1); \
> +				len = (size_t)ret; \
> +			} \
> +		} \
> +\
> +		callback(__VA_ARGS__); \
> +end: \
> +		/* Don't call a potentially instrumented forbidden free needlessly. */ \
> +		if (caa_unlikely(alloc_buff)) \
> +			free(alloc_buff); \
> +	} while(0)
> +
> +#define LTTNG_UST_TRACELOG_VALIST(fmt, ap, callback, ...) \
> +	do { \
> +		char local_buf[LTTNG_TRACE_PRINTF_BUFSIZE]; \
> +		size_t len = 0; \
> +		char *msg = local_buf; \
> +		char *alloc_buff = NULL; \
> +\
> +		if (caa_unlikely(fmt[0] == '%' && fmt[1] == 's' && fmt[2] == '\0')) { \
> +			msg = va_arg(ap, char *); \
> +			len = strlen(msg); \
> +		} else { \
> +			va_list ap2; \
> +			int ret; \
> +			size_t buflen = sizeof(local_buf); \
> +\
> +			va_copy(ap2, ap); \
> +			ret = vsnprintf(msg, buflen, fmt, ap2); \
> +			va_end(ap2); \
> +			if (caa_unlikely(ret < 0)) \
> +				break; \
> +			len = (size_t)ret; \
> +\
> +			if (caa_unlikely(len >= sizeof(local_buf))) { \
> +				buflen = len + 1; \
> +				alloc_buff = (char *)malloc(buflen); \
> +				if (!alloc_buff) \
> +					goto end; \
> +				msg = alloc_buff; \
> +				ret = vsnprintf(msg, buflen, fmt, ap); \
> +				lttng_ust_runtime_bug_on(ret < 0 || (size_t)ret != buflen - 1); \
> +				len = (size_t)ret; \
> +			} \
> +		} \
> +\
> +		callback(__VA_ARGS__); \
> +end: \
> +		/* Don't call a potentially instrumented forbidden free needlessly. */ \
> +		if (caa_unlikely(alloc_buff)) \
> +			free(alloc_buff); \
> +	} while (0)
> diff --git a/src/lib/lttng-ust/tracelog.c b/src/lib/lttng-ust/tracelog.c
> index 8147d7a3..bd38032c 100644
> --- a/src/lib/lttng-ust/tracelog.c
> +++ b/src/lib/lttng-ust/tracelog.c
> @@ -7,6 +7,7 @@
> #define _LGPL_SOURCE
> #include <stdio.h>
> #include "common/macros.h"
> +#include "common/tracer.h"
> 
> /* The tracepoint definition is public, but the provider definition is hidden.
> */
> #define LTTNG_UST_TRACEPOINT_PROVIDER_HIDDEN_DEFINITION
> @@ -15,32 +16,9 @@
> #define LTTNG_UST_TRACEPOINT_DEFINE
> #include "lttng-ust-tracelog-provider.h"
> 
> +#include "tracelog-internal.h"
> +
> #define LTTNG_UST_TRACELOG_CB(level) \
> -	static inline \
> -	void lttng_ust___vtracelog_##level(const char *file, \
> -			int line, const char *func, \
> -			const char *fmt, va_list ap) \
> -		__attribute__((always_inline, format(printf, 4, 0))); \
> -	\
> -	static inline \
> -	void lttng_ust___vtracelog_##level(const char *file, \
> -			int line, const char *func, \
> -			const char *fmt, va_list ap) \
> -	{ \
> -		char *msg; \
> -		const int len = vasprintf(&msg, fmt, ap); \
> -		\
> -		/* len does not include the final \0 */ \
> -		if (len < 0) \
> -			goto end; \
> -		lttng_ust_tracepoint_cb_lttng_ust_tracelog___##level(file, \
> -			line, func, msg, len, \
> -			LTTNG_UST_CALLER_IP()); \
> -		free(msg); \
> -	end: \
> -		return; \
> -	} \
> -	\
> 	void lttng_ust__vtracelog_##level(const char *file, \
> 			int line, const char *func, \
> 			const char *fmt, va_list ap) \
> @@ -53,7 +31,9 @@
> 			int line, const char *func, \
> 			const char *fmt, va_list ap) \
> 	{ \
> -		lttng_ust___vtracelog_##level(file, line, func, fmt, ap); \
> +		LTTNG_UST_TRACELOG_VALIST(fmt, ap, \
> +			lttng_ust_tracepoint_cb_lttng_ust_tracelog___##level, \
> +			file, line, func, msg, len, LTTNG_UST_CALLER_IP()); \
> 	} \
> 	\
> 	void lttng_ust__tracelog_##level(const char *file, \
> @@ -68,11 +48,9 @@
> 			int line, const char *func, \
> 			const char *fmt, ...) \
> 	{ \
> -		va_list ap; \
> -		\
> -		va_start(ap, fmt); \
> -		lttng_ust___vtracelog_##level(file, line, func, fmt, ap); \
> -		va_end(ap); \
> +		LTTNG_UST_TRACELOG_VARARG(fmt, \
> +			lttng_ust_tracepoint_cb_lttng_ust_tracelog___##level, \
> +			file, line, func, msg, len, LTTNG_UST_CALLER_IP()); \
> 	}
> 
> LTTNG_UST_TRACELOG_CB(LTTNG_UST_TRACEPOINT_LOGLEVEL_EMERG)
> --
> 2.35.1
> 
> _______________________________________________
> lttng-dev mailing list
> lttng-dev at lists.lttng.org
> https://lists.lttng.org/cgi-bin/mailman/listinfo/lttng-dev
Mathieu Desnoyers Aug. 1, 2022, 3:24 p.m. UTC | #3
----- On Aug 1, 2022, at 10:44 AM, Norbert Lange via lttng-dev lttng-dev at lists.lttng.org wrote:
[...]
> 
> Any chance of not having to link liblttng-ust for the tracelog/tracef functions,
> like all other tracepoints (only hard dependency on liblttng-ust-tracepoint)?
> My involvement is limited to "complain-only" for that one, but think of
> how technically great that would be!

Here is how we could do this:

1) move the implementation of src/lib/lttng-ust/tracelog.c to its own .so, e.g.
   liblttng-ust-tracelog.so,
2) modify the tracelog/tracef public headers so they dlopen() this new .so from
   a library constructor, similarly to what tracepoint.h does.

We'd need to make sure the symbols from tracelog.c are still emitted in
liblttng-ust.so, _and_ have another version (with a prefix/suffix) emitted from
liblttng-ust-tracelog.so, so we don't break ABI.

Thoughts ?

Thanks,

Mathieu
diff mbox series

Patch

diff --git a/src/common/tracer.h b/src/common/tracer.h
index 2affd6ab..8e18c9b5 100644
--- a/src/common/tracer.h
+++ b/src/common/tracer.h
@@ -26,6 +26,8 @@ 
 #define LTTNG_RFLAG_EXTENDED		RING_BUFFER_RFLAG_END
 #define LTTNG_RFLAG_END			(LTTNG_RFLAG_EXTENDED << 1)
 
+#define LTTNG_TRACE_PRINTF_BUFSIZE	512
+
 /*
  * LTTng client type enumeration. Used by the consumer to map the
  * callbacks from its own address space.
diff --git a/src/lib/lttng-ust/tracef.c b/src/lib/lttng-ust/tracef.c
index c05c7811..92911e1d 100644
--- a/src/lib/lttng-ust/tracef.c
+++ b/src/lib/lttng-ust/tracef.c
@@ -7,6 +7,7 @@ 
 #define _LGPL_SOURCE
 #include <stdio.h>
 #include "common/macros.h"
+#include "common/tracer.h"
 
 /* The tracepoint definition is public, but the provider definition is hidden. */
 #define LTTNG_UST_TRACEPOINT_PROVIDER_HIDDEN_DEFINITION
@@ -15,39 +16,22 @@ 
 #define LTTNG_UST_TRACEPOINT_DEFINE
 #include "lttng-ust-tracef-provider.h"
 
-static inline
-void lttng_ust___vtracef(const char *fmt, va_list ap)
-	__attribute__((always_inline, format(printf, 1, 0)));
-static inline
-void lttng_ust___vtracef(const char *fmt, va_list ap)
-{
-	char *msg;
-	const int len = vasprintf(&msg, fmt, ap);
-
-	/* len does not include the final \0 */
-	if (len < 0)
-		goto end;
-	lttng_ust_tracepoint_cb_lttng_ust_tracef___event(msg, len,
-		LTTNG_UST_CALLER_IP());
-	free(msg);
-end:
-	return;
-}
+#include "tracelog-internal.h"
 
 void lttng_ust__vtracef(const char *fmt, va_list ap)
 	__attribute__((format(printf, 1, 0)));
 void lttng_ust__vtracef(const char *fmt, va_list ap)
 {
-	lttng_ust___vtracef(fmt, ap);
+	LTTNG_UST_TRACELOG_VALIST(fmt, ap,
+		lttng_ust_tracepoint_cb_lttng_ust_tracef___event,
+		msg, len, LTTNG_UST_CALLER_IP());
 }
 
 void lttng_ust__tracef(const char *fmt, ...)
 	__attribute__((format(printf, 1, 2)));
 void lttng_ust__tracef(const char *fmt, ...)
 {
-	va_list ap;
-
-	va_start(ap, fmt);
-	lttng_ust___vtracef(fmt, ap);
-	va_end(ap);
+	LTTNG_UST_TRACELOG_VARARG(fmt,
+		lttng_ust_tracepoint_cb_lttng_ust_tracef___event,
+		msg, len, LTTNG_UST_CALLER_IP());
 }
diff --git a/src/lib/lttng-ust/tracelog-internal.h b/src/lib/lttng-ust/tracelog-internal.h
new file mode 100644
index 00000000..34537023
--- /dev/null
+++ b/src/lib/lttng-ust/tracelog-internal.h
@@ -0,0 +1,95 @@ 
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright (C) 2013-2014 Mathieu Desnoyers <mathieu.desnoyers at efficios.com>
+ * Copyright (C) 2021 Norbert Lange <nolange79 at gmail.com>
+ *
+ * Shared helper macro for tracelog and tracef.
+ */
+
+#define LTTNG_UST_TRACELOG_VARARG(fmt, callback, ...) \
+	do { \
+		va_list ap; \
+		char local_buf[LTTNG_TRACE_PRINTF_BUFSIZE]; \
+		size_t len = 0; \
+		char *msg = local_buf; \
+		char *alloc_buff = NULL; \
+\
+		if (caa_unlikely(fmt[0] == '%' && fmt[1] == 's' && fmt[2] == '\0')) { \
+			va_start(ap, fmt); \
+			msg = va_arg(ap, char *); \
+			va_end(ap); \
+			len = strlen(msg); \
+		} else { \
+			int ret; \
+			size_t buflen = sizeof(local_buf); \
+\
+			/* On-stack buffer attempt */ \
+			va_start(ap, fmt); \
+			ret = vsnprintf(msg, buflen, fmt, ap); \
+			va_end(ap); \
+			if (caa_unlikely(ret < 0)) \
+				break; \
+			len = (size_t)ret; \
+\
+			if (caa_unlikely(len >= sizeof(local_buf))) { \
+				buflen = len + 1; \
+				alloc_buff = (char *)malloc(buflen); \
+				if (!alloc_buff) \
+					goto end; \
+				msg = alloc_buff; \
+				va_start(ap, fmt); \
+				ret = vsnprintf(msg, buflen, fmt, ap); \
+				va_end(ap); \
+				lttng_ust_runtime_bug_on(ret < 0 || (size_t)ret != buflen - 1); \
+				len = (size_t)ret; \
+			} \
+		} \
+\
+		callback(__VA_ARGS__); \
+end: \
+		/* Don't call a potentially instrumented forbidden free needlessly. */ \
+		if (caa_unlikely(alloc_buff)) \
+			free(alloc_buff); \
+	} while(0)
+
+#define LTTNG_UST_TRACELOG_VALIST(fmt, ap, callback, ...) \
+	do { \
+		char local_buf[LTTNG_TRACE_PRINTF_BUFSIZE]; \
+		size_t len = 0; \
+		char *msg = local_buf; \
+		char *alloc_buff = NULL; \
+\
+		if (caa_unlikely(fmt[0] == '%' && fmt[1] == 's' && fmt[2] == '\0')) { \
+			msg = va_arg(ap, char *); \
+			len = strlen(msg); \
+		} else { \
+			va_list ap2; \
+			int ret; \
+			size_t buflen = sizeof(local_buf); \
+\
+			va_copy(ap2, ap); \
+			ret = vsnprintf(msg, buflen, fmt, ap2); \
+			va_end(ap2); \
+			if (caa_unlikely(ret < 0)) \
+				break; \
+			len = (size_t)ret; \
+\
+			if (caa_unlikely(len >= sizeof(local_buf))) { \
+				buflen = len + 1; \
+				alloc_buff = (char *)malloc(buflen); \
+				if (!alloc_buff) \
+					goto end; \
+				msg = alloc_buff; \
+				ret = vsnprintf(msg, buflen, fmt, ap); \
+				lttng_ust_runtime_bug_on(ret < 0 || (size_t)ret != buflen - 1); \
+				len = (size_t)ret; \
+			} \
+		} \
+\
+		callback(__VA_ARGS__); \
+end: \
+		/* Don't call a potentially instrumented forbidden free needlessly. */ \
+		if (caa_unlikely(alloc_buff)) \
+			free(alloc_buff); \
+	} while (0)
diff --git a/src/lib/lttng-ust/tracelog.c b/src/lib/lttng-ust/tracelog.c
index 8147d7a3..bd38032c 100644
--- a/src/lib/lttng-ust/tracelog.c
+++ b/src/lib/lttng-ust/tracelog.c
@@ -7,6 +7,7 @@ 
 #define _LGPL_SOURCE
 #include <stdio.h>
 #include "common/macros.h"
+#include "common/tracer.h"
 
 /* The tracepoint definition is public, but the provider definition is hidden. */
 #define LTTNG_UST_TRACEPOINT_PROVIDER_HIDDEN_DEFINITION
@@ -15,32 +16,9 @@ 
 #define LTTNG_UST_TRACEPOINT_DEFINE
 #include "lttng-ust-tracelog-provider.h"
 
+#include "tracelog-internal.h"
+
 #define LTTNG_UST_TRACELOG_CB(level) \
-	static inline \
-	void lttng_ust___vtracelog_##level(const char *file, \
-			int line, const char *func, \
-			const char *fmt, va_list ap) \
-		__attribute__((always_inline, format(printf, 4, 0))); \
-	\
-	static inline \
-	void lttng_ust___vtracelog_##level(const char *file, \
-			int line, const char *func, \
-			const char *fmt, va_list ap) \
-	{ \
-		char *msg; \
-		const int len = vasprintf(&msg, fmt, ap); \
-		\
-		/* len does not include the final \0 */ \
-		if (len < 0) \
-			goto end; \
-		lttng_ust_tracepoint_cb_lttng_ust_tracelog___##level(file, \
-			line, func, msg, len, \
-			LTTNG_UST_CALLER_IP()); \
-		free(msg); \
-	end: \
-		return; \
-	} \
-	\
 	void lttng_ust__vtracelog_##level(const char *file, \
 			int line, const char *func, \
 			const char *fmt, va_list ap) \
@@ -53,7 +31,9 @@ 
 			int line, const char *func, \
 			const char *fmt, va_list ap) \
 	{ \
-		lttng_ust___vtracelog_##level(file, line, func, fmt, ap); \
+		LTTNG_UST_TRACELOG_VALIST(fmt, ap, \
+			lttng_ust_tracepoint_cb_lttng_ust_tracelog___##level, \
+			file, line, func, msg, len, LTTNG_UST_CALLER_IP()); \
 	} \
 	\
 	void lttng_ust__tracelog_##level(const char *file, \
@@ -68,11 +48,9 @@ 
 			int line, const char *func, \
 			const char *fmt, ...) \
 	{ \
-		va_list ap; \
-		\
-		va_start(ap, fmt); \
-		lttng_ust___vtracelog_##level(file, line, func, fmt, ap); \
-		va_end(ap); \
+		LTTNG_UST_TRACELOG_VARARG(fmt, \
+			lttng_ust_tracepoint_cb_lttng_ust_tracelog___##level, \
+			file, line, func, msg, len, LTTNG_UST_CALLER_IP()); \
 	}
 
 LTTNG_UST_TRACELOG_CB(LTTNG_UST_TRACEPOINT_LOGLEVEL_EMERG)