test: replace hand-rolled backtrace function with gstack

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>
This commit is contained in:
Peter Hutterer 2018-08-08 16:32:06 +10:00
parent 3a9c8aa77d
commit c8e0e94c00
3 changed files with 43 additions and 172 deletions

View file

@ -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 #############################

View file

@ -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)
@ -748,7 +742,6 @@ if get_option('tests')
deps_litest = [
dep_libinput,
dep_check,
dep_libunwind,
dep_udev,
dep_libevdev,
dep_dl,

View file

@ -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;
*s = '\0';
s++;
sscanf(s, "%d", line_return);
/* now strip cwd from buffer */
s = buffer;
i = 0;
while(i < strlen(cwd) && *s != '\0' && cwd[i] == *s) {
*s = '\0';
s++;
i++;
}
if (i > 0)
*(--s) = '.';
strcpy(file_return, s);
return true;
#else /* HAVE_ADDR2LINE */
return false;
#endif
}
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;
#if HAVE_GSTACK
pid_t parent, child;
int pipefd[2];
pip.unwind_info = NULL;
ret = unw_getcontext(&context);
if (ret) {
litest_log("unw_getcontext failed: %s [%d]\n",
unw_strerror(ret),
ret);
if (pipe(pipefd) == -1)
return;
parent = getpid();
child = fork();
if (child == 0) {
char pid[8];
close(pipefd[0]);
dup2(pipefd[1], STDOUT_FILENO);
sprintf(pid, "%d", parent);
execlp("gstack", "gstack", pid, NULL);
exit(errno);
}
ret = unw_init_local(&cursor, &context);
if (ret) {
litest_log("unw_init_local failed: %s [%d]\n",
unw_strerror(ret),
ret);
return;
/* parent */
char buf[1024];
int status, nread;
close(pipefd[1]);
waitpid(child, &status, 0);
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);
}
litest_log("\n");
}
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;
}
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);
}
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));
}
i++;
ret = unw_step(&cursor);
if (ret < 0)
litest_log("unw_step failed: %s [%d]\n",
unw_strerror(ret),
ret);
}
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))