This is the mail archive of the
elfutils-devel@sourceware.org
mailing list for the elfutils project.
Re: [patch 0/3] Live PIDs with deleted files by /dev/PID/mem
- From: Mark Wielaard <mjw at redhat dot com>
- To: elfutils-devel at lists dot fedorahosted dot org
- Date: Tue, 04 Mar 2014 11:40:21 +0100
- Subject: Re: [patch 0/3] Live PIDs with deleted files by /dev/PID/mem
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 <mjw@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 <mjw@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 <mjw@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 <mjw@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