Skip to content

Commit

Permalink
script: Support native C/C++ shared object scripting
Browse files Browse the repository at this point in the history
This patch adds a native script support that can use a shared object as
a script plugin.

The basic interface is same as python and luajit, but the benefit of
this native script is that it doesn't add any overhead from the
scripting engines.  It just bypasses the execution flow into the
dynamically loaded shared object.

The target shared object has to be compiled from C/C++ programs that
provides the following functions.

  void uftrace_begin(void);
  void uftrace_entry(struct script_context *sc_ctx);
  void uftrace_exit(struct script_context *sc_ctx);
  void uftrace_end(void);

The 'struct script_context' is same as the struct used inside uftrace so
it's just passed without any manipulation.

The example C/C++ files are in sctipts directory and the shared objects
can be compiled as follows.

  $ make -C scripts/
  make: Entering directory '/home/honggyu/work/uftrace/scripts'
  gcc -fPIC -shared -o simple_c.so simple.c
  gcc -fPIC -shared -o simple_cpp.so simple.cpp
  make: Leaving directory '/home/honggyu/work/uftrace/scripts'

The basic usage is as follows.

  $ uftrace record -S scripts/simple_c.so --no-libcall t-abc
  program begins...
  entry : main()
  entry : a()
  entry : b()
  entry : c()
  exit  : c()
  exit  : b()
  exit  : a()
  exit  : main()
  program is finished

The output looks exactly same as python and luajit scripts.

Signed-off-by: Honggyu Kim <[email protected]>
  • Loading branch information
honggyukim committed Sep 29, 2020
1 parent 9d1ca0c commit 803bc45
Show file tree
Hide file tree
Showing 10 changed files with 240 additions and 1 deletion.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ LIBMCOUNT_UTILS_SRCS += $(srcdir)/utils/debug.c $(srcdir)/utils/regs.c
LIBMCOUNT_UTILS_SRCS += $(srcdir)/utils/rbtree.c $(srcdir)/utils/filter.c
LIBMCOUNT_UTILS_SRCS += $(srcdir)/utils/demangle.c $(srcdir)/utils/utils.c
LIBMCOUNT_UTILS_SRCS += $(srcdir)/utils/script.c $(srcdir)/utils/script-python.c $(srcdir)/utils/script-luajit.c
LIBMCOUNT_UTILS_SRCS += $(srcdir)/utils/script-native.c
LIBMCOUNT_UTILS_SRCS += $(srcdir)/utils/auto-args.c $(srcdir)/utils/dwarf.c
LIBMCOUNT_UTILS_SRCS += $(srcdir)/utils/hashmap.c
LIBMCOUNT_UTILS_SRCS += $(wildcard $(srcdir)/utils/symbol*.c)
Expand Down
2 changes: 2 additions & 0 deletions check-deps/Makefile.check
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ ifneq ($(wildcard $(srcdir)/check-deps/have_libluajit),)
COMMON_CFLAGS += $(shell pkg-config --cflags luajit)
endif

COMMON_CFLAGS += -DHAVE_LIBNATIVE

ifneq ($(wildcard $(srcdir)/check-deps/perf_clockid),)
COMMON_CFLAGS += -DHAVE_PERF_CLOCKID
endif
Expand Down
6 changes: 6 additions & 0 deletions scripts/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
script:
gcc -fPIC -shared -o simple_c.so simple.c
gcc -fPIC -shared -o simple_cpp.so simple.cpp

clean:
rm -f *.so
37 changes: 37 additions & 0 deletions scripts/simple.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#include <stdio.h>
#include <stdint.h>

struct script_context {
int tid;
int depth;
uint64_t timestamp;
uint64_t duration; /* exit only */
unsigned long address;
char *name;
#if 0
/* for arguments and return value */
int arglen;
void *argbuf;
struct list_head *argspec;
#endif
};

void uftrace_begin(void)
{
printf("program begins...\n");
}

void uftrace_entry(struct script_context *sc_ctx)
{
printf("entry : %s()\n", sc_ctx->name);
}

void uftrace_exit(struct script_context *sc_ctx)
{
printf("exit : %s()\n", sc_ctx->name);
}

void uftrace_end(void)
{
printf("program is finished\n");
}
41 changes: 41 additions & 0 deletions scripts/simple.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#include <iostream>
#include <cstdint>

struct script_context {
int tid;
int depth;
uint64_t timestamp;
uint64_t duration; /* exit only */
unsigned long address;
char *name;
#if 0
/* for arguments and return value */
int arglen;
void *argbuf;
struct list_head *argspec;
#endif
};

extern "C"
void uftrace_begin(void)
{
std::cout << "program begins...\n";
}

extern "C"
void uftrace_entry(struct script_context *sc_ctx)
{
std::cout << "entry : " << sc_ctx->name << "()\n";
}

extern "C"
void uftrace_exit(struct script_context *sc_ctx)
{
std::cout << "exit : " << sc_ctx->name << "()\n";
}

extern "C"
void uftrace_end(void)
{
std::cout << "program is finished\n";
}
3 changes: 2 additions & 1 deletion uftrace.c
Original file line number Diff line number Diff line change
Expand Up @@ -1065,7 +1065,8 @@ void parse_script_opt(struct opts *opts)
const char* comments[SCRIPT_TYPE_COUNT] = {
"",
"#",
"--"
"--",
"//",
};
const char* comment;
size_t comment_len;
Expand Down
109 changes: 109 additions & 0 deletions utils/script-native.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
#ifdef HAVE_LIBNATIVE

#define PR_FMT "script"
#define PR_DOMAIN DBG_SCRIPT

#include <dlfcn.h>
#include "utils/utils.h"
#include "utils/symbol.h"
#include "utils/fstack.h"
#include "utils/filter.h"
#include "utils/script.h"
#include "utils/script-native.h"

static void *native_handle;

static void (*__uftrace_begin)(void);
static void (*__uftrace_entry)(struct script_context *sc_ctx);
static void (*__uftrace_exit)(struct script_context *sc_ctx);
static void (*__uftrace_end)(void);

static int native_uftrace_begin(struct script_info *info)
{
pr_dbg("%s()\n", __func__);
__uftrace_begin();
return 0;
}

static int native_uftrace_entry(struct script_context *sc_ctx)
{
pr_dbg("%s()\n", __func__);
__uftrace_entry(sc_ctx);
return 0;
}

static int native_uftrace_exit(struct script_context *sc_ctx)
{
pr_dbg("%s()\n", __func__);
__uftrace_exit(sc_ctx);
return 0;
}

static int native_uftrace_end(void)
{
pr_dbg("%s()\n", __func__);
__uftrace_end();
return 0;
}

/* do nothing but needed */
static int native_atfork_prepare(void)
{
return 0;
}

#define INIT_NATIVE_API_FUNC(func) \
do { \
__##func = dlsym(native_handle, #func); \
if (!__##func) { \
pr_err("dlsym for \"" #func "\" is failed!\n"); \
return -1; \
} \
} while (0)

static int load_native_api_funcs(const char *so_pathname)
{
native_handle = dlopen(so_pathname, RTLD_LAZY | RTLD_GLOBAL);
if (!native_handle) {
pr_warn("%s cannot be loaded!\n", so_pathname);
return -1;
}
pr_dbg("%s is loaded\n", so_pathname);

INIT_NATIVE_API_FUNC(uftrace_begin);
INIT_NATIVE_API_FUNC(uftrace_entry);
INIT_NATIVE_API_FUNC(uftrace_exit);
INIT_NATIVE_API_FUNC(uftrace_end);

return 0;
}

int script_init_for_native(struct script_info *info,
enum uftrace_pattern_type ptype)
{
pr_dbg("%s()\n", __func__);

script_uftrace_entry = native_uftrace_entry;
script_uftrace_exit = native_uftrace_exit;
script_uftrace_end = native_uftrace_end;
script_atfork_prepare = native_atfork_prepare;

if (load_native_api_funcs(info->name) < 0)
return -1;

/* TODO: do more work here. */

native_uftrace_begin(info);

return 0;
}

void script_finish_for_native(void)
{
pr_dbg("%s()\n", __func__);

dlclose(native_handle);
native_handle = NULL;
}

#endif
28 changes: 28 additions & 0 deletions utils/script-native.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#ifndef UFTRACE_SCRIPT_NATIVE_H
#define UFTRACE_SCRIPT_NATIVE_H

#include "utils/filter.h"

struct script_info;

#ifdef HAVE_LIBNATIVE

#define SCRIPT_NATIVE_ENABLED 1
int script_init_for_native(struct script_info *info,
enum uftrace_pattern_type ptype);
void script_finish_for_native(void);

#else /* HAVE_LIBNATIVE */

#define SCRIPT_NATIVE_ENABLED 0
static inline int script_init_for_native(struct script_info *info,
enum uftrace_pattern_type ptype)
{
return -1;
}

static inline void script_finish_for_native(void) {}

#endif /* HAVE_LIBNATIVE */

#endif /* UFTRACE_SCRIPT_NATIVE_H */
12 changes: 12 additions & 0 deletions utils/script.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "utils/utils.h"
#include "utils/script-python.h"
#include "utils/script-luajit.h"
#include "utils/script-native.h"

/* This will be set by getenv("UFTRACE_SCRIPT"). */
char *script_str;
Expand Down Expand Up @@ -48,6 +49,8 @@ enum script_type_t get_script_type(const char *str)
return SCRIPT_PYTHON;
else if (!strcmp(ext, ".lua"))
return SCRIPT_LUAJIT;
else if (!strcmp(ext, ".so"))
return SCRIPT_NATIVE;

return SCRIPT_UNKNOWN;
}
Expand Down Expand Up @@ -119,6 +122,12 @@ int script_init(struct script_info *info, enum uftrace_pattern_type ptype)
script_pathname = NULL;
}
break;
case SCRIPT_NATIVE:
if (script_init_for_native(info, ptype) < 0) {
pr_warn("failed to init native scripting\n");
script_pathname = NULL;
}
break;
default:
pr_warn("unsupported script type: %s\n", script_pathname);
script_pathname = NULL;
Expand All @@ -140,6 +149,9 @@ void script_finish(void)
case SCRIPT_LUAJIT:
script_finish_for_luajit();
break;
case SCRIPT_NATIVE:
script_finish_for_native();
break;
default:
break;
}
Expand Down
2 changes: 2 additions & 0 deletions utils/script.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "libmcount/mcount.h"
#include "utils/script-python.h"
#include "utils/script-luajit.h"
#include "utils/script-native.h"
#include "utils/utils.h"

#define SCRIPT_ENABLED (SCRIPT_LUAJIT_ENABLED || SCRIPT_PYTHON_ENABLED)
Expand All @@ -20,6 +21,7 @@ enum script_type_t {
SCRIPT_UNKNOWN = 0,
SCRIPT_PYTHON,
SCRIPT_LUAJIT,
SCRIPT_NATIVE,
SCRIPT_TYPE_COUNT
};

Expand Down

0 comments on commit 803bc45

Please sign in to comment.