On Mon, 2014-03-03 at 16:48 +0100, Mark Wielaard wrote: > Lets see how to combine the approaches most effectively. > I'll try and cleanup my patch to linux-proc-maps.c to make it possible > to use elf_from_remote_memory for "(deleted)" files to make it easier to > test things out.
Attached is a patch that does this. I'll push it to the mjw/pending branch. For now only tested on a RHEL6 x86_64 2.6.32 kernel. It does seem to work at least for that setup. I'll try on some other setups later. $ src/stack -1 -m -p `pidof firefox` TID 25950: #0 0x0000003ea8cdf343 __poll - /lib64/libc.so.6 #1 0x00007f16813680dc PollWrapper(_GPollFD*, unsigned int, int) - /usr/lib64/firefox/libxul.so (deleted) #2 0x0000003eb3843ac9 g_main_context_iterate - /lib64/libglib-2.0.so.0.2600.1 #3 0x0000003eb3843f1c g_main_context_iteration - /lib64/libglib-2.0.so.0.2600.1 #4 0x00007f1681367f39 nsAppShell::ProcessNextNativeEvent(bool) - /usr/lib64/firefox/libxul.so (deleted) #5 0x00007f168138e711 nsBaseAppShell::DoProcessNextNativeEvent(bool, unsigned int) - /usr/lib64/firefox/libxul.so (deleted) #6 0x00007f168138e890 nsBaseAppShell::OnProcessNextEvent(nsIThreadInternal*, bool, unsigned int) - /usr/lib64/firefox/libxul.so (deleted) #7 0x00007f1681762240 nsThread::ProcessNextEvent(bool, bool*) - /usr/lib64/firefox/libxul.so (deleted) #8 0x00007f16817319bc NS_ProcessNextEvent(nsIThread*, bool) - /usr/lib64/firefox/libxul.so (deleted) #9 0x00007f16813f10b3 mozilla::ipc::MessagePump::Run(base::MessagePump::Delegate*) - /usr/lib64/firefox/libxul.so (deleted) #10 0x00007f1681788cb2 MessageLoop::Run() - /usr/lib64/firefox/libxul.so (deleted) #11 0x00007f168138e9c1 nsBaseAppShell::Run() - /usr/lib64/firefox/libxul.so (deleted) #12 0x00007f1681245e42 nsAppStartup::Run() - /usr/lib64/firefox/libxul.so (deleted) #13 0x00007f168085aeee XREMain::XRE_mainRun() - /usr/lib64/firefox/libxul.so (deleted) #14 0x00007f168085eb6e XREMain::XRE_main(int, char**, nsXREAppData const*) - /usr/lib64/firefox/libxul.so (deleted) #15 0x00007f168085edc6 XRE_main - /usr/lib64/firefox/libxul.so (deleted) #16 0x0000000000403bf6 do_main(int, char**, nsIFile*) - /usr/lib64/firefox/firefox (deleted) #17 0x0000000000403d3b main - /usr/lib64/firefox/firefox (deleted) #18 0x0000003ea8c1ed1d __libc_start_main - /lib64/libc.so.6 #19 0x0000000000403469 _start - /usr/lib64/firefox/firefox (deleted) Cheers, Mark
>From 4c2a0276abdba2589bc6a93988caebf54615ae43 Mon Sep 17 00:00:00 2001 From: Mark Wielaard <m...@redhat.com> Date: Tue, 4 Mar 2014 11:27:15 +0100 Subject: [PATCH] libdwfl: dwfl_linux_proc_find_elf use elf_from_remote_memory for (deleted). If a module has a "(deleted)" main ELF file, then try to read it from remote memory if the Dwfl has process state attached by reusing the ptrace mechanism from linux-pid-attach. Signed-off-by: Mark Wielaard <m...@redhat.com> --- libdwfl/ChangeLog | 19 ++++++++++++++++ libdwfl/dwfl_frame.c | 16 +++++++++---- libdwfl/libdwflP.h | 36 ++++++++++++++++++++++++++++++- libdwfl/linux-pid-attach.c | 51 +++++++++++++++++++++++++------------------- libdwfl/linux-proc-maps.c | 51 ++++++++++++++++++++++++++++++++++--------- 5 files changed, 134 insertions(+), 39 deletions(-) diff --git a/libdwfl/ChangeLog b/libdwfl/ChangeLog index 8fcdc3b..40b3cf1 100644 --- a/libdwfl/ChangeLog +++ b/libdwfl/ChangeLog @@ -1,3 +1,22 @@ +2014-03-04 Mark Wielaard <m...@redhat.com> + + * libdwflP.h (struct pid_arg): Moved here from linux-pid-attach.c. + (__libdwfl_get_pid_arg): New internal function declaration. + (__libdwfl_ptrace_attach): Likewise. + (__libdwfl_ptrace_detach): Likewise. + * dwfl_frame.c (dwfl_attach_state): Add "(deleted)" files to the + special exception modules that cannot be checked at this point. + * linux-pid-attach.c (struct pid_arg): Moved to libdwflP.h + (ptrace_attach): Renamed to... + (__libdwfl_ptrace_attach): New internal function. + (__libdwfl_ptrace_detach): Likewise. Extracted from ... + (pid_thread_detach): Call __libdwfl_ptrace_detach now. + (__libdwfl_get_pid_arg): New internal function. + * linux-proc-maps.c (dwfl_linux_proc_find_elf): Check if special + module name contains "(deleted)" and dwfl_pid gives an attached + pid. If pid is set and try to (re)use ptrace attach state of + process before reading memory. + 2014-03-03 Mark Wielaard <m...@redhat.com> * elf-from-memory.c (elf_from_remote_memory): Keep track of diff --git a/libdwfl/dwfl_frame.c b/libdwfl/dwfl_frame.c index e45cf14..fd0b9ae 100644 --- a/libdwfl/dwfl_frame.c +++ b/libdwfl/dwfl_frame.c @@ -157,11 +157,17 @@ dwfl_attach_state (Dwfl *dwfl, Elf *elf, pid_t pid, ebl = NULL; for (Dwfl_Module *mod = dwfl->modulelist; mod != NULL; mod = mod->next) { - /* Reading of the vDSO module may fail as /proc/PID/mem is unreadable - without PTRACE_ATTACH and we may not be PTRACE_ATTACH-ed now. - MOD would not be re-read later to unwind it when we are already - PTRACE_ATTACH-ed to PID. */ - if (strncmp (mod->name, "[vdso: ", 7) == 0) + /* Reading of the vDSO or (deleted) modules may fail as + /proc/PID/mem is unreadable without PTRACE_ATTACH and + we may not be PTRACE_ATTACH-ed now. MOD would not be + re-read later to unwind it when we are already + PTRACE_ATTACH-ed to PID. This happens when this function + is called from dwfl_linux_proc_attach with elf == NULL. + __libdwfl_module_getebl will call __libdwfl_getelf which + will call the find_elf callback. */ + if (strncmp (mod->name, "[vdso: ", 7) == 0 + || strcmp (strrchr (mod->name, ' ') ?: "", + " (deleted)") == 0) continue; Dwfl_Error error = __libdwfl_module_getebl (mod); if (error != DWFL_E_NOERROR) diff --git a/libdwfl/libdwflP.h b/libdwfl/libdwflP.h index 710e699..0b033fe 100644 --- a/libdwfl/libdwflP.h +++ b/libdwfl/libdwflP.h @@ -1,5 +1,5 @@ /* Internal definitions for libdwfl. - Copyright (C) 2005-2013 Red Hat, Inc. + Copyright (C) 2005-2014 Red Hat, Inc. This file is part of elfutils. This file is free software; you can redistribute it and/or modify @@ -35,6 +35,7 @@ #include <libdwfl.h> #include <libebl.h> #include <assert.h> +#include <dirent.h> #include <errno.h> #include <stdbool.h> #include <stdlib.h> @@ -387,6 +388,39 @@ struct dwfl_arange }; +/* Structure used for keeping track of ptrace attaching a thread. + Shared by linux-pid-attach and linux-proc-maps. If it has been setup + then get the instance through __libdwfl_get_pid_arg. */ +struct pid_arg +{ + DIR *dir; + /* It is 0 if not used. */ + pid_t tid_attached; + /* Valid only if TID_ATTACHED is not zero. */ + bool tid_was_stopped; + /* True if threads are ptrace stopped by caller. */ + bool assume_ptrace_stopped; +}; + +/* If DWfl is not NULL and a Dwfl_Process has been setup that has + Dwfl_Thread_Callbacks set to pid_thread_callbacks, then return the + callbacks_arg, which will be a struct pid_arg. Otherwise + returns NULL. */ +extern struct pid_arg *__libdwfl_get_pid_arg (Dwfl *dwfl) + internal_function; + +/* Makes sure the given tid is attached. On success returns true and + sets tid_was_stopped. */ +extern bool __libdwfl_ptrace_attach (pid_t tid, bool *tid_was_stoppedp) + internal_function; + +/* Detaches a tid that was attached through + __libdwfl_ptrace_attach. Must be given the tid_was_stopped as set + by __libdwfl_ptrace_attach. */ +extern void __libdwfl_ptrace_detach (pid_t tid, bool tid_was_stopped) + internal_function; + + /* Internal wrapper for old dwfl_module_getsym and new dwfl_module_getsym_info. adjust_st_value set to true returns adjusted SYM st_value, set to false it will not adjust SYM at all, but does match against resolved *ADDR. */ diff --git a/libdwfl/linux-pid-attach.c b/libdwfl/linux-pid-attach.c index 58d6942..0ec7324 100644 --- a/libdwfl/linux-pid-attach.c +++ b/libdwfl/linux-pid-attach.c @@ -37,16 +37,6 @@ # define MAX(a, b) ((a) > (b) ? (a) : (b)) #endif -struct pid_arg -{ - DIR *dir; - /* It is 0 if not used. */ - pid_t tid_attached; - /* Valid only if TID_ATTACHED is not zero. */ - bool tid_was_stopped; - /* True if threads are ptrace stopped by caller. */ - bool assume_ptrace_stopped; -}; static bool linux_proc_pid_is_stopped (pid_t pid) @@ -72,8 +62,9 @@ linux_proc_pid_is_stopped (pid_t pid) return retval; } -static bool -ptrace_attach (pid_t tid, bool *tid_was_stoppedp) +bool +internal_function +__libdwfl_ptrace_attach (pid_t tid, bool *tid_was_stoppedp) { if (ptrace (PTRACE_ATTACH, tid, NULL, NULL) != 0) { @@ -242,7 +233,7 @@ pid_set_initial_registers (Dwfl_Thread *thread, void *thread_arg) assert (pid_arg->tid_attached == 0); pid_t tid = INTUSE(dwfl_thread_tid) (thread); if (! pid_arg->assume_ptrace_stopped - && ! ptrace_attach (tid, &pid_arg->tid_was_stopped)) + && ! __libdwfl_ptrace_attach (tid, &pid_arg->tid_was_stopped)) return false; pid_arg->tid_attached = tid; Dwfl_Process *process = thread->process; @@ -259,6 +250,19 @@ pid_detach (Dwfl *dwfl __attribute__ ((unused)), void *dwfl_arg) free (pid_arg); } +void +internal_function +__libdwfl_ptrace_detach (pid_t tid, bool tid_was_stopped) +{ + /* This handling is needed only on older Linux kernels such as + 2.6.32-358.23.2.el6.ppc64. Later kernels such as + 3.11.7-200.fc19.x86_64 remember the T (stopped) state + themselves and no longer need to pass SIGSTOP during + PTRACE_DETACH. */ + ptrace (PTRACE_DETACH, tid, NULL, + (void *) (intptr_t) (tid_was_stopped ? SIGSTOP : 0)); +} + static void pid_thread_detach (Dwfl_Thread *thread, void *thread_arg) { @@ -267,15 +271,7 @@ pid_thread_detach (Dwfl_Thread *thread, void *thread_arg) assert (pid_arg->tid_attached == tid); pid_arg->tid_attached = 0; if (! pid_arg->assume_ptrace_stopped) - { - /* This handling is needed only on older Linux kernels such as - 2.6.32-358.23.2.el6.ppc64. Later kernels such as - 3.11.7-200.fc19.x86_64 remember the T (stopped) state - themselves and no longer need to pass SIGSTOP during - PTRACE_DETACH. */ - ptrace (PTRACE_DETACH, tid, NULL, - (void *) (intptr_t) (pid_arg->tid_was_stopped ? SIGSTOP : 0)); - } + __libdwfl_ptrace_detach (tid, pid_arg->tid_was_stopped); } static const Dwfl_Thread_Callbacks pid_thread_callbacks = @@ -347,3 +343,14 @@ dwfl_linux_proc_attach (Dwfl *dwfl, pid_t pid, bool assume_ptrace_stopped) return 0; } INTDEF (dwfl_linux_proc_attach) + +struct pid_arg * +internal_function +__libdwfl_get_pid_arg (Dwfl *dwfl) +{ + if (dwfl != NULL && dwfl->process != NULL + && dwfl->process->callbacks == &pid_thread_callbacks) + return (struct pid_arg *) dwfl->process->callbacks_arg; + + return NULL; +} diff --git a/libdwfl/linux-proc-maps.c b/libdwfl/linux-proc-maps.c index 3384403..8d18274 100644 --- a/libdwfl/linux-proc-maps.c +++ b/libdwfl/linux-proc-maps.c @@ -339,34 +339,60 @@ dwfl_linux_proc_find_elf (Dwfl_Module *mod __attribute__ ((unused)), const char *module_name, Dwarf_Addr base, char **file_name, Elf **elfp) { + int pid = -1; if (module_name[0] == '/') { /* When this callback is used together with dwfl_linux_proc_report then we might see mappings of special character devices. Make sure we only open and return regular files. Special devices - might hang on open or read. */ + might hang on open or read. (deleted) files are super special. + The image might come from memory if we are attached. */ struct stat sb; if (stat (module_name, &sb) == -1 || (sb.st_mode & S_IFMT) != S_IFREG) - return -1; + { + if (strcmp (strrchr (module_name, ' ') ?: "", " (deleted)") == 0) + pid = INTUSE(dwfl_pid) (mod->dwfl); + else + return -1; + } - int fd = open64 (module_name, O_RDONLY); - if (fd >= 0) + if (pid == -1) { - *file_name = strdup (module_name); - if (*file_name == NULL) + int fd = open64 (module_name, O_RDONLY); + if (fd >= 0) { - close (fd); - return ENOMEM; + *file_name = strdup (module_name); + if (*file_name == NULL) + { + close (fd); + return ENOMEM; + } } + return fd; } - return fd; } - int pid; - if (sscanf (module_name, "[vdso: %d]", &pid) == 1) + if (pid != -1 || sscanf (module_name, "[vdso: %d]", &pid) == 1) { /* Special case for in-memory ELF image. */ + bool detach = false; + bool tid_was_stopped = false; + struct pid_arg *pid_arg = __libdwfl_get_pid_arg (mod->dwfl); + if (pid_arg != NULL && ! pid_arg->assume_ptrace_stopped) + { + pid_t tid = pid_arg->tid_attached; + if (tid != 0) + { + /* If the pid already is attached we are fine, otherwise + just read through the thread that is attached. */ + if (tid != pid) + pid = tid; + } + else + detach = __libdwfl_ptrace_attach (pid, &tid_was_stopped); + } + char *fname; if (asprintf (&fname, PROCMEMFMT, pid) < 0) return -1; @@ -381,6 +407,9 @@ dwfl_linux_proc_find_elf (Dwfl_Module *mod __attribute__ ((unused)), close (fd); + if (detach) + __libdwfl_ptrace_detach (pid, tid_was_stopped); + *file_name = NULL; return -1; } -- 1.7.1