Skip to content

Commit

Permalink
test: replace hand-rolled backtrace function with gstack
Browse files Browse the repository at this point in the history
Let's use something that specializes in that task and does a better job of it
than whatever we'll come up with. Due to how it's implemented the stacktrace
will always show waitpid() as frame 0 now but we can live with that.

gstack prints to stdout but litest_log() uses stderr, so we cannot just call
system(), we have do do the pipe/fork/exec/waitpid/read dance.
We could use that to filter the #0 frame showing waidpid() from gstack but
meh.

This drops the libunwind and addr2line dependency and replaces it with gstack
instead.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
  • Loading branch information
whot committed Aug 13, 2018
1 parent 3a9c8aa commit c8e0e94
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 168 deletions.
6 changes: 3 additions & 3 deletions .gitlab-ci.yml
Expand Up @@ -41,9 +41,9 @@ variables:
# See the documentation here: #
# https://wayland.freedesktop.org/libinput/doc/latest/building_libinput.html #
###############################################################################
FEDORA_RPMS: 'git gcc gcc-c++ pkgconf-pkg-config meson check-devel libudev-devel libevdev-devel doxygen graphviz python3-sphinx python3-recommonmark valgrind binutils libwacom-devel cairo-devel gtk3-devel glib2-devel mtdev-devel'
UBUNTU_DEBS: 'git gcc g++ pkg-config meson check libudev-dev libevdev-dev doxygen graphviz python3-sphinx python3-recommonmark python3-sphinx-rtd-theme valgrind binutils libwacom-dev libcairo2-dev libgtk-3-dev libglib2.0-dev libmtdev-dev'
ARCH_PKGS: 'git gcc pkgconfig meson check libsystemd libevdev doxygen graphviz python-sphinx python-recommonmark valgrind binutils libwacom gtk3 mtdev '
FEDORA_RPMS: 'git gcc gcc-c++ pkgconf-pkg-config meson check-devel libudev-devel libevdev-devel doxygen graphviz python3-sphinx python3-recommonmark valgrind libwacom-devel cairo-devel gtk3-devel glib2-devel mtdev-devel'
UBUNTU_DEBS: 'git gcc g++ pkg-config meson check libudev-dev libevdev-dev doxygen graphviz python3-sphinx python3-recommonmark python3-sphinx-rtd-theme valgrind libwacom-dev libcairo2-dev libgtk-3-dev libglib2.0-dev libmtdev-dev'
ARCH_PKGS: 'git gcc pkgconfig meson check libsystemd libevdev doxygen graphviz python-sphinx python-recommonmark valgrind libwacom gtk3 mtdev '
FREEBSD_BUILD_PKGS: 'meson'
FREEBSD_PKGS: 'libepoll-shim libudev-devd libevdev libwacom gtk3 libmtdev '
############################ end of package lists #############################
Expand Down
11 changes: 2 additions & 9 deletions meson.build
Expand Up @@ -643,18 +643,12 @@ executable('test-build-cxx',
if get_option('tests')
dep_check = dependency('check', version : '>= 0.9.10')
valgrind = find_program('valgrind')
addr2line = find_program('addr2line')

if addr2line.found()
config_h.set('HAVE_ADDR2LINE', '1')
config_h.set_quoted('ADDR2LINE', addr2line.path())
endif

leftover_rules = find_program('test/check-leftover-udev-rules.sh')
test('leftover-rules', leftover_rules, is_parallel : false)

dep_libunwind = dependency('libunwind', required : false)
config_h.set10('HAVE_LIBUNWIND', dep_libunwind.found())
gstack = find_program('gstack', required : false)
config_h.set10('HAVE_GSTACK', gstack.found())

# for inhibit support during test run
dep_libsystemd = dependency('libsystemd', version : '>= 221', required : false)
Expand Down Expand Up @@ -748,7 +742,6 @@ if get_option('tests')
deps_litest = [
dep_libinput,
dep_check,
dep_libunwind,
dep_udev,
dep_libevdev,
dep_dl,
Expand Down
190 changes: 34 additions & 156 deletions test/litest.c
Expand Up @@ -103,175 +103,53 @@ static inline char *litest_install_quirks(struct list *created_files_list);
#define litest_vlog(...) { /* __VA_ARGS__ */ }
#endif

#if HAVE_LIBUNWIND
#define UNW_LOCAL_ONLY
#include <libunwind.h>
#include <dlfcn.h>

static char cwd[PATH_MAX];

static bool
litest_backtrace_get_lineno(const char *executable,
unw_word_t addr,
char *file_return,
int *line_return)
{
#if HAVE_ADDR2LINE
FILE* f;
char buffer[PATH_MAX];
char *s;
unsigned int i;

if (!cwd[0]) {
if (getcwd(cwd, sizeof(cwd)) == NULL)
cwd[0] = 0; /* contents otherwise undefined. */
}

sprintf (buffer,
ADDR2LINE " -C -e %s -i %lx",
executable,
(unsigned long) addr);

f = popen(buffer, "r");
if (f == NULL) {
litest_log("Failed to execute: %s\n", buffer);
return false;
}

buffer[0] = '?';
if (fgets(buffer, sizeof(buffer), f) == NULL) {
pclose(f);
return false;
}
pclose(f);

if (buffer[0] == '?')
return false;

s = strrchr(buffer, ':');
if (!s)
return false;
static void
litest_backtrace(void)
{
#if HAVE_GSTACK
pid_t parent, child;
int pipefd[2];

*s = '\0';
s++;
sscanf(s, "%d", line_return);
if (pipe(pipefd) == -1)
return;

/* now strip cwd from buffer */
s = buffer;
i = 0;
while(i < strlen(cwd) && *s != '\0' && cwd[i] == *s) {
*s = '\0';
s++;
i++;
}
parent = getpid();
child = fork();

if (i > 0)
*(--s) = '.';
strcpy(file_return, s);
if (child == 0) {
char pid[8];

return true;
#else /* HAVE_ADDR2LINE */
return false;
#endif
}
close(pipefd[0]);
dup2(pipefd[1], STDOUT_FILENO);

static void
litest_backtrace(void)
{
unw_cursor_t cursor;
unw_context_t context;
unw_word_t off;
unw_proc_info_t pip;
int ret;
char procname[256];
Dl_info dlinfo;

pip.unwind_info = NULL;
ret = unw_getcontext(&context);
if (ret) {
litest_log("unw_getcontext failed: %s [%d]\n",
unw_strerror(ret),
ret);
return;
}
sprintf(pid, "%d", parent);

ret = unw_init_local(&cursor, &context);
if (ret) {
litest_log("unw_init_local failed: %s [%d]\n",
unw_strerror(ret),
ret);
return;
execlp("gstack", "gstack", pid, NULL);
exit(errno);
}

litest_log("\nBacktrace:\n");
ret = unw_step(&cursor);
while (ret > 0) {
char file[PATH_MAX];
int line;
bool have_lineno = false;
const char *filename = "?";
int i = 0;

ret = unw_get_proc_info(&cursor, &pip);
if (ret) {
litest_log("unw_get_proc_info failed: %s [%d]\n",
unw_strerror(ret),
ret);
break;
}

ret = unw_get_proc_name(&cursor, procname, 256, &off);
if (ret && ret != -UNW_ENOMEM) {
if (ret != -UNW_EUNSPEC)
litest_log("unw_get_proc_name failed: %s [%d]\n",
unw_strerror(ret),
ret);
procname[0] = '?';
procname[1] = 0;
}
/* parent */
char buf[1024];
int status, nread;

if (dladdr((void *)(pip.start_ip + off), &dlinfo) &&
dlinfo.dli_fname &&
*dlinfo.dli_fname) {
filename = dlinfo.dli_fname;
have_lineno = litest_backtrace_get_lineno(filename,
(pip.start_ip + off),
file,
&line);
}
close(pipefd[1]);
waitpid(child, &status, 0);

if (have_lineno) {
litest_log("%d: %s() (%s:%d)\n",
i,
procname,
file,
line);
} else {
litest_log("%d: %s (%s%s+%#x) [%p]\n",
i,
filename,
procname,
ret == -UNW_ENOMEM ? "..." : "",
(int)off,
(void *)(pip.start_ip + off));
status = WEXITSTATUS(status);
if (status != 0) {
litest_log("ERROR: gstack failed, no backtrace available: %s\n",
strerror(status));
} else {
litest_log("\nBacktrace:\n");
while ((nread = read(pipefd[0], buf, sizeof(buf) - 1)) > 0) {
buf[nread] = '\0';
litest_log("%s", buf);
}

i++;
ret = unw_step(&cursor);
if (ret < 0)
litest_log("unw_step failed: %s [%d]\n",
unw_strerror(ret),
ret);
litest_log("\n");
}
litest_log("\n");
}
#else /* HAVE_LIBUNWIND */
static inline void
litest_backtrace(void)
{
/* thou shall install libunwind */
}
close(pipefd[0]);
#endif
}

LIBINPUT_ATTRIBUTE_PRINTF(5, 6)
__attribute__((noreturn))
Expand Down

0 comments on commit c8e0e94

Please sign in to comment.