diff mbox series

[v2,33/39] gdbserver: multithread debugging support

Message ID 20220420065013.222816-34-npiggin@gmail.com
State New
Headers show
Series gdbserver multi-threaded debugging and POWER9/10 support | expand

Commit Message

Nicholas Piggin April 20, 2022, 6:50 a.m. UTC
This implements the threadinfo gdb commands, and removes the
restriction of a single target.

Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
 README.md                   |  17 +-
 src/gdb_parser.rl           |  16 +-
 src/gdb_parser_precompile.c | 328 +++++++++++++++++++-----------------
 src/pdbgproxy.c             |  66 +++++++-
 src/pdbgproxy.h             |  10 +-
 5 files changed, 267 insertions(+), 170 deletions(-)

Comments

Joel Stanley May 3, 2022, 7:37 a.m. UTC | #1
On Wed, 20 Apr 2022 at 06:51, Nicholas Piggin <npiggin@gmail.com> wrote:
>
> This implements the threadinfo gdb commands, and removes the
> restriction of a single target.
>
> Signed-off-by: Nicholas Piggin <npiggin@gmail.com>

Reviewed-by: Joel Stanley <joel@jms.id.au>

> ---
>  README.md                   |  17 +-
>  src/gdb_parser.rl           |  16 +-
>  src/gdb_parser_precompile.c | 328 +++++++++++++++++++-----------------
>  src/pdbgproxy.c             |  66 +++++++-
>  src/pdbgproxy.h             |  10 +-
>  5 files changed, 267 insertions(+), 170 deletions(-)
>
> diff --git a/README.md b/README.md
> index c3447c35..778269a6 100644
> --- a/README.md
> +++ b/README.md
> @@ -538,13 +538,20 @@ At the moment gdbserver is only supported on P8 and P9 and P10.
>
>  Memory access can only be performed on kernel memory.
>
> -To run a gdbserver on a P8 machine from a BMC running openbmc:
> +To run a gdbserver on a machine from a BMC running OpenBMC:
>
> -Stop all the threads of the core you want to look at
> -$ ./pdbg -d p8 -c11 -a stop
> +Stop all the threads of the core(s) you want to look at. Ideally all
> +threads in the machine should be debugged:
>
> -Run gdbserver on thread 0 of core 11, accessible through port 44
> -$ ./pdbg -d p8 -p0 -c11 -t0 gdbserver 44
> +$ ./pdbg -a stop
> +
> +Run gdbserver on the target threads, accessible through port 44
> +$ ./pdbg -a gdbserver 44
> +
> +The thread-id tid is set to the PIR of the corresponding thread, the
> +hard_smp_processor_id. Be warned, "info threads" or other gdb operations
> +that iterate over all threads may be very slow when debugging a lot
> +of threads, especially over slow remote links.
>
>  On your local machine:
>  $ gdb
> diff --git a/src/gdb_parser.rl b/src/gdb_parser.rl
> index dc1275c3..abb361ae 100644
> --- a/src/gdb_parser.rl
> +++ b/src/gdb_parser.rl
> @@ -111,14 +111,15 @@
>
>         # TODO: We don't actually listen to what's supported
>         q_attached = ('qAttached:' xdigit* @{rsp = "1";});
> -       q_C = ('qC' @{rsp = "QC1";});
> -       q_supported = ('qSupported:' any* >{rsp = "multiprocess+;vContSupported+;QStartNoAckMode+"; ack_mode = true;});
> -       qf_threadinfo = ('qfThreadInfo' @{rsp = "m1l";});
> +       q_supported = ('qSupported:' any* >{rsp = "multiprocess+;swbreak+;hwbreak-;qRelocInsn-;vContSupported+;QThreadEvents-;no-resumed-;QStartNoAckMode+"; ack_mode = true;});
>         q_start_noack = ('QStartNoAckMode' @{rsp = "OK"; send_ack(priv); ack_mode = false;});
>
>         # thread info
> +       is_alive = ('T' ('p' xdigit+ '.')+ xdigit+ @{rsp = "OK";});
>         get_thread = ('qC' @{cmd = GET_THREAD;});
>         set_thread = ('Hg' ('p' xdigit+ '.')+ xdigit+ $hex_digit %push @{cmd = SET_THREAD;});
> +       qf_threadinfo = ('qfThreadInfo' @{cmd = QF_THREADINFO;});
> +       qs_threadinfo = ('qsThreadInfo' @{cmd = QS_THREADINFO;});
>
>         # vCont packet parsing
>         v_contq = ('vCont?' @{rsp = "vCont;c;C;s;S";});
> @@ -128,9 +129,12 @@
>
>         interrupt = (3 @{ if (command_callbacks) command_callbacks[INTERRUPT](stack, priv); PR_INFO("RAGEL:interrupt\n");});
>
> -       commands = (get_mem | get_gprs | get_spr | stop_reason | get_thread | set_thread |
> -                   q_attached | q_C | q_supported | qf_threadinfo | q_C |
> -                   q_start_noack | v_contq | v_contc | v_conts | put_mem |
> +       commands = (get_gprs | get_spr |
> +                   q_attached | q_supported | q_start_noack |
> +                   stop_reason | is_alive | get_thread | set_thread |
> +                   v_contq | v_contc | v_conts |
> +                   qf_threadinfo | qs_threadinfo |
> +                   get_mem | put_mem |
>                     detach | unknown );
>
>         cmd = (('$' ((commands & ^'#'*) >reset $crc)
> diff --git a/src/gdb_parser_precompile.c b/src/gdb_parser_precompile.c
> index 567f1dd6..13c89c9a 100644
> --- a/src/gdb_parser_precompile.c
> +++ b/src/gdb_parser_precompile.c
> @@ -11,7 +11,7 @@
>  #include "debug.h"
>
>
> -#line 145 "src/gdb_parser.rl"
> +#line 149 "src/gdb_parser.rl"
>
>
>  static enum gdb_command cmd = NONE;
> @@ -31,76 +31,86 @@ static bool ack_mode = true;
>  #line 32 "src/gdb_parser_precompile.c"
>  static const char _gdb_actions[] = {
>         0, 1, 0, 1, 1, 1, 2, 1,
> -       3, 1, 15, 1, 23, 1, 24, 1,
> -       25, 1, 26, 2, 0, 1, 2, 2,
> +       3, 1, 14, 1, 24, 1, 25, 1,
> +       26, 1, 27, 2, 0, 1, 2, 2,
>         1, 2, 3, 1, 2, 3, 5, 2,
> -       4, 1, 2, 13, 1, 2, 15, 1,
> -       2, 16, 1, 2, 17, 1, 2, 20,
> -       1, 2, 21, 1, 2, 22, 1, 3,
> -       0, 6, 1, 3, 0, 7, 1, 3,
> -       0, 9, 1, 3, 0, 10, 1, 3,
> -       0, 11, 1, 3, 0, 12, 1, 3,
> -       2, 8, 1, 3, 3, 19, 1, 3,
> -       18, 14, 1
> +       4, 1, 2, 13, 1, 2, 14, 1,
> +       2, 15, 1, 2, 16, 1, 2, 17,
> +       1, 2, 19, 1, 2, 20, 1, 2,
> +       21, 1, 2, 22, 1, 2, 23, 1,
> +       3, 0, 6, 1, 3, 0, 7, 1,
> +       3, 0, 9, 1, 3, 0, 10, 1,
> +       3, 0, 11, 1, 3, 0, 12, 1,
> +       3, 2, 8, 1, 3, 3, 18, 1
> +
>  };
>
> -static const unsigned char _gdb_key_offsets[] = {
> -       0, 0, 11, 12, 18, 24, 31, 38,
> -       40, 42, 49, 57, 65, 72, 79, 87,
> -       94, 102, 109, 111, 113, 115, 117, 119,
> -       121, 123, 125, 127, 129, 131, 133, 135,
> -       137, 144, 152, 157, 159, 161, 163, 165,
> -       167, 169, 171, 173, 180, 182, 184, 186,
> -       188, 190, 192, 194, 196, 198, 199, 201,
> -       203, 205, 207, 209, 211, 213, 215, 217,
> -       219, 221, 223, 225, 227, 230, 233, 234,
> -       235
> +static const short _gdb_key_offsets[] = {
> +       0, 0, 12, 13, 19, 25, 32, 39,
> +       41, 43, 50, 58, 66, 73, 80, 88,
> +       95, 103, 110, 112, 114, 116, 118, 120,
> +       122, 124, 126, 128, 130, 132, 134, 136,
> +       138, 140, 147, 155, 163, 170, 177, 185,
> +       191, 193, 195, 197, 199, 201, 203, 205,
> +       207, 214, 216, 218, 220, 222, 224, 226,
> +       228, 230, 232, 233, 235, 237, 239, 241,
> +       243, 245, 247, 249, 251, 253, 255, 257,
> +       259, 261, 263, 265, 267, 269, 271, 273,
> +       275, 277, 279, 281, 284, 287, 288, 289
>  };
>
>  static const char _gdb_trans_keys[] = {
> -       35, 63, 68, 72, 77, 81, 103, 109,
> -       112, 113, 118, 35, 48, 57, 65, 70,
> -       97, 102, 48, 57, 65, 70, 97, 102,
> -       35, 48, 57, 65, 70, 97, 102, 35,
> -       48, 57, 65, 70, 97, 102, 35, 103,
> -       35, 112, 35, 48, 57, 65, 70, 97,
> -       102, 35, 46, 48, 57, 65, 70, 97,
> -       102, 35, 112, 48, 57, 65, 70, 97,
> +       35, 63, 68, 72, 77, 81, 84, 103,
> +       109, 112, 113, 118, 35, 48, 57, 65,
> +       70, 97, 102, 48, 57, 65, 70, 97,
>         102, 35, 48, 57, 65, 70, 97, 102,
>         35, 48, 57, 65, 70, 97, 102, 35,
> -       44, 48, 57, 65, 70, 97, 102, 35,
> -       48, 57, 65, 70, 97, 102, 35, 58,
> -       48, 57, 65, 70, 97, 102, 35, 48,
> -       57, 65, 70, 97, 102, 35, 83, 35,
> -       116, 35, 97, 35, 114, 35, 116, 35,
> -       78, 35, 111, 35, 65, 35, 99, 35,
> -       107, 35, 77, 35, 111, 35, 100, 35,
> -       101, 35, 48, 57, 65, 70, 97, 102,
> +       103, 35, 112, 35, 48, 57, 65, 70,
> +       97, 102, 35, 46, 48, 57, 65, 70,
> +       97, 102, 35, 112, 48, 57, 65, 70,
> +       97, 102, 35, 48, 57, 65, 70, 97,
> +       102, 35, 48, 57, 65, 70, 97, 102,
>         35, 44, 48, 57, 65, 70, 97, 102,
> -       35, 65, 67, 83, 102, 35, 116, 35,
> -       116, 35, 97, 35, 99, 35, 104, 35,
> -       101, 35, 100, 35, 58, 35, 48, 57,
> -       65, 70, 97, 102, 35, 117, 35, 112,
> -       35, 112, 35, 111, 35, 114, 35, 116,
> -       35, 101, 35, 100, 35, 58, 35, 35,
> -       84, 35, 104, 35, 114, 35, 101, 35,
> -       97, 35, 100, 35, 73, 35, 110, 35,
> -       102, 35, 111, 35, 67, 35, 111, 35,
> -       110, 35, 116, 35, 59, 63, 35, 99,
> -       115, 35, 35, 3, 36, 43, 45, 0
> +       35, 48, 57, 65, 70, 97, 102, 35,
> +       58, 48, 57, 65, 70, 97, 102, 35,
> +       48, 57, 65, 70, 97, 102, 35, 83,
> +       35, 116, 35, 97, 35, 114, 35, 116,
> +       35, 78, 35, 111, 35, 65, 35, 99,
> +       35, 107, 35, 77, 35, 111, 35, 100,
> +       35, 101, 35, 112, 35, 48, 57, 65,
> +       70, 97, 102, 35, 46, 48, 57, 65,
> +       70, 97, 102, 35, 112, 48, 57, 65,
> +       70, 97, 102, 35, 48, 57, 65, 70,
> +       97, 102, 35, 48, 57, 65, 70, 97,
> +       102, 35, 44, 48, 57, 65, 70, 97,
> +       102, 35, 65, 67, 83, 102, 115, 35,
> +       116, 35, 116, 35, 97, 35, 99, 35,
> +       104, 35, 101, 35, 100, 35, 58, 35,
> +       48, 57, 65, 70, 97, 102, 35, 117,
> +       35, 112, 35, 112, 35, 111, 35, 114,
> +       35, 116, 35, 101, 35, 100, 35, 58,
> +       35, 35, 84, 35, 104, 35, 114, 35,
> +       101, 35, 97, 35, 100, 35, 73, 35,
> +       110, 35, 102, 35, 111, 35, 84, 35,
> +       104, 35, 114, 35, 101, 35, 97, 35,
> +       100, 35, 73, 35, 110, 35, 102, 35,
> +       111, 35, 67, 35, 111, 35, 110, 35,
> +       116, 35, 59, 63, 35, 99, 115, 35,
> +       35, 3, 36, 43, 45, 0
>  };
>
>  static const char _gdb_single_lengths[] = {
> -       0, 11, 1, 0, 0, 1, 1, 2,
> +       0, 12, 1, 0, 0, 1, 1, 2,
>         2, 1, 2, 2, 1, 1, 2, 1,
>         2, 1, 2, 2, 2, 2, 2, 2,
>         2, 2, 2, 2, 2, 2, 2, 2,
> -       1, 2, 5, 2, 2, 2, 2, 2,
> -       2, 2, 2, 1, 2, 2, 2, 2,
> -       2, 2, 2, 2, 2, 1, 2, 2,
> +       2, 1, 2, 2, 1, 1, 2, 6,
>         2, 2, 2, 2, 2, 2, 2, 2,
> -       2, 2, 2, 2, 3, 3, 1, 1,
> -       4
> +       1, 2, 2, 2, 2, 2, 2, 2,
> +       2, 2, 1, 2, 2, 2, 2, 2,
> +       2, 2, 2, 2, 2, 2, 2, 2,
> +       2, 2, 2, 2, 2, 2, 2, 2,
> +       2, 2, 2, 3, 3, 1, 1, 4
>  };
>
>  static const char _gdb_range_lengths[] = {
> @@ -108,111 +118,125 @@ static const char _gdb_range_lengths[] = {
>         0, 3, 3, 3, 3, 3, 3, 3,
>         3, 3, 0, 0, 0, 0, 0, 0,
>         0, 0, 0, 0, 0, 0, 0, 0,
> -       3, 3, 0, 0, 0, 0, 0, 0,
> -       0, 0, 0, 3, 0, 0, 0, 0,
> +       0, 3, 3, 3, 3, 3, 3, 0,
>         0, 0, 0, 0, 0, 0, 0, 0,
> +       3, 0, 0, 0, 0, 0, 0, 0,
>         0, 0, 0, 0, 0, 0, 0, 0,
>         0, 0, 0, 0, 0, 0, 0, 0,
> -       0
> +       0, 0, 0, 0, 0, 0, 0, 0,
> +       0, 0, 0, 0, 0, 0, 0, 0
>  };
>
>  static const short _gdb_index_offsets[] = {
> -       0, 0, 12, 14, 18, 22, 27, 32,
> -       35, 38, 43, 49, 55, 60, 65, 71,
> -       76, 82, 87, 90, 93, 96, 99, 102,
> -       105, 108, 111, 114, 117, 120, 123, 126,
> -       129, 134, 140, 146, 149, 152, 155, 158,
> -       161, 164, 167, 170, 175, 178, 181, 184,
> -       187, 190, 193, 196, 199, 202, 204, 207,
> -       210, 213, 216, 219, 222, 225, 228, 231,
> -       234, 237, 240, 243, 246, 250, 254, 256,
> -       258
> +       0, 0, 13, 15, 19, 23, 28, 33,
> +       36, 39, 44, 50, 56, 61, 66, 72,
> +       77, 83, 88, 91, 94, 97, 100, 103,
> +       106, 109, 112, 115, 118, 121, 124, 127,
> +       130, 133, 138, 144, 150, 155, 160, 166,
> +       173, 176, 179, 182, 185, 188, 191, 194,
> +       197, 202, 205, 208, 211, 214, 217, 220,
> +       223, 226, 229, 231, 234, 237, 240, 243,
> +       246, 249, 252, 255, 258, 261, 264, 267,
> +       270, 273, 276, 279, 282, 285, 288, 291,
> +       294, 297, 300, 303, 307, 311, 313, 315
>  };
>
>  static const char _gdb_indicies[] = {
>         1, 2, 3, 4, 5, 6, 7, 8,
> -       9, 10, 11, 0, 13, 12, 14, 14,
> -       14, 15, 16, 16, 16, 15, 13, 17,
> -       17, 17, 12, 18, 17, 17, 17, 12,
> -       13, 19, 12, 13, 20, 12, 13, 21,
> -       21, 21, 12, 13, 22, 21, 21, 21,
> -       12, 13, 20, 23, 23, 23, 12, 18,
> -       23, 23, 23, 12, 13, 24, 24, 24,
> -       12, 13, 25, 24, 24, 24, 12, 13,
> -       26, 26, 26, 12, 13, 27, 26, 26,
> -       26, 12, 13, 28, 28, 28, 12, 13,
> -       29, 12, 13, 30, 12, 13, 31, 12,
> -       13, 32, 12, 13, 33, 12, 13, 34,
> -       12, 13, 35, 12, 13, 36, 12, 13,
> -       37, 12, 13, 38, 12, 13, 39, 12,
> -       13, 40, 12, 13, 41, 12, 13, 42,
> -       12, 13, 43, 43, 43, 12, 13, 44,
> -       43, 43, 43, 12, 13, 45, 46, 47,
> -       48, 12, 13, 49, 12, 13, 50, 12,
> -       13, 51, 12, 13, 52, 12, 13, 53,
> -       12, 13, 54, 12, 13, 55, 12, 13,
> -       56, 12, 13, 57, 57, 57, 12, 13,
> -       58, 12, 13, 59, 12, 13, 60, 12,
> -       13, 61, 12, 13, 62, 12, 13, 63,
> -       12, 13, 64, 12, 13, 65, 12, 13,
> -       66, 12, 68, 67, 13, 69, 12, 13,
> -       70, 12, 13, 71, 12, 13, 72, 12,
> -       13, 73, 12, 13, 74, 12, 13, 75,
> -       12, 13, 76, 12, 13, 77, 12, 13,
> -       78, 12, 13, 79, 12, 13, 80, 12,
> -       13, 81, 12, 13, 82, 12, 13, 83,
> -       84, 12, 13, 85, 86, 12, 13, 87,
> -       13, 88, 89, 90, 91, 92, 15, 0
> +       9, 10, 11, 12, 0, 14, 13, 15,
> +       15, 15, 16, 17, 17, 17, 16, 14,
> +       18, 18, 18, 13, 19, 18, 18, 18,
> +       13, 14, 20, 13, 14, 21, 13, 14,
> +       22, 22, 22, 13, 14, 23, 22, 22,
> +       22, 13, 14, 21, 24, 24, 24, 13,
> +       19, 24, 24, 24, 13, 14, 25, 25,
> +       25, 13, 14, 26, 25, 25, 25, 13,
> +       14, 27, 27, 27, 13, 14, 28, 27,
> +       27, 27, 13, 14, 29, 29, 29, 13,
> +       14, 30, 13, 14, 31, 13, 14, 32,
> +       13, 14, 33, 13, 14, 34, 13, 14,
> +       35, 13, 14, 36, 13, 14, 37, 13,
> +       14, 38, 13, 14, 39, 13, 14, 40,
> +       13, 14, 41, 13, 14, 42, 13, 14,
> +       43, 13, 14, 44, 13, 14, 45, 45,
> +       45, 13, 14, 46, 45, 45, 45, 13,
> +       14, 44, 47, 47, 47, 13, 14, 47,
> +       47, 47, 13, 14, 48, 48, 48, 13,
> +       14, 49, 48, 48, 48, 13, 14, 50,
> +       51, 52, 53, 54, 13, 14, 55, 13,
> +       14, 56, 13, 14, 57, 13, 14, 58,
> +       13, 14, 59, 13, 14, 60, 13, 14,
> +       61, 13, 14, 62, 13, 14, 63, 63,
> +       63, 13, 14, 64, 13, 14, 65, 13,
> +       14, 66, 13, 14, 67, 13, 14, 68,
> +       13, 14, 69, 13, 14, 70, 13, 14,
> +       71, 13, 14, 72, 13, 74, 73, 14,
> +       75, 13, 14, 76, 13, 14, 77, 13,
> +       14, 78, 13, 14, 79, 13, 14, 80,
> +       13, 14, 81, 13, 14, 82, 13, 14,
> +       83, 13, 14, 84, 13, 14, 85, 13,
> +       14, 86, 13, 14, 87, 13, 14, 88,
> +       13, 14, 89, 13, 14, 90, 13, 14,
> +       91, 13, 14, 92, 13, 14, 93, 13,
> +       14, 94, 13, 14, 95, 13, 14, 96,
> +       13, 14, 97, 13, 14, 98, 13, 14,
> +       99, 100, 13, 14, 101, 102, 13, 14,
> +       103, 14, 104, 105, 106, 107, 108, 16,
> +       0
>  };
>
>  static const char _gdb_trans_targs[] = {
> -       2, 3, 2, 5, 7, 13, 18, 2,
> -       32, 5, 34, 64, 2, 3, 4, 0,
> -       72, 6, 3, 8, 9, 10, 11, 12,
> -       14, 15, 16, 17, 17, 19, 20, 21,
> -       22, 23, 24, 25, 26, 27, 28, 29,
> -       30, 31, 2, 33, 5, 35, 2, 44,
> -       54, 36, 37, 38, 39, 40, 41, 42,
> -       43, 43, 45, 46, 47, 48, 49, 50,
> -       51, 52, 53, 2, 3, 55, 56, 57,
> -       58, 59, 60, 61, 62, 63, 2, 65,
> -       66, 67, 68, 69, 2, 70, 71, 70,
> -       71, 72, 1, 72, 72
> +       2, 3, 2, 5, 7, 13, 18, 32,
> +       2, 37, 5, 39, 79, 2, 3, 4,
> +       0, 87, 6, 3, 8, 9, 10, 11,
> +       12, 14, 15, 16, 17, 17, 19, 20,
> +       21, 22, 23, 24, 25, 26, 27, 28,
> +       29, 30, 31, 2, 33, 34, 35, 36,
> +       38, 5, 40, 2, 49, 59, 69, 41,
> +       42, 43, 44, 45, 46, 47, 48, 48,
> +       50, 51, 52, 53, 54, 55, 56, 57,
> +       58, 2, 3, 60, 61, 62, 63, 64,
> +       65, 66, 67, 68, 2, 70, 71, 72,
> +       73, 74, 75, 76, 77, 78, 2, 80,
> +       81, 82, 83, 84, 2, 85, 86, 85,
> +       86, 87, 1, 87, 87
>  };
>
>  static const char _gdb_trans_actions[] = {
> -       19, 1, 71, 75, 19, 59, 19, 63,
> -       55, 67, 19, 19, 3, 0, 7, 0,
> -       28, 25, 5, 3, 3, 3, 3, 83,
> -       25, 22, 25, 79, 31, 3, 3, 3,
> +       19, 1, 80, 84, 19, 68, 19, 19,
> +       72, 64, 76, 19, 19, 3, 0, 7,
> +       0, 28, 25, 5, 3, 3, 3, 3,
> +       92, 25, 22, 25, 88, 31, 3, 3,
>         3, 3, 3, 3, 3, 3, 3, 3,
> -       3, 3, 43, 25, 22, 3, 87, 3,
> +       3, 3, 3, 40, 3, 3, 3, 43,
> +       25, 22, 3, 46, 3, 3, 3, 3,
> +       3, 3, 3, 3, 3, 3, 3, 34,
>         3, 3, 3, 3, 3, 3, 3, 3,
> -       3, 34, 3, 3, 3, 3, 3, 3,
> -       3, 3, 3, 37, 9, 3, 3, 3,
> -       3, 3, 3, 3, 3, 3, 40, 3,
> -       3, 3, 3, 3, 46, 3, 3, 49,
> -       52, 11, 13, 15, 17
> +       3, 37, 9, 3, 3, 3, 3, 3,
> +       3, 3, 3, 3, 49, 3, 3, 3,
> +       3, 3, 3, 3, 3, 3, 52, 3,
> +       3, 3, 3, 3, 55, 3, 3, 58,
> +       61, 11, 13, 15, 17
>  };
>
> -static const int gdb_start = 72;
> -static const int gdb_first_final = 72;
> +static const int gdb_start = 87;
> +static const int gdb_first_final = 87;
>  static const int gdb_error = 0;
>
> -static const int gdb_en_main = 72;
> +static const int gdb_en_main = 87;
>
>
> -#line 161 "src/gdb_parser.rl"
> +#line 165 "src/gdb_parser.rl"
>
>  void parser_init(command_cb *callbacks)
>  {
>
> -#line 211 "src/gdb_parser_precompile.c"
> +#line 235 "src/gdb_parser_precompile.c"
>         {
>         cs = gdb_start;
>         }
>
> -#line 165 "src/gdb_parser.rl"
> +#line 169 "src/gdb_parser.rl"
>
>         command_callbacks = callbacks;
>  }
> @@ -223,7 +247,7 @@ int parse_buffer(char *buf, size_t len, void *priv)
>         char *pe = p + len;
>
>
> -#line 227 "src/gdb_parser_precompile.c"
> +#line 251 "src/gdb_parser_precompile.c"
>         {
>         int _klen;
>         unsigned int _trans;
> @@ -418,57 +442,61 @@ _match:
>         break;
>         case 14:
>  #line 114 "src/gdb_parser.rl"
> -       {rsp = "QC1";}
> +       {rsp = "multiprocess+;swbreak+;hwbreak-;qRelocInsn-;vContSupported+;QThreadEvents-;no-resumed-;QStartNoAckMode+"; ack_mode = true;}
>         break;
>         case 15:
>  #line 115 "src/gdb_parser.rl"
> -       {rsp = "multiprocess+;vContSupported+;QStartNoAckMode+"; ack_mode = true;}
> +       {rsp = "OK"; send_ack(priv); ack_mode = false;}
>         break;
>         case 16:
> -#line 116 "src/gdb_parser.rl"
> -       {rsp = "m1l";}
> +#line 118 "src/gdb_parser.rl"
> +       {rsp = "OK";}
>         break;
>         case 17:
> -#line 117 "src/gdb_parser.rl"
> -       {rsp = "OK"; send_ack(priv); ack_mode = false;}
> +#line 119 "src/gdb_parser.rl"
> +       {cmd = GET_THREAD;}
>         break;
>         case 18:
>  #line 120 "src/gdb_parser.rl"
> -       {cmd = GET_THREAD;}
> +       {cmd = SET_THREAD;}
>         break;
>         case 19:
>  #line 121 "src/gdb_parser.rl"
> -       {cmd = SET_THREAD;}
> +       {cmd = QF_THREADINFO;}
>         break;
>         case 20:
> -#line 124 "src/gdb_parser.rl"
> -       {rsp = "vCont;c;C;s;S";}
> +#line 122 "src/gdb_parser.rl"
> +       {cmd = QS_THREADINFO;}
>         break;
>         case 21:
>  #line 125 "src/gdb_parser.rl"
> -       {cmd = V_CONTC;}
> +       {rsp = "vCont;c;C;s;S";}
>         break;
>         case 22:
>  #line 126 "src/gdb_parser.rl"
> -       {cmd = V_CONTS;}
> +       {cmd = V_CONTC;}
>         break;
>         case 23:
> -#line 129 "src/gdb_parser.rl"
> -       { if (command_callbacks) command_callbacks[INTERRUPT](stack, priv); PR_INFO("RAGEL:interrupt\n");}
> +#line 127 "src/gdb_parser.rl"
> +       {cmd = V_CONTS;}
>         break;
>         case 24:
> -#line 137 "src/gdb_parser.rl"
> -       {PR_INFO("RAGEL:cmd\n");}
> +#line 130 "src/gdb_parser.rl"
> +       { if (command_callbacks) command_callbacks[INTERRUPT](stack, priv); PR_INFO("RAGEL:interrupt\n");}
>         break;
>         case 25:
> -#line 140 "src/gdb_parser.rl"
> -       {PR_INFO("RAGEL:ack\n");}
> +#line 141 "src/gdb_parser.rl"
> +       {PR_INFO("RAGEL:cmd\n");}
>         break;
>         case 26:
> -#line 141 "src/gdb_parser.rl"
> +#line 144 "src/gdb_parser.rl"
> +       {PR_INFO("RAGEL:ack\n");}
> +       break;
> +       case 27:
> +#line 145 "src/gdb_parser.rl"
>         {PR_INFO("RAGEL:nack\n");}
>         break;
> -#line 472 "src/gdb_parser_precompile.c"
> +#line 500 "src/gdb_parser_precompile.c"
>                 }
>         }
>
> @@ -481,7 +509,7 @@ _again:
>         _out: {}
>         }
>
> -#line 175 "src/gdb_parser.rl"
> +#line 179 "src/gdb_parser.rl"
>
>         if (cs == gdb_error) {
>                 printf("parse error\n");
> diff --git a/src/pdbgproxy.c b/src/pdbgproxy.c
> index 52f34a91..1d24e68a 100644
> --- a/src/pdbgproxy.c
> +++ b/src/pdbgproxy.c
> @@ -142,6 +142,58 @@ static void set_thread(uint64_t *stack, void *priv)
>         send_response(fd, ERROR(EEXIST));
>  }
>
> +static int threadinfo_iterator;
> +
> +static void qs_threadinfo(uint64_t *stack, void *priv)
> +{
> +       struct pdbg_target *target;
> +       char data[MAX_RESP_LEN] = "m";
> +       size_t s = 1;
> +       int iter = 0;
> +       bool found = false;
> +
> +       for_each_path_target_class("thread", target) {
> +               struct thread *thread = target_to_thread(target);
> +               struct gdb_thread *gdb_thread = thread->gdbserver_priv;
> +
> +               if (pdbg_target_status(target) != PDBG_TARGET_ENABLED)
> +                       continue;
> +
> +               if (!found && iter < threadinfo_iterator) {
> +                       iter++;
> +                       continue;
> +               }
> +
> +               found = true;
> +
> +               if (iter == threadinfo_iterator)
> +                       s += snprintf(data + s, sizeof(data) - s,
> +                                     "%04" PRIx64, gdb_thread->pir);
> +               else
> +                       s += snprintf(data + s, sizeof(data) - s,
> +                                     ",%04" PRIx64, gdb_thread->pir);
> +               threadinfo_iterator++;
> +
> +               if (sizeof(data) - s < 9) /* comma, 7 digits, NUL */
> +                       break;
> +
> +       }
> +
> +       PR_INFO("qf_threadinfo %s\n", data);
> +
> +       if (s > 1)
> +               send_response(fd, data);
> +       else
> +               send_response(fd, "l");
> +}
> +
> +static void qf_threadinfo(uint64_t *stack, void *priv)
> +{
> +       threadinfo_iterator = 0;
> +
> +       qs_threadinfo(stack, priv);
> +}
> +
>  static void send_stop_for_thread(struct pdbg_target *target)
>  {
>         struct thread *thread = target_to_thread(target);
> @@ -880,12 +932,14 @@ static command_cb callbacks[LAST_CMD + 1] = {
>         cmd_default,
>         get_gprs,
>         get_spr,
> -       get_mem,
>         stop_reason,
>         get_thread,
>         set_thread,
>         v_contc,
>         v_conts,
> +       qf_threadinfo,
> +       qs_threadinfo,
> +       get_mem,
>         put_mem,
>         interrupt,
>         detach,
> @@ -1018,12 +1072,8 @@ static int gdbserver(uint16_t port)
>                 memset(gdb_thread, 0, sizeof(*gdb_thread));
>                 thread->gdbserver_priv = gdb_thread;
>
> -               if (!first_target) {
> +               if (!first_target)
>                         first_target = target;
> -               } else {
> -                       fprintf(stderr, "GDB server cannot be run on multiple threads at once.\n");
> -                       return 0;
> -               }
>         }
>
>         if (!first_target) {
> @@ -1039,6 +1089,10 @@ static int gdbserver(uint16_t port)
>                 PR_WARNING("Breakpoints may cause host crashes on POWER9 and should not be used\n");
>         }
>
> +       if (!path_target_all_selected("thread", NULL)) {
> +               PR_WARNING("GDBSERVER works best when targeting all threads (-a)\n");
> +       }
> +
>         thread_target = first_target;
>
>         for_each_path_target_class("thread", target) {
> diff --git a/src/pdbgproxy.h b/src/pdbgproxy.h
> index 2005083d..cafd4cbc 100644
> --- a/src/pdbgproxy.h
> +++ b/src/pdbgproxy.h
> @@ -1,9 +1,13 @@
>  #ifndef __PDBGPROXY_H
>  #define __PDBGPROXY_H
>
> -enum gdb_command {NONE, GET_GPRS, GET_SPR, GET_MEM,
> -                 STOP_REASON, GET_THREAD, SET_THREAD, V_CONTC, V_CONTS,
> -                 PUT_MEM, INTERRUPT, DETACH, LAST_CMD};
> +enum gdb_command {NONE, GET_GPRS, GET_SPR,
> +                 STOP_REASON, GET_THREAD, SET_THREAD,
> +                 V_CONTC, V_CONTS,
> +                 QF_THREADINFO, QS_THREADINFO,
> +                 GET_MEM, PUT_MEM,
> +                 INTERRUPT, DETACH, LAST_CMD};
> +
>  typedef void (*command_cb)(uint64_t *stack, void *priv);
>
>  void parser_init(command_cb *callbacks);
> --
> 2.35.1
>
> --
> Pdbg mailing list
> Pdbg@lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/pdbg
diff mbox series

Patch

diff --git a/README.md b/README.md
index c3447c35..778269a6 100644
--- a/README.md
+++ b/README.md
@@ -538,13 +538,20 @@  At the moment gdbserver is only supported on P8 and P9 and P10.
 
 Memory access can only be performed on kernel memory.
 
-To run a gdbserver on a P8 machine from a BMC running openbmc:
+To run a gdbserver on a machine from a BMC running OpenBMC:
 
-Stop all the threads of the core you want to look at
-$ ./pdbg -d p8 -c11 -a stop
+Stop all the threads of the core(s) you want to look at. Ideally all
+threads in the machine should be debugged:
 
-Run gdbserver on thread 0 of core 11, accessible through port 44
-$ ./pdbg -d p8 -p0 -c11 -t0 gdbserver 44
+$ ./pdbg -a stop
+
+Run gdbserver on the target threads, accessible through port 44
+$ ./pdbg -a gdbserver 44
+
+The thread-id tid is set to the PIR of the corresponding thread, the
+hard_smp_processor_id. Be warned, "info threads" or other gdb operations
+that iterate over all threads may be very slow when debugging a lot
+of threads, especially over slow remote links.
 
 On your local machine:
 $ gdb
diff --git a/src/gdb_parser.rl b/src/gdb_parser.rl
index dc1275c3..abb361ae 100644
--- a/src/gdb_parser.rl
+++ b/src/gdb_parser.rl
@@ -111,14 +111,15 @@ 
 
 	# TODO: We don't actually listen to what's supported
 	q_attached = ('qAttached:' xdigit* @{rsp = "1";});
-	q_C = ('qC' @{rsp = "QC1";});
-	q_supported = ('qSupported:' any* >{rsp = "multiprocess+;vContSupported+;QStartNoAckMode+"; ack_mode = true;});
-	qf_threadinfo = ('qfThreadInfo' @{rsp = "m1l";});
+	q_supported = ('qSupported:' any* >{rsp = "multiprocess+;swbreak+;hwbreak-;qRelocInsn-;vContSupported+;QThreadEvents-;no-resumed-;QStartNoAckMode+"; ack_mode = true;});
 	q_start_noack = ('QStartNoAckMode' @{rsp = "OK"; send_ack(priv); ack_mode = false;});
 
 	# thread info
+	is_alive = ('T' ('p' xdigit+ '.')+ xdigit+ @{rsp = "OK";});
 	get_thread = ('qC' @{cmd = GET_THREAD;});
 	set_thread = ('Hg' ('p' xdigit+ '.')+ xdigit+ $hex_digit %push @{cmd = SET_THREAD;});
+	qf_threadinfo = ('qfThreadInfo' @{cmd = QF_THREADINFO;});
+	qs_threadinfo = ('qsThreadInfo' @{cmd = QS_THREADINFO;});
 
 	# vCont packet parsing
 	v_contq = ('vCont?' @{rsp = "vCont;c;C;s;S";});
@@ -128,9 +129,12 @@ 
 
 	interrupt = (3 @{ if (command_callbacks) command_callbacks[INTERRUPT](stack, priv); PR_INFO("RAGEL:interrupt\n");});
 
-	commands = (get_mem | get_gprs | get_spr | stop_reason | get_thread | set_thread |
-		    q_attached | q_C | q_supported | qf_threadinfo | q_C |
-		    q_start_noack | v_contq | v_contc | v_conts | put_mem |
+	commands = (get_gprs | get_spr |
+		    q_attached | q_supported | q_start_noack |
+		    stop_reason | is_alive | get_thread | set_thread |
+		    v_contq | v_contc | v_conts |
+		    qf_threadinfo | qs_threadinfo |
+		    get_mem | put_mem |
 		    detach | unknown );
 
 	cmd = (('$' ((commands & ^'#'*) >reset $crc)
diff --git a/src/gdb_parser_precompile.c b/src/gdb_parser_precompile.c
index 567f1dd6..13c89c9a 100644
--- a/src/gdb_parser_precompile.c
+++ b/src/gdb_parser_precompile.c
@@ -11,7 +11,7 @@ 
 #include "debug.h"
 
 
-#line 145 "src/gdb_parser.rl"
+#line 149 "src/gdb_parser.rl"
 
 
 static enum gdb_command cmd = NONE;
@@ -31,76 +31,86 @@  static bool ack_mode = true;
 #line 32 "src/gdb_parser_precompile.c"
 static const char _gdb_actions[] = {
 	0, 1, 0, 1, 1, 1, 2, 1, 
-	3, 1, 15, 1, 23, 1, 24, 1, 
-	25, 1, 26, 2, 0, 1, 2, 2, 
+	3, 1, 14, 1, 24, 1, 25, 1, 
+	26, 1, 27, 2, 0, 1, 2, 2, 
 	1, 2, 3, 1, 2, 3, 5, 2, 
-	4, 1, 2, 13, 1, 2, 15, 1, 
-	2, 16, 1, 2, 17, 1, 2, 20, 
-	1, 2, 21, 1, 2, 22, 1, 3, 
-	0, 6, 1, 3, 0, 7, 1, 3, 
-	0, 9, 1, 3, 0, 10, 1, 3, 
-	0, 11, 1, 3, 0, 12, 1, 3, 
-	2, 8, 1, 3, 3, 19, 1, 3, 
-	18, 14, 1
+	4, 1, 2, 13, 1, 2, 14, 1, 
+	2, 15, 1, 2, 16, 1, 2, 17, 
+	1, 2, 19, 1, 2, 20, 1, 2, 
+	21, 1, 2, 22, 1, 2, 23, 1, 
+	3, 0, 6, 1, 3, 0, 7, 1, 
+	3, 0, 9, 1, 3, 0, 10, 1, 
+	3, 0, 11, 1, 3, 0, 12, 1, 
+	3, 2, 8, 1, 3, 3, 18, 1
+	
 };
 
-static const unsigned char _gdb_key_offsets[] = {
-	0, 0, 11, 12, 18, 24, 31, 38, 
-	40, 42, 49, 57, 65, 72, 79, 87, 
-	94, 102, 109, 111, 113, 115, 117, 119, 
-	121, 123, 125, 127, 129, 131, 133, 135, 
-	137, 144, 152, 157, 159, 161, 163, 165, 
-	167, 169, 171, 173, 180, 182, 184, 186, 
-	188, 190, 192, 194, 196, 198, 199, 201, 
-	203, 205, 207, 209, 211, 213, 215, 217, 
-	219, 221, 223, 225, 227, 230, 233, 234, 
-	235
+static const short _gdb_key_offsets[] = {
+	0, 0, 12, 13, 19, 25, 32, 39, 
+	41, 43, 50, 58, 66, 73, 80, 88, 
+	95, 103, 110, 112, 114, 116, 118, 120, 
+	122, 124, 126, 128, 130, 132, 134, 136, 
+	138, 140, 147, 155, 163, 170, 177, 185, 
+	191, 193, 195, 197, 199, 201, 203, 205, 
+	207, 214, 216, 218, 220, 222, 224, 226, 
+	228, 230, 232, 233, 235, 237, 239, 241, 
+	243, 245, 247, 249, 251, 253, 255, 257, 
+	259, 261, 263, 265, 267, 269, 271, 273, 
+	275, 277, 279, 281, 284, 287, 288, 289
 };
 
 static const char _gdb_trans_keys[] = {
-	35, 63, 68, 72, 77, 81, 103, 109, 
-	112, 113, 118, 35, 48, 57, 65, 70, 
-	97, 102, 48, 57, 65, 70, 97, 102, 
-	35, 48, 57, 65, 70, 97, 102, 35, 
-	48, 57, 65, 70, 97, 102, 35, 103, 
-	35, 112, 35, 48, 57, 65, 70, 97, 
-	102, 35, 46, 48, 57, 65, 70, 97, 
-	102, 35, 112, 48, 57, 65, 70, 97, 
+	35, 63, 68, 72, 77, 81, 84, 103, 
+	109, 112, 113, 118, 35, 48, 57, 65, 
+	70, 97, 102, 48, 57, 65, 70, 97, 
 	102, 35, 48, 57, 65, 70, 97, 102, 
 	35, 48, 57, 65, 70, 97, 102, 35, 
-	44, 48, 57, 65, 70, 97, 102, 35, 
-	48, 57, 65, 70, 97, 102, 35, 58, 
-	48, 57, 65, 70, 97, 102, 35, 48, 
-	57, 65, 70, 97, 102, 35, 83, 35, 
-	116, 35, 97, 35, 114, 35, 116, 35, 
-	78, 35, 111, 35, 65, 35, 99, 35, 
-	107, 35, 77, 35, 111, 35, 100, 35, 
-	101, 35, 48, 57, 65, 70, 97, 102, 
+	103, 35, 112, 35, 48, 57, 65, 70, 
+	97, 102, 35, 46, 48, 57, 65, 70, 
+	97, 102, 35, 112, 48, 57, 65, 70, 
+	97, 102, 35, 48, 57, 65, 70, 97, 
+	102, 35, 48, 57, 65, 70, 97, 102, 
 	35, 44, 48, 57, 65, 70, 97, 102, 
-	35, 65, 67, 83, 102, 35, 116, 35, 
-	116, 35, 97, 35, 99, 35, 104, 35, 
-	101, 35, 100, 35, 58, 35, 48, 57, 
-	65, 70, 97, 102, 35, 117, 35, 112, 
-	35, 112, 35, 111, 35, 114, 35, 116, 
-	35, 101, 35, 100, 35, 58, 35, 35, 
-	84, 35, 104, 35, 114, 35, 101, 35, 
-	97, 35, 100, 35, 73, 35, 110, 35, 
-	102, 35, 111, 35, 67, 35, 111, 35, 
-	110, 35, 116, 35, 59, 63, 35, 99, 
-	115, 35, 35, 3, 36, 43, 45, 0
+	35, 48, 57, 65, 70, 97, 102, 35, 
+	58, 48, 57, 65, 70, 97, 102, 35, 
+	48, 57, 65, 70, 97, 102, 35, 83, 
+	35, 116, 35, 97, 35, 114, 35, 116, 
+	35, 78, 35, 111, 35, 65, 35, 99, 
+	35, 107, 35, 77, 35, 111, 35, 100, 
+	35, 101, 35, 112, 35, 48, 57, 65, 
+	70, 97, 102, 35, 46, 48, 57, 65, 
+	70, 97, 102, 35, 112, 48, 57, 65, 
+	70, 97, 102, 35, 48, 57, 65, 70, 
+	97, 102, 35, 48, 57, 65, 70, 97, 
+	102, 35, 44, 48, 57, 65, 70, 97, 
+	102, 35, 65, 67, 83, 102, 115, 35, 
+	116, 35, 116, 35, 97, 35, 99, 35, 
+	104, 35, 101, 35, 100, 35, 58, 35, 
+	48, 57, 65, 70, 97, 102, 35, 117, 
+	35, 112, 35, 112, 35, 111, 35, 114, 
+	35, 116, 35, 101, 35, 100, 35, 58, 
+	35, 35, 84, 35, 104, 35, 114, 35, 
+	101, 35, 97, 35, 100, 35, 73, 35, 
+	110, 35, 102, 35, 111, 35, 84, 35, 
+	104, 35, 114, 35, 101, 35, 97, 35, 
+	100, 35, 73, 35, 110, 35, 102, 35, 
+	111, 35, 67, 35, 111, 35, 110, 35, 
+	116, 35, 59, 63, 35, 99, 115, 35, 
+	35, 3, 36, 43, 45, 0
 };
 
 static const char _gdb_single_lengths[] = {
-	0, 11, 1, 0, 0, 1, 1, 2, 
+	0, 12, 1, 0, 0, 1, 1, 2, 
 	2, 1, 2, 2, 1, 1, 2, 1, 
 	2, 1, 2, 2, 2, 2, 2, 2, 
 	2, 2, 2, 2, 2, 2, 2, 2, 
-	1, 2, 5, 2, 2, 2, 2, 2, 
-	2, 2, 2, 1, 2, 2, 2, 2, 
-	2, 2, 2, 2, 2, 1, 2, 2, 
+	2, 1, 2, 2, 1, 1, 2, 6, 
 	2, 2, 2, 2, 2, 2, 2, 2, 
-	2, 2, 2, 2, 3, 3, 1, 1, 
-	4
+	1, 2, 2, 2, 2, 2, 2, 2, 
+	2, 2, 1, 2, 2, 2, 2, 2, 
+	2, 2, 2, 2, 2, 2, 2, 2, 
+	2, 2, 2, 2, 2, 2, 2, 2, 
+	2, 2, 2, 3, 3, 1, 1, 4
 };
 
 static const char _gdb_range_lengths[] = {
@@ -108,111 +118,125 @@  static const char _gdb_range_lengths[] = {
 	0, 3, 3, 3, 3, 3, 3, 3, 
 	3, 3, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
-	3, 3, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 3, 0, 0, 0, 0, 
+	0, 3, 3, 3, 3, 3, 3, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
+	3, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
-	0
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0
 };
 
 static const short _gdb_index_offsets[] = {
-	0, 0, 12, 14, 18, 22, 27, 32, 
-	35, 38, 43, 49, 55, 60, 65, 71, 
-	76, 82, 87, 90, 93, 96, 99, 102, 
-	105, 108, 111, 114, 117, 120, 123, 126, 
-	129, 134, 140, 146, 149, 152, 155, 158, 
-	161, 164, 167, 170, 175, 178, 181, 184, 
-	187, 190, 193, 196, 199, 202, 204, 207, 
-	210, 213, 216, 219, 222, 225, 228, 231, 
-	234, 237, 240, 243, 246, 250, 254, 256, 
-	258
+	0, 0, 13, 15, 19, 23, 28, 33, 
+	36, 39, 44, 50, 56, 61, 66, 72, 
+	77, 83, 88, 91, 94, 97, 100, 103, 
+	106, 109, 112, 115, 118, 121, 124, 127, 
+	130, 133, 138, 144, 150, 155, 160, 166, 
+	173, 176, 179, 182, 185, 188, 191, 194, 
+	197, 202, 205, 208, 211, 214, 217, 220, 
+	223, 226, 229, 231, 234, 237, 240, 243, 
+	246, 249, 252, 255, 258, 261, 264, 267, 
+	270, 273, 276, 279, 282, 285, 288, 291, 
+	294, 297, 300, 303, 307, 311, 313, 315
 };
 
 static const char _gdb_indicies[] = {
 	1, 2, 3, 4, 5, 6, 7, 8, 
-	9, 10, 11, 0, 13, 12, 14, 14, 
-	14, 15, 16, 16, 16, 15, 13, 17, 
-	17, 17, 12, 18, 17, 17, 17, 12, 
-	13, 19, 12, 13, 20, 12, 13, 21, 
-	21, 21, 12, 13, 22, 21, 21, 21, 
-	12, 13, 20, 23, 23, 23, 12, 18, 
-	23, 23, 23, 12, 13, 24, 24, 24, 
-	12, 13, 25, 24, 24, 24, 12, 13, 
-	26, 26, 26, 12, 13, 27, 26, 26, 
-	26, 12, 13, 28, 28, 28, 12, 13, 
-	29, 12, 13, 30, 12, 13, 31, 12, 
-	13, 32, 12, 13, 33, 12, 13, 34, 
-	12, 13, 35, 12, 13, 36, 12, 13, 
-	37, 12, 13, 38, 12, 13, 39, 12, 
-	13, 40, 12, 13, 41, 12, 13, 42, 
-	12, 13, 43, 43, 43, 12, 13, 44, 
-	43, 43, 43, 12, 13, 45, 46, 47, 
-	48, 12, 13, 49, 12, 13, 50, 12, 
-	13, 51, 12, 13, 52, 12, 13, 53, 
-	12, 13, 54, 12, 13, 55, 12, 13, 
-	56, 12, 13, 57, 57, 57, 12, 13, 
-	58, 12, 13, 59, 12, 13, 60, 12, 
-	13, 61, 12, 13, 62, 12, 13, 63, 
-	12, 13, 64, 12, 13, 65, 12, 13, 
-	66, 12, 68, 67, 13, 69, 12, 13, 
-	70, 12, 13, 71, 12, 13, 72, 12, 
-	13, 73, 12, 13, 74, 12, 13, 75, 
-	12, 13, 76, 12, 13, 77, 12, 13, 
-	78, 12, 13, 79, 12, 13, 80, 12, 
-	13, 81, 12, 13, 82, 12, 13, 83, 
-	84, 12, 13, 85, 86, 12, 13, 87, 
-	13, 88, 89, 90, 91, 92, 15, 0
+	9, 10, 11, 12, 0, 14, 13, 15, 
+	15, 15, 16, 17, 17, 17, 16, 14, 
+	18, 18, 18, 13, 19, 18, 18, 18, 
+	13, 14, 20, 13, 14, 21, 13, 14, 
+	22, 22, 22, 13, 14, 23, 22, 22, 
+	22, 13, 14, 21, 24, 24, 24, 13, 
+	19, 24, 24, 24, 13, 14, 25, 25, 
+	25, 13, 14, 26, 25, 25, 25, 13, 
+	14, 27, 27, 27, 13, 14, 28, 27, 
+	27, 27, 13, 14, 29, 29, 29, 13, 
+	14, 30, 13, 14, 31, 13, 14, 32, 
+	13, 14, 33, 13, 14, 34, 13, 14, 
+	35, 13, 14, 36, 13, 14, 37, 13, 
+	14, 38, 13, 14, 39, 13, 14, 40, 
+	13, 14, 41, 13, 14, 42, 13, 14, 
+	43, 13, 14, 44, 13, 14, 45, 45, 
+	45, 13, 14, 46, 45, 45, 45, 13, 
+	14, 44, 47, 47, 47, 13, 14, 47, 
+	47, 47, 13, 14, 48, 48, 48, 13, 
+	14, 49, 48, 48, 48, 13, 14, 50, 
+	51, 52, 53, 54, 13, 14, 55, 13, 
+	14, 56, 13, 14, 57, 13, 14, 58, 
+	13, 14, 59, 13, 14, 60, 13, 14, 
+	61, 13, 14, 62, 13, 14, 63, 63, 
+	63, 13, 14, 64, 13, 14, 65, 13, 
+	14, 66, 13, 14, 67, 13, 14, 68, 
+	13, 14, 69, 13, 14, 70, 13, 14, 
+	71, 13, 14, 72, 13, 74, 73, 14, 
+	75, 13, 14, 76, 13, 14, 77, 13, 
+	14, 78, 13, 14, 79, 13, 14, 80, 
+	13, 14, 81, 13, 14, 82, 13, 14, 
+	83, 13, 14, 84, 13, 14, 85, 13, 
+	14, 86, 13, 14, 87, 13, 14, 88, 
+	13, 14, 89, 13, 14, 90, 13, 14, 
+	91, 13, 14, 92, 13, 14, 93, 13, 
+	14, 94, 13, 14, 95, 13, 14, 96, 
+	13, 14, 97, 13, 14, 98, 13, 14, 
+	99, 100, 13, 14, 101, 102, 13, 14, 
+	103, 14, 104, 105, 106, 107, 108, 16, 
+	0
 };
 
 static const char _gdb_trans_targs[] = {
-	2, 3, 2, 5, 7, 13, 18, 2, 
-	32, 5, 34, 64, 2, 3, 4, 0, 
-	72, 6, 3, 8, 9, 10, 11, 12, 
-	14, 15, 16, 17, 17, 19, 20, 21, 
-	22, 23, 24, 25, 26, 27, 28, 29, 
-	30, 31, 2, 33, 5, 35, 2, 44, 
-	54, 36, 37, 38, 39, 40, 41, 42, 
-	43, 43, 45, 46, 47, 48, 49, 50, 
-	51, 52, 53, 2, 3, 55, 56, 57, 
-	58, 59, 60, 61, 62, 63, 2, 65, 
-	66, 67, 68, 69, 2, 70, 71, 70, 
-	71, 72, 1, 72, 72
+	2, 3, 2, 5, 7, 13, 18, 32, 
+	2, 37, 5, 39, 79, 2, 3, 4, 
+	0, 87, 6, 3, 8, 9, 10, 11, 
+	12, 14, 15, 16, 17, 17, 19, 20, 
+	21, 22, 23, 24, 25, 26, 27, 28, 
+	29, 30, 31, 2, 33, 34, 35, 36, 
+	38, 5, 40, 2, 49, 59, 69, 41, 
+	42, 43, 44, 45, 46, 47, 48, 48, 
+	50, 51, 52, 53, 54, 55, 56, 57, 
+	58, 2, 3, 60, 61, 62, 63, 64, 
+	65, 66, 67, 68, 2, 70, 71, 72, 
+	73, 74, 75, 76, 77, 78, 2, 80, 
+	81, 82, 83, 84, 2, 85, 86, 85, 
+	86, 87, 1, 87, 87
 };
 
 static const char _gdb_trans_actions[] = {
-	19, 1, 71, 75, 19, 59, 19, 63, 
-	55, 67, 19, 19, 3, 0, 7, 0, 
-	28, 25, 5, 3, 3, 3, 3, 83, 
-	25, 22, 25, 79, 31, 3, 3, 3, 
+	19, 1, 80, 84, 19, 68, 19, 19, 
+	72, 64, 76, 19, 19, 3, 0, 7, 
+	0, 28, 25, 5, 3, 3, 3, 3, 
+	92, 25, 22, 25, 88, 31, 3, 3, 
 	3, 3, 3, 3, 3, 3, 3, 3, 
-	3, 3, 43, 25, 22, 3, 87, 3, 
+	3, 3, 3, 40, 3, 3, 3, 43, 
+	25, 22, 3, 46, 3, 3, 3, 3, 
+	3, 3, 3, 3, 3, 3, 3, 34, 
 	3, 3, 3, 3, 3, 3, 3, 3, 
-	3, 34, 3, 3, 3, 3, 3, 3, 
-	3, 3, 3, 37, 9, 3, 3, 3, 
-	3, 3, 3, 3, 3, 3, 40, 3, 
-	3, 3, 3, 3, 46, 3, 3, 49, 
-	52, 11, 13, 15, 17
+	3, 37, 9, 3, 3, 3, 3, 3, 
+	3, 3, 3, 3, 49, 3, 3, 3, 
+	3, 3, 3, 3, 3, 3, 52, 3, 
+	3, 3, 3, 3, 55, 3, 3, 58, 
+	61, 11, 13, 15, 17
 };
 
-static const int gdb_start = 72;
-static const int gdb_first_final = 72;
+static const int gdb_start = 87;
+static const int gdb_first_final = 87;
 static const int gdb_error = 0;
 
-static const int gdb_en_main = 72;
+static const int gdb_en_main = 87;
 
 
-#line 161 "src/gdb_parser.rl"
+#line 165 "src/gdb_parser.rl"
 
 void parser_init(command_cb *callbacks)
 {
 	
-#line 211 "src/gdb_parser_precompile.c"
+#line 235 "src/gdb_parser_precompile.c"
 	{
 	cs = gdb_start;
 	}
 
-#line 165 "src/gdb_parser.rl"
+#line 169 "src/gdb_parser.rl"
 
 	command_callbacks = callbacks;
 }
@@ -223,7 +247,7 @@  int parse_buffer(char *buf, size_t len, void *priv)
 	char *pe = p + len;
 
 	
-#line 227 "src/gdb_parser_precompile.c"
+#line 251 "src/gdb_parser_precompile.c"
 	{
 	int _klen;
 	unsigned int _trans;
@@ -418,57 +442,61 @@  _match:
 	break;
 	case 14:
 #line 114 "src/gdb_parser.rl"
-	{rsp = "QC1";}
+	{rsp = "multiprocess+;swbreak+;hwbreak-;qRelocInsn-;vContSupported+;QThreadEvents-;no-resumed-;QStartNoAckMode+"; ack_mode = true;}
 	break;
 	case 15:
 #line 115 "src/gdb_parser.rl"
-	{rsp = "multiprocess+;vContSupported+;QStartNoAckMode+"; ack_mode = true;}
+	{rsp = "OK"; send_ack(priv); ack_mode = false;}
 	break;
 	case 16:
-#line 116 "src/gdb_parser.rl"
-	{rsp = "m1l";}
+#line 118 "src/gdb_parser.rl"
+	{rsp = "OK";}
 	break;
 	case 17:
-#line 117 "src/gdb_parser.rl"
-	{rsp = "OK"; send_ack(priv); ack_mode = false;}
+#line 119 "src/gdb_parser.rl"
+	{cmd = GET_THREAD;}
 	break;
 	case 18:
 #line 120 "src/gdb_parser.rl"
-	{cmd = GET_THREAD;}
+	{cmd = SET_THREAD;}
 	break;
 	case 19:
 #line 121 "src/gdb_parser.rl"
-	{cmd = SET_THREAD;}
+	{cmd = QF_THREADINFO;}
 	break;
 	case 20:
-#line 124 "src/gdb_parser.rl"
-	{rsp = "vCont;c;C;s;S";}
+#line 122 "src/gdb_parser.rl"
+	{cmd = QS_THREADINFO;}
 	break;
 	case 21:
 #line 125 "src/gdb_parser.rl"
-	{cmd = V_CONTC;}
+	{rsp = "vCont;c;C;s;S";}
 	break;
 	case 22:
 #line 126 "src/gdb_parser.rl"
-	{cmd = V_CONTS;}
+	{cmd = V_CONTC;}
 	break;
 	case 23:
-#line 129 "src/gdb_parser.rl"
-	{ if (command_callbacks) command_callbacks[INTERRUPT](stack, priv); PR_INFO("RAGEL:interrupt\n");}
+#line 127 "src/gdb_parser.rl"
+	{cmd = V_CONTS;}
 	break;
 	case 24:
-#line 137 "src/gdb_parser.rl"
-	{PR_INFO("RAGEL:cmd\n");}
+#line 130 "src/gdb_parser.rl"
+	{ if (command_callbacks) command_callbacks[INTERRUPT](stack, priv); PR_INFO("RAGEL:interrupt\n");}
 	break;
 	case 25:
-#line 140 "src/gdb_parser.rl"
-	{PR_INFO("RAGEL:ack\n");}
+#line 141 "src/gdb_parser.rl"
+	{PR_INFO("RAGEL:cmd\n");}
 	break;
 	case 26:
-#line 141 "src/gdb_parser.rl"
+#line 144 "src/gdb_parser.rl"
+	{PR_INFO("RAGEL:ack\n");}
+	break;
+	case 27:
+#line 145 "src/gdb_parser.rl"
 	{PR_INFO("RAGEL:nack\n");}
 	break;
-#line 472 "src/gdb_parser_precompile.c"
+#line 500 "src/gdb_parser_precompile.c"
 		}
 	}
 
@@ -481,7 +509,7 @@  _again:
 	_out: {}
 	}
 
-#line 175 "src/gdb_parser.rl"
+#line 179 "src/gdb_parser.rl"
 
 	if (cs == gdb_error) {
 		printf("parse error\n");
diff --git a/src/pdbgproxy.c b/src/pdbgproxy.c
index 52f34a91..1d24e68a 100644
--- a/src/pdbgproxy.c
+++ b/src/pdbgproxy.c
@@ -142,6 +142,58 @@  static void set_thread(uint64_t *stack, void *priv)
 	send_response(fd, ERROR(EEXIST));
 }
 
+static int threadinfo_iterator;
+
+static void qs_threadinfo(uint64_t *stack, void *priv)
+{
+	struct pdbg_target *target;
+	char data[MAX_RESP_LEN] = "m";
+	size_t s = 1;
+	int iter = 0;
+	bool found = false;
+
+	for_each_path_target_class("thread", target) {
+		struct thread *thread = target_to_thread(target);
+		struct gdb_thread *gdb_thread = thread->gdbserver_priv;
+
+		if (pdbg_target_status(target) != PDBG_TARGET_ENABLED)
+			continue;
+
+		if (!found && iter < threadinfo_iterator) {
+			iter++;
+			continue;
+		}
+
+		found = true;
+
+		if (iter == threadinfo_iterator)
+			s += snprintf(data + s, sizeof(data) - s,
+				      "%04" PRIx64, gdb_thread->pir);
+		else
+			s += snprintf(data + s, sizeof(data) - s,
+				      ",%04" PRIx64, gdb_thread->pir);
+		threadinfo_iterator++;
+
+		if (sizeof(data) - s < 9) /* comma, 7 digits, NUL */
+			break;
+
+	}
+
+	PR_INFO("qf_threadinfo %s\n", data);
+
+	if (s > 1)
+		send_response(fd, data);
+	else
+		send_response(fd, "l");
+}
+
+static void qf_threadinfo(uint64_t *stack, void *priv)
+{
+	threadinfo_iterator = 0;
+
+	qs_threadinfo(stack, priv);
+}
+
 static void send_stop_for_thread(struct pdbg_target *target)
 {
 	struct thread *thread = target_to_thread(target);
@@ -880,12 +932,14 @@  static command_cb callbacks[LAST_CMD + 1] = {
 	cmd_default,
 	get_gprs,
 	get_spr,
-	get_mem,
 	stop_reason,
 	get_thread,
 	set_thread,
 	v_contc,
 	v_conts,
+	qf_threadinfo,
+	qs_threadinfo,
+	get_mem,
 	put_mem,
 	interrupt,
 	detach,
@@ -1018,12 +1072,8 @@  static int gdbserver(uint16_t port)
 		memset(gdb_thread, 0, sizeof(*gdb_thread));
 		thread->gdbserver_priv = gdb_thread;
 
-		if (!first_target) {
+		if (!first_target)
 			first_target = target;
-		} else {
-			fprintf(stderr, "GDB server cannot be run on multiple threads at once.\n");
-			return 0;
-		}
 	}
 
 	if (!first_target) {
@@ -1039,6 +1089,10 @@  static int gdbserver(uint16_t port)
 		PR_WARNING("Breakpoints may cause host crashes on POWER9 and should not be used\n");
 	}
 
+	if (!path_target_all_selected("thread", NULL)) {
+		PR_WARNING("GDBSERVER works best when targeting all threads (-a)\n");
+	}
+
 	thread_target = first_target;
 
 	for_each_path_target_class("thread", target) {
diff --git a/src/pdbgproxy.h b/src/pdbgproxy.h
index 2005083d..cafd4cbc 100644
--- a/src/pdbgproxy.h
+++ b/src/pdbgproxy.h
@@ -1,9 +1,13 @@ 
 #ifndef __PDBGPROXY_H
 #define __PDBGPROXY_H
 
-enum gdb_command {NONE, GET_GPRS, GET_SPR, GET_MEM,
-                 STOP_REASON, GET_THREAD, SET_THREAD, V_CONTC, V_CONTS,
-                 PUT_MEM, INTERRUPT, DETACH, LAST_CMD};
+enum gdb_command {NONE, GET_GPRS, GET_SPR,
+                 STOP_REASON, GET_THREAD, SET_THREAD,
+                 V_CONTC, V_CONTS,
+                 QF_THREADINFO, QS_THREADINFO,
+                 GET_MEM, PUT_MEM,
+                 INTERRUPT, DETACH, LAST_CMD};
+
 typedef void (*command_cb)(uint64_t *stack, void *priv);
 
 void parser_init(command_cb *callbacks);