[lttng-dev] instrumenting shared library

Thibault, Daniel Daniel.Thibault at drdc-rddc.gc.ca
Fri Apr 4 10:43:02 EDT 2014


De : Anand Neeli [mailto:anand.neeli at gmail.com]
Envoyé : 4 avril 2014 01:45

> Thanks for your reply.
> Sorry for my ignorance but how are you generating libtp.so in your example?

Non-boilerplate parts are in red.

tp.c:
#####
/*
* Defining macro creates the code objects of the traceprobes.
* Must be done only once per file (this #define controls tracepoint.h and
* tracepoint-event.h).
*/
#define TRACEPOINT_CREATE_PROBES
/*
* The header containing our TRACEPOINT_EVENTs.
*/
#include "tp.h"
#####
tp.h:
#####
#undef TRACEPOINT_PROVIDER
#define TRACEPOINT_PROVIDER sample_component
/*
* include file (this files's name)
*/
#undef TRACEPOINT_INCLUDE
#define TRACEPOINT_INCLUDE "./tp.h"
/*
* Add this precompiler conditional to ensure the tracepoint event generation
* can include this file more than once.
*/
#if !defined(_SAMPLE_COMPONENT_PROVIDER_H) || defined(TRACEPOINT_HEADER_MULTI_READ)
#define _SAMPLE_COMPONENT_PROVIDER_H
/*
* Add this to allow programs to call "tracepoint(...)".
* This #include must not be within an extern "C" block; if it is,
* C++ instrumentation will fail when SystemTap is also installed.
* (This may have been fixed since I wrote this)
*/
#include <lttng/tracepoint.h>
/*
* Add this macro and its matching element to make sure the program
* works in C++.
*/
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */

/*
* The following tracepoint event writes a message (C string) into the
* field "message" of the trace event "event" in the provider
* "sample_component" in other words:
*
*    sample_component:event:message = text.
*/
TRACEPOINT_EVENT(
            /*
            * provider name, not a variable but a string starting with a letter
            * and containing either letters, numbers or underscores.
            * Needs to be the same as TRACEPOINT_PROVIDER
            */
            sample_component,
            /*
            * tracepoint name, same format as sample provider. Does not need to be
            * declared before. in this case the name is "event"
            */
            event,
            /*
            * TP_ARGS macro contains the arguments passed for the tracepoint.
            * It is in the following format:
            *                     TP_ARGS( type1, name1, type2, name2, ... type10, name10)
            * where there can be from zero to ten elements.
            * "typeN" is the datatype, such as int, struct or double **.
            * "name" is the variable name (in "int myInt" the name would be "myInt")
            *                     TP_ARGS() is valid (no arguments)
            *                     TP_ARGS( void ) is valid too
            */
            TP_ARGS(const char *, text),
            /*
            * TP_FIELDS describes how to write the fields of the trace event.
            * You can use the args here
            */
            TP_FIELDS(
            /*
            * The ctf_string macro takes a C string and writes it into a field
            * named "message"
            */
                        ctf_string(message, text)
            )
)
/*
* Trace loglevel, shows the level of the trace event. It can be TRACE_EMERG,
* TRACE_ALERT, TRACE_CRIT, TRACE_ERR, TRACE_WARNING, TRACE_INFO, etc.
* If this is not set, TRACE_DEFAULT is assumed.
* The first two arguments identify the tracepoint.
* See details in <lttng/tracepoint.h> line 347
*/
TRACEPOINT_LOGLEVEL(
       /*
        * The provider name, must be the same as the provider name in the
        * TRACEPOINT_EVENT and as TRACEPOINT_PROVIDER above.
        */
            sample_component,
       /*
        * The tracepoint name, must be the same as the tracepoint name in the
        * TRACEPOINT_EVENT
        */
            event,
       /*
        * The tracepoint loglevel. WARNING: Some levels are abbreviated and
        * others are not, please see <lttng/tracepoint.h>
        */
            TRACE_WARNING
)
(more tracepoints...)

/*
* Add this macro and its matching element to make sure the program
* works in C++.
*/
#ifdef __cplusplus
}
#endif /* __cplusplus */
/*
* Add this after defining the tracepoint events to expand the macros.
*/
#include <lttng/tracepoint-event.h>
#endif /* _SAMPLE_COMPONENT_PROVIDER_H */
#####
The relevant Makefile fragments:
#####
CC = gcc
LIBUST = -llttng-ust
LOCAL_CPPFLAGS += -I. $(AM_CPPFLAGS)
LDFLAGS += -L/usr/local/lib $(AM_LDFLAGS)
SOFLAGS = -fPIC
SOVERSION_MAJOR = 1
SOVERSION_MINOR = 0

libtp.so: libtp.o
            @echo "~~~~~~Packaging $@:"
            $(CC) -shared -Wl,-soname,$@.$(SOVERSION_MAJOR) -Wl,-no-as-needed \
               -o $@.$(SOVERSION_MAJOR).$(SOVERSION_MINOR) $(LDFLAGS) $(LIBUST) $<
            ln -sf $@.$(SOVERSION_MAJOR).$(SOVERSION_MINOR) $@.$(SOVERSION_MAJOR)
            ln -sf $@.$(SOVERSION_MAJOR) $@

libtp.o: tp.c tp.h
            @echo "~~~~~~Compiling $@:"
            $(CC) $(CPPFLAGS) $(LOCAL_CPPFLAGS) $(SOFLAGS) -c -o $@ $<

> 1) Also will this solution scale if my binary depends on lots of libraries which are instrumented?

Sure.  I'd expect linear scaling.  Each library, upon being loaded, looks for its tracepoint provider and turns its tracepoints off if they're not found.

> 2) i'm fine with linking statically, can we avoid LD_PRELOAD?

Besides the LD_PRELOAD technique, you could also use LD_LIBRARY_PATH, but that works only if the application statically includes the tracepoint provider (I suppose it would likewise work for an instrumented shared object only if it statically includes the tracepoint provider).

You could also add "-Wl,-rpath,'$$ORIGIN'" to the linker call to add an RPATH tag to the application (or shared object).  This makes it look for its dependencies in its starting directory ($ORIGIN).  RPATH is, unlike RUNPATH, applicable to libraries as well: a library with an RPATH tag will look there for its dependencies; failing its own RPATH, it will use its executable's RPATH.

Finally, you can dynamically include the tracepoint provider (libtp) as a dependency.  The application (sample_static_aware) is then statically aware of libtp and won't run without it.  The application (sample_dynamic) can alternately dynamically include the tracepoint provider without static awareness.  Here's how it's done:

LIBDL = -ldl # On Linux
#LIBDL = -lc # On BSD
TP_DEFINE = -D TRACEPOINT_DEFINE
TP_DEFINE_DYNAMIC = $(TP_DEFINE) -D TRACEPOINT_PROBE_DYNAMIC_LINKAGE

#(the inclusion of a libtp NEEDED tag requires "-no-as-needed -ltp").
#Note that "-L. -ltp", when used, should appear before $(LDFLAGS) $(LIBDL).
static_aware: static.o libtp.so
            @echo "~~~~~~Linking sample_$@:"
#          $(CC) -Wl,-no-as-needed -o sample_$@ -L. -ltp $< $(LDFLAGS) $(LIBDL) \
#             -Wl,-rpath,'$$ORIGIN',--enable-new-dtags
            $(CC) -Wl,-no-as-needed -o sample_$@ -L. -ltp $< $(LDFLAGS) $(LIBDL)
            @echo "   Use 'LD_PRELOAD=./libtp.so ./sample_$@' to run sample_$@"

static.o: sample.c tp.h
            @echo "~~~~~~Compiling $@:"
            $(CC) $(CPPFLAGS) $(LOCAL_CPPFLAGS) $(TP_DEFINE) -c -o $@ $<

#If not preloaded, the sample application runs but won't be traceable.
#Putting libtp in the path won't work: the sample does not look for it.
dynamic: dynamic.o libtp.so
            @echo "~~~~~~Linking sample_$@:"
            $(CC) -o sample_$@ $< $(LDFLAGS) $(LIBDL)
            @echo "   Use '[LD_PRELOAD=./libtp.so] ./sample_$@' to run sample_$@"

#Compare with the 'static.o' target.
dynamic.o: sample.c tp.h
            @echo "~~~~~~Compiling $@:"
            $(CC) $(CPPFLAGS) $(LOCAL_CPPFLAGS) $(TP_DEFINE_DYNAMIC) \
               -c -o $@ $<

   The case where the app isn't traced but the library it uses is traced should be a variation on this.  I'll try it out sometime in the near future, but you should be able to figure it out from the above.  Here's a first guess (it may need $(LIBDL) alongside $(LIBUST)):

libtp.so: libtp.o
            @echo "~~~~~~Packaging $@:"
            $(CC) -shared -Wl,-soname,$@.$(SOVERSION_MAJOR) -Wl,-no-as-needed -L. -ltp \
               -o $@.$(SOVERSION_MAJOR).$(SOVERSION_MINOR) $(LDFLAGS) $(LIBUST) $<
            ln -sf $@.$(SOVERSION_MAJOR).$(SOVERSION_MINOR) $@.$(SOVERSION_MAJOR)
            ln -sf $@.$(SOVERSION_MAJOR) $@


> 3) Is there any page which explains on why and how to use
> TRACEPOINT_PROVIDER, TRACEPOINT_CREATE_PROBES, TRACEPOINT_INCLUDE, TRACEPOINT_DEFINE, TRACEPOINT_PROBE_DYNAMIC_LINKAGE

They're reasonably explained in the demonstration apps.  I discuss them a little more in depth in the forthcoming LTTng Comprehensive User's Guide (I could send you a copy of the draft, as we've just begun its first review round).

> my tp.h is, are there any errors in this?
> #############

#undef TRACEPOINT_PROVIDER

> #define TRACEPOINT_PROVIDER sample_component
>
> #undef TRACEPOINT_INCLUDE
> #define TRACEPOINT_INCLUDE "./tp.h"
>
> #if !defined(_TP_H) || defined(TRACEPOINT_HEADER_MULTI_READ)
> #define _TP_H

(Ideally, use _SAMPLE_COMPONENT_PROVIDER_H instead of _TP_H)

> #include <lttng/tracepoint.h>

#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */

> TRACEPOINT_EVENT(
>        sample_component,
>        event,
>        TP_ARGS(char *, text),

(the compiler may be happier with const char * instead)

>        TP_FIELDS(
>                ctf_string(message, text)
>        )
>)
> TRACEPOINT_LOGLEVEL(
>        sample_component,
>        event,
>        TRACE_WARNING)

#ifdef __cplusplus
}
#endif /* __cplusplus */

> #endif /* _TP_H */
> #include <lttng/tracepoint-event.h>

(move #include <lttng/tracepoint-event.h> inside of the #if !defined(_TP_H) || defined(TRACEPOINT_HEADER_MULTI_READ))

> #############
>
> Thanks,
> Anand Neeli

   One key change I made to my sample applications is to remove the "#define TRACEPOINT_DEFINE" that precedes the "#include "tp.h"" line.  I prefer to control that #define using a compiler -D option.  It makes the makefile clearer and resolves the problem of making sure there is just one module that turns that #define on.

   Hope this helps.

Daniel U. Thibault
Protection des systèmes et contremesures (PSC) | Systems Protection & Countermeasures (SPC)
Cyber sécurité pour les missions essentielles (CME) | Mission Critical Cyber Security (MCCS)
R & D pour la défense Canada - Valcartier (RDDC Valcartier) | Defence R&D Canada - Valcartier (DRDC Valcartier)
2459 route de la Bravoure
Québec QC  G3J 1X5
CANADA
Vox : (418) 844-4000 x4245
Fax : (418) 844-4538
NAC : 918V QSDJ <http://www.travelgis.com/map.asp?addr=918V%20QSDJ<--ESFSECEV-TY3011-------------------------------->>
Gouvernement du Canada | Government of Canada
<http://www.valcartier.drdc-rddc.gc.ca/<--ESFSECEV-TY3011--------------------->>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.lttng.org/pipermail/lttng-dev/attachments/20140404/58db9e78/attachment-0001.html>


More information about the lttng-dev mailing list