diff mbox series

[v2,38/39] gdbserver: handle gdb breakpoint commands

Message ID 20220420065013.222816-39-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
Handle the z/Z breakpoint clear/set gdb commands and maintain
the list of breakpoints in the server.

This allows breakpoint matching to distinguish between attn in host
software, and avoids the problematic gdb break op logic in put_mem.

Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
 src/gdb_parser.rl           |   5 +
 src/gdb_parser_precompile.c | 343 ++++++++++++++++++++----------------
 src/pdbgproxy.c             | 214 +++++++++++++++++++++-
 src/pdbgproxy.h             |   1 +
 4 files changed, 401 insertions(+), 162 deletions(-)
diff mbox series

Patch

diff --git a/src/gdb_parser.rl b/src/gdb_parser.rl
index abb361ae..44048332 100644
--- a/src/gdb_parser.rl
+++ b/src/gdb_parser.rl
@@ -121,6 +121,10 @@ 
 	qf_threadinfo = ('qfThreadInfo' @{cmd = QF_THREADINFO;});
 	qs_threadinfo = ('qsThreadInfo' @{cmd = QS_THREADINFO;});
 
+	# breakpoints
+	set_break = ('Z0,' xdigit+ $hex_digit %push ',4' @{cmd = SET_BREAK;});
+	clear_break = ('z0,' xdigit+ $hex_digit %push ',4' @{cmd = CLEAR_BREAK;});
+
 	# vCont packet parsing
 	v_contq = ('vCont?' @{rsp = "vCont;c;C;s;S";});
 	v_contc = ('vCont;c' any* @{cmd = V_CONTC;});
@@ -135,6 +139,7 @@ 
 		    v_contq | v_contc | v_conts |
 		    qf_threadinfo | qs_threadinfo |
 		    get_mem | put_mem |
+		    set_break | clear_break |
 		    detach | unknown );
 
 	cmd = (('$' ((commands & ^'#'*) >reset $crc)
diff --git a/src/gdb_parser_precompile.c b/src/gdb_parser_precompile.c
index 13c89c9a..d2eeb61c 100644
--- a/src/gdb_parser_precompile.c
+++ b/src/gdb_parser_precompile.c
@@ -11,7 +11,7 @@ 
 #include "debug.h"
 
 
-#line 149 "src/gdb_parser.rl"
+#line 154 "src/gdb_parser.rl"
 
 
 static enum gdb_command cmd = NONE;
@@ -31,86 +31,96 @@  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, 14, 1, 24, 1, 25, 1, 
-	26, 1, 27, 2, 0, 1, 2, 2, 
+	3, 1, 14, 1, 26, 1, 27, 1, 
+	28, 1, 29, 2, 0, 1, 2, 2, 
 	1, 2, 3, 1, 2, 3, 5, 2, 
 	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
-	
+	2, 24, 1, 2, 25, 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 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
+	0, 0, 14, 15, 21, 27, 34, 41, 
+	43, 45, 52, 60, 68, 75, 82, 90, 
+	97, 105, 112, 114, 116, 118, 120, 122, 
+	124, 126, 128, 130, 132, 134, 136, 138, 
+	140, 142, 149, 157, 165, 172, 174, 176, 
+	183, 191, 193, 200, 208, 214, 216, 218, 
+	220, 222, 224, 226, 228, 230, 237, 239, 
+	241, 243, 245, 247, 249, 251, 253, 255, 
+	256, 258, 260, 262, 264, 266, 268, 270, 
+	272, 274, 276, 278, 280, 282, 284, 286, 
+	288, 290, 292, 294, 296, 298, 300, 302, 
+	304, 307, 310, 311, 312, 314, 316, 323, 
+	331, 333
 };
 
 static const char _gdb_trans_keys[] = {
-	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, 
-	103, 35, 112, 35, 48, 57, 65, 70, 
-	97, 102, 35, 46, 48, 57, 65, 70, 
-	97, 102, 35, 112, 48, 57, 65, 70, 
+	35, 63, 68, 72, 77, 81, 84, 90, 
+	103, 109, 112, 113, 118, 122, 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, 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, 112, 35, 48, 57, 65, 
-	70, 97, 102, 35, 46, 48, 57, 65, 
-	70, 97, 102, 35, 112, 48, 57, 65, 
+	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, 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, 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
+	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, 35, 44, 
+	35, 48, 57, 65, 70, 97, 102, 35, 
+	44, 48, 57, 65, 70, 97, 102, 35, 
+	52, 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, 
+	35, 48, 35, 44, 35, 48, 57, 65, 
+	70, 97, 102, 35, 44, 48, 57, 65, 
+	70, 97, 102, 35, 52, 3, 36, 43, 
+	45, 0
 };
 
 static const char _gdb_single_lengths[] = {
-	0, 12, 1, 0, 0, 1, 1, 2, 
+	0, 14, 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, 
-	2, 1, 2, 2, 1, 1, 2, 6, 
+	2, 1, 2, 2, 1, 2, 2, 1, 
+	2, 2, 1, 2, 6, 2, 2, 2, 
+	2, 2, 2, 2, 2, 1, 2, 2, 
+	2, 2, 2, 2, 2, 2, 2, 1, 
 	2, 2, 2, 2, 2, 2, 2, 2, 
-	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
+	3, 3, 1, 1, 2, 2, 1, 2, 
+	2, 4
 };
 
 static const char _gdb_range_lengths[] = {
@@ -118,125 +128,138 @@  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, 
-	0, 3, 3, 3, 3, 3, 3, 0, 
+	0, 3, 3, 3, 3, 0, 0, 3, 
+	3, 0, 3, 3, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 3, 0, 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, 0, 0, 0, 0, 0, 3, 3, 
+	0, 0
 };
 
 static const short _gdb_index_offsets[] = {
-	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
+	0, 0, 15, 17, 21, 25, 30, 35, 
+	38, 41, 46, 52, 58, 63, 68, 74, 
+	79, 85, 90, 93, 96, 99, 102, 105, 
+	108, 111, 114, 117, 120, 123, 126, 129, 
+	132, 135, 140, 146, 152, 157, 160, 163, 
+	168, 174, 177, 182, 188, 195, 198, 201, 
+	204, 207, 210, 213, 216, 219, 224, 227, 
+	230, 233, 236, 239, 242, 245, 248, 251, 
+	253, 256, 259, 262, 265, 268, 271, 274, 
+	277, 280, 283, 286, 289, 292, 295, 298, 
+	301, 304, 307, 310, 313, 316, 319, 322, 
+	325, 329, 333, 335, 337, 340, 343, 348, 
+	354, 357
 };
 
 static const char _gdb_indicies[] = {
 	1, 2, 3, 4, 5, 6, 7, 8, 
-	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
+	9, 10, 11, 12, 13, 14, 0, 16, 
+	15, 17, 17, 17, 18, 19, 19, 19, 
+	18, 16, 20, 20, 20, 15, 21, 20, 
+	20, 20, 15, 16, 22, 15, 16, 23, 
+	15, 16, 24, 24, 24, 15, 16, 25, 
+	24, 24, 24, 15, 16, 23, 26, 26, 
+	26, 15, 21, 26, 26, 26, 15, 16, 
+	27, 27, 27, 15, 16, 28, 27, 27, 
+	27, 15, 16, 29, 29, 29, 15, 16, 
+	30, 29, 29, 29, 15, 16, 31, 31, 
+	31, 15, 16, 32, 15, 16, 33, 15, 
+	16, 34, 15, 16, 35, 15, 16, 36, 
+	15, 16, 37, 15, 16, 38, 15, 16, 
+	39, 15, 16, 40, 15, 16, 41, 15, 
+	16, 42, 15, 16, 43, 15, 16, 44, 
+	15, 16, 45, 15, 16, 46, 15, 16, 
+	47, 47, 47, 15, 16, 48, 47, 47, 
+	47, 15, 16, 46, 49, 49, 49, 15, 
+	16, 49, 49, 49, 15, 16, 50, 15, 
+	16, 51, 15, 16, 52, 52, 52, 15, 
+	16, 53, 52, 52, 52, 15, 16, 54, 
+	15, 16, 55, 55, 55, 15, 16, 56, 
+	55, 55, 55, 15, 16, 57, 58, 59, 
+	60, 61, 15, 16, 62, 15, 16, 63, 
+	15, 16, 64, 15, 16, 65, 15, 16, 
+	66, 15, 16, 67, 15, 16, 68, 15, 
+	16, 69, 15, 16, 70, 70, 70, 15, 
+	16, 71, 15, 16, 72, 15, 16, 73, 
+	15, 16, 74, 15, 16, 75, 15, 16, 
+	76, 15, 16, 77, 15, 16, 78, 15, 
+	16, 79, 15, 81, 80, 16, 82, 15, 
+	16, 83, 15, 16, 84, 15, 16, 85, 
+	15, 16, 86, 15, 16, 87, 15, 16, 
+	88, 15, 16, 89, 15, 16, 90, 15, 
+	16, 91, 15, 16, 92, 15, 16, 93, 
+	15, 16, 94, 15, 16, 95, 15, 16, 
+	96, 15, 16, 97, 15, 16, 98, 15, 
+	16, 99, 15, 16, 100, 15, 16, 101, 
+	15, 16, 102, 15, 16, 103, 15, 16, 
+	104, 15, 16, 105, 15, 16, 106, 107, 
+	15, 16, 108, 109, 15, 16, 110, 16, 
+	111, 16, 112, 15, 16, 113, 15, 16, 
+	114, 114, 114, 15, 16, 115, 114, 114, 
+	114, 15, 16, 116, 15, 117, 118, 119, 
+	120, 18, 0
 };
 
 static const char _gdb_trans_targs[] = {
 	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
+	37, 2, 42, 5, 44, 84, 92, 2, 
+	3, 4, 0, 97, 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, 39, 40, 41, 2, 43, 
+	5, 45, 2, 54, 64, 74, 46, 47, 
+	48, 49, 50, 51, 52, 53, 53, 55, 
+	56, 57, 58, 59, 60, 61, 62, 63, 
+	2, 3, 65, 66, 67, 68, 69, 70, 
+	71, 72, 73, 2, 75, 76, 77, 78, 
+	79, 80, 81, 82, 83, 2, 85, 86, 
+	87, 88, 89, 2, 90, 91, 90, 91, 
+	93, 94, 95, 96, 2, 97, 1, 97, 
+	97
 };
 
 static const char _gdb_trans_actions[] = {
-	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, 
+	19, 1, 86, 90, 19, 74, 19, 19, 
+	19, 78, 70, 82, 19, 19, 19, 3, 
+	0, 7, 0, 28, 25, 5, 3, 3, 
+	3, 3, 98, 25, 22, 25, 94, 31, 
 	3, 3, 3, 3, 3, 3, 3, 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, 40, 3, 3, 
+	3, 43, 3, 3, 25, 22, 55, 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, 
-	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
+	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, 61, 3, 3, 64, 67, 
+	3, 3, 25, 22, 58, 11, 13, 15, 
+	17
 };
 
-static const int gdb_start = 87;
-static const int gdb_first_final = 87;
+static const int gdb_start = 97;
+static const int gdb_first_final = 97;
 static const int gdb_error = 0;
 
-static const int gdb_en_main = 87;
+static const int gdb_en_main = 97;
 
 
-#line 165 "src/gdb_parser.rl"
+#line 170 "src/gdb_parser.rl"
 
 void parser_init(command_cb *callbacks)
 {
 	
-#line 235 "src/gdb_parser_precompile.c"
+#line 258 "src/gdb_parser_precompile.c"
 	{
 	cs = gdb_start;
 	}
 
-#line 169 "src/gdb_parser.rl"
+#line 174 "src/gdb_parser.rl"
 
 	command_callbacks = callbacks;
 }
@@ -247,7 +270,7 @@  int parse_buffer(char *buf, size_t len, void *priv)
 	char *pe = p + len;
 
 	
-#line 251 "src/gdb_parser_precompile.c"
+#line 274 "src/gdb_parser_precompile.c"
 	{
 	int _klen;
 	unsigned int _trans;
@@ -470,33 +493,41 @@  _match:
 	break;
 	case 21:
 #line 125 "src/gdb_parser.rl"
-	{rsp = "vCont;c;C;s;S";}
+	{cmd = SET_BREAK;}
 	break;
 	case 22:
 #line 126 "src/gdb_parser.rl"
-	{cmd = V_CONTC;}
+	{cmd = CLEAR_BREAK;}
 	break;
 	case 23:
-#line 127 "src/gdb_parser.rl"
-	{cmd = V_CONTS;}
+#line 129 "src/gdb_parser.rl"
+	{rsp = "vCont;c;C;s;S";}
 	break;
 	case 24:
 #line 130 "src/gdb_parser.rl"
-	{ if (command_callbacks) command_callbacks[INTERRUPT](stack, priv); PR_INFO("RAGEL:interrupt\n");}
+	{cmd = V_CONTC;}
 	break;
 	case 25:
-#line 141 "src/gdb_parser.rl"
-	{PR_INFO("RAGEL:cmd\n");}
+#line 131 "src/gdb_parser.rl"
+	{cmd = V_CONTS;}
 	break;
 	case 26:
-#line 144 "src/gdb_parser.rl"
-	{PR_INFO("RAGEL:ack\n");}
+#line 134 "src/gdb_parser.rl"
+	{ if (command_callbacks) command_callbacks[INTERRUPT](stack, priv); PR_INFO("RAGEL:interrupt\n");}
 	break;
 	case 27:
-#line 145 "src/gdb_parser.rl"
+#line 146 "src/gdb_parser.rl"
+	{PR_INFO("RAGEL:cmd\n");}
+	break;
+	case 28:
+#line 149 "src/gdb_parser.rl"
+	{PR_INFO("RAGEL:ack\n");}
+	break;
+	case 29:
+#line 150 "src/gdb_parser.rl"
 	{PR_INFO("RAGEL:nack\n");}
 	break;
-#line 500 "src/gdb_parser_precompile.c"
+#line 531 "src/gdb_parser_precompile.c"
 		}
 	}
 
@@ -509,7 +540,7 @@  _again:
 	_out: {}
 	}
 
-#line 179 "src/gdb_parser.rl"
+#line 184 "src/gdb_parser.rl"
 
 	if (cs == gdb_error) {
 		printf("parse error\n");
diff --git a/src/pdbgproxy.c b/src/pdbgproxy.c
index e81dba40..cc73a36d 100644
--- a/src/pdbgproxy.c
+++ b/src/pdbgproxy.c
@@ -664,6 +664,192 @@  out:
 		send_response(fd, OK);
 }
 
+#define MAX_BPS 64
+static uint64_t bps[MAX_BPS];
+static uint32_t saved_insn[MAX_BPS];
+static int nr_bps = 0;
+
+static int set_bp(uint64_t addr)
+{
+	int i;
+
+	if (nr_bps == MAX_BPS)
+		return -1;
+
+	if (addr == -1ULL)
+		return -1;
+
+	for (i = 0; i < MAX_BPS; i++) {
+		if (bps[i] == -1ULL) {
+			bps[i] = addr;
+			nr_bps++;
+			return 0;
+		}
+	}
+
+	return -1;
+}
+
+static int clear_bp(uint64_t addr)
+{
+	int i;
+
+	if (nr_bps == 0)
+		return -1;
+
+	if (addr == -1ULL)
+		return -1;
+
+	for (i = 0; i < MAX_BPS; i++) {
+		if (bps[i] == addr) {
+			bps[i] = -1ULL;
+			nr_bps--;
+			return 0;
+		}
+	}
+
+	return -1;
+}
+
+static int get_insn(uint64_t addr, uint32_t *insn)
+{
+	uint64_t linear_map;
+
+	linear_map = get_real_addr(addr);
+	if (linear_map != -1UL) {
+		if (read_memory(linear_map, 4, insn, 1)) {
+			PR_ERROR("Unable to read memory\n");
+			return -1;
+		}
+	} else {
+		/* Virtual address */
+		return -1;
+	}
+
+	return 0;
+}
+
+static int put_insn(uint64_t addr, uint32_t insn)
+{
+	uint64_t linear_map;
+
+	linear_map = get_real_addr(addr);
+	if (linear_map != -1UL) {
+		if (write_memory(linear_map, 4, &insn, 8)) {
+			PR_ERROR("Unable to write memory\n");
+			return -1;
+		}
+	} else {
+		/* Virtual address */
+		return -1;
+	}
+
+	return 0;
+}
+
+static void init_breakpoints(void)
+{
+	int i;
+
+	for (i = 0; i < MAX_BPS; i++)
+		bps[i] = -1ULL;
+}
+
+static bool match_breakpoint(uint64_t addr)
+{
+	int i;
+
+	if (nr_bps == 0)
+		return false;
+
+	for (i = 0; i < MAX_BPS; i++) {
+		if (bps[i] == addr)
+			return true;
+	}
+
+	return false;
+}
+
+static int install_breakpoints(void)
+{
+	uint8_t attn_opcode[] = {0x00, 0x00, 0x02, 0x00};
+	uint32_t *attn = (uint32_t *)&attn_opcode[0];
+	uint64_t msr;
+	int i;
+
+	if (!nr_bps)
+		return 0;
+
+	/* Check endianess in MSR */
+	if (thread_getmsr(thread_target, &msr)) {
+		PR_ERROR("Couldn't read the MSR to set breakpoint endian");
+		return -1;
+	}
+
+	if (msr & 1) /* currently little endian */
+		*attn = bswap_32(*attn);
+
+	for (i = 0; i < MAX_BPS; i++) {
+		if (bps[i] == -1ULL)
+			continue;
+		if (get_insn(bps[i], &saved_insn[i]))
+			continue; /* XXX: handle properly */
+		if (put_insn(bps[i], *attn))
+			continue; /* XXX: handle properly */
+	}
+
+	set_attn(true);
+
+	return 0;
+}
+
+static int uninstall_breakpoints(void)
+{
+	int i;
+
+	if (!nr_bps)
+		return 0;
+
+	for (i = 0; i < MAX_BPS; i++) {
+		if (bps[i] == -1ULL)
+			continue;
+		if (put_insn(bps[i], saved_insn[i]))
+			continue; /* XXX: handle properly */
+
+	}
+
+	set_attn(false);
+
+	return 0;
+}
+
+
+static void set_break(uint64_t *stack, void *priv)
+{
+	uint64_t addr;
+
+	/* stack[0] is the address */
+	addr = stack[0];
+
+	if (!set_bp(addr))
+		send_response(fd, OK);
+	else
+		send_response(fd, ERROR(ENOMEM));
+}
+
+static void clear_break(uint64_t *stack, void *priv)
+{
+	uint64_t addr;
+
+	/* stack[0] is the address */
+	addr = stack[0];
+
+	if (!clear_bp(addr))
+		send_response(fd, OK);
+	else
+		send_response(fd, ERROR(ENOENT));
+}
+
 static void v_conts(uint64_t *stack, void *priv)
 {
 	struct thread *thread = target_to_thread(thread_target);
@@ -709,6 +895,8 @@  static void start_all(void)
 {
 	struct pdbg_target *target;
 
+	install_breakpoints();
+
 	for_each_path_target_class("thread", target) {
 		struct thread *thread = target_to_thread(target);
 		struct gdb_thread *gdb_thread;
@@ -843,14 +1031,26 @@  static void stop_all(void)
 			gdb_thread->stop_attn = true;
 
 			if (!(status.active))
-				PR_ERROR("Error thread inactive after trap\n");
-			/* Restore NIA to before break */
+				PR_ERROR("Error thread inactive after attn\n");
+
 			if (thread_getnia(target, &nia))
 				PR_ERROR("Error during getnia\n");
-			if (thread_putnia(target, nia - 4))
-				PR_ERROR("Error during putnia\n");
+
+			/*
+			 * If we hit a non-breakpoint attn we still want to
+			 * switch to that thread, but we don't rewind the nip
+			 * so as to advance over the attn.
+			 */
+			if (match_breakpoint(nia - 4)) {
+				PR_INFO("thread pir=%"PRIx64" attn is breakpoint\n", gdb_thread->pir);
+				/* Restore NIA to breakpoint address */
+				if (thread_putnia(target, nia - 4))
+					PR_ERROR("Error during putnia\n");
+			}
 		}
 	}
+
+	uninstall_breakpoints();
 }
 
 static void interrupt(uint64_t *stack, void *priv)
@@ -918,8 +1118,6 @@  static void poll(void)
 		}
 	}
 
-	set_attn(false);
-
 	state = IDLE;
 	poll_interval = VCONT_POLL_DELAY;
 
@@ -991,6 +1189,8 @@  static command_cb callbacks[LAST_CMD + 1] = {
 	qs_threadinfo,
 	get_mem,
 	put_mem,
+	set_break,
+	clear_break,
 	interrupt,
 	detach,
 	NULL};
@@ -1107,6 +1307,8 @@  static int gdbserver(uint16_t port)
 	struct pdbg_target *first_stopped_target = NULL;
 	struct pdbg_target *first_attn_target = NULL;
 
+	init_breakpoints();
+
 	for_each_path_target_class("thread", target) {
 		struct thread *thread = target_to_thread(target);
 		struct gdb_thread *gdb_thread;
diff --git a/src/pdbgproxy.h b/src/pdbgproxy.h
index cafd4cbc..6a89444f 100644
--- a/src/pdbgproxy.h
+++ b/src/pdbgproxy.h
@@ -6,6 +6,7 @@  enum gdb_command {NONE, GET_GPRS, GET_SPR,
                  V_CONTC, V_CONTS,
                  QF_THREADINFO, QS_THREADINFO,
                  GET_MEM, PUT_MEM,
+                 SET_BREAK, CLEAR_BREAK,
                  INTERRUPT, DETACH, LAST_CMD};
 
 typedef void (*command_cb)(uint64_t *stack, void *priv);