diff mbox

[1/1] scripts/qemugdb/coroutine.py: get pthread pointer from '(gdb) thread info $id' output

Message ID 20170314100838.12647-2-roman.penyaev@profitbricks.com
State New
Headers show

Commit Message

Roman Pen March 14, 2017, 10:08 a.m. UTC
This is a first step towards coroutines debugging using corefiles.
It is much simpler to follow single path and always parse the line

 * 1    Thread 0x7f4475e33700 (LWP 7806)
               ^^^^^^^^^^^^^^

of a '(gdb) thread info $id' and get pthread pointer instead of rely
on libc debugging information, which is not always the case.

For sure under corefile debugging it is not possible to invoke
any syscalls, like arch_prctl(), so avoid doing that.  That will
simplify the script.

The other problem which is left unsolved for coroutines debugging
using corefiles is gdb restriction to modify registers (that is
only possible for live process, not for a corefile).  This problem
is solved in the next patch for a gdb project itself.

Signed-off-by: Roman Pen <roman.penyaev@profitbricks.com>
Cc: Stefan Hajnoczi <stefanha@redhat.com
Cc: Paolo Bonzini <pbonzini@redhat.com>
Cc: qemu-devel@nongnu.org
---
 scripts/qemugdb/coroutine.py | 29 ++++++++++-------------------
 1 file changed, 10 insertions(+), 19 deletions(-)

Comments

Paolo Bonzini March 14, 2017, 10:21 a.m. UTC | #1
On 14/03/2017 11:08, Roman Pen wrote:
> For sure under corefile debugging it is not possible to invoke
> any syscalls, like arch_prctl(), so avoid doing that.  That will
> simplify the script.

Is the issue that start_thread only works in the main thread stack (i.e.
not on coroutines)?

> +    num = gdb.selected_thread().num
> +    thr = gdb.execute('info thread %d' % num, to_string=True)

What versions of gdb support gdb.execute?  Also, what happens if
localization is in effect?

Paolo

> +    thr = thr.split('\n')[1]
>      try:
> -        return f.read_var("arg")
> -    except ValueError:
> -        return get_fs_base()
> +        return re.search('Thread ([0-9a-zx]+)', thr).group(1)
> +    except:
> +        raise ValueError("Unable to find pthread address in 'info thread %d' output.\n"
> +                         "Probably version mismatch of libthread_db.so library?" %
> +                         num)
Roman Pen March 14, 2017, 11 a.m. UTC | #2
On Tue, Mar 14, 2017 at 11:21 AM, Paolo Bonzini <pbonzini@redhat.com> wrote:
>
>
> On 14/03/2017 11:08, Roman Pen wrote:
>> For sure under corefile debugging it is not possible to invoke
>> any syscalls, like arch_prctl(), so avoid doing that.  That will
>> simplify the script.
>
> Is the issue that start_thread only works in the main thread stack (i.e.
> not on coroutines)?

In my case I simply do not have libc6-dbg around.  And yes, what
you've mentioned is also the issue.

>
>> +    num = gdb.selected_thread().num
>> +    thr = gdb.execute('info thread %d' % num, to_string=True)
>
> What versions of gdb support gdb.execute?

Seems the beginning of python support in gdb (starting from gdb-7, right?).
At least git blame shows me this modification date on particular function:

d57a3c85f6eee (Thiago Jung Bauermann 2008-08-06 19:41:33 +0000 315)
execute_gdb_command (PyObject *self, PyObject *args)

and gdb-7 was released on October 06, 2009.

> Also, what happens if localization is in effect?

According to the gdb sources (I am not gdb developer, but this is quite
straightforward to find) this is not the issue.

What is the issue is that different targets output different lines.
Linux is always the same, no worries, take a look

   gdb/linux-thread-db.c: thread_db_pid_to_str():
        snprintf (buf, sizeof (buf), "Thread 0x%lx (LWP %ld)",

but e.g. bsd output should be different:

   gdb/bsd-uthread.c: bsd_uthread_pid_to_str():
        xsnprintf (buf, sizeof buf, "process %d, thread 0x%lx",

If this is the issue I can add third variant of getting correct
pthread_self() and make the priority:

  1. parse 'info thread'
  2. parse 'start_thread(arg=XXX)'
  3. go to arch_prctl() invocation.

But frankly this is far from simplification, from what I've started :)

--
Roman
Paolo Bonzini March 14, 2017, 11:10 a.m. UTC | #3
On 14/03/2017 12:00, Roman Penyaev wrote:
> Linux is always the same, no worries, take a look
> 
>    gdb/linux-thread-db.c: thread_db_pid_to_str():
>         snprintf (buf, sizeof (buf), "Thread 0x%lx (LWP %ld)",
> 
> but e.g. bsd output should be different:
> 
>    gdb/bsd-uthread.c: bsd_uthread_pid_to_str():
>         xsnprintf (buf, sizeof buf, "process %d, thread 0x%lx",
> 
> If this is the issue I can add third variant of getting correct
> pthread_self() and make the priority:
> 
>   1. parse 'info thread'
>   2. parse 'start_thread(arg=XXX)'
>   3. go to arch_prctl() invocation.
> 
> But frankly this is far from simplification, from what I've started :)

I think it's okay.  Absence of libc debugging symbols is not an issue,
but the existing code was Linux-specific anyway and your version has
some advantages for robustness.

Paolo
diff mbox

Patch

diff --git a/scripts/qemugdb/coroutine.py b/scripts/qemugdb/coroutine.py
index ab699794abf6..1cfe3cd97e80 100644
--- a/scripts/qemugdb/coroutine.py
+++ b/scripts/qemugdb/coroutine.py
@@ -14,31 +14,22 @@ 
 # GNU GPL, version 2 or (at your option) any later version.
 
 import gdb
+import re
 
 VOID_PTR = gdb.lookup_type('void').pointer()
 
-def get_fs_base():
-    '''Fetch %fs base value using arch_prctl(ARCH_GET_FS).  This is
-       pthread_self().'''
-    # %rsp - 120 is scratch space according to the SystemV ABI
-    old = gdb.parse_and_eval('*(uint64_t*)($rsp - 120)')
-    gdb.execute('call arch_prctl(0x1003, $rsp - 120)', False, True)
-    fs_base = gdb.parse_and_eval('*(uint64_t*)($rsp - 120)')
-    gdb.execute('set *(uint64_t*)($rsp - 120) = %s' % old, False, True)
-    return fs_base
-
 def pthread_self():
-    '''Fetch pthread_self() from the glibc start_thread function.'''
-    f = gdb.newest_frame()
-    while f.name() != 'start_thread':
-        f = f.older()
-        if f is None:
-            return get_fs_base()
+    '''Get pthread_self() from '(gdb) info thread $id' output'''
 
+    num = gdb.selected_thread().num
+    thr = gdb.execute('info thread %d' % num, to_string=True)
+    thr = thr.split('\n')[1]
     try:
-        return f.read_var("arg")
-    except ValueError:
-        return get_fs_base()
+        return re.search('Thread ([0-9a-zx]+)', thr).group(1)
+    except:
+        raise ValueError("Unable to find pthread address in 'info thread %d' output.\n"
+                         "Probably version mismatch of libthread_db.so library?" %
+                         num)
 
 def get_glibc_pointer_guard():
     '''Fetch glibc pointer guard value'''