diff --git a/configure.ac b/configure.ac index 037a55520..90a32c03c 100644 --- a/configure.ac +++ b/configure.ac @@ -102,7 +102,10 @@ AC_SUBST([ENABLE_CLANG_PARSER]) PKG_CHECK_MODULES([LIBFFI], [libffi >= 3.2]) PKG_CHECK_MODULES([BABELTRACE2], [babeltrace2 >= 2.0]) -PKG_CHECK_MODULES([LTTNG_UST], [lttng-ust >= 2.10]) +# Use of __attribute__((constructor)) requires `lttng-ust >= 2.12.8` to work properly. +# Specifically, the following fix: +# https://github.com/lttng/lttng-ust/commit/a8fafb675a9f580f6a889223e26664ea11cb0c99. +PKG_CHECK_MODULES([LTTNG_UST], [lttng-ust >= 2.12.8]) PKG_CHECK_MODULES([PROTOBUF], [protobuf >= 3.0]) AX_RUBY_EXTENSION([cast-to-yaml], [yes]) @@ -137,6 +140,8 @@ AC_FUNC_MMAP AC_FUNC_REALLOC AC_CHECK_FUNCS([clock_gettime ftruncate memmove memset strdup strstr strtoull strlen strchr]) +AX_GCC_FUNC_ATTRIBUTE(constructor) + # Required for configuring thapi.pc.in PKG_PROG_PKG_CONFIG diff --git a/integration_tests/toggle.bats b/integration_tests/toggle.bats new file mode 100644 index 000000000..706998b41 --- /dev/null +++ b/integration_tests/toggle.bats @@ -0,0 +1,85 @@ +#!/usr/bin/env bats + +setup_file() { + export THAPI_INCFLAGS="-I$(pkg-config --variable=includedir thapi)" + export THAPI_LDFLAGS="-Wl,-rpath,$(pkg-config --variable=libdir thapi) $(pkg-config --libs thapi)" +} + +get_unique_jobid() { + echo ${BATS_TEST_NAME}.${RANDOM} +} + +@test "toggle_api" { + cc ${THAPI_INCFLAGS} ./integration_tests/toggle.c -o toggle ${THAPI_LDFLAGS} + + rm -rf toggle_traces 2>/dev/null + iprof --trace-output toggle_traces --no-analysis -- ./toggle + dir=$(ls -d -1 ./toggle_traces/*/) + + # Make sure auto_stop comes before stop. + babeltrace_thapi ./toggle_traces | awk 'BEGIN { seen_auto = 0 } + $0 ~ /lttng_ust_toggle:auto_stop/ { seen_auto = 1 } + $0 ~ /lttng_ust_toggle:stop/ { if (seen_auto == 1) { exit 0 } else { exit 1 } } + ' + + # Check expected trace counts. + start_count=$(babeltrace_thapi -c $dir | grep lttng_ust_toggle:start | wc -l) + [ "$start_count" -eq 1 ] + + stop_count=$(babeltrace_thapi -c $dir | grep lttng_ust_toggle:stop | wc -l) + [ "$stop_count" -eq 1 ] + + auto_stop_count=$(babeltrace_thapi -c $dir | grep lttng_ust_toggle:auto_stop | wc -l) + [ "$auto_stop_count" -eq 1 ] + + rm -rf toggle_traces 2>/dev/null + iprof --trace-output toggle_traces --no-analysis -- ./toggle + dir=$(ls -d -1 ./toggle_traces/*/) + + auto_stop_count=$(babeltrace_thapi -c $dir | grep lttng_ust_toggle:auto_stop | wc -l) + [ "$auto_stop_count" -eq 1 ] +} + +toggle_count_base() { + rm -rf toggle_traces 2>/dev/null + + THAPI_SYNC_DAEMON=fs THAPI_JOBID=$(get_unique_jobid) timeout 40s mpirun -n $1 \ + iprof --trace-output toggle_traces --no-analysis -- ./toggle_mpi $2 + + traces=$(babeltrace_thapi ./toggle_traces) + + echo $traces +} + +toggle_count_traces() { + traces=$(toggle_count_base $1 $2) + echo $traces | sed -e "s/ \[/\n[/g" | grep . | wc -l +} + +@test "toggle_plugin_mpi_np_1" { + mpicc ${THAPI_INCFLAGS} ./integration_tests/toggle_mpi.c -o toggle_mpi ${THAPI_LDFLAGS} + + count_0=$(toggle_count_traces 1 0) + count_1=$(toggle_count_traces 1 1) + count_2=$(toggle_count_traces 1 2) + + [ "$count_2" -eq 0 ] + [ "$count_0" -gt "$count_1" ] +} + +toggle_count_vpids() { + traces=$(toggle_count_base $1 $2) + echo $traces | sed -e "s/ - /, /g" | sed -e "s/,/\n/g" | grep vpid | sort | uniq | wc -l +} + +@test "toggle_plugin_mpi_np_2" { + mpicc ${THAPI_INCFLAGS} ./integration_tests/toggle_mpi.c -o toggle_mpi ${THAPI_LDFLAGS} + + count_0=$(toggle_count_vpids 2 0) + count_1=$(toggle_count_vpids 2 1) + count_2=$(toggle_count_vpids 2 2) + + [ "$count_0" -eq 2 ] + [ "$count_1" -eq 1 ] + [ "$count_2" -eq 0 ] +} diff --git a/integration_tests/toggle.c b/integration_tests/toggle.c new file mode 100644 index 000000000..58a3f19f4 --- /dev/null +++ b/integration_tests/toggle.c @@ -0,0 +1,27 @@ +#define _GNU_SOURCE + +#include +#include +#include + +#include + +int main(int argc, char *argv[]) { + int dlopen_test = (argc > 1 ? atoi(argv[1]): 0); + if (!dlopen_test) goto regular_test; + + dlerror(); + void *thapi1 = dlmopen(LM_ID_NEWLM, "libThapi.so", RTLD_LAZY | RTLD_LOCAL); + if (!thapi1) { fprintf(stderr, "%s\n", dlerror()); return 1; } + else dlclose(thapi1); + + void *thapi2 = dlmopen(LM_ID_NEWLM, "libThapi.so", RTLD_LAZY | RTLD_LOCAL); + if (!thapi2) { fprintf(stderr, "%s\n", dlerror()); return 1; } + else dlclose(thapi2); + +regular_test: + thapi_start(); + thapi_stop(); + + return 0; +} diff --git a/integration_tests/toggle_mpi.c b/integration_tests/toggle_mpi.c new file mode 100644 index 000000000..d98fc77cf --- /dev/null +++ b/integration_tests/toggle_mpi.c @@ -0,0 +1,30 @@ +#include +#include + +#include + +int main(int argc, char *argv[]) { + int variant = (argc > 1) ? atoi(argv[1]) : 0; + + MPI_Init(&argc, &argv); + + int rank, size; + + switch (variant) { + case 0: + thapi_start(); + case 1: + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + if (rank == 0) thapi_start(); + MPI_Comm_size(MPI_COMM_WORLD, &size); + break; + default: + break; + } + + thapi_stop(); + + MPI_Finalize(); + + return 0; +} diff --git a/m4/ax_gcc_func_attribute.m4 b/m4/ax_gcc_func_attribute.m4 new file mode 100644 index 000000000..fa4e089d6 --- /dev/null +++ b/m4/ax_gcc_func_attribute.m4 @@ -0,0 +1,242 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_gcc_func_attribute.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_GCC_FUNC_ATTRIBUTE(ATTRIBUTE) +# +# DESCRIPTION +# +# This macro checks if the compiler supports one of GCC's function +# attributes; many other compilers also provide function attributes with +# the same syntax. Compiler warnings are used to detect supported +# attributes as unsupported ones are ignored by default so quieting +# warnings when using this macro will yield false positives. +# +# The ATTRIBUTE parameter holds the name of the attribute to be checked. +# +# If ATTRIBUTE is supported define HAVE_FUNC_ATTRIBUTE_. +# +# The macro caches its result in the ax_cv_have_func_attribute_ +# variable. +# +# The macro currently supports the following function attributes: +# +# alias +# aligned +# alloc_size +# always_inline +# artificial +# cold +# const +# constructor +# constructor_priority for constructor attribute with priority +# deprecated +# destructor +# dllexport +# dllimport +# error +# externally_visible +# fallthrough +# flatten +# format +# format_arg +# gnu_format +# gnu_inline +# hot +# ifunc +# leaf +# malloc +# noclone +# noinline +# nonnull +# noreturn +# nothrow +# optimize +# pure +# sentinel +# sentinel_position +# unused +# used +# visibility +# warning +# warn_unused_result +# weak +# weakref +# +# Unsupported function attributes will be tested with a prototype +# returning an int and not accepting any arguments and the result of the +# check might be wrong or meaningless so use with care. +# +# LICENSE +# +# Copyright (c) 2013 Gabriele Svelto +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 13 + +AC_DEFUN([AX_GCC_FUNC_ATTRIBUTE], [ + AS_VAR_PUSHDEF([ac_var], [ax_cv_have_func_attribute_$1]) + + AC_CACHE_CHECK([for __attribute__(($1))], [ac_var], [ + AC_LINK_IFELSE([AC_LANG_PROGRAM([ + m4_case([$1], + [alias], [ + int foo( void ) { return 0; } + int bar( void ) __attribute__(($1("foo"))); + ], + [aligned], [ + int foo( void ) __attribute__(($1(32))); + ], + [alloc_size], [ + void *foo(int a) __attribute__(($1(1))); + ], + [always_inline], [ + inline __attribute__(($1)) int foo( void ) { return 0; } + ], + [artificial], [ + inline __attribute__(($1)) int foo( void ) { return 0; } + ], + [cold], [ + int foo( void ) __attribute__(($1)); + ], + [const], [ + int foo( void ) __attribute__(($1)); + ], + [constructor_priority], [ + int foo( void ) __attribute__((__constructor__(65535/2))); + ], + [constructor], [ + int foo( void ) __attribute__(($1)); + ], + [deprecated], [ + int foo( void ) __attribute__(($1(""))); + ], + [destructor], [ + int foo( void ) __attribute__(($1)); + ], + [dllexport], [ + __attribute__(($1)) int foo( void ) { return 0; } + ], + [dllimport], [ + int foo( void ) __attribute__(($1)); + ], + [error], [ + int foo( void ) __attribute__(($1(""))); + ], + [externally_visible], [ + int foo( void ) __attribute__(($1)); + ], + [fallthrough], [ + void foo( int x ) {switch (x) { case 1: __attribute__(($1)); case 2: break ; }}; + ], + [flatten], [ + int foo( void ) __attribute__(($1)); + ], + [format], [ + int foo(const char *p, ...) __attribute__(($1(printf, 1, 2))); + ], + [gnu_format], [ + int foo(const char *p, ...) __attribute__((format(gnu_printf, 1, 2))); + ], + [format_arg], [ + char *foo(const char *p) __attribute__(($1(1))); + ], + [gnu_inline], [ + inline __attribute__(($1)) int foo( void ) { return 0; } + ], + [hot], [ + int foo( void ) __attribute__(($1)); + ], + [ifunc], [ + int my_foo( void ) { return 0; } + static int (*resolve_foo(void))(void) { return my_foo; } + int foo( void ) __attribute__(($1("resolve_foo"))); + ], + [leaf], [ + __attribute__(($1)) int foo( void ) { return 0; } + ], + [malloc], [ + void *foo( void ) __attribute__(($1)); + ], + [noclone], [ + int foo( void ) __attribute__(($1)); + ], + [noinline], [ + __attribute__(($1)) int foo( void ) { return 0; } + ], + [nonnull], [ + int foo(char *p) __attribute__(($1(1))); + ], + [noreturn], [ + void foo( void ) __attribute__(($1)); + ], + [nothrow], [ + int foo( void ) __attribute__(($1)); + ], + [optimize], [ + __attribute__(($1(3))) int foo( void ) { return 0; } + ], + [pure], [ + int foo( void ) __attribute__(($1)); + ], + [sentinel], [ + int foo(void *p, ...) __attribute__(($1)); + ], + [sentinel_position], [ + int foo(void *p, ...) __attribute__(($1(1))); + ], + [returns_nonnull], [ + void *foo( void ) __attribute__(($1)); + ], + [unused], [ + int foo( void ) __attribute__(($1)); + ], + [used], [ + int foo( void ) __attribute__(($1)); + ], + [visibility], [ + int foo_def( void ) __attribute__(($1("default"))); + int foo_hid( void ) __attribute__(($1("hidden"))); + int foo_int( void ) __attribute__(($1("internal"))); + int foo_pro( void ) __attribute__(($1("protected"))); + ], + [warning], [ + int foo( void ) __attribute__(($1(""))); + ], + [warn_unused_result], [ + int foo( void ) __attribute__(($1)); + ], + [weak], [ + int foo( void ) __attribute__(($1)); + ], + [weakref], [ + static int foo( void ) { return 0; } + static int bar( void ) __attribute__(($1("foo"))); + ], + [ + m4_warn([syntax], [Unsupported attribute $1, the test may fail]) + int foo( void ) __attribute__(($1)); + ] + )], []) + ], + dnl GCC doesn't exit with an error if an unknown attribute is + dnl provided but only outputs a warning, so accept the attribute + dnl only if no warning were issued. + [AS_IF([grep -- -Wattributes conftest.err], + [AS_VAR_SET([ac_var], [no])], + [AS_VAR_SET([ac_var], [yes])])], + [AS_VAR_SET([ac_var], [no])]) + ]) + + AS_IF([test yes = AS_VAR_GET([ac_var])], + [AC_DEFINE_UNQUOTED(AS_TR_CPP(HAVE_FUNC_ATTRIBUTE_$1), 1, + [Define to 1 if the system has the `$1' function attribute])], []) + + AS_VAR_POPDEF([ac_var]) +]) diff --git a/thapi.pc.in b/thapi.pc.in index e85150d38..70b3252f5 100644 --- a/thapi.pc.in +++ b/thapi.pc.in @@ -7,3 +7,5 @@ bindir=@bindir@ Name: Thapi Description: A tracing infrastructure for heterogeneous computing applications. Version: @PACKAGE_VERSION@ +Cflags: -I${includedir} +Libs: -L${libdir} -lThapi diff --git a/utils/Makefile.am b/utils/Makefile.am index 8e27fa9c3..b6a6e9f29 100644 --- a/utils/Makefile.am +++ b/utils/Makefile.am @@ -43,6 +43,47 @@ dlinfo_wrapper_CFLAGS = -Wall -Wextra bin_PROGRAMS += thapi_metadata +include_HEADERS = thapi.h + +lib_LTLIBRARIES = libThapi.la +nodist_libThapi_la_SOURCES = \ + thapi_toggle_tracepoints.h \ + thapi_toggle_tracepoints.c +libThapi_la_SOURCES = thapi_toggle.c +libThapi_la_CFLAGS = $(LTTNG_FLAGS) $(LTTNG_UST_CFLAGS) +libThapi_la_LDFLAGS = $(LTTNG_UST_LIBS) + +BTX_THAPI_TOGGLE_GENERATED = \ + btx_thapi_toggle/metababel/metababel.h \ + btx_thapi_toggle/metababel/btx_component.h \ + btx_thapi_toggle/metababel/btx_component.c \ + btx_thapi_toggle/metababel/btx_upstream.h \ + btx_thapi_toggle/metababel/btx_upstream.c \ + btx_thapi_toggle/metababel/btx_downstream.h \ + btx_thapi_toggle/metababel/btx_downstream.c \ + btx_thapi_toggle/btx_main.c + +$(BTX_THAPI_TOGGLE_GENERATED) &: $(srcdir)/btx_thapi_toggle.yaml + $(METABABEL) --enable-callbacks on_downstream -t FILTER -p toggle -c toggle \ + --upstream $(srcdir)/btx_thapi_toggle.yaml --downstream $(srcdir)/btx_thapi_toggle.yaml \ + -o btx_thapi_toggle + +bt2dir = $(pkglibdir)/bt2 +bt2_LTLIBRARIES = libThapiToggle.la + +nodist_libThapiToggle_la_SOURCES = $(BTX_THAPI_TOGGLE_GENERATED) +libThapiToggle_la_SOURCES = thapi_toggle_callbacks.cpp +libThapiToggle_la_CFLAGS = -fPIC -shared -Wall -Wextra -Wno-unused-parameter $(BABELTRACE2_CFLAGS) \ + -I./btx_thapi_toggle -I$(top_srcdir)/utils/include +libThapiToggle_la_CXXFLAGS = -fPIC -shared -Wall -Wextra -Wno-unused-parameter $(BABELTRACE2_CFLAGS) \ + -I./btx_thapi_toggle -I$(top_srcdir)/utils/include +libThapiToggle_la_LDFLAGS = $(BABELTRACE2_LIBS) -avoid-version -module + +BUILT_SOURCES += \ + thapi_toggle_tracepoints.h \ + thapi_toggle_tracepoints.c \ + $(BTX_THAPI_TOGGLE_GENERATED) + bin_SCRIPTS = \ babeltrace_thapi @@ -62,6 +103,7 @@ CLEANFILES = \ version \ optparse_thapi.rb \ lttng/tracepoint_gen.h \ + $(BTX_THAPI_TOGGLE_GENERATED) \ $(BUILT_SOURCES) EXTRA_DIST = \ @@ -74,6 +116,8 @@ EXTRA_DIST = \ gen_library_base.rb \ dump_trace_format.rb \ thapi_metadata_tracepoints.tp \ + thapi_toggle_tracepoints.tp \ + btx_thapi_toggle.yaml \ command.rb \ meta_parameters.rb \ optparse_thapi.rb \ diff --git a/utils/btx_thapi_toggle.yaml b/utils/btx_thapi_toggle.yaml new file mode 100644 index 000000000..53266a1c0 --- /dev/null +++ b/utils/btx_thapi_toggle.yaml @@ -0,0 +1,28 @@ +:environment: + :entries: + - :name: hostname + :type: string +:stream_classes: +- :name: thapi_toggle + :default_clock_class: {} + :packet_context_field_class: + :type: structure + :members: + - :name: cpu_id + :field_class: + :type: integer_signed + :cast_type: int64_t + :event_common_context_field_class: + :type: structure + :members: + - :name: vpid + :field_class: + :type: integer_signed + :cast_type: int64_t + - :name: vtid + :field_class: + :type: integer_signed + :cast_type: int64_t + :event_classes: + - :name: lttng_ust_toggle:start + - :name: lttng_ust_toggle:stop diff --git a/utils/thapi.h b/utils/thapi.h new file mode 100644 index 000000000..b94918a4c --- /dev/null +++ b/utils/thapi.h @@ -0,0 +1,15 @@ +#if !defined(THAPI) +#define THAPI + +#ifdef __cplusplus +extern "C" { +#endif + +void thapi_start(void); +void thapi_stop(void); + +#ifdef __cplusplus +} +#endif + +#endif // THAPI diff --git a/utils/thapi_toggle.c b/utils/thapi_toggle.c new file mode 100644 index 000000000..33124ec64 --- /dev/null +++ b/utils/thapi_toggle.c @@ -0,0 +1,13 @@ +#include "thapi.h" +#include "thapi_toggle_tracepoints.h" + +#ifndef LTTNG_UST_CONSTRUCTOR_PRIO +#error "LTTNG_UST_CONSTRUCTOR_PRIO is not defined." +#endif + +void thapi_start(void) { tracepoint(lttng_ust_toggle, start); } + +void thapi_stop(void) { tracepoint(lttng_ust_toggle, stop); } + +void __attribute__((constructor(LTTNG_UST_CONSTRUCTOR_PRIO + 1))) +thapi_auto_stop(void) { tracepoint(lttng_ust_toggle, auto_stop); } diff --git a/utils/thapi_toggle_callbacks.cpp b/utils/thapi_toggle_callbacks.cpp new file mode 100644 index 000000000..00e6c183d --- /dev/null +++ b/utils/thapi_toggle_callbacks.cpp @@ -0,0 +1,65 @@ +#include +#include +#include +#include + +#include +#include + +#include + +using ToggleKey = std::tuple; +using ToggleMap = std::map; + +static char hostname_s[HOST_NAME_MAX + 1]; + +static void init(void **data) { *data = new ToggleMap; } + +static void finalize(void *data) { delete static_cast(data); } + +static void thapi_start_callback(void *btx_handle, void *tmap, int64_t cpuid, const char *hostname, + int64_t vpid, int64_t vtid) { + auto map = static_cast(tmap); + auto key = ToggleKey{std::string(hostname), vpid}; + (*map)[key] = true; + strncpy(hostname_s, hostname, HOST_NAME_MAX); +} + +static void thapi_stop_callback(void *btx_handle, void *tmap, int64_t cpuid, const char *hostname, + int64_t vpid, int64_t vtid) { + auto map = static_cast(tmap); + auto key = ToggleKey{std::string(hostname), vpid}; + (*map)[key] = false; +} + +static void push_downstream(void *btx_handle, void *tmap, const bt_message *msg) { + bool push_msg = true; + + if (bt_message_get_type(msg) == BT_MESSAGE_TYPE_EVENT) { + const bt_event *event = bt_message_event_borrow_event_const(msg); + const bt_field *ccf = bt_event_borrow_common_context_field_const(event); + const bt_field *vpid = bt_field_structure_borrow_member_field_by_name_const(ccf, "vpid"); + uint64_t vpid_v = bt_field_integer_signed_get_value(vpid); + + auto map = static_cast(tmap); + auto key = ToggleKey{std::string(hostname_s), vpid_v}; + push_msg = (*map)[key]; + } + + if (push_msg) { + btx_push_message(btx_handle, msg); + } else { + bt_message_put_ref(msg); + } +} + +void btx_register_usr_callbacks(void *btx_handle) { + btx_register_callbacks_initialize_component(btx_handle, &init); + btx_register_callbacks_lttng_ust_toggle_start(btx_handle, + &thapi_start_callback); + btx_register_callbacks_lttng_ust_toggle_stop(btx_handle, + &thapi_stop_callback); + btx_register_callbacks_finalize_component(btx_handle, &finalize); + + btx_register_on_downstream_message_callback(btx_handle, &push_downstream); +} diff --git a/utils/thapi_toggle_tracepoints.tp b/utils/thapi_toggle_tracepoints.tp new file mode 100644 index 000000000..3a2b478ea --- /dev/null +++ b/utils/thapi_toggle_tracepoints.tp @@ -0,0 +1,20 @@ +TRACEPOINT_EVENT( + lttng_ust_toggle, + start, + TP_ARGS(), + TP_FIELDS() +) + +TRACEPOINT_EVENT( + lttng_ust_toggle, + auto_stop, + TP_ARGS(), + TP_FIELDS() +) + +TRACEPOINT_EVENT( + lttng_ust_toggle, + stop, + TP_ARGS(), + TP_FIELDS() +) diff --git a/xprof/xprof.rb.in b/xprof/xprof.rb.in index ede27ab99..3b2e9d6ce 100755 --- a/xprof/xprof.rb.in +++ b/xprof/xprof.rb.in @@ -656,6 +656,11 @@ def enable_events_metadata(channel_name, tracing_mode: 'default', profiling: tru exec("#{lttng_enable} lttng_ust_thapi:*") end +def enable_events_toggle(channel_name, tracing_mode: 'default', profiling: true) + lttng_enable = "lttng enable-event --userspace --session=#{lttng_session_uuid} --channel=#{channel_name}" + exec("#{lttng_enable} lttng_ust_toggle:*") +end + module LocalMaster extend self @@ -739,7 +744,7 @@ module LocalMaster exec("lttng add-context --userspace --session=#{lttng_session_uuid} --channel=#{channel_name} -t vpid -t vtid") # Enable backend events - (backends + ['metadata']).each do |name| + (backends + ['metadata', 'toggle']).each do |name| send("enable_events_#{name}", channel_name, tracing_mode: OPTIONS[:'tracing-mode'], profiling: OPTIONS[:profile])