Commit 1e9fa730163c2a445014ff8324b169cd82a50df1
Committed by
Paul Brook
1 parent
4548eaea
fix gdbstub support for multiple threads in usermode, v3
When debugging multi-threaded programs, QEMU's gdb stub would report the correct number of threads (the qfThreadInfo and qsThreadInfo packets). However, the stub was unable to actually switch between threads (the T packet), since it would report every thread except the first as being dead. Furthermore, the stub relied upon cpu_index as a reliable means of assigning IDs to the threads. This was a bad idea; if you have this sequence of events: initial thread created new thread #1 new thread #2 thread #1 exits new thread #3 thread #3 will have the same cpu_index as thread #1, which would confuse GDB. (This problem is partly due to the remote protocol not having a good way to send thread creation/destruction events.) We fix this by using the host thread ID for the identifier passed to GDB when debugging a multi-threaded userspace program. The thread ID might wrap, but the same sort of problems with wrapping thread IDs would come up with debugging programs natively, so this doesn't represent a problem. Signed-off-by: Nathan Froyd <froydnj@codesourcery.com>
Showing
4 changed files
with
49 additions
and
27 deletions
cpu-defs.h
... | ... | @@ -184,6 +184,7 @@ typedef struct CPUWatchpoint { |
184 | 184 | \ |
185 | 185 | CPUState *next_cpu; /* next CPU sharing TB cache */ \ |
186 | 186 | int cpu_index; /* CPU index (informative) */ \ |
187 | + uint32_t host_tid; /* host thread ID */ \ | |
187 | 188 | int numa_node; /* NUMA node this cpu is belonging to */ \ |
188 | 189 | int running; /* Nonzero if cpu is currently running(usermode). */ \ |
189 | 190 | /* user data */ \ | ... | ... |
exec.c
gdbstub.c
... | ... | @@ -1567,11 +1567,34 @@ static void gdb_set_cpu_pc(GDBState *s, target_ulong pc) |
1567 | 1567 | #endif |
1568 | 1568 | } |
1569 | 1569 | |
1570 | +static inline int gdb_id(CPUState *env) | |
1571 | +{ | |
1572 | +#if defined(CONFIG_USER_ONLY) && defined(USE_NPTL) | |
1573 | + return env->host_tid; | |
1574 | +#else | |
1575 | + return env->cpu_index + 1; | |
1576 | +#endif | |
1577 | +} | |
1578 | + | |
1579 | +static CPUState *find_cpu(uint32_t thread_id) | |
1580 | +{ | |
1581 | + CPUState *env; | |
1582 | + | |
1583 | + for (env = first_cpu; env != NULL; env = env->next_cpu) { | |
1584 | + if (gdb_id(env) == thread_id) { | |
1585 | + return env; | |
1586 | + } | |
1587 | + } | |
1588 | + | |
1589 | + return NULL; | |
1590 | +} | |
1591 | + | |
1570 | 1592 | static int gdb_handle_packet(GDBState *s, const char *line_buf) |
1571 | 1593 | { |
1572 | 1594 | CPUState *env; |
1573 | 1595 | const char *p; |
1574 | - int ch, reg_size, type, res, thread; | |
1596 | + uint32_t thread; | |
1597 | + int ch, reg_size, type, res; | |
1575 | 1598 | char buf[MAX_PACKET_LENGTH]; |
1576 | 1599 | uint8_t mem_buf[MAX_PACKET_LENGTH]; |
1577 | 1600 | uint8_t *registers; |
... | ... | @@ -1586,7 +1609,7 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) |
1586 | 1609 | case '?': |
1587 | 1610 | /* TODO: Make this return the correct value for user-mode. */ |
1588 | 1611 | snprintf(buf, sizeof(buf), "T%02xthread:%02x;", GDB_SIGNAL_TRAP, |
1589 | - s->c_cpu->cpu_index+1); | |
1612 | + gdb_id(s->c_cpu)); | |
1590 | 1613 | put_packet(s, buf); |
1591 | 1614 | /* Remove all the breakpoints when this query is issued, |
1592 | 1615 | * because gdb is doing and initial connect and the state |
... | ... | @@ -1750,9 +1773,7 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) |
1750 | 1773 | put_packet(s, "OK"); |
1751 | 1774 | break; |
1752 | 1775 | } |
1753 | - for (env = first_cpu; env != NULL; env = env->next_cpu) | |
1754 | - if (env->cpu_index + 1 == thread) | |
1755 | - break; | |
1776 | + env = find_cpu(thread); | |
1756 | 1777 | if (env == NULL) { |
1757 | 1778 | put_packet(s, "E22"); |
1758 | 1779 | break; |
... | ... | @@ -1773,14 +1794,13 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) |
1773 | 1794 | break; |
1774 | 1795 | case 'T': |
1775 | 1796 | thread = strtoull(p, (char **)&p, 16); |
1776 | -#ifndef CONFIG_USER_ONLY | |
1777 | - if (thread > 0 && thread < smp_cpus + 1) | |
1778 | -#else | |
1779 | - if (thread == 1) | |
1780 | -#endif | |
1781 | - put_packet(s, "OK"); | |
1782 | - else | |
1797 | + env = find_cpu(thread); | |
1798 | + | |
1799 | + if (env != NULL) { | |
1800 | + put_packet(s, "OK"); | |
1801 | + } else { | |
1783 | 1802 | put_packet(s, "E22"); |
1803 | + } | |
1784 | 1804 | break; |
1785 | 1805 | case 'q': |
1786 | 1806 | case 'Q': |
... | ... | @@ -1818,7 +1838,7 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) |
1818 | 1838 | } else if (strcmp(p,"sThreadInfo") == 0) { |
1819 | 1839 | report_cpuinfo: |
1820 | 1840 | if (s->query_cpu) { |
1821 | - snprintf(buf, sizeof(buf), "m%x", s->query_cpu->cpu_index+1); | |
1841 | + snprintf(buf, sizeof(buf), "m%x", gdb_id(s->query_cpu)); | |
1822 | 1842 | put_packet(s, buf); |
1823 | 1843 | s->query_cpu = s->query_cpu->next_cpu; |
1824 | 1844 | } else |
... | ... | @@ -1826,16 +1846,15 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) |
1826 | 1846 | break; |
1827 | 1847 | } else if (strncmp(p,"ThreadExtraInfo,", 16) == 0) { |
1828 | 1848 | thread = strtoull(p+16, (char **)&p, 16); |
1829 | - for (env = first_cpu; env != NULL; env = env->next_cpu) | |
1830 | - if (env->cpu_index + 1 == thread) { | |
1831 | - cpu_synchronize_state(env, 0); | |
1832 | - len = snprintf((char *)mem_buf, sizeof(mem_buf), | |
1833 | - "CPU#%d [%s]", env->cpu_index, | |
1834 | - env->halted ? "halted " : "running"); | |
1835 | - memtohex(buf, mem_buf, len); | |
1836 | - put_packet(s, buf); | |
1837 | - break; | |
1838 | - } | |
1849 | + env = find_cpu(thread); | |
1850 | + if (env != NULL) { | |
1851 | + cpu_synchronize_state(env, 0); | |
1852 | + len = snprintf((char *)mem_buf, sizeof(mem_buf), | |
1853 | + "CPU#%d [%s]", env->cpu_index, | |
1854 | + env->halted ? "halted " : "running"); | |
1855 | + memtohex(buf, mem_buf, len); | |
1856 | + put_packet(s, buf); | |
1857 | + } | |
1839 | 1858 | break; |
1840 | 1859 | } |
1841 | 1860 | #ifdef CONFIG_USER_ONLY |
... | ... | @@ -1965,7 +1984,7 @@ static void gdb_vm_state_change(void *opaque, int running, int reason) |
1965 | 1984 | } |
1966 | 1985 | snprintf(buf, sizeof(buf), |
1967 | 1986 | "T%02xthread:%02x;%swatch:" TARGET_FMT_lx ";", |
1968 | - GDB_SIGNAL_TRAP, env->cpu_index+1, type, | |
1987 | + GDB_SIGNAL_TRAP, gdb_id(env), type, | |
1969 | 1988 | env->watchpoint_hit->vaddr); |
1970 | 1989 | put_packet(s, buf); |
1971 | 1990 | env->watchpoint_hit = NULL; |
... | ... | @@ -1976,7 +1995,7 @@ static void gdb_vm_state_change(void *opaque, int running, int reason) |
1976 | 1995 | } else { |
1977 | 1996 | ret = GDB_SIGNAL_INT; |
1978 | 1997 | } |
1979 | - snprintf(buf, sizeof(buf), "T%02xthread:%02x;", ret, env->cpu_index+1); | |
1998 | + snprintf(buf, sizeof(buf), "T%02xthread:%02x;", ret, gdb_id(env)); | |
1980 | 1999 | put_packet(s, buf); |
1981 | 2000 | } |
1982 | 2001 | #endif | ... | ... |
linux-user/syscall.c
... | ... | @@ -3202,6 +3202,7 @@ static void *clone_func(void *arg) |
3202 | 3202 | env = info->env; |
3203 | 3203 | thread_env = env; |
3204 | 3204 | info->tid = gettid(); |
3205 | + env->host_tid = info->tid; | |
3205 | 3206 | if (info->child_tidptr) |
3206 | 3207 | put_user_u32(info->tid, info->child_tidptr); |
3207 | 3208 | if (info->parent_tidptr) |
... | ... | @@ -3792,6 +3793,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, |
3792 | 3793 | /* FIXME: This probably breaks if a signal arrives. We should probably |
3793 | 3794 | be disabling signals. */ |
3794 | 3795 | if (first_cpu->next_cpu) { |
3796 | + TaskState *ts; | |
3795 | 3797 | CPUState **lastp; |
3796 | 3798 | CPUState *p; |
3797 | 3799 | |
... | ... | @@ -3809,7 +3811,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, |
3809 | 3811 | /* Remove the CPU from the list. */ |
3810 | 3812 | *lastp = p->next_cpu; |
3811 | 3813 | cpu_list_unlock(); |
3812 | - TaskState *ts = ((CPUState *)cpu_env)->opaque; | |
3814 | + ts = ((CPUState *)cpu_env)->opaque; | |
3813 | 3815 | if (ts->child_tidptr) { |
3814 | 3816 | put_user_u32(0, ts->child_tidptr); |
3815 | 3817 | sys_futex(g2h(ts->child_tidptr), FUTEX_WAKE, INT_MAX, | ... | ... |