* [RFC/WIP PATCH 02/14] Mask software breakpoints from memory writes too
2011-11-28 15:39 [RFC/WIP PATCH 00/14] I/T sets Pedro Alves
@ 2011-11-28 15:39 ` Pedro Alves
2011-12-06 20:40 ` Pedro Alves
2011-11-28 15:39 ` [RFC/WIP PATCH 03/14] Flip to set target-async on by default Pedro Alves
` (16 subsequent siblings)
17 siblings, 1 reply; 53+ messages in thread
From: Pedro Alves @ 2011-11-28 15:39 UTC (permalink / raw)
To: gdb-patches
When running the testsuite with both always-inserted and displaced
stepping on, I see nostdlib.exp failing. The symptom is a breakpoint
at marker being missed.
The problem is that the test sets a breakpoint at *_start, and another
at *marker, and when displace stepping *_start, when we copy a chunk
of copy to the scratch pad (to step it there), we also copy *marker,
because that address is just a few bytes away from *_start. When the
displace step finishes, we restore the scratch pad's original
contents, which again writes over *marker. But, while doing so, we
_don't_ restore the original breakpoint, because target_read_memory
masks out breakpoints, but conversely target_read_memory does not make
sure to staple breakpoint insns in place before actually writting to
memory! This patch fixes that, and adds a target_write_raw_memory
wrapper that callers that want to write to real memory without
stapling breakpoint insns on top can use -- mem-break.c is one such a
user. An alternative to changing mem-break.c would be to clear the
breakpoints inserted flag _before_ telling the target to remove the
breakpoint (this is how gdbserver does it). I haven't tried that yet.
Now that I've written this, I wonder if it's safe against a remote
target that implements software breakpoints itself...
---
gdb/breakpoint.c | 21 ++++++++++++--
gdb/breakpoint.h | 13 ++++++---
gdb/mem-break.c | 8 +++--
gdb/target.c | 80 ++++++++++++++++++++++++++++++++++++++++++++----------
gdb/target.h | 3 ++
5 files changed, 99 insertions(+), 26 deletions(-)
diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index 37e177b..7b8ab77 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -1049,7 +1049,8 @@ bp_location_has_shadow (struct bp_location *bl)
bl->address + bp_location_shadow_len_after_address_max <= memaddr */
void
-breakpoint_restore_shadows (gdb_byte *buf, ULONGEST memaddr, LONGEST len)
+breakpoint_xfer_memory (gdb_byte *readbuf, gdb_byte *writebuf,
+ ULONGEST memaddr, LONGEST len)
{
/* Left boundary, right boundary and median element of our binary
search. */
@@ -1161,8 +1162,22 @@ breakpoint_restore_shadows (gdb_byte *buf, ULONGEST memaddr, LONGEST len)
bp_size -= (bp_addr + bp_size) - (memaddr + len);
}
- memcpy (buf + bp_addr - memaddr,
- bl->target_info.shadow_contents + bptoffset, bp_size);
+ if (readbuf != NULL)
+ {
+ memcpy (readbuf + bp_addr - memaddr,
+ bl->target_info.shadow_contents + bptoffset, bp_size);
+ }
+ else
+ {
+ struct gdbarch *gdbarch = bl->gdbarch;
+ const unsigned char *bp;
+ CORE_ADDR placed_address = bl->target_info.placed_address;
+ unsigned placed_size = bl->target_info.placed_size;
+
+ /* Determine appropriate breakpoint contents and size for this address. */
+ bp = gdbarch_breakpoint_from_pc (gdbarch, &placed_address, &placed_size);
+ memcpy (writebuf + bp_addr - memaddr, bp + bptoffset, bp_size);
+ }
}
}
\f
diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h
index 438f347..6b51137 100644
--- a/gdb/breakpoint.h
+++ b/gdb/breakpoint.h
@@ -1297,10 +1297,15 @@ extern int deprecated_remove_raw_breakpoint (struct gdbarch *, void *);
target. */
int watchpoints_triggered (struct target_waitstatus *);
-/* Update BUF, which is LEN bytes read from the target address MEMADDR,
- by replacing any memory breakpoints with their shadowed contents. */
-void breakpoint_restore_shadows (gdb_byte *buf, ULONGEST memaddr,
- LONGEST len);
+/* Helper for transparent breakpoint hiding for memory read and write
+ routines.
+
+ Update one of READBUF or WRITEBUF with either the shadows
+ (READBUF), or the breakpoint instructions (WRITEBUF) of inserted
+ breakpoints at the memory range defined by MEMADDR and extending
+ for LEN bytes. */
+extern void breakpoint_xfer_memory (gdb_byte *readbuf, gdb_byte *writebuf,
+ ULONGEST memaddr, LONGEST len);
extern int breakpoints_always_inserted_mode (void);
diff --git a/gdb/mem-break.c b/gdb/mem-break.c
index ba7dc24..31ca45c 100644
--- a/gdb/mem-break.c
+++ b/gdb/mem-break.c
@@ -60,8 +60,8 @@ default_memory_insert_breakpoint (struct gdbarch *gdbarch,
/* Write the breakpoint. */
if (val == 0)
- val = target_write_memory (bp_tgt->placed_address, bp,
- bp_tgt->placed_size);
+ val = target_write_raw_memory (bp_tgt->placed_address, bp,
+ bp_tgt->placed_size);
return val;
}
@@ -71,8 +71,8 @@ int
default_memory_remove_breakpoint (struct gdbarch *gdbarch,
struct bp_target_info *bp_tgt)
{
- return target_write_memory (bp_tgt->placed_address, bp_tgt->shadow_contents,
- bp_tgt->placed_size);
+ return target_write_raw_memory (bp_tgt->placed_address, bp_tgt->shadow_contents,
+ bp_tgt->placed_size);
}
diff --git a/gdb/target.c b/gdb/target.c
index 6358b00..3bc4be2 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -1388,19 +1388,15 @@ memory_xfer_live_readonly_partial (struct target_ops *ops,
For docs see target.h, to_xfer_partial. */
static LONGEST
-memory_xfer_partial (struct target_ops *ops, enum target_object object,
- void *readbuf, const void *writebuf, ULONGEST memaddr,
- LONGEST len)
+memory_xfer_partial_1 (struct target_ops *ops, enum target_object object,
+ void *readbuf, const void *writebuf, ULONGEST memaddr,
+ LONGEST len)
{
LONGEST res;
int reg_len;
struct mem_region *region;
struct inferior *inf;
- /* Zero length requests are ok and require no work. */
- if (len == 0)
- return 0;
-
/* For accesses to unmapped overlay sections, read directly from
files. Must do this first, as MEMADDR may need adjustment. */
if (readbuf != NULL && overlay_debugging)
@@ -1551,11 +1547,7 @@ memory_xfer_partial (struct target_ops *ops, enum target_object object,
if (res <= 0)
return -1;
else
- {
- if (readbuf && !show_memory_breakpoints)
- breakpoint_restore_shadows (readbuf, memaddr, reg_len);
- return res;
- }
+ return res;
}
/* If none of those methods found the memory we wanted, fall back
@@ -1584,9 +1576,6 @@ memory_xfer_partial (struct target_ops *ops, enum target_object object,
}
while (ops != NULL);
- if (res > 0 && readbuf != NULL && !show_memory_breakpoints)
- breakpoint_restore_shadows (readbuf, memaddr, reg_len);
-
/* Make sure the cache gets updated no matter what - if we are writing
to the stack. Even if this write is not tagged as such, we still need
to update the cache. */
@@ -1606,6 +1595,48 @@ memory_xfer_partial (struct target_ops *ops, enum target_object object,
return res;
}
+/* Perform a partial memory transfer. For docs see target.h,
+ to_xfer_partial. */
+
+static LONGEST
+memory_xfer_partial (struct target_ops *ops, enum target_object object,
+ void *readbuf, const void *writebuf, ULONGEST memaddr,
+ LONGEST len)
+{
+ int res;
+
+ /* Zero length requests are ok and require no work. */
+ if (len == 0)
+ return 0;
+
+ /* Fill in READBUF with breakpoint shadows, or WRITEBUF with
+ breakpoint insns, thus hiding out from higher layers whether
+ there are software breakpoints inserted in the code stream. */
+ if (readbuf != NULL)
+ {
+ res = memory_xfer_partial_1 (ops, object, readbuf, NULL, memaddr, len);
+
+ if (res > 0 && !show_memory_breakpoints)
+ breakpoint_xfer_memory (readbuf, NULL, memaddr, res);
+ }
+ else
+ {
+ void *buf;
+ struct cleanup *old_chain;
+
+ buf = xmalloc (len);
+ old_chain = make_cleanup (xfree, buf);
+ memcpy (buf, writebuf, len);
+
+ breakpoint_xfer_memory (NULL, buf, memaddr, len);
+ res = memory_xfer_partial_1 (ops, object, NULL, buf, memaddr, len);
+
+ do_cleanups (old_chain);
+ }
+
+ return res;
+}
+
static void
restore_show_memory_breakpoints (void *arg)
{
@@ -1761,6 +1792,25 @@ target_write_memory (CORE_ADDR memaddr, const gdb_byte *myaddr, int len)
return EIO;
}
+/* Write LEN bytes from MYADDR to target raw memory at address
+ MEMADDR. Returns either 0 for success or an errno value if any
+ error occurs. If an error occurs, no guarantee is made about how
+ much data got written. Callers that can deal with partial writes
+ should call target_write. */
+
+int
+target_write_raw_memory (CORE_ADDR memaddr, const gdb_byte *myaddr, int len)
+{
+ /* Dispatch to the topmost target, not the flattened current_target.
+ Memory accesses check target->to_has_(all_)memory, and the
+ flattened target doesn't inherit those. */
+ if (target_write (current_target.beneath, TARGET_OBJECT_RAW_MEMORY, NULL,
+ myaddr, memaddr, len) == len)
+ return 0;
+ else
+ return EIO;
+}
+
/* Fetch the target's memory map. */
VEC(mem_region_s) *
diff --git a/gdb/target.h b/gdb/target.h
index 73c8f7c..25e833f 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -939,6 +939,9 @@ extern int target_read_stack (CORE_ADDR memaddr, gdb_byte *myaddr, int len);
extern int target_write_memory (CORE_ADDR memaddr, const gdb_byte *myaddr,
int len);
+extern int target_write_raw_memory (CORE_ADDR memaddr, const gdb_byte *myaddr,
+ int len);
+
/* Fetches the target's memory map. If one is found it is sorted
and returned, after some consistency checking. Otherwise, NULL
is returned. */
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [RFC/WIP PATCH 02/14] Mask software breakpoints from memory writes too
2011-11-28 15:39 ` [RFC/WIP PATCH 02/14] Mask software breakpoints from memory writes too Pedro Alves
@ 2011-12-06 20:40 ` Pedro Alves
2011-12-13 21:26 ` Andreas Schwab
0 siblings, 1 reply; 53+ messages in thread
From: Pedro Alves @ 2011-12-06 20:40 UTC (permalink / raw)
To: gdb-patches
On Monday 28 November 2011 15:39:04, Pedro Alves wrote:
> Now that I've written this, I wonder if it's safe against a remote
> target that implements software breakpoints itself...
Duh, it is. Memory breakpoints don't get shadows (on gdb) in that case.
I've now committed this updated version, which remembers to update
the breakpoints' shadows on writes this time (duh2!), and also adds
new simple tests that don't rely on displaced stepping.
2011-12-06 Pedro Alves <pedro@codesourcery.com>
gdb/
* breakpoint.c (breakpoint_restore_shadows): Rename to ...
(breakpoint_xfer_memory): ... this. Change prototype. Handle
memory writes too.
* breakpoint.h (breakpoint_restore_shadows): Delete.
(breakpoint_xfer_memory): Declare.
* mem-break.c (default_memory_insert_breakpoint)
(default_memory_remove_breakpoint): Use target_write_raw_memory.
(memory_xfer_partial): Rename to ...
(memory_xfer_partial_1): ... this. Don't mask out breakpoints
here.
(memory_xfer_partial): New.
(target_write_raw_memory): New.
* target.h (target_write_raw_memory): New.
gdb/testsuite/
* gdb.base/break-always.exp: Test changing memory at addresses
with breakpoints inserted.
---
gdb/breakpoint.c | 32 +++++++++++-
gdb/breakpoint.h | 15 ++++--
gdb/mem-break.c | 8 ++-
gdb/target.c | 80 +++++++++++++++++++++++++------
gdb/target.h | 3 +
gdb/testsuite/gdb.base/break-always.exp | 37 ++++++++++++++
6 files changed, 148 insertions(+), 27 deletions(-)
diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index bd0a0e3..d9d5bbe 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -1049,7 +1049,9 @@ bp_location_has_shadow (struct bp_location *bl)
bl->address + bp_location_shadow_len_after_address_max <= memaddr */
void
-breakpoint_restore_shadows (gdb_byte *buf, ULONGEST memaddr, LONGEST len)
+breakpoint_xfer_memory (gdb_byte *readbuf, gdb_byte *writebuf,
+ const gdb_byte *writebuf_org,
+ ULONGEST memaddr, LONGEST len)
{
/* Left boundary, right boundary and median element of our binary
search. */
@@ -1161,8 +1163,32 @@ breakpoint_restore_shadows (gdb_byte *buf, ULONGEST memaddr, LONGEST len)
bp_size -= (bp_addr + bp_size) - (memaddr + len);
}
- memcpy (buf + bp_addr - memaddr,
- bl->target_info.shadow_contents + bptoffset, bp_size);
+ if (readbuf != NULL)
+ {
+ /* Update the read buffer with this inserted breakpoint's
+ shadow. */
+ memcpy (readbuf + bp_addr - memaddr,
+ bl->target_info.shadow_contents + bptoffset, bp_size);
+ }
+ else
+ {
+ struct gdbarch *gdbarch = bl->gdbarch;
+ const unsigned char *bp;
+ CORE_ADDR placed_address = bl->target_info.placed_address;
+ unsigned placed_size = bl->target_info.placed_size;
+
+ /* Update the shadow with what we want to write to memory. */
+ memcpy (bl->target_info.shadow_contents + bptoffset,
+ writebuf_org + bp_addr - memaddr, bp_size);
+
+ /* Determine appropriate breakpoint contents and size for this
+ address. */
+ bp = gdbarch_breakpoint_from_pc (gdbarch, &placed_address, &placed_size);
+
+ /* Update the final write buffer with this inserted
+ breakpoint's INSN. */
+ memcpy (writebuf + bp_addr - memaddr, bp + bptoffset, bp_size);
+ }
}
}
\f
diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h
index ead3930..ddf1881 100644
--- a/gdb/breakpoint.h
+++ b/gdb/breakpoint.h
@@ -1296,10 +1296,17 @@ extern int deprecated_remove_raw_breakpoint (struct gdbarch *, void *);
target. */
int watchpoints_triggered (struct target_waitstatus *);
-/* Update BUF, which is LEN bytes read from the target address MEMADDR,
- by replacing any memory breakpoints with their shadowed contents. */
-void breakpoint_restore_shadows (gdb_byte *buf, ULONGEST memaddr,
- LONGEST len);
+/* Helper for transparent breakpoint hiding for memory read and write
+ routines.
+
+ Update one of READBUF or WRITEBUF with either the shadows
+ (READBUF), or the breakpoint instructions (WRITEBUF) of inserted
+ breakpoints at the memory range defined by MEMADDR and extending
+ for LEN bytes. If writing, then WRITEBUF is a copy of WRITEBUF_ORG
+ on entry.*/
+extern void breakpoint_xfer_memory (gdb_byte *readbuf, gdb_byte *writebuf,
+ const gdb_byte *writebuf_org,
+ ULONGEST memaddr, LONGEST len);
extern int breakpoints_always_inserted_mode (void);
diff --git a/gdb/mem-break.c b/gdb/mem-break.c
index ba7dc24..31ca45c 100644
--- a/gdb/mem-break.c
+++ b/gdb/mem-break.c
@@ -60,8 +60,8 @@ default_memory_insert_breakpoint (struct gdbarch *gdbarch,
/* Write the breakpoint. */
if (val == 0)
- val = target_write_memory (bp_tgt->placed_address, bp,
- bp_tgt->placed_size);
+ val = target_write_raw_memory (bp_tgt->placed_address, bp,
+ bp_tgt->placed_size);
return val;
}
@@ -71,8 +71,8 @@ int
default_memory_remove_breakpoint (struct gdbarch *gdbarch,
struct bp_target_info *bp_tgt)
{
- return target_write_memory (bp_tgt->placed_address, bp_tgt->shadow_contents,
- bp_tgt->placed_size);
+ return target_write_raw_memory (bp_tgt->placed_address, bp_tgt->shadow_contents,
+ bp_tgt->placed_size);
}
diff --git a/gdb/target.c b/gdb/target.c
index 6358b00..5700066 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -1388,19 +1388,15 @@ memory_xfer_live_readonly_partial (struct target_ops *ops,
For docs see target.h, to_xfer_partial. */
static LONGEST
-memory_xfer_partial (struct target_ops *ops, enum target_object object,
- void *readbuf, const void *writebuf, ULONGEST memaddr,
- LONGEST len)
+memory_xfer_partial_1 (struct target_ops *ops, enum target_object object,
+ void *readbuf, const void *writebuf, ULONGEST memaddr,
+ LONGEST len)
{
LONGEST res;
int reg_len;
struct mem_region *region;
struct inferior *inf;
- /* Zero length requests are ok and require no work. */
- if (len == 0)
- return 0;
-
/* For accesses to unmapped overlay sections, read directly from
files. Must do this first, as MEMADDR may need adjustment. */
if (readbuf != NULL && overlay_debugging)
@@ -1551,11 +1547,7 @@ memory_xfer_partial (struct target_ops *ops, enum target_object object,
if (res <= 0)
return -1;
else
- {
- if (readbuf && !show_memory_breakpoints)
- breakpoint_restore_shadows (readbuf, memaddr, reg_len);
- return res;
- }
+ return res;
}
/* If none of those methods found the memory we wanted, fall back
@@ -1584,9 +1576,6 @@ memory_xfer_partial (struct target_ops *ops, enum target_object object,
}
while (ops != NULL);
- if (res > 0 && readbuf != NULL && !show_memory_breakpoints)
- breakpoint_restore_shadows (readbuf, memaddr, reg_len);
-
/* Make sure the cache gets updated no matter what - if we are writing
to the stack. Even if this write is not tagged as such, we still need
to update the cache. */
@@ -1606,6 +1595,48 @@ memory_xfer_partial (struct target_ops *ops, enum target_object object,
return res;
}
+/* Perform a partial memory transfer. For docs see target.h,
+ to_xfer_partial. */
+
+static LONGEST
+memory_xfer_partial (struct target_ops *ops, enum target_object object,
+ void *readbuf, const void *writebuf, ULONGEST memaddr,
+ LONGEST len)
+{
+ int res;
+
+ /* Zero length requests are ok and require no work. */
+ if (len == 0)
+ return 0;
+
+ /* Fill in READBUF with breakpoint shadows, or WRITEBUF with
+ breakpoint insns, thus hiding out from higher layers whether
+ there are software breakpoints inserted in the code stream. */
+ if (readbuf != NULL)
+ {
+ res = memory_xfer_partial_1 (ops, object, readbuf, NULL, memaddr, len);
+
+ if (res > 0 && !show_memory_breakpoints)
+ breakpoint_xfer_memory (readbuf, NULL, NULL, memaddr, res);
+ }
+ else
+ {
+ void *buf;
+ struct cleanup *old_chain;
+
+ buf = xmalloc (len);
+ old_chain = make_cleanup (xfree, buf);
+ memcpy (buf, writebuf, len);
+
+ breakpoint_xfer_memory (NULL, buf, writebuf, memaddr, len);
+ res = memory_xfer_partial_1 (ops, object, NULL, buf, memaddr, len);
+
+ do_cleanups (old_chain);
+ }
+
+ return res;
+}
+
static void
restore_show_memory_breakpoints (void *arg)
{
@@ -1761,6 +1792,25 @@ target_write_memory (CORE_ADDR memaddr, const gdb_byte *myaddr, int len)
return EIO;
}
+/* Write LEN bytes from MYADDR to target raw memory at address
+ MEMADDR. Returns either 0 for success or an errno value if any
+ error occurs. If an error occurs, no guarantee is made about how
+ much data got written. Callers that can deal with partial writes
+ should call target_write. */
+
+int
+target_write_raw_memory (CORE_ADDR memaddr, const gdb_byte *myaddr, int len)
+{
+ /* Dispatch to the topmost target, not the flattened current_target.
+ Memory accesses check target->to_has_(all_)memory, and the
+ flattened target doesn't inherit those. */
+ if (target_write (current_target.beneath, TARGET_OBJECT_RAW_MEMORY, NULL,
+ myaddr, memaddr, len) == len)
+ return 0;
+ else
+ return EIO;
+}
+
/* Fetch the target's memory map. */
VEC(mem_region_s) *
diff --git a/gdb/target.h b/gdb/target.h
index 1261d90..fd488d6 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -941,6 +941,9 @@ extern int target_read_stack (CORE_ADDR memaddr, gdb_byte *myaddr, int len);
extern int target_write_memory (CORE_ADDR memaddr, const gdb_byte *myaddr,
int len);
+extern int target_write_raw_memory (CORE_ADDR memaddr, const gdb_byte *myaddr,
+ int len);
+
/* Fetches the target's memory map. If one is found it is sorted
and returned, after some consistency checking. Otherwise, NULL
is returned. */
diff --git a/gdb/testsuite/gdb.base/break-always.exp b/gdb/testsuite/gdb.base/break-always.exp
index ce76af7..e20794e 100644
--- a/gdb/testsuite/gdb.base/break-always.exp
+++ b/gdb/testsuite/gdb.base/break-always.exp
@@ -49,7 +49,42 @@ gdb_test_no_output "enable 2" "enable 2.H"
gdb_test_no_output "disable 2" "disable 2.I"
gdb_test "info breakpoints" "keep n.*keep n.*keep y.*keep n.*keep n.*" "before re-enable check breakpoint state"
gdb_test_no_output "enable" "re-enable all breakpoints"
-gdb_continue_to_breakpoint "bar" ".*break-always.c:$bar_location.*"
+set bp_address 0
+set test "set breakpoint on bar 2"
+gdb_test_multiple "break bar" $test {
+ -re "Breakpoint 6 at ($hex).*$gdb_prompt $" {
+ set bp_address $expect_out(1,string)
+ pass $test
+ }
+}
+
+# Save the original INSN under the breakpoint.
+gdb_test "p /x \$shadow = *(char *) $bp_address" \
+ " = $hex" \
+ "save shadow"
+# Overwrite memory where the breakpoint is planted. GDB should update
+# its memory breakpoint's shadows, to account for the new contents,
+# and still leave the breakpoint insn planted. Try twice with
+# different values, in case we happen to be writting exactly what was
+# there already.
+gdb_test "p /x *(char *) $bp_address = 0" \
+ " = 0x0" \
+ "write 0 to breakpoint's address"
+gdb_test "p /x *(char *) $bp_address" \
+ " = 0x0" \
+ "read back 0 from the breakpoint's address"
+gdb_test "p /x *(char *) $bp_address = 1" \
+ " = 0x1" \
+ "write 1 to breakpoint's address"
+gdb_test "p /x *(char *) $bp_address" \
+ " = 0x1" \
+ "read back 1 from the breakpoint's address"
+
+# Restore the original contents.
+gdb_test "p /x *(char *) $bp_address = \$shadow" ""
+
+# Run to breakpoint.
+gdb_continue_to_breakpoint "bar" ".*break-always.c:$bar_location.*"
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [RFC/WIP PATCH 02/14] Mask software breakpoints from memory writes too
2011-12-06 20:40 ` Pedro Alves
@ 2011-12-13 21:26 ` Andreas Schwab
2011-12-13 21:38 ` Pedro Alves
0 siblings, 1 reply; 53+ messages in thread
From: Andreas Schwab @ 2011-12-13 21:26 UTC (permalink / raw)
To: Pedro Alves; +Cc: gdb-patches
Pedro Alves <pedro@codesourcery.com> writes:
> 2011-12-06 Pedro Alves <pedro@codesourcery.com>
>
> gdb/
> * breakpoint.c (breakpoint_restore_shadows): Rename to ...
> (breakpoint_xfer_memory): ... this. Change prototype. Handle
> memory writes too.
> * breakpoint.h (breakpoint_restore_shadows): Delete.
> (breakpoint_xfer_memory): Declare.
> * mem-break.c (default_memory_insert_breakpoint)
> (default_memory_remove_breakpoint): Use target_write_raw_memory.
> (memory_xfer_partial): Rename to ...
> (memory_xfer_partial_1): ... this. Don't mask out breakpoints
> here.
> (memory_xfer_partial): New.
> (target_write_raw_memory): New.
> * target.h (target_write_raw_memory): New.
That completely breaks gdb on powerpc. Breakpoints cause gdb to infloop.
Andreas.
--
Andreas Schwab, schwab@linux-m68k.org
GPG Key fingerprint = 58CA 54C7 6D53 942B 1756 01D3 44D5 214B 8276 4ED5
"And now for something completely different."
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [RFC/WIP PATCH 02/14] Mask software breakpoints from memory writes too
2011-12-13 21:26 ` Andreas Schwab
@ 2011-12-13 21:38 ` Pedro Alves
2011-12-14 2:08 ` Andreas Schwab
0 siblings, 1 reply; 53+ messages in thread
From: Pedro Alves @ 2011-12-13 21:38 UTC (permalink / raw)
To: Andreas Schwab; +Cc: gdb-patches
On Tuesday 13 December 2011 21:24:24, Andreas Schwab wrote:
> Pedro Alves <pedro@codesourcery.com> writes:
>
> > 2011-12-06 Pedro Alves <pedro@codesourcery.com>
> >
> > gdb/
> > * breakpoint.c (breakpoint_restore_shadows): Rename to ...
> > (breakpoint_xfer_memory): ... this. Change prototype. Handle
> > memory writes too.
> > * breakpoint.h (breakpoint_restore_shadows): Delete.
> > (breakpoint_xfer_memory): Declare.
> > * mem-break.c (default_memory_insert_breakpoint)
> > (default_memory_remove_breakpoint): Use target_write_raw_memory.
> > (memory_xfer_partial): Rename to ...
> > (memory_xfer_partial_1): ... this. Don't mask out breakpoints
> > here.
> > (memory_xfer_partial): New.
> > (target_write_raw_memory): New.
> > * target.h (target_write_raw_memory): New.
>
> That completely breaks gdb on powerpc. Breakpoints cause gdb to infloop.
Got a backtrace showing the loop? It'd be really helpful.
--
Pedro Alves
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [RFC/WIP PATCH 02/14] Mask software breakpoints from memory writes too
2011-12-13 21:38 ` Pedro Alves
@ 2011-12-14 2:08 ` Andreas Schwab
2011-12-14 12:53 ` Pedro Alves
0 siblings, 1 reply; 53+ messages in thread
From: Andreas Schwab @ 2011-12-14 2:08 UTC (permalink / raw)
To: Pedro Alves; +Cc: gdb-patches
Pedro Alves <pedro@codesourcery.com> writes:
> On Tuesday 13 December 2011 21:24:24, Andreas Schwab wrote:
>> Pedro Alves <pedro@codesourcery.com> writes:
>>
>> > 2011-12-06 Pedro Alves <pedro@codesourcery.com>
>> >
>> > gdb/
>> > * breakpoint.c (breakpoint_restore_shadows): Rename to ...
>> > (breakpoint_xfer_memory): ... this. Change prototype. Handle
>> > memory writes too.
>> > * breakpoint.h (breakpoint_restore_shadows): Delete.
>> > (breakpoint_xfer_memory): Declare.
>> > * mem-break.c (default_memory_insert_breakpoint)
>> > (default_memory_remove_breakpoint): Use target_write_raw_memory.
>> > (memory_xfer_partial): Rename to ...
>> > (memory_xfer_partial_1): ... this. Don't mask out breakpoints
>> > here.
>> > (memory_xfer_partial): New.
>> > (target_write_raw_memory): New.
>> > * target.h (target_write_raw_memory): New.
>>
>> That completely breaks gdb on powerpc. Breakpoints cause gdb to infloop.
>
> Got a backtrace showing the loop? It'd be really helpful.
This is repeated endlessly with stop_pc never advancing:
infrun: proceed (addr=0xf7fe26b8, signal=0, step=0)
infrun: resume (step=0, signal=0), trap_expected=0, current thread [process 26306] at 0xf7fe26b8
infrun: wait_for_inferior ()
infrun: target_wait (-1, status) =
infrun: 26306 [process 26306],
infrun: status->kind = stopped, signal = SIGTRAP
infrun: infwait_normal_state
infrun: TARGET_WAITKIND_STOPPED
infrun: stop_pc = 0xf7fdb5a0
bpstat_what: bp_shlib_event
infrun: BPSTAT_WHAT_SINGLE
infrun: no stepping, continue
infrun: resume (step=1, signal=0), trap_expected=1, current thread [process 26306] at 0xf7fdb5a0
infrun: prepare_to_wait
infrun: target_wait (-1, status) =
infrun: 26306 [process 26306],
infrun: status->kind = stopped, signal = SIGTRAP
infrun: infwait_normal_state
infrun: TARGET_WAITKIND_STOPPED
infrun: stop_pc = 0xf7fdb5a0
bpstat_what: bp_shlib_event
infrun: BPSTAT_WHAT_SINGLE
infrun: no stepping, continue
Andreas.
--
Andreas Schwab, schwab@linux-m68k.org
GPG Key fingerprint = 58CA 54C7 6D53 942B 1756 01D3 44D5 214B 8276 4ED5
"And now for something completely different."
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [RFC/WIP PATCH 02/14] Mask software breakpoints from memory writes too
2011-12-14 2:08 ` Andreas Schwab
@ 2011-12-14 12:53 ` Pedro Alves
2011-12-14 12:53 ` Andreas Schwab
0 siblings, 1 reply; 53+ messages in thread
From: Pedro Alves @ 2011-12-14 12:53 UTC (permalink / raw)
To: Andreas Schwab; +Cc: gdb-patches
On Wednesday 14 December 2011 00:18:01, Andreas Schwab wrote:
> Pedro Alves <pedro@codesourcery.com> writes:
>
> > On Tuesday 13 December 2011 21:24:24, Andreas Schwab wrote:
> >> Pedro Alves <pedro@codesourcery.com> writes:
> >>
> >> > 2011-12-06 Pedro Alves <pedro@codesourcery.com>
> >> >
> >> > gdb/
> >> > * breakpoint.c (breakpoint_restore_shadows): Rename to ...
> >> > (breakpoint_xfer_memory): ... this. Change prototype. Handle
> >> > memory writes too.
> >> > * breakpoint.h (breakpoint_restore_shadows): Delete.
> >> > (breakpoint_xfer_memory): Declare.
> >> > * mem-break.c (default_memory_insert_breakpoint)
> >> > (default_memory_remove_breakpoint): Use target_write_raw_memory.
> >> > (memory_xfer_partial): Rename to ...
> >> > (memory_xfer_partial_1): ... this. Don't mask out breakpoints
> >> > here.
> >> > (memory_xfer_partial): New.
> >> > (target_write_raw_memory): New.
> >> > * target.h (target_write_raw_memory): New.
> >>
> >> That completely breaks gdb on powerpc. Breakpoints cause gdb to infloop.
> >
> > Got a backtrace showing the loop? It'd be really helpful.
>
> This is repeated endlessly with stop_pc never advancing:
So ppc doesn't use mem-break.c:default_memory_remove_breakpoint.
Does this fix it?
--- a/gdb/ppc-linux-tdep.c
+++ b/gdb/ppc-linux-tdep.c
@@ -218,7 +218,7 @@ ppc_linux_memory_remove_breakpoint (struct gdbarch *gdbarch,
program modified the code on us, so it is wrong to put back the
old value. */
if (val == 0 && memcmp (bp, old_contents, bplen) == 0)
- val = target_write_memory (addr, bp_tgt->shadow_contents, bplen);
+ val = target_write_raw_memory (addr, bp_tgt->shadow_contents, bplen);
do_cleanups (cleanup);
return val;
--
Pedro Alves
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [RFC/WIP PATCH 02/14] Mask software breakpoints from memory writes too
2011-12-14 12:53 ` Pedro Alves
@ 2011-12-14 12:53 ` Andreas Schwab
2011-12-14 15:06 ` Pedro Alves
0 siblings, 1 reply; 53+ messages in thread
From: Andreas Schwab @ 2011-12-14 12:53 UTC (permalink / raw)
To: Pedro Alves; +Cc: gdb-patches
Pedro Alves <pedro@codesourcery.com> writes:
> Does this fix it?
>
> --- a/gdb/ppc-linux-tdep.c
> +++ b/gdb/ppc-linux-tdep.c
> @@ -218,7 +218,7 @@ ppc_linux_memory_remove_breakpoint (struct gdbarch *gdbarch,
> program modified the code on us, so it is wrong to put back the
> old value. */
> if (val == 0 && memcmp (bp, old_contents, bplen) == 0)
> - val = target_write_memory (addr, bp_tgt->shadow_contents, bplen);
> + val = target_write_raw_memory (addr, bp_tgt->shadow_contents, bplen);
>
> do_cleanups (cleanup);
> return val;
Looks good, thanks.
Andreas.
--
Andreas Schwab, schwab@linux-m68k.org
GPG Key fingerprint = 58CA 54C7 6D53 942B 1756 01D3 44D5 214B 8276 4ED5
"And now for something completely different."
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [RFC/WIP PATCH 02/14] Mask software breakpoints from memory writes too
2011-12-14 12:53 ` Andreas Schwab
@ 2011-12-14 15:06 ` Pedro Alves
2011-12-14 15:38 ` Joel Brobecker
0 siblings, 1 reply; 53+ messages in thread
From: Pedro Alves @ 2011-12-14 15:06 UTC (permalink / raw)
To: Andreas Schwab; +Cc: gdb-patches
On Wednesday 14 December 2011 12:53:04, Andreas Schwab wrote:
> Pedro Alves <pedro@codesourcery.com> writes:
>
> > Does this fix it?
> >
> > --- a/gdb/ppc-linux-tdep.c
> > +++ b/gdb/ppc-linux-tdep.c
> > @@ -218,7 +218,7 @@ ppc_linux_memory_remove_breakpoint (struct gdbarch *gdbarch,
> > program modified the code on us, so it is wrong to put back the
> > old value. */
> > if (val == 0 && memcmp (bp, old_contents, bplen) == 0)
> > - val = target_write_memory (addr, bp_tgt->shadow_contents, bplen);
> > + val = target_write_raw_memory (addr, bp_tgt->shadow_contents, bplen);
> >
> > do_cleanups (cleanup);
> > return val;
>
> Looks good, thanks.
Thanks. I've applied the patch below to do the same adjustment to
all memory_remove_breakpoint callbacks. Applied to both mainline
and the 7.4 branch.
--
Pedro Alves
2011-12-14 Pedro Alves <pedro@codesourcery.com>
* ia64-tdep.c (ia64_memory_remove_breakpoint): Use
target_write_raw_memory.
* m32r-tdep.c (m32r_memory_remove_breakpoint): Use
target_write_raw_memory.
* microblaze-linux-tdep.c
(microblaze_linux_memory_remove_breakpoint): Use
target_write_raw_memory.
* ppc-linux-tdep.c (ppc_linux_memory_remove_breakpoint): Use
target_write_raw_memory.
---
gdb/ia64-tdep.c | 2 +-
gdb/m32r-tdep.c | 2 +-
gdb/microblaze-linux-tdep.c | 2 +-
gdb/ppc-linux-tdep.c | 2 +-
4 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/gdb/ia64-tdep.c b/gdb/ia64-tdep.c
index 68e5021..d1940e6 100644
--- a/gdb/ia64-tdep.c
+++ b/gdb/ia64-tdep.c
@@ -811,7 +811,7 @@ ia64_memory_remove_breakpoint (struct gdbarch *gdbarch,
/* In BUNDLE_MEM, be careful to modify only the bits belonging to SLOTNUM
and not any of the other ones that are stored in SHADOW_CONTENTS. */
replace_slotN_contents (bundle_mem, instr_saved, slotnum);
- val = target_write_memory (addr, bundle_mem, BUNDLE_LEN);
+ val = target_write_raw_memory (addr, bundle_mem, BUNDLE_LEN);
do_cleanups (cleanup);
return val;
diff --git a/gdb/m32r-tdep.c b/gdb/m32r-tdep.c
index 136fd7b..b417db0 100644
--- a/gdb/m32r-tdep.c
+++ b/gdb/m32r-tdep.c
@@ -163,7 +163,7 @@ m32r_memory_remove_breakpoint (struct gdbarch *gdbarch,
}
/* Write contents. */
- val = target_write_memory (addr & 0xfffffffc, buf, 4);
+ val = target_write_raw_memory (addr & 0xfffffffc, buf, 4);
return val;
}
diff --git a/gdb/microblaze-linux-tdep.c b/gdb/microblaze-linux-tdep.c
index c14e01b..7b2662d 100644
--- a/gdb/microblaze-linux-tdep.c
+++ b/gdb/microblaze-linux-tdep.c
@@ -58,7 +58,7 @@ microblaze_linux_memory_remove_breakpoint (struct gdbarch *gdbarch,
program modified the code on us, so it is wrong to put back the
old value. */
if (val == 0 && memcmp (bp, old_contents, bplen) == 0)
- val = target_write_memory (addr, bp_tgt->shadow_contents, bplen);
+ val = target_write_raw_memory (addr, bp_tgt->shadow_contents, bplen);
return val;
}
diff --git a/gdb/ppc-linux-tdep.c b/gdb/ppc-linux-tdep.c
index e623742..9968621 100644
--- a/gdb/ppc-linux-tdep.c
+++ b/gdb/ppc-linux-tdep.c
@@ -218,7 +218,7 @@ ppc_linux_memory_remove_breakpoint (struct gdbarch *gdbarch,
program modified the code on us, so it is wrong to put back the
old value. */
if (val == 0 && memcmp (bp, old_contents, bplen) == 0)
- val = target_write_memory (addr, bp_tgt->shadow_contents, bplen);
+ val = target_write_raw_memory (addr, bp_tgt->shadow_contents, bplen);
do_cleanups (cleanup);
return val;
^ permalink raw reply [flat|nested] 53+ messages in thread
* [RFC/WIP PATCH 03/14] Flip to set target-async on by default
2011-11-28 15:39 [RFC/WIP PATCH 00/14] I/T sets Pedro Alves
2011-11-28 15:39 ` [RFC/WIP PATCH 02/14] Mask software breakpoints from memory writes too Pedro Alves
@ 2011-11-28 15:39 ` Pedro Alves
2011-11-29 21:18 ` Tom Tromey
2011-12-02 19:16 ` Marc Khouzam
2011-11-28 15:39 ` [RFC/WIP PATCH 05/14] Add a small helper to get at a thread's inferior Pedro Alves
` (15 subsequent siblings)
17 siblings, 2 replies; 53+ messages in thread
From: Pedro Alves @ 2011-11-28 15:39 UTC (permalink / raw)
To: gdb-patches
This flips "set target-async" to default to on. I see no failures
left with this on x86_64-linux, and there used to be none either
against gdbserver, but I haven't tried recently. One thing left to
decide is what to do with MI. If the frontend never flips async
explicitly, should we bother to emulate sync MI output, or can we
expect that all frontends can and should cope with async on by default
by now?
---
gdb/target.c | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/gdb/target.c b/gdb/target.c
index 3bc4be2..c2aaa7e 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -4259,11 +4259,11 @@ maintenance_print_target_stack (char *cmd, int from_tty)
}
/* Controls if async mode is permitted. */
-int target_async_permitted = 0;
+int target_async_permitted = 1;
/* The set command writes to this variable. If the inferior is
executing, linux_nat_async_permitted is *not* updated. */
-static int target_async_permitted_1 = 0;
+static int target_async_permitted_1 = 1;
static void
set_maintenance_target_async_permitted (char *args, int from_tty,
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [RFC/WIP PATCH 03/14] Flip to set target-async on by default
2011-11-28 15:39 ` [RFC/WIP PATCH 03/14] Flip to set target-async on by default Pedro Alves
@ 2011-11-29 21:18 ` Tom Tromey
2011-12-02 19:16 ` Marc Khouzam
1 sibling, 0 replies; 53+ messages in thread
From: Tom Tromey @ 2011-11-29 21:18 UTC (permalink / raw)
To: Pedro Alves; +Cc: gdb-patches
>>>>> "Pedro" == Pedro Alves <pedro@codesourcery.com> writes:
Pedro> This flips "set target-async" to default to on. I see no failures
Pedro> left with this on x86_64-linux, and there used to be none either
Pedro> against gdbserver, but I haven't tried recently. One thing left to
Pedro> decide is what to do with MI. If the frontend never flips async
Pedro> explicitly, should we bother to emulate sync MI output, or can we
Pedro> expect that all frontends can and should cope with async on by default
Pedro> by now?
First, I want us to make target-async default to on. I have a few other
defaults I'd like to flip, too.
For the MI part, I think we should reach out to the "main" MI consumers
out there -- and by "main" I just mean ones that we know of and that
seem to be maintained -- and ask. (It seems like it would be good to
have some open channel for doing this more regularly.)
Actually, come to think of it, instead of asking, we should probably
just announce the change and see what kind of push-back we get.
In the extreme we can make the next release "8.0" as a way of
advertising that we're making bigger changes.
Tom
^ permalink raw reply [flat|nested] 53+ messages in thread
* RE: [RFC/WIP PATCH 03/14] Flip to set target-async on by default
2011-11-28 15:39 ` [RFC/WIP PATCH 03/14] Flip to set target-async on by default Pedro Alves
2011-11-29 21:18 ` Tom Tromey
@ 2011-12-02 19:16 ` Marc Khouzam
1 sibling, 0 replies; 53+ messages in thread
From: Marc Khouzam @ 2011-12-02 19:16 UTC (permalink / raw)
To: 'Pedro Alves', 'gdb-patches@sourceware.org'
> -----Original Message-----
> From: gdb-patches-owner@sourceware.org
> [mailto:gdb-patches-owner@sourceware.org] On Behalf Of Pedro Alves
> Sent: Monday, November 28, 2011 10:39 AM
> To: gdb-patches@sourceware.org
> Subject: [RFC/WIP PATCH 03/14] Flip to set target-async on by default
>
> This flips "set target-async" to default to on. I see no failures
> left with this on x86_64-linux, and there used to be none either
> against gdbserver, but I haven't tried recently. One thing left to
> decide is what to do with MI. If the frontend never flips async
> explicitly, should we bother to emulate sync MI output, or can we
> expect that all frontends can and should cope with async on by default
> by now?
I've just caught up with the 1400 GDB emails I had neglected in the last
couple of months so I surely missed some interesting points that may be
of interested to Eclipse.
But I did catch this one :-)
Eclipse is not ready for a default target-async on. But that change
wouldn't be very hard to adapt to, so I'll do it anyway for safety.
(As a side note, I would like Eclipse to always use target-async on,
even for all-stop, but that would be a large change that I don't have
the bandwith to work on. Besides, we still support GDB back to 6.6,
so we couldn't get rid of the dealing with the sync behavior anyway.)
Marc
> ---
> gdb/target.c | 4 ++--
> 1 files changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/gdb/target.c b/gdb/target.c
> index 3bc4be2..c2aaa7e 100644
> --- a/gdb/target.c
> +++ b/gdb/target.c
> @@ -4259,11 +4259,11 @@ maintenance_print_target_stack (char
> *cmd, int from_tty)
> }
>
> /* Controls if async mode is permitted. */
> -int target_async_permitted = 0;
> +int target_async_permitted = 1;
>
> /* The set command writes to this variable. If the inferior is
> executing, linux_nat_async_permitted is *not* updated. */
> -static int target_async_permitted_1 = 0;
> +static int target_async_permitted_1 = 1;
>
> static void
> set_maintenance_target_async_permitted (char *args, int from_tty,
>
>
^ permalink raw reply [flat|nested] 53+ messages in thread
* [RFC/WIP PATCH 05/14] Add a small helper to get at a thread's inferior
2011-11-28 15:39 [RFC/WIP PATCH 00/14] I/T sets Pedro Alves
2011-11-28 15:39 ` [RFC/WIP PATCH 02/14] Mask software breakpoints from memory writes too Pedro Alves
2011-11-28 15:39 ` [RFC/WIP PATCH 03/14] Flip to set target-async on by default Pedro Alves
@ 2011-11-28 15:39 ` Pedro Alves
2011-11-29 21:19 ` Tom Tromey
2011-11-28 15:39 ` [RFC/WIP PATCH 01/14] Breakpoints always-inserted and the record target Pedro Alves
` (14 subsequent siblings)
17 siblings, 1 reply; 53+ messages in thread
From: Pedro Alves @ 2011-11-28 15:39 UTC (permalink / raw)
To: gdb-patches
Add a helper to get at a given thread's inferior. There are places
that open code this that can just call this helper instead.
---
gdb/gdbthread.h | 2 ++
gdb/thread.c | 9 +++++++++
2 files changed, 11 insertions(+), 0 deletions(-)
diff --git a/gdb/gdbthread.h b/gdb/gdbthread.h
index 97ded57..0135219 100644
--- a/gdb/gdbthread.h
+++ b/gdb/gdbthread.h
@@ -411,4 +411,6 @@ extern struct thread_info* inferior_thread (void);
extern void update_thread_list (void);
+extern struct inferior *get_thread_inferior (struct thread_info *thr);
+
#endif /* GDBTHREAD_H */
diff --git a/gdb/thread.c b/gdb/thread.c
index 0777887..b42c8c3 100644
--- a/gdb/thread.c
+++ b/gdb/thread.c
@@ -74,6 +74,15 @@ inferior_thread (void)
return tp;
}
+struct inferior *
+get_thread_inferior (struct thread_info *thr)
+{
+ int pid;
+
+ pid = ptid_get_pid (thr->ptid);
+ return find_inferior_pid (pid);
+}
+
void
delete_step_resume_breakpoint (struct thread_info *tp)
{
^ permalink raw reply [flat|nested] 53+ messages in thread
* [RFC/WIP PATCH 01/14] Breakpoints always-inserted and the record target
2011-11-28 15:39 [RFC/WIP PATCH 00/14] I/T sets Pedro Alves
` (2 preceding siblings ...)
2011-11-28 15:39 ` [RFC/WIP PATCH 05/14] Add a small helper to get at a thread's inferior Pedro Alves
@ 2011-11-28 15:39 ` Pedro Alves
2011-11-29 21:09 ` Tom Tromey
2011-11-28 15:40 ` [RFC/WIP PATCH 10/14] Comment out new info breakpoints output, in order to not break the test suite Pedro Alves
` (13 subsequent siblings)
17 siblings, 1 reply; 53+ messages in thread
From: Pedro Alves @ 2011-11-28 15:39 UTC (permalink / raw)
To: gdb-patches
I tried running the testsuite with always-inserted forced on, and got
a bunch of regressions on gdb.reverse/ with the record target.
Breakpoints always-inserted and the record target don't play along
well currently. E.g., if you set a breakpoint while recording, the
breakpoint is installed in the live target. When the target stops,
the breakpoint is left inserted (due to always-inserted). Now, if
e.g., you rewind history, and replay, and the program needs to step
over a breakpoint, infrun will try to remove the breakpoint
temporarily to do the usual step-over-breakpoint dance. But, record.c
is actually wired to ignore breakpoint insertions and removals when
replaying. This means breakpoint.c now considers the breakpoint
not-inserted, but the breakpoint insn is still planted on the target.
If you set a new breakpoint at the same location, breakpoint.c
(thinking there's no breakpoint inserted at that address), will
happilly try to insert the new breakpoint, but, since the breakpoint
insn from the other breakpoint is still planted in memory,
breakpoint.c will think we're trying to set a breakpoint on top of a
permanent breakpoint. Things go downhill from here. This happens
e.g., sigall-reverse.exp.
For now, simply disable always-inserted if record is on, exactly like
we already disable displaced stepping.
---
gdb/breakpoint.c | 6 ++++--
1 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index fa80018..37e177b 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -67,6 +67,7 @@
#include "continuations.h"
#include "stack.h"
#include "skip.h"
+#include "record.h"
/* readline include files */
#include "readline/readline.h"
@@ -377,8 +378,9 @@ show_always_inserted_mode (struct ui_file *file, int from_tty,
int
breakpoints_always_inserted_mode (void)
{
- return (always_inserted_mode == always_inserted_on
- || (always_inserted_mode == always_inserted_auto && non_stop));
+ return ((always_inserted_mode == always_inserted_on
+ || (always_inserted_mode == always_inserted_auto && non_stop))
+ && !RECORD_IS_USED);
}
void _initialize_breakpoint (void);
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [RFC/WIP PATCH 01/14] Breakpoints always-inserted and the record target
2011-11-28 15:39 ` [RFC/WIP PATCH 01/14] Breakpoints always-inserted and the record target Pedro Alves
@ 2011-11-29 21:09 ` Tom Tromey
2011-12-05 17:04 ` Pedro Alves
0 siblings, 1 reply; 53+ messages in thread
From: Tom Tromey @ 2011-11-29 21:09 UTC (permalink / raw)
To: Pedro Alves; +Cc: gdb-patches
>>>>> "Pedro" == Pedro Alves <pedro@codesourcery.com> writes:
Pedro> For now, simply disable always-inserted if record is on, exactly like
Pedro> we already disable displaced stepping.
This one seems like a plain bug fix that could go in immediately.
Tom
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [RFC/WIP PATCH 01/14] Breakpoints always-inserted and the record target
2011-11-29 21:09 ` Tom Tromey
@ 2011-12-05 17:04 ` Pedro Alves
0 siblings, 0 replies; 53+ messages in thread
From: Pedro Alves @ 2011-12-05 17:04 UTC (permalink / raw)
To: Tom Tromey; +Cc: gdb-patches
On Tuesday 29 November 2011 21:09:04, Tom Tromey wrote:
> >>>>> "Pedro" == Pedro Alves <pedro@codesourcery.com> writes:
>
> Pedro> For now, simply disable always-inserted if record is on, exactly like
> Pedro> we already disable displaced stepping.
>
> This one seems like a plain bug fix that could go in immediately.
Yeah. I've applied it now, which this ChangeLog entry:
2011-12-05 Pedro Alves <pedro@codesourcery.com>
* breakpoint.c: Include record.h.
(breakpoints_always_inserted_mode): Return false when the record
target is in use.
Thanks,
--
Pedro Alves
^ permalink raw reply [flat|nested] 53+ messages in thread
* [RFC/WIP PATCH 10/14] Comment out new info breakpoints output, in order to not break the test suite.
2011-11-28 15:39 [RFC/WIP PATCH 00/14] I/T sets Pedro Alves
` (3 preceding siblings ...)
2011-11-28 15:39 ` [RFC/WIP PATCH 01/14] Breakpoints always-inserted and the record target Pedro Alves
@ 2011-11-28 15:40 ` Pedro Alves
2011-11-28 15:40 ` [RFC/WIP PATCH 12/14] Fix deref of stale pointer Pedro Alves
` (12 subsequent siblings)
17 siblings, 0 replies; 53+ messages in thread
From: Pedro Alves @ 2011-11-28 15:40 UTC (permalink / raw)
To: gdb-patches
---
gdb/breakpoint.c | 2 ++
1 files changed, 2 insertions(+), 0 deletions(-)
diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index 23bae18..75badf9 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -4962,6 +4962,7 @@ print_one_breakpoint_location (struct breakpoint *b,
ui_out_text (uiout, "\n");
}
+#if 0 // disabled in order to be able to run the testsuite
if (!part_of_multiple && b->trigger_set != NULL)
{
ui_out_text (uiout, "\tstop only in trigger-set: [");
@@ -4981,6 +4982,7 @@ print_one_breakpoint_location (struct breakpoint *b,
ui_out_field_string (uiout, "stop-set", itset_spec (b->stop_set));
ui_out_text (uiout, "]\n");
}
+#endif
if (!part_of_multiple && b->hit_count)
{
^ permalink raw reply [flat|nested] 53+ messages in thread
* [RFC/WIP PATCH 12/14] Fix deref of stale pointer
2011-11-28 15:39 [RFC/WIP PATCH 00/14] I/T sets Pedro Alves
` (4 preceding siblings ...)
2011-11-28 15:40 ` [RFC/WIP PATCH 10/14] Comment out new info breakpoints output, in order to not break the test suite Pedro Alves
@ 2011-11-28 15:40 ` Pedro Alves
2011-11-28 15:40 ` [RFC/WIP PATCH 07/14] Expand %ITSET% in the prompt to the current I/T set Pedro Alves
` (11 subsequent siblings)
17 siblings, 0 replies; 53+ messages in thread
From: Pedro Alves @ 2011-11-28 15:40 UTC (permalink / raw)
To: gdb-patches
ecs->event_thread was being referenced somewhere after
target_mourn_inferior. I don't recall where now though... :-P In any
case, I'm not sure this is the right fix, so I'll need to revert and
revisit. Meanwhile, this is necessary to get things working.
---
gdb/infrun.c | 2 ++
1 files changed, 2 insertions(+), 0 deletions(-)
diff --git a/gdb/infrun.c b/gdb/infrun.c
index 6b16184..44fa074 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -4011,6 +4011,7 @@ handle_inferior_event (struct execution_control_state *ecs)
current_inferior ()->exit_code = (LONGEST) ecs->ws.value.integer;
gdb_flush (gdb_stdout);
+ ecs->event_thread = NULL;
target_mourn_inferior ();
singlestep_breakpoints_inserted_p = 0;
cancel_single_step_breakpoints ();
@@ -4033,6 +4034,7 @@ handle_inferior_event (struct execution_control_state *ecs)
target_kill() was called here, which hints that fatal signals aren't
really fatal on some systems. If that's true, then some changes
may be needed. */
+ ecs->event_thread = NULL;
target_mourn_inferior ();
print_signal_exited_reason (ecs->ws.value.sig);
^ permalink raw reply [flat|nested] 53+ messages in thread
* [RFC/WIP PATCH 07/14] Expand %ITSET% in the prompt to the current I/T set.
2011-11-28 15:39 [RFC/WIP PATCH 00/14] I/T sets Pedro Alves
` (5 preceding siblings ...)
2011-11-28 15:40 ` [RFC/WIP PATCH 12/14] Fix deref of stale pointer Pedro Alves
@ 2011-11-28 15:40 ` Pedro Alves
2011-11-29 21:22 ` Tom Tromey
2011-11-28 15:40 ` [RFC/WIP PATCH 09/14] I/T set support for breakpoints - trigger set, and stop set Pedro Alves
` (10 subsequent siblings)
17 siblings, 1 reply; 53+ messages in thread
From: Pedro Alves @ 2011-11-28 15:40 UTC (permalink / raw)
To: gdb-patches
This adds a simple hook that allows expanding "%ITSET%" in the prompt
to the current set.
Here's how I use it currently:
$ gdb -q -nx -ex "set prompt %ITSET%> " ~/gdb/tests/threads
[all]> info threads
Id Target Id Frame
3 Thread 0x7ffff7028700 (LWP 6890) "threads" 0x00007ffff7bcc78e in __lll_lock_wait_private () from /lib/x86_64-linux-gnu/libpthread.so.0
* 2 Thread 0x7ffff7829700 (LWP 6889) "threads" thread_function0 (arg=0x0) at threads.c:63
1 Thread 0x7ffff7fcb720 (LWP 6884) "threads" 0x00007ffff7910011 in clone () from /lib/x86_64-linux-gnu/libc.so.6
[all]> itfocus [1.1]
New focus: Current inferior is 1.
[1.1]>
Not sure this is the way to go, or whether we'll only support this
through python. This was simple enough to make sure I could work on
the rest of the stuff without getting lost. I'll also make use of
this in all examples in this series.
---
gdb/event-top.c | 40 +++++++++++++++++++++++++++++++++++++++-
1 files changed, 39 insertions(+), 1 deletions(-)
diff --git a/gdb/event-top.c b/gdb/event-top.c
index a276690..de040cb 100644
--- a/gdb/event-top.c
+++ b/gdb/event-top.c
@@ -23,6 +23,7 @@
#include "defs.h"
#include "top.h"
#include "inferior.h"
+#include "itset.h"
#include "target.h"
#include "terminal.h" /* for job_control */
#include "event-loop.h"
@@ -36,6 +37,7 @@
#include "observer.h"
#include "continuations.h"
#include "gdbcmd.h" /* for dont_repeat() */
+#include "gdb_obstack.h"
/* readline include files. */
#include "readline/readline.h"
@@ -215,6 +217,38 @@ change_line_handler (void)
}
}
+static char *
+expand_gdb_prompt (char *prompt)
+{
+ struct obstack obstack;
+ char *p;
+
+ obstack_init (&obstack);
+
+ p = prompt;
+ while (*p)
+ {
+ if (CONST_STRNEQ (p, "%ITSET%"))
+ {
+ obstack_grow_str (&obstack, "[");
+ if (itset_name (current_itset) != NULL)
+ obstack_grow_str (&obstack, itset_name (current_itset));
+ else
+ obstack_grow_str (&obstack, itset_spec (current_itset));
+ obstack_grow_str (&obstack, "]");
+ p += sizeof ("%ITSET%") - 1;
+ continue;
+ }
+
+ obstack_1grow (&obstack, *p);
+ p++;
+ }
+
+ obstack_1grow (&obstack, '\0');
+
+ return xstrdup (obstack_finish (&obstack));
+}
+
/* Displays the prompt. If the argument NEW_PROMPT is NULL, the
prompt that is displayed is the current top level prompt.
Otherwise, it displays whatever NEW_PROMPT is as a local/secondary
@@ -276,8 +310,12 @@ display_gdb_prompt (char *new_prompt)
}
else
{
+ char *top;
+
/* Display the top level prompt. */
- actual_gdb_prompt = top_level_prompt ();
+ top = top_level_prompt ();
+ actual_gdb_prompt = expand_gdb_prompt (top);
+ xfree (top);
}
}
else
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [RFC/WIP PATCH 07/14] Expand %ITSET% in the prompt to the current I/T set.
2011-11-28 15:40 ` [RFC/WIP PATCH 07/14] Expand %ITSET% in the prompt to the current I/T set Pedro Alves
@ 2011-11-29 21:22 ` Tom Tromey
2011-12-16 19:07 ` Pedro Alves
0 siblings, 1 reply; 53+ messages in thread
From: Tom Tromey @ 2011-11-29 21:22 UTC (permalink / raw)
To: Pedro Alves; +Cc: gdb-patches
>>>>> "Pedro" == Pedro Alves <pedro@codesourcery.com> writes:
Pedro> Not sure this is the way to go, or whether we'll only support this
Pedro> through python. This was simple enough to make sure I could work on
Pedro> the rest of the stuff without getting lost. I'll also make use of
Pedro> this in all examples in this series.
I don't mind if you want to push some prompt stuff into the core, but I
think it would be better if done in a way that was compatible with the
Python prompt substitution code -- command-wise,
substitution-style-wise, and whatever else.
That code hasn't seen a release yet, so it can be changed if need be.
(Though time is running out.)
Tom
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [RFC/WIP PATCH 07/14] Expand %ITSET% in the prompt to the current I/T set.
2011-11-29 21:22 ` Tom Tromey
@ 2011-12-16 19:07 ` Pedro Alves
2011-12-16 19:09 ` Tom Tromey
0 siblings, 1 reply; 53+ messages in thread
From: Pedro Alves @ 2011-12-16 19:07 UTC (permalink / raw)
To: gdb-patches; +Cc: Tom Tromey
On Tuesday 29 November 2011 21:21:08, Tom Tromey wrote:
> >>>>> "Pedro" == Pedro Alves <pedro@codesourcery.com> writes:
>
> Pedro> Not sure this is the way to go, or whether we'll only support this
> Pedro> through python. This was simple enough to make sure I could work on
> Pedro> the rest of the stuff without getting lost. I'll also make use of
> Pedro> this in all examples in this series.
>
> I don't mind if you want to push some prompt stuff into the core, but I
> think it would be better if done in a way that was compatible with the
> Python prompt substitution code -- command-wise,
> substitution-style-wise, and whatever else.
I'm not certain about what you mean by this. The %%
sustitution added applies after the python substitution, basically
a sed before display. The result isn't stored anywhere.
The "only" thing affected AFAICS is that to print '%', we'd have
to escape it (e.g., %% => '%'), though I haven't implemented that.
> That code hasn't seen a release yet, so it can be changed if need be.
> (Though time is running out.)
ETIMEDOUT
--
Pedro Alves
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [RFC/WIP PATCH 07/14] Expand %ITSET% in the prompt to the current I/T set.
2011-12-16 19:07 ` Pedro Alves
@ 2011-12-16 19:09 ` Tom Tromey
2011-12-16 19:38 ` Pedro Alves
0 siblings, 1 reply; 53+ messages in thread
From: Tom Tromey @ 2011-12-16 19:09 UTC (permalink / raw)
To: Pedro Alves; +Cc: gdb-patches
>>>>> "Pedro" == Pedro Alves <pedro@codesourcery.com> writes:
Tom> I don't mind if you want to push some prompt stuff into the core, but I
Tom> think it would be better if done in a way that was compatible with the
Tom> Python prompt substitution code -- command-wise,
Tom> substitution-style-wise, and whatever else.
Pedro> I'm not certain about what you mean by this. The %%
Pedro> sustitution added applies after the python substitution, basically
Pedro> a sed before display. The result isn't stored anywhere.
Pedro> The "only" thing affected AFAICS is that to print '%', we'd have
Pedro> to escape it (e.g., %% => '%'), though I haven't implemented that.
It just seems strange to me for set extended-prompt to use one syntax,
but for this to use a different syntax. I realize consistency is not
GDB's strong suit, but this goes too far :)
Tom
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [RFC/WIP PATCH 07/14] Expand %ITSET% in the prompt to the current I/T set.
2011-12-16 19:09 ` Tom Tromey
@ 2011-12-16 19:38 ` Pedro Alves
0 siblings, 0 replies; 53+ messages in thread
From: Pedro Alves @ 2011-12-16 19:38 UTC (permalink / raw)
To: Tom Tromey; +Cc: gdb-patches
On Friday 16 December 2011 19:06:47, Tom Tromey wrote:
> >>>>> "Pedro" == Pedro Alves <pedro@codesourcery.com> writes:
>
> Tom> I don't mind if you want to push some prompt stuff into the core, but I
> Tom> think it would be better if done in a way that was compatible with the
> Tom> Python prompt substitution code -- command-wise,
> Tom> substitution-style-wise, and whatever else.
>
> Pedro> I'm not certain about what you mean by this. The %%
> Pedro> sustitution added applies after the python substitution, basically
> Pedro> a sed before display. The result isn't stored anywhere.
> Pedro> The "only" thing affected AFAICS is that to print '%', we'd have
> Pedro> to escape it (e.g., %% => '%'), though I haven't implemented that.
>
> It just seems strange to me for set extended-prompt to use one syntax,
> but for this to use a different syntax. I realize consistency is not
> GDB's strong suit, but this goes too far :)
Oh, I was aware of gdb.prompt_hook, but I completely missed
"set extended-prompt". :-) And it looks like a just built gdb doesn't
pick up commands from $srcdir/gdb/python/lib/gdb/command/ . grep
found it though.
--
Pedro Alves
^ permalink raw reply [flat|nested] 53+ messages in thread
* [RFC/WIP PATCH 09/14] I/T set support for breakpoints - trigger set, and stop set
2011-11-28 15:39 [RFC/WIP PATCH 00/14] I/T sets Pedro Alves
` (6 preceding siblings ...)
2011-11-28 15:40 ` [RFC/WIP PATCH 07/14] Expand %ITSET% in the prompt to the current I/T set Pedro Alves
@ 2011-11-28 15:40 ` Pedro Alves
2011-11-29 22:02 ` Tom Tromey
2011-11-28 15:40 ` [RFC/WIP PATCH 04/14] Implement all-stop on top of a target running non-stop mode Pedro Alves
` (9 subsequent siblings)
17 siblings, 1 reply; 53+ messages in thread
From: Pedro Alves @ 2011-11-28 15:40 UTC (permalink / raw)
To: gdb-patches
This adds support for setting a breakpoint that only triggers on a
given set (a superset of the current thread specific breakpoints
support). In addition, it adds support for specifying the set of
threads that are suspended when the breakpoint is triggered.
Breakpoints need two sets. The trigger set, which is a generalization
of the "break foo thread N", meaning the set of inferiors/threads
where the breakpoint should fire, and, a suspend/stop set, which is
the set of inferiors/threads that should be suspended when the
breakpoint fires.
The trigger set of breakpoints is set from the current set at the time
the breakpoint is created. The stop set is passed explicitly as
optional switch. E.g.,:
[TRIGGER-SET] break [-stop [STOP-SET]] LINESPEC
This leaves LINESPEC last, so that we can keep supporting the current
form, but avoid more hacks in linespecs like the special termination
for "thread/task/if" in the lexers --- that wouldn't work for `['.
So the old:
(gdb) break LINESPEC
still works just the same. The breakpoint's trigger set will be
inferred from the current set as set by itfocus, or a [SET] prefix,
and, the stop set is inferred from the "set non-stop" global option.
If non-stop is on, only the thread that triggers the breakpoint should
be suspended; if non-stop is off, then all threads will be suspended
when the breakpoint fires.
E.g.,
(gdb) info threads
Id Target Id Frame
3 Thread 0x7ffff7028700 (LWP 2296) "threads" (running)
* 2 Thread 0x7ffff7829700 (LWP 2295) "threads" thread_function0 (arg=0x0) at threads.c:63
1 Thread 0x7ffff7fcb720 (LWP 2290) "threads" (running)
(gdb) [.2] break -stop [.3] 63
Breakpoint 4 at 0x40076d: file threads.c, line 63.
Breakpoint 4 triggers on thread 2 (equivalent to break 63 thread 2),
and when the breakpoint fires, thread 3 is suspended. Like so:
(gdb) c -a&
Continuing.
(gdb)
Breakpoint 4, thread_function0 (arg=0x0) at threads.c:63
63 (*myp) ++;
info threads
Id Target Id Frame
3 Thread 0x7ffff7028700 (LWP 2296) "threads" 0x00007ffff78d75ad in nanosleep () from /lib/x86_64-linux-gnu/libc.so.6
* 2 Thread 0x7ffff7829700 (LWP 2295) "threads" thread_function0 (arg=0x0) at threads.c:63
1 Thread 0x7ffff7fcb720 (LWP 2290) "threads" (running)
(gdb) info breakpoints
Num Type Disp Enb Address What
4 breakpoint keep y 0x000000000040076d in thread_function0 at threads.c:63
stop only in trigger-set: [.2]
suspend all in stop-set: [.3]
breakpoint already hit 1 time
We can make a breakpoint that stops the world with (recall I
had non-stop on):
(gdb) del 4
(gdb) [.2] break -stop [all] 63
Breakpoint 5 at 0x40076d: file threads.c, line 63.
(gdb) c -a&
Continuing.
(gdb)
Breakpoint 5, thread_function0 (arg=0x0) at threads.c:63
63 (*myp) ++;
info threads
Id Target Id Frame
3 Thread 0x7ffff7028700 (LWP 2296) "threads" 0x00007ffff78d75ad in nanosleep () from /lib/x86_64-linux-gnu/libc.so.6
* 2 Thread 0x7ffff7829700 (LWP 2295) "threads" thread_function0 (arg=0x0) at threads.c:63
1 Thread 0x7ffff7fcb720 (LWP 2290) "threads" 0x00007ffff7bc606d in pthread_join () from /lib/x86_64-linux-gnu/libpthread.so.0
Since all-stop was reimplemented on top of the target running in
non-stop mode, when "set non-stop" is off, not specifying a stop set
(or specifying it as empty, like -stop []) means that all threads stop
by default, e.g., `[.2] break 63', but you can still override that
explicitly by specifying "-stop [set]", so the breakpoint only forces
suspension of a given itset, with:
(gdb) [.2] break -stop [.3] 63
Breakpoint 5 at 0x40076d: file threads.c, line 63.
(gdb) c &
Continuing.
(gdb)
Breakpoint 5, thread_function0 (arg=0x0) at threads.c:63
63 (*myp) ++;
info threads
Id Target Id Frame
3 Thread 0x7ffff7028700 (LWP 2645) "threads" 0x00007ffff78d75ad in nanosleep () from /lib/x86_64-linux-gnu/libc.so.6
* 2 Thread 0x7ffff7829700 (LWP 2644) "threads" thread_function0 (arg=0x0) at threads.c:63
1 Thread 0x7ffff7fcb720 (LWP 2610) "threads" (running)
---
gdb/breakpoint.c | 135 ++++++++++++++++++++++++++++++++++++++++++++++++++----
gdb/breakpoint.h | 13 +++++
gdb/infrun.c | 52 +++++++++++++++++----
3 files changed, 182 insertions(+), 18 deletions(-)
diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index bdc5d38..23bae18 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -34,6 +34,7 @@
#include "value.h"
#include "command.h"
#include "inferior.h"
+#include "itset.h"
#include "gdbthread.h"
#include "target.h"
#include "language.h"
@@ -3035,6 +3036,21 @@ hardware_watchpoint_inserted_in_range (struct address_space *aspace,
return 0;
}
+/* Test whether this stopping thread is in the I/T set for this
+ breakpoint. */
+
+static int
+bpstat_check_trigger_set (const struct breakpoint *b, struct thread_info *thread)
+{
+ if (b->trigger_set == NULL)
+ return 1;
+
+ if (itset_contains_thread (b->trigger_set, thread))
+ return 1;
+
+ return 0;
+}
+
/* breakpoint_thread_match (PC, PTID) returns true if the breakpoint at
PC is valid for process/thread PTID. */
@@ -3044,7 +3060,7 @@ breakpoint_thread_match (struct address_space *aspace, CORE_ADDR pc,
{
struct bp_location *bl, **blp_tmp;
/* The thread and task IDs associated to PTID, computed lazily. */
- int thread = -1;
+ struct thread_info *thread = NULL;
int task = 0;
ALL_BP_LOCATIONS (bl, blp_tmp)
@@ -3066,9 +3082,9 @@ breakpoint_thread_match (struct address_space *aspace, CORE_ADDR pc,
/* This is a thread-specific breakpoint. Check that ptid
matches that thread. If thread hasn't been computed yet,
it is now time to do so. */
- if (thread == -1)
- thread = pid_to_thread_id (ptid);
- if (bl->owner->thread != thread)
+ if (thread == NULL)
+ thread = find_thread_ptid (ptid);
+ if (bl->owner->thread != thread->num)
continue;
}
@@ -3083,6 +3099,16 @@ breakpoint_thread_match (struct address_space *aspace, CORE_ADDR pc,
continue;
}
+ if (bl->owner->trigger_set != NULL)
+ {
+ /* A breakpoint with a trigger itset. Check that ptid
+ matches that set. */
+ if (thread == NULL)
+ thread = find_thread_ptid (ptid);
+ if (!bpstat_check_trigger_set (bl->owner, thread))
+ continue;
+ }
+
if (overlay_debugging
&& section_is_overlay (bl->section)
&& !section_is_mapped (bl->section))
@@ -3974,7 +4000,7 @@ bpstat_check_watchpoint (bpstat bs)
static void
bpstat_check_breakpoint_conditions (bpstat bs, ptid_t ptid)
{
- int thread_id = pid_to_thread_id (ptid);
+ struct thread_info *thread = find_thread_ptid (ptid);
const struct bp_location *bl;
struct breakpoint *b;
@@ -4076,7 +4102,12 @@ bpstat_check_breakpoint_conditions (bpstat bs, ptid_t ptid)
{
bs->stop = 0;
}
- else if (b->thread != -1 && b->thread != thread_id)
+ else if (b->thread != -1 && b->thread != thread->num)
+ {
+ bs->stop = 0;
+ }
+ else if (b->trigger_set != NULL
+ && !bpstat_check_trigger_set (b, thread))
{
bs->stop = 0;
}
@@ -4504,6 +4535,33 @@ bpstat_what (bpstat bs_head)
return retval;
}
+/* Tell us what we should suspend. */
+
+struct itset *
+bpstat_stop_set (bpstat bs_head)
+{
+ bpstat bs;
+
+ for (bs = bs_head; bs != NULL; bs = bs->next)
+ {
+ if (bs->breakpoint_at == NULL)
+ {
+ /* I suspect this can happen if it was a momentary
+ breakpoint which has since been deleted. */
+ continue;
+ }
+
+ if (!bs->stop)
+ continue;
+
+ /* XXX: Should be the union of the sets of all breakpoints that
+ caused a stop? */
+ return itset_reference (bs->breakpoint_at->stop_set);
+ }
+
+ return NULL;
+}
+
/* Nonzero if we should step constantly (e.g. watchpoints on machines
without hardware support). This isn't related to a specific bpstat,
just to things like whether watchpoints are set. */
@@ -4903,7 +4961,27 @@ print_one_breakpoint_location (struct breakpoint *b,
ui_out_field_int (uiout, "thread", b->thread);
ui_out_text (uiout, "\n");
}
-
+
+ if (!part_of_multiple && b->trigger_set != NULL)
+ {
+ ui_out_text (uiout, "\tstop only in trigger-set: [");
+ if (itset_name (b->trigger_set) != NULL)
+ ui_out_field_string (uiout, "trigger-set", itset_name (b->trigger_set));
+ else
+ ui_out_field_string (uiout, "trigger-set", itset_spec (b->trigger_set));
+ ui_out_text (uiout, "]\n");
+ }
+
+ if (!part_of_multiple && b->stop_set != NULL)
+ {
+ ui_out_text (uiout, "\tsuspend all in stop-set: [");
+ if (itset_name (b->stop_set) != NULL)
+ ui_out_field_string (uiout, "stop-set", itset_name (b->stop_set));
+ else
+ ui_out_field_string (uiout, "stop-set", itset_spec (b->stop_set));
+ ui_out_text (uiout, "]\n");
+ }
+
if (!part_of_multiple && b->hit_count)
{
/* FIXME should make an annotation for this. */
@@ -5731,6 +5809,7 @@ init_raw_breakpoint_without_location (struct breakpoint *b,
b->language = current_language->la_language;
b->input_radix = input_radix;
b->thread = -1;
+ b->trigger_set = NULL;
b->enable_state = bp_enabled;
b->next = 0;
b->silent = 0;
@@ -7274,6 +7353,7 @@ init_breakpoint_sal (struct breakpoint *b, struct gdbarch *gdbarch,
struct symtabs_and_lines sals, char *addr_string,
char *cond_string,
enum bptype type, enum bpdisp disposition,
+ struct itset *trigger_set, struct itset *stop_set,
int thread, int task, int ignore_count,
const struct breakpoint_ops *ops, int from_tty,
int enabled, int internal, int display_canonical)
@@ -7314,6 +7394,8 @@ init_breakpoint_sal (struct breakpoint *b, struct gdbarch *gdbarch,
init_raw_breakpoint (b, gdbarch, sal, type, ops);
b->thread = thread;
b->task = task;
+ b->trigger_set = trigger_set;
+ b->stop_set = stop_set;
b->cond_string = cond_string;
b->ignore_count = ignore_count;
@@ -7399,6 +7481,7 @@ create_breakpoint_sal (struct gdbarch *gdbarch,
struct symtabs_and_lines sals, char *addr_string,
char *cond_string,
enum bptype type, enum bpdisp disposition,
+ struct itset *trigger_set, struct itset *stop_set,
int thread, int task, int ignore_count,
const struct breakpoint_ops *ops, int from_tty,
int enabled, int internal, int display_canonical)
@@ -7422,6 +7505,7 @@ create_breakpoint_sal (struct gdbarch *gdbarch,
sals, addr_string,
cond_string,
type, disposition,
+ trigger_set, stop_set,
thread, task, ignore_count,
ops, from_tty,
enabled, internal, display_canonical);
@@ -7583,6 +7667,7 @@ create_breakpoints_sal (struct gdbarch *gdbarch,
struct linespec_result *canonical,
char *cond_string,
enum bptype type, enum bpdisp disposition,
+ struct itset *trigger_set, struct itset *stop_set,
int thread, int task, int ignore_count,
const struct breakpoint_ops *ops, int from_tty,
int enabled, int internal)
@@ -7596,6 +7681,7 @@ create_breakpoints_sal (struct gdbarch *gdbarch,
create_breakpoint_sal (gdbarch, expanded, canonical->canonical[i],
cond_string, type, disposition,
+ trigger_set, stop_set,
thread, task, ignore_count, ops,
from_tty, enabled, internal,
canonical->special_display);
@@ -7619,7 +7705,8 @@ parse_breakpoint_sals (char **address,
/* If no arg given, or if first arg is 'if ', use the default
breakpoint. */
- if ((*address) == NULL
+ if (*address == NULL
+ || **address == '\0'
|| (strncmp ((*address), "if", 2) == 0 && isspace ((*address)[2])))
{
/* The last displayed codepoint, if it's valid, is our default breakpoint
@@ -7884,6 +7971,9 @@ create_breakpoint (struct gdbarch *gdbarch,
int pending = 0;
int task = 0;
int prev_bkpt_count = breakpoint_count;
+ char *p;
+ struct itset *trigger_set = itset_reference (current_itset);
+ struct itset *stop_set = itset_reference (trigger_set);
gdb_assert (ops != NULL);
@@ -7904,6 +7994,30 @@ create_breakpoint (struct gdbarch *gdbarch,
goto done;
}
+ if (arg != NULL)
+ {
+ while (*arg)
+ {
+ arg = skip_spaces (arg);
+ p = skip_to_space (arg);
+
+ if (strncmp (arg, "-stop", p - arg) == 0)
+ {
+ p = skip_spaces (p);
+ itset_free (stop_set);
+ stop_set = itset_create (&p);
+ arg = p;
+ }
+ else if (strcmp (arg, "--") == 0)
+ {
+ arg += 2;
+ break;
+ }
+ else
+ break;
+ }
+ }
+
TRY_CATCH (e, RETURN_MASK_ALL)
{
parse_breakpoint_sals (&arg, &sals, &canonical);
@@ -8046,7 +8160,7 @@ create_breakpoint (struct gdbarch *gdbarch,
canonical.canonical[i],
cond_string, type_wanted,
tempflag ? disp_del : disp_donttouch,
- thread, task, ignore_count, ops,
+ NULL, NULL, thread, task, ignore_count, ops,
from_tty, enabled, internal,
canonical.special_display);
/* Given that its possible to have multiple markers with
@@ -8066,6 +8180,7 @@ create_breakpoint (struct gdbarch *gdbarch,
create_breakpoints_sal (gdbarch, sals, &canonical, cond_string,
type_wanted,
tempflag ? disp_del : disp_donttouch,
+ trigger_set, stop_set,
thread, task, ignore_count, ops, from_tty,
enabled, internal);
}
@@ -10941,6 +11056,8 @@ base_breakpoint_dtor (struct breakpoint *self)
xfree (self->addr_string);
xfree (self->addr_string_range_end);
xfree (self->source_file);
+ itset_free (self->trigger_set);
+ itset_free (self->stop_set);
}
static struct bp_location *
diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h
index df50438..45c98cb 100644
--- a/gdb/breakpoint.h
+++ b/gdb/breakpoint.h
@@ -32,6 +32,7 @@ struct get_number_or_range_state;
struct thread_info;
struct bpstats;
struct bp_location;
+struct itset;
/* This is the maximum number of bytes a breakpoint instruction can
take. Feel free to increase it. It's just used in a few places to
@@ -609,6 +610,15 @@ struct breakpoint
or 0 if don't care. */
int task;
+ /* I/T set controlling where this breakpoint will stop. */
+ struct itset *trigger_set;
+
+ /* The set that should be suspended once the breakpoint has
+ triggered. If empty, defaults to consulting the "non-stop"
+ setting: if that is on, just the triggering thread should stop;
+ otherwise, all threads in the TRIGGER_SET set stop. */
+ struct itset *stop_set;
+
/* Count of the number of times this breakpoint was taken, dumped
with the info, but not used for anything else. Useful for
seeing how many times you hit a break prior to the program
@@ -860,6 +870,9 @@ enum print_stop_action
/* Tell what to do about this bpstat. */
struct bpstat_what bpstat_what (bpstat);
+
+/* Tell us what we should suspend. */
+extern struct itset *bpstat_stop_set (bpstat);
\f
/* Find the bpstat associated with a breakpoint. NULL otherwise. */
bpstat bpstat_find_breakpoint (bpstat, struct breakpoint *);
diff --git a/gdb/infrun.c b/gdb/infrun.c
index e0b282f..c279508 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -26,6 +26,7 @@
#include "symtab.h"
#include "frame.h"
#include "inferior.h"
+#include "itset.h"
#include "exceptions.h"
#include "breakpoint.h"
#include "gdb_wait.h"
@@ -2527,6 +2528,8 @@ struct execution_control_state
char *stop_func_name;
int new_thread_event;
int wait_some_more;
+
+ struct itset *stop_set;
};
static void handle_inferior_event (struct execution_control_state *ecs);
@@ -2900,7 +2903,7 @@ prepare_for_detach (void)
discard_cleanups (old_chain_1);
}
-static void stop_all_threads (void);
+static void stop_all_threads (struct itset *stop_set);
static int adjust_pc_after_break (struct thread_info *thread,
struct target_waitstatus *ws);
@@ -3002,7 +3005,7 @@ wait_for_inferior (void)
if (!non_stop && target_is_non_stop_p () && !stop_only_if_needed)
{
- stop_all_threads ();
+ stop_all_threads (NULL);
if (ecs->ws.kind != TARGET_WAITKIND_NO_RESUMED
&& ecs->ws.kind != TARGET_WAITKIND_EXITED
@@ -3173,7 +3176,7 @@ fetch_inferior_event (void *client_data)
if (!non_stop && target_is_non_stop_p () && !stop_only_if_needed
&& ecs->ws.kind != TARGET_WAITKIND_IGNORE)
{
- stop_all_threads ();
+ stop_all_threads (NULL);
/* select event thread */
/* cancel breakpoints */
}
@@ -3580,8 +3583,25 @@ wait_one (ptid_t wait_ptid, struct target_waitstatus *ws)
return event_ptid;
}
+#define itset_spec_empty(SET) \
+ ((SET) == NULL || itset_spec (SET)[0] == '\0')
+
+static int
+stop_set_match (struct itset *stop_set, struct thread_info *t)
+{
+ int inf_id;
+
+ if (itset_spec_empty (stop_set))
+ return !non_stop;
+
+ if (itset_contains_thread (stop_set, t))
+ return 1;
+
+ return 0;
+}
+
static void
-stop_all_threads (void)
+stop_all_threads (struct itset *stop_set)
{
/* We may need multiple passes to discover all threads. */
int pass;
@@ -3611,6 +3631,7 @@ stop_all_threads (void)
/* Go through all threads looking for threads that we need
to tell the target to stop. */
ALL_LIVE_THREADS (t)
+ if (stop_set_match (stop_set, t))
{
if (t->executing)
{
@@ -3655,6 +3676,11 @@ stop_all_threads (void)
pass = -1;
event_ptid = wait_one (minus_one_ptid, &ws);
+
+ /* FIXME: if EVENT_PTID isn't in the trigger or stop sets,
+ we'll need to re-resume or handle the event
+ afterwards. */
+
if (ws.kind == TARGET_WAITKIND_NO_RESUMED)
/* All resumed threads exited. */
;
@@ -5129,7 +5155,11 @@ process_event_stop_test:
/* We are about to nuke the step_resume_breakpointt via the
cleanup chain, so no need to worry about it here. */
+ ecs->stop_set
+ = bpstat_stop_set (ecs->event_thread->control.stop_bpstat);
stop_stepping (ecs);
+ itset_free (ecs->stop_set);
+ ecs->stop_set = NULL;
return;
case BPSTAT_WHAT_STOP_SILENT:
@@ -5140,7 +5170,11 @@ process_event_stop_test:
/* We are about to nuke the step_resume_breakpoin via the
cleanup chain, so no need to worry about it here. */
+ ecs->stop_set
+ = bpstat_stop_set (ecs->event_thread->control.stop_bpstat);
stop_stepping (ecs);
+ itset_free (ecs->stop_set);
+ ecs->stop_set = NULL;
return;
case BPSTAT_WHAT_HP_STEP_RESUME:
@@ -6157,13 +6191,12 @@ stop_stepping (struct execution_control_state *ecs)
/* Let callers know we don't want to wait for the inferior anymore. */
ecs->wait_some_more = 0;
- if (!non_stop
- && target_is_non_stop_p ()
+ if (target_is_non_stop_p ()
&& stop_only_if_needed)
{
struct thread_info *t;
- stop_all_threads ();
+ stop_all_threads (ecs->stop_set);
cancel_breakpoints ();
@@ -6171,7 +6204,8 @@ stop_stepping (struct execution_control_state *ecs)
are now stopped until a new resume action is sent
over. */
ALL_LIVE_THREADS (t)
- t->control.resumed = 0;
+ if (stop_set_match (ecs->stop_set, t))
+ t->control.resumed = 0;
}
}
@@ -6230,7 +6264,7 @@ keep_going (struct execution_control_state *ecs)
/* Since we can't do a displaced step, we have to remove
the breakpoint while we step it. To keep things
simple, we remove them all. */
- stop_all_threads ();
+ stop_all_threads (NULL);
cancel_breakpoints ();
/* In all-stop, from the core's perspective, all threads
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [RFC/WIP PATCH 09/14] I/T set support for breakpoints - trigger set, and stop set
2011-11-28 15:40 ` [RFC/WIP PATCH 09/14] I/T set support for breakpoints - trigger set, and stop set Pedro Alves
@ 2011-11-29 22:02 ` Tom Tromey
2011-11-30 19:38 ` Tom Tromey
2011-12-16 19:29 ` Pedro Alves
0 siblings, 2 replies; 53+ messages in thread
From: Tom Tromey @ 2011-11-29 22:02 UTC (permalink / raw)
To: Pedro Alves; +Cc: gdb-patches
>>>>> "Pedro" == Pedro Alves <pedro@codesourcery.com> writes:
Pedro> This adds support for setting a breakpoint that only triggers on a
Pedro> given set (a superset of the current thread specific breakpoints
Pedro> support). In addition, it adds support for specifying the set of
Pedro> threads that are suspended when the breakpoint is triggered.
Pedro> Breakpoints need two sets. The trigger set, which is a generalization
Pedro> of the "break foo thread N", meaning the set of inferiors/threads
Pedro> where the breakpoint should fire, and, a suspend/stop set, which is
Pedro> the set of inferiors/threads that should be suspended when the
Pedro> breakpoint fires.
What happens if the user types:
[1.*] break function thread 3
How about something contradictory like
[.2] break function thread 3
Pedro> The trigger set of breakpoints is set from the current set at the time
Pedro> the breakpoint is created. The stop set is passed explicitly as
Pedro> optional switch. E.g.,:
Pedro> [TRIGGER-SET] break [-stop [STOP-SET]] LINESPEC
Pedro> This leaves LINESPEC last, so that we can keep supporting the current
Pedro> form, but avoid more hacks in linespecs like the special termination
Pedro> for "thread/task/if" in the lexers --- that wouldn't work for `['.
Looks good to me. Overdue, even :-)
Pedro> and, the stop set is inferred from the "set non-stop" global option.
Pedro> If non-stop is on, only the thread that triggers the breakpoint should
Pedro> be suspended; if non-stop is off, then all threads will be suspended
Pedro> when the breakpoint fires.
It seems to me that the stop set has to be a superset of the trigger
set. Otherwise you get into a funny situation where a thread hits a
breakpoint, causing only other threads to stop.
Or maybe this is intentional? I can't picture a use for it myself,
though.
Anyway, if this is a requirement, I think it should be enforced.
Pedro> +static int
Pedro> +bpstat_check_trigger_set (const struct breakpoint *b, struct thread_info *thread)
Pedro> +{
Pedro> + if (b->trigger_set == NULL)
Pedro> + return 1;
Pedro> +
Pedro> + if (itset_contains_thread (b->trigger_set, thread))
Pedro> + return 1;
Pedro> +
Pedro> + return 0;
Delightfully simple. A couple notes though...
First, I've been thinking we should probably make breakpoint re-setting
more fine-grained. The idea would be to classify the events that
current cause a re-set, giving them separate APIs in breakpoint.c, so
that we can make re-setting more efficient. E.g., a new inferior should
not cause a breakpoint to reset if the breakpoint cannot possibly match
that inferior. I'm just trolling for your reaction to this.
Second, a while back on gdb@ there was some talk of pushing
thread-specific breakpoints to the target. This still seems like a good
idea to me. I just wonder how this would interact with trigger sets,
which are fairly general and which can depend on information known only
to gdb, like the inferior numbering. I suppose one answer is, "use the
'thread' syntax for that".
Tom
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [RFC/WIP PATCH 09/14] I/T set support for breakpoints - trigger set, and stop set
2011-11-29 22:02 ` Tom Tromey
@ 2011-11-30 19:38 ` Tom Tromey
2011-12-16 19:29 ` Pedro Alves
1 sibling, 0 replies; 53+ messages in thread
From: Tom Tromey @ 2011-11-30 19:38 UTC (permalink / raw)
To: Pedro Alves; +Cc: gdb-patches
>>>>> "Tom" == Tom Tromey <tromey@redhat.com> writes:
Tom> First, I've been thinking we should probably make breakpoint re-setting
Tom> more fine-grained. The idea would be to classify the events that
Tom> current cause a re-set, giving them separate APIs in breakpoint.c, so
Tom> that we can make re-setting more efficient. E.g., a new inferior should
Tom> not cause a breakpoint to reset if the breakpoint cannot possibly match
Tom> that inferior. I'm just trolling for your reaction to this.
Also, I remembered that I think this has a small implication for the
itset code. Some event may make it impossible for a breakpoint to ever
trigger again; for example it could be inferior-specific and the
inferior could exit. It seems like it would be nice to either delete
the breakpoint in this case, or at least warn the user. To do this, an
itset would need a method for "can this ever possibly match again", with
static sets being able to answer "no".
Tom
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [RFC/WIP PATCH 09/14] I/T set support for breakpoints - trigger set, and stop set
2011-11-29 22:02 ` Tom Tromey
2011-11-30 19:38 ` Tom Tromey
@ 2011-12-16 19:29 ` Pedro Alves
1 sibling, 0 replies; 53+ messages in thread
From: Pedro Alves @ 2011-12-16 19:29 UTC (permalink / raw)
To: gdb-patches; +Cc: Tom Tromey
On Tuesday 29 November 2011 22:02:11, Tom Tromey wrote:
> >>>>> "Pedro" == Pedro Alves <pedro@codesourcery.com> writes:
>
> Pedro> This adds support for setting a breakpoint that only triggers on a
> Pedro> given set (a superset of the current thread specific breakpoints
> Pedro> support). In addition, it adds support for specifying the set of
> Pedro> threads that are suspended when the breakpoint is triggered.
>
> Pedro> Breakpoints need two sets. The trigger set, which is a generalization
> Pedro> of the "break foo thread N", meaning the set of inferiors/threads
> Pedro> where the breakpoint should fire, and, a suspend/stop set, which is
> Pedro> the set of inferiors/threads that should be suspended when the
> Pedro> breakpoint fires.
>
> What happens if the user types:
>
> [1.*] break function thread 3
The breakpoint triggers if the thread that hits it is both
in [1.*], and is thread 3.
> How about something contradictory like
>
> [.2] break function thread 3
Breakpoint never triggers. Given that the trigger set may well
be a dynamic set, it seems futile to try to detect such cases. Just
don't do it.
>
> Pedro> The trigger set of breakpoints is set from the current set at the time
> Pedro> the breakpoint is created. The stop set is passed explicitly as
> Pedro> optional switch. E.g.,:
>
> Pedro> [TRIGGER-SET] break [-stop [STOP-SET]] LINESPEC
>
> Pedro> This leaves LINESPEC last, so that we can keep supporting the current
> Pedro> form, but avoid more hacks in linespecs like the special termination
> Pedro> for "thread/task/if" in the lexers --- that wouldn't work for `['.
>
> Looks good to me. Overdue, even :-)
>
> Pedro> and, the stop set is inferred from the "set non-stop" global option.
> Pedro> If non-stop is on, only the thread that triggers the breakpoint should
> Pedro> be suspended; if non-stop is off, then all threads will be suspended
> Pedro> when the breakpoint fires.
>
> It seems to me that the stop set has to be a superset of the trigger
> set. Otherwise you get into a funny situation where a thread hits a
> breakpoint, causing only other threads to stop.
I've defined it such that the thread that triggered the breakpoint
always stops, even if it is not in the stop set. That meant that
and empty stop set is the same behavior as a breakpoint hit in
non-stop mode. The alternative, and because we can't know upfront
which thread will hit the breakpoint, is to have a special set
for the thread that triggered (or more generaly, the current thread),
e.g.:
[all] break -stop [@] LINESPEC
OTOH, `break -stop []' resuming the thread that hit the
breakpoint make the breakpoint look like a tracepoint. Hmm.
>
> Or maybe this is intentional? I can't picture a use for it myself,
> though.
E.g., when the event handling thread triggers this breakpoint (an
incoming event), stop everything in the DSP core. Or all in the
inferior that sent the event.
> Anyway, if this is a requirement, I think it should be enforced.
>
> Pedro> +static int
> Pedro> +bpstat_check_trigger_set (const struct breakpoint *b, struct thread_info *thread)
> Pedro> +{
> Pedro> + if (b->trigger_set == NULL)
> Pedro> + return 1;
> Pedro> +
> Pedro> + if (itset_contains_thread (b->trigger_set, thread))
> Pedro> + return 1;
> Pedro> +
> Pedro> + return 0;
>
> Delightfully simple. A couple notes though...
>
> First, I've been thinking we should probably make breakpoint re-setting
> more fine-grained. The idea would be to classify the events that
> current cause a re-set, giving them separate APIs in breakpoint.c, so
> that we can make re-setting more efficient.
Yeah, we've talked about that in the context of the objfile
sharing discussion, even IIRC.
> E.g., a new inferior should
> not cause a breakpoint to reset if the breakpoint cannot possibly match
> that inferior. I'm just trolling for your reaction to this.
Well, the reaction is "yes". :-)
> Second, a while back on gdb@ there was some talk of pushing
> thread-specific breakpoints to the target. This still seems like a good
> idea to me. I just wonder how this would interact with trigger sets,
> which are fairly general and which can depend on information known only
> to gdb, like the inferior numbering. I suppose one answer is, "use the
> 'thread' syntax for that".
Thread-specific breakpoints on the target would necessarily get the
GDB thread number translated to the target thread number. The target
never sees GDB thread numbers in any circunstance. Thread-specific
breakpoints are already specified with the GDB generic thread number
(break foo thread N). sets could be translated similarly. Even if
the translation isn't 1-1, it's sufficient that the stop set as
sent to the target is larger and includes the whole of the set
specified by the user. E.g., say we only support passing down one
thread id associated with a breakpoint. If the set is unambiguously
for that thread, great, pass that thread id down to the target along
with the breakpoint. If the breakpoint is set with a trigger set
that includes e.g., two threads, threads 1 and 2 of inferior 1,
select the next best target set that includes both of those threads.
That'd be, planting the breakpoint on the process of inferior 1 only,
but not in any other process, and have gdb transparently re-resume all
other threads if they trip on the breakpoint, just like thread-specific
breakpoints are handled today.
--
Pedro Alves
^ permalink raw reply [flat|nested] 53+ messages in thread
* [RFC/WIP PATCH 04/14] Implement all-stop on top of a target running non-stop mode
2011-11-28 15:39 [RFC/WIP PATCH 00/14] I/T sets Pedro Alves
` (7 preceding siblings ...)
2011-11-28 15:40 ` [RFC/WIP PATCH 09/14] I/T set support for breakpoints - trigger set, and stop set Pedro Alves
@ 2011-11-28 15:40 ` Pedro Alves
2011-11-28 15:40 ` [RFC/WIP PATCH 13/14] Make "thread apply all" only loop over threads in the current set Pedro Alves
` (8 subsequent siblings)
17 siblings, 0 replies; 53+ messages in thread
From: Pedro Alves @ 2011-11-28 15:40 UTC (permalink / raw)
To: gdb-patches
This implements all-stop on top of a target running in non-stop mode.
From the user's perspective, the all-stop mode is really just a
special case of being able to stop and resume specific sets of
threads, so it makes sense to do this step first.
This starts by adding a new target method "target_is_non_stop_p",
where targets that can do non-stop can always return true, even if
"set non-stop" is off. With this, even in all-stop, the target is no
longer in charge of stopping all threads before reporting an event to
the core -- the core takes care of it when it sees fit. For example,
when "next"- or "step"-ing, we can avoid stopping and resuming every
thread at each internal single-step, but instead stop all threads when
we're about to present the stop to the user. While developing this, I
progressed in stages. First I made wait_for_inferior stop all threads
right after the target returning an event, as that's the least
invasive change. When I got that regression free, I added the
`stop_only_if_needed' global, which when true, means what I mentioned
above -- we only stop all threads when stop_stepping is called, which
means we're reporting an event to the user. This flag will probably
go away at a later stage.
This forces always-inserted on for such targets that can always do
non-stop. I think it would make sense to make the "breakpoint
always-inserted" auto mode actually remove breakpoints from the
inferior when all is stopped. That way, we'd get the classic
behavior, that is a bit safer in that is avoids leaving breakpoints
planted on the target if gdb crashes in most cases, but, it'd also
leave breakpoints always inserted whenever there's a chance a thread
is running. That'd get us the best of both worlds.
---
gdb/breakpoint.c | 4
gdb/gdbthread.h | 22 ++
gdb/inf-ptrace.c | 4
gdb/infcall.c | 10 +
gdb/infrun.c | 729 ++++++++++++++++++++++++++++++++++++++++++++++++++----
gdb/linux-nat.c | 26 +-
gdb/remote.c | 10 +
gdb/target.c | 3
gdb/target.h | 4
gdb/thread.c | 3
10 files changed, 742 insertions(+), 73 deletions(-)
diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index 7b8ab77..bdc5d38 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -379,7 +379,7 @@ int
breakpoints_always_inserted_mode (void)
{
return ((always_inserted_mode == always_inserted_on
- || (always_inserted_mode == always_inserted_auto && non_stop))
+ || (always_inserted_mode == always_inserted_auto && target_is_non_stop_p ()))
&& !RECORD_IS_USED);
}
@@ -10681,7 +10681,7 @@ update_global_location_list (int should_insert)
if (!found_object)
{
- if (removed && non_stop
+ if (removed && target_is_non_stop_p ()
&& breakpoint_address_is_meaningful (old_loc->owner)
&& !is_hardware_watchpoint (old_loc->owner))
{
diff --git a/gdb/gdbthread.h b/gdb/gdbthread.h
index af987f2..97ded57 100644
--- a/gdb/gdbthread.h
+++ b/gdb/gdbthread.h
@@ -38,6 +38,15 @@ enum thread_state
THREAD_EXITED,
};
+#define ALL_THREADS(T) \
+ for ((T) = thread_list; (T); (T) = (T)->next)
+
+#define ALL_LIVE_THREADS(T) \
+ for ((T) = thread_list; (T); (T) = (T)->next) \
+ if ((T)->state != THREAD_EXITED)
+
+extern struct thread_info *thread_list;
+
/* Inferior thread specific part of `struct infcall_control_state'.
Inferior process counterpart is `struct inferior_control_state'. */
@@ -114,6 +123,14 @@ struct thread_control_state
/* Chain containing status of breakpoint(s) the thread stopped
at. */
bpstat stop_bpstat;
+
+ /* Non-zero if this thread will be/has been resumed. Note that a
+ thread can be marked both as stopped and resumed at the same
+ time. This happens if we try to resume a thread that has a wait
+ status pending. We shouldn't let the thread run until that wait
+ status has been processed, but we should not report that wait
+ status if GDB didn't try to let the thread run. */
+ int resumed;
};
/* Inferior thread specific part of `struct infcall_suspend_state'.
@@ -124,6 +141,11 @@ struct thread_suspend_state
{
/* Last signal that the inferior received (why it stopped). */
enum target_signal stop_signal;
+
+ /* The waitstatus for this thread's last event. */
+ struct target_waitstatus waitstatus;
+ /* If true WAITSTATUS hasn't been handled yet. */
+ int waitstatus_pending_p;
};
struct thread_info
diff --git a/gdb/inf-ptrace.c b/gdb/inf-ptrace.c
index 110e825..0617631 100644
--- a/gdb/inf-ptrace.c
+++ b/gdb/inf-ptrace.c
@@ -194,6 +194,7 @@ inf_ptrace_attach (struct target_ops *ops, char *args, int from_tty)
char *exec_file;
pid_t pid;
struct inferior *inf;
+ struct thread_info *tp;
/* Do not change either targets above or the same target if already present.
The reason is the target stack is shared across multiple inferiors. */
@@ -243,7 +244,8 @@ inf_ptrace_attach (struct target_ops *ops, char *args, int from_tty)
/* Always add a main thread. If some target extends the ptrace
target, it should decorate the ptid later with more info. */
- add_thread_silent (inferior_ptid);
+ tp = add_thread_silent (inferior_ptid);
+ tp->control.resumed = 1;
if (! ops_already_pushed)
discard_cleanups (back_to);
diff --git a/gdb/infcall.c b/gdb/infcall.c
index b8b7ff5..50f1289 100644
--- a/gdb/infcall.c
+++ b/gdb/infcall.c
@@ -389,11 +389,21 @@ run_inferior_call (struct thread_info *call_thread, CORE_ADDR real_pc)
volatile struct gdb_exception e;
int saved_in_infcall = call_thread->control.in_infcall;
ptid_t call_thread_ptid = call_thread->ptid;
+ int state = call_thread->state;
call_thread->control.in_infcall = 1;
+ /* Fudge the thread's state so clear_proceed_status doesn't ignore
+ it. We may be running an infcall with a THREAD_RUNNING but
+ !executing thread, e.g., when evaluating a breakpoint's
+ condition. */
+ state = call_thread->state;
+ call_thread->state = THREAD_STOPPED;
+
clear_proceed_status ();
+ call_thread->state = state;
+
disable_watchpoints_before_interactive_call_start ();
/* We want stop_registers, please... */
diff --git a/gdb/infrun.c b/gdb/infrun.c
index 5a0f1d2..e0b282f 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -57,6 +57,12 @@
#include "continuations.h"
#include "interps.h"
#include "skip.h"
+#include "event-loop.h"
+
+/* Asynchronous signal handle registered as event loop source for
+ when we have pending events ready to be passed to the core. */
+
+static struct async_event_handler *infrun_async_inferior_event_token;
/* Prototypes for local functions */
@@ -1219,7 +1225,7 @@ static int
use_displaced_stepping (struct gdbarch *gdbarch)
{
return (((can_use_displaced_stepping == can_use_displaced_stepping_auto
- && non_stop)
+ && target_is_non_stop_p ())
|| can_use_displaced_stepping == can_use_displaced_stepping_on)
&& gdbarch_displaced_step_copy_insn_p (gdbarch)
&& !RECORD_IS_USED);
@@ -1413,6 +1419,8 @@ displaced_step_restore (struct displaced_step_inferior_state *displaced,
displaced->step_copy));
}
+static void do_target_resume (ptid_t ptid, int step, enum target_signal signo);
+
static void
displaced_step_fixup (ptid_t event_ptid, enum target_signal signal)
{
@@ -1505,9 +1513,9 @@ displaced_step_fixup (ptid_t event_ptid, enum target_signal signal)
if (gdbarch_displaced_step_hw_singlestep (gdbarch,
displaced->step_closure))
- target_resume (ptid, 1, TARGET_SIGNAL_0);
+ do_target_resume (ptid, 1, TARGET_SIGNAL_0);
else
- target_resume (ptid, 0, TARGET_SIGNAL_0);
+ do_target_resume (ptid, 0, TARGET_SIGNAL_0);
/* Done, we're stepping a thread. */
break;
@@ -1529,7 +1537,7 @@ displaced_step_fixup (ptid_t event_ptid, enum target_signal signal)
"breakpoint is gone %s: step(%d)\n",
target_pid_to_str (tp->ptid), step);
- target_resume (ptid, step, TARGET_SIGNAL_0);
+ do_target_resume (ptid, step, TARGET_SIGNAL_0);
tp->suspend.stop_signal = TARGET_SIGNAL_0;
/* This request was discarded. See if there's any other
@@ -1677,6 +1685,160 @@ user_visible_resume_ptid (int step)
return resume_ptid;
}
+static void
+do_target_resume (ptid_t ptid, int step, enum target_signal signo)
+{
+ int resume_many;
+ struct thread_info *tp;
+ struct regcache *regcache = get_current_regcache ();
+ struct gdbarch *gdbarch = get_regcache_arch (regcache);
+ int want_all_signals;
+ int can_wildcard;
+ int any_to_resume;
+ ptid_t leader_ptid = inferior_ptid;
+
+ /* A specific PTID means `step only this process id'. */
+ resume_many = (ptid_equal (minus_one_ptid, ptid)
+ || ptid_is_pid (ptid));
+
+ /* See if it's the current inferior that should be handled
+ specially. */
+ if (resume_many)
+ tp = find_thread_ptid (inferior_ptid);
+ else
+ tp = find_thread_ptid (ptid);
+ gdb_assert (tp != NULL);
+ gdb_assert (tp->state != THREAD_EXITED);
+
+ /* Advise target which signals may be handled silently. If we have
+ removed breakpoints because we are stepping over one (which can
+ happen only if we are not using displaced stepping), we need to
+ receive all signals to avoid accidentally skipping a breakpoint
+ during execution of a signal handler. */
+ want_all_signals = ((step || singlestep_breakpoints_inserted_p)
+ && tp->control.trap_expected
+ && !use_displaced_stepping (gdbarch));
+ if (want_all_signals)
+ target_pass_signals (0, NULL);
+ else
+ target_pass_signals ((int) TARGET_SIGNAL_LAST, signal_pass);
+
+ can_wildcard = 1;
+
+ ALL_LIVE_THREADS (tp)
+ {
+ if (ptid_match (tp->ptid, ptid))
+ {
+ /* Avoid confusing the next resume, if the next stop/resume
+ happens to apply to another thread. */
+ tp->suspend.stop_signal = TARGET_SIGNAL_0;
+ tp->control.resumed = 1;
+
+ if (tp->suspend.waitstatus_pending_p)
+ {
+ /* FIXME: What should we do if we are supposed to
+ continue this thread with a signal? */
+ gdb_assert (signo == TARGET_SIGNAL_0);
+
+ if (!step
+ && tp->suspend.waitstatus.kind == TARGET_WAITKIND_STOPPED
+ && !want_all_signals
+ && signal_pass[signo])
+ {
+ if (debug_infrun)
+ {
+ char *statstr;
+
+ statstr = target_waitstatus_to_string (&tp->suspend.waitstatus);
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: Not short circuiting for ignored "
+ "status %s\n",
+ statstr);
+ xfree (statstr);
+ }
+
+ signo = tp->suspend.waitstatus.value.sig;
+ tp->suspend.waitstatus_pending_p = 0;
+ }
+ else
+ {
+ can_wildcard = 0;
+
+ if (target_can_async_p ())
+ {
+ target_async (inferior_event_handler, 0);
+
+ /* Tell the event loop we have something to
+ process. */
+ mark_async_event_handler (infrun_async_inferior_event_token);
+ }
+ return;
+ }
+ }
+
+ if (!tp->suspend.waitstatus_pending_p)
+ any_to_resume = 1;
+ }
+ }
+
+ if (can_wildcard)
+ {
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: managed a wildcard target_resume\n");
+ target_resume (ptid, step, signo);
+ }
+ else
+ {
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: resuming threads individually\n");
+ ALL_LIVE_THREADS (tp)
+ {
+ if (!ptid_equal (tp->ptid, inferior_ptid)
+ && ptid_match (tp->ptid, ptid))
+ {
+ if (tp->executing)
+ {
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: Not resuming %s (not stopped)\n",
+ target_pid_to_str (tp->ptid));
+ }
+ else if (!tp->suspend.waitstatus_pending_p)
+ {
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: resuming %s (no pending status)\n",
+ target_pid_to_str (tp->ptid));
+ switch_to_thread (tp->ptid);
+
+ if (ptid_equal (tp->ptid, leader_ptid))
+ target_resume (tp->ptid, step, signo);
+ else
+ target_resume (tp->ptid, 0, TARGET_SIGNAL_0);
+ }
+ else
+ {
+ if (debug_infrun)
+ {
+ char *statstr;
+
+ statstr = target_waitstatus_to_string (&tp->suspend.waitstatus);
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: not resuming %s, has pending status %s\n",
+ target_pid_to_str (tp->ptid), statstr);
+ xfree (statstr);
+ }
+ }
+ }
+ }
+
+ set_running (ptid, 1);
+ clear_inline_frame_state (ptid);
+ }
+}
+
/* Resume the inferior, but allow a QUIT. This is useful if the user
wants to interrupt some lengthy single-stepping operation
(for child processes, the SIGINT goes to the inferior, and so
@@ -1900,23 +2062,7 @@ a command like `return' or `jump' to continue execution."));
/* Install inferior's terminal modes. */
target_terminal_inferior ();
- /* Avoid confusing the next resume, if the next stop/resume
- happens to apply to another thread. */
- tp->suspend.stop_signal = TARGET_SIGNAL_0;
-
- /* Advise target which signals may be handled silently. If we have
- removed breakpoints because we are stepping over one (which can
- happen only if we are not using displaced stepping), we need to
- receive all signals to avoid accidentally skipping a breakpoint
- during execution of a signal handler. */
- if ((step || singlestep_breakpoints_inserted_p)
- && tp->control.trap_expected
- && !use_displaced_stepping (gdbarch))
- target_pass_signals (0, NULL);
- else
- target_pass_signals ((int) TARGET_SIGNAL_LAST, signal_pass);
-
- target_resume (resume_ptid, step, sig);
+ do_target_resume (resume_ptid, step, sig);
}
discard_cleanups (old_cleanups);
@@ -1930,6 +2076,15 @@ a command like `return' or `jump' to continue execution."));
static void
clear_proceed_status_thread (struct thread_info *tp)
{
+ if (tp->state == THREAD_RUNNING)
+ {
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: clear_proceed_status_thread (%s): ignored, thread is running\n",
+ target_pid_to_str (tp->ptid));
+ return;
+ }
+
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog,
"infrun: clear_proceed_status_thread (%s)\n",
@@ -2558,14 +2713,20 @@ print_target_wait_results (ptid_t waiton_ptid, ptid_t result_ptid,
is set. */
fprintf_unfiltered (tmp_stream,
- "infrun: target_wait (%d", PIDGET (waiton_ptid));
+ "infrun: target_wait (%d.%ld.%ld",
+ ptid_get_pid (waiton_ptid),
+ ptid_get_lwp (waiton_ptid),
+ ptid_get_tid (waiton_ptid));
if (PIDGET (waiton_ptid) != -1)
fprintf_unfiltered (tmp_stream,
" [%s]", target_pid_to_str (waiton_ptid));
fprintf_unfiltered (tmp_stream, ", status) =\n");
fprintf_unfiltered (tmp_stream,
- "infrun: %d [%s],\n",
- PIDGET (result_ptid), target_pid_to_str (result_ptid));
+ "infrun: %d.%ld.%ld [%s],\n",
+ ptid_get_pid (result_ptid),
+ ptid_get_lwp (result_ptid),
+ ptid_get_tid (result_ptid),
+ target_pid_to_str (result_ptid));
fprintf_unfiltered (tmp_stream,
"infrun: %s\n",
status_string);
@@ -2581,6 +2742,88 @@ print_target_wait_results (ptid_t waiton_ptid, ptid_t result_ptid,
ui_file_delete (tmp_stream);
}
+static ptid_t
+do_target_wait (ptid_t ptid, struct target_waitstatus *status, int options)
+{
+ ptid_t event_ptid;
+ struct thread_info *tp;
+
+ /* First check if there is a thread with a wait status pending. */
+ if (ptid_equal (ptid, minus_one_ptid) || ptid_is_pid (ptid))
+ {
+ ALL_LIVE_THREADS (tp)
+ {
+ /* Only report a pending wait status if we pretend that this
+ has indeed been resumed. */
+ if (ptid_match (tp->ptid, ptid)
+ && tp->control.resumed
+ && tp->suspend.waitstatus_pending_p)
+ {
+ gdb_assert (!tp->executing);
+ break;
+ }
+ }
+ }
+ else
+ {
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: Waiting for specific thread %s.\n",
+ target_pid_to_str (ptid));
+
+ /* We have a specific thread to check. */
+ tp = find_thread_ptid (ptid);
+ gdb_assert (tp);
+ if (!tp->suspend.waitstatus_pending_p)
+ tp = NULL;
+ }
+
+ if (tp)
+ {
+ if (debug_infrun)
+ {
+ char *statstr;
+
+ statstr = target_waitstatus_to_string (&tp->suspend.waitstatus);
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: Using pending wait status %s for %s.\n",
+ statstr,
+ target_pid_to_str (tp->ptid));
+ xfree (statstr);
+ }
+
+ *status = tp->suspend.waitstatus;
+ tp->suspend.waitstatus_pending_p = 0;
+ return tp->ptid;
+ }
+
+ /* But if we don't find one, we'll have to wait. */
+
+ if (deprecated_target_wait_hook)
+ event_ptid = deprecated_target_wait_hook (ptid, status, options);
+ else
+ event_ptid = target_wait (ptid, status, options);
+
+ if (status->kind != TARGET_WAITKIND_EXITED
+ && status->kind != TARGET_WAITKIND_SIGNALLED
+ /* If a thread not the main thread execl's, we'll see the event
+ happening on the main thread, even if it was stopped. It's
+ as if the pre-exec thread changes its thread id (well, it
+ really is that way on the Linux kernel). */
+ && status->kind != TARGET_WAITKIND_EXECD
+ && status->kind != TARGET_WAITKIND_IGNORE)
+ {
+ tp = find_thread_ptid (event_ptid);
+ if (tp)
+ {
+ gdb_assert (tp->control.resumed);
+ tp->suspend.waitstatus = *status;
+ }
+ }
+
+ return event_ptid;
+}
+
/* Prepare and stabilize the inferior for detaching it. E.g.,
detaching while a thread is displaced stepping is a recipe for
crashing it, as nothing would readjust the PC out of the scratch
@@ -2619,10 +2862,7 @@ prepare_for_detach (void)
overlay_cache_invalid = 1;
- if (deprecated_target_wait_hook)
- ecs->ptid = deprecated_target_wait_hook (pid_ptid, &ecs->ws, 0);
- else
- ecs->ptid = target_wait (pid_ptid, &ecs->ws, 0);
+ ecs->ptid = do_target_wait (pid_ptid, &ecs->ws, 0);
if (debug_infrun)
print_target_wait_results (pid_ptid, ecs->ptid, &ecs->ws);
@@ -2660,6 +2900,54 @@ prepare_for_detach (void)
discard_cleanups (old_chain_1);
}
+static void stop_all_threads (void);
+static int adjust_pc_after_break (struct thread_info *thread,
+ struct target_waitstatus *ws);
+
+static void
+cancel_breakpoints (void)
+{
+ struct thread_info *t;
+
+ /* Cancel any breakpoints in other threads that have hit a
+ breakpoint. If we do not do this, then we run the risk that the
+ user will delete or disable the breakpoint, but the thread will
+ have already tripped on it. */
+ ALL_LIVE_THREADS (t)
+ if (t->control.resumed
+ && t->suspend.waitstatus_pending_p
+ && adjust_pc_after_break (t, &t->suspend.waitstatus))
+ {
+ /* Throw away the trap. The thread will re-trap if
+ re-resumed and the breakpoint is still
+ installed. */
+ t->suspend.waitstatus_pending_p = 0;
+ t->suspend.waitstatus.kind = TARGET_WAITKIND_IGNORE;
+ }
+}
+
+static void
+set_executing_from_state (struct execution_control_state *ecs)
+{
+ if (ecs->ws.kind == TARGET_WAITKIND_IGNORE
+ || ecs->ws.kind == TARGET_WAITKIND_NO_RESUMED)
+ ;
+ else if (ecs->ws.kind == TARGET_WAITKIND_SIGNALLED
+ || ecs->ws.kind == TARGET_WAITKIND_EXITED)
+ {
+ int exited_pid = ptid_get_pid (ecs->ptid);
+ struct thread_info *t;
+
+ ALL_THREADS (t)
+ if (ptid_get_pid (t->ptid) == exited_pid)
+ t->executing = 0;
+ }
+ else
+ set_executing (ecs->ptid, 0);
+}
+
+int stop_only_if_needed = 1;
+
/* Wait for control to return from inferior to debugger.
If inferior gets a signal, we may decide to start it up again
@@ -2690,14 +2978,13 @@ wait_for_inferior (void)
overlay_cache_invalid = 1;
- if (deprecated_target_wait_hook)
- ecs->ptid = deprecated_target_wait_hook (waiton_ptid, &ecs->ws, 0);
- else
- ecs->ptid = target_wait (waiton_ptid, &ecs->ws, 0);
+ ecs->ptid = do_target_wait (waiton_ptid, &ecs->ws, 0);
if (debug_infrun)
print_target_wait_results (waiton_ptid, ecs->ptid, &ecs->ws);
+ set_executing_from_state (ecs);
+
/* If an error happens while handling the event, propagate GDB's
knowledge of the executing state to the frontend/user running
state. */
@@ -2707,6 +2994,112 @@ wait_for_inferior (void)
|| ecs->ws.kind == TARGET_WAITKIND_SYSCALL_RETURN)
ecs->ws.value.syscall_number = UNKNOWN_SYSCALL;
+ if (ecs->ws.kind != TARGET_WAITKIND_EXITED
+ && ecs->ws.kind != TARGET_WAITKIND_SIGNALLED
+ && ecs->ws.kind != TARGET_WAITKIND_NO_RESUMED
+ && !in_thread_list (ecs->ptid))
+ add_thread (ecs->ptid);
+
+ if (!non_stop && target_is_non_stop_p () && !stop_only_if_needed)
+ {
+ stop_all_threads ();
+
+ if (ecs->ws.kind != TARGET_WAITKIND_NO_RESUMED
+ && ecs->ws.kind != TARGET_WAITKIND_EXITED
+ && ecs->ws.kind != TARGET_WAITKIND_SIGNALLED
+ && (ptid_equal (waiton_ptid, minus_one_ptid)
+ || ptid_is_pid (waiton_ptid)))
+ {
+ /* If we're not waiting for a specific thread, choose an
+ event thread from among those that have had events.
+ Giving equal priority to all threads that have had
+ events helps prevent starvation. */
+
+ int num_events = 0;
+ int random_selector;
+ struct thread_info *event_tp;
+
+ ecs->event_thread = find_thread_ptid (ecs->ptid);
+ /* Record the wait status for the original thread. */
+ ecs->event_thread->suspend.waitstatus = ecs->ws;
+
+ /* Give preference to any thread that is being
+ single-stepped. */
+ ALL_LIVE_THREADS (event_tp)
+ if (ptid_match (event_tp->ptid, waiton_ptid)
+ && event_tp->control.resumed
+ && event_tp->suspend.waitstatus_pending_p
+ && currently_stepping (event_tp))
+ break;
+
+ if (event_tp != NULL)
+ {
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: Select single-step %s\n",
+ target_pid_to_str (event_tp->ptid));
+ }
+ else
+ {
+ /* No single-stepping thread. Select one at random,
+ out of those which have had events.
+
+ First see how many events we have. Count only
+ resumed threads that have an event pending. */
+ ALL_LIVE_THREADS (event_tp)
+ if (ptid_match (event_tp->ptid, waiton_ptid)
+ && event_tp->control.resumed
+ && event_tp->suspend.waitstatus_pending_p)
+ num_events++;
+
+ /* Now randomly pick a LWP out of those that have had a SIGTRAP. */
+ random_selector = (int)
+ ((num_events * (double) rand ()) / (RAND_MAX + 1.0));
+
+ if (debug_infrun && num_events > 1)
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: Found %d events, selecting #%d\n",
+ num_events, random_selector);
+
+ /* Select the Nth thread that has had an event. */
+ ALL_LIVE_THREADS (event_tp)
+ if (ptid_match (event_tp->ptid, waiton_ptid)
+ && event_tp->control.resumed
+ && event_tp->suspend.waitstatus_pending_p)
+ if (random_selector-- == 0)
+ break;
+ }
+
+ if (event_tp != NULL)
+ {
+ /* Switch the event thread. */
+ ecs->ptid = event_tp->ptid;
+ ecs->event_thread = event_tp;
+ ecs->ws = event_tp->suspend.waitstatus;
+ }
+
+ /* Flush the wait status for the event thread. */
+ ecs->event_thread->suspend.waitstatus_pending_p = 0;
+ }
+
+ {
+ struct thread_info *t;
+
+ /* Now that we've selected our final event thread, cancel
+ any breakpoints in other threads that have hit a
+ breakpoint. If we do not do this, then we run the risk
+ that the user will delete or disable the breakpoint,
+ but the thread will have already tripped on it. */
+ cancel_breakpoints ();
+
+ /* In all-stop, from the core's perspective, all threads
+ are now stopped until a new resume action is sent
+ over. */
+ ALL_LIVE_THREADS (t)
+ t->control.resumed = 0;
+ }
+ }
+
/* Now figure out what to do with the result of the result. */
handle_inferior_event (ecs);
@@ -2763,15 +3156,28 @@ fetch_inferior_event (void *client_data)
make_cleanup_restore_integer (&execution_direction);
execution_direction = target_execution_direction ();
- if (deprecated_target_wait_hook)
- ecs->ptid =
- deprecated_target_wait_hook (waiton_ptid, &ecs->ws, TARGET_WNOHANG);
- else
- ecs->ptid = target_wait (waiton_ptid, &ecs->ws, TARGET_WNOHANG);
+ ecs->ptid = do_target_wait (waiton_ptid, &ecs->ws, TARGET_WNOHANG);
if (debug_infrun)
print_target_wait_results (waiton_ptid, ecs->ptid, &ecs->ws);
+ if (ecs->ws.kind != TARGET_WAITKIND_EXITED
+ && ecs->ws.kind != TARGET_WAITKIND_SIGNALLED
+ && ecs->ws.kind != TARGET_WAITKIND_IGNORE
+ && ecs->ws.kind != TARGET_WAITKIND_NO_RESUMED
+ && !in_thread_list (ecs->ptid))
+ add_thread (ecs->ptid);
+
+ set_executing_from_state (ecs);
+
+ if (!non_stop && target_is_non_stop_p () && !stop_only_if_needed
+ && ecs->ws.kind != TARGET_WAITKIND_IGNORE)
+ {
+ stop_all_threads ();
+ /* select event thread */
+ /* cancel breakpoints */
+ }
+
if (non_stop
&& ecs->ws.kind != TARGET_WAITKIND_IGNORE
&& ecs->ws.kind != TARGET_WAITKIND_NO_RESUMED
@@ -2897,13 +3303,15 @@ context_switch (ptid_t ptid)
switch_to_thread (ptid);
}
-static void
-adjust_pc_after_break (struct execution_control_state *ecs)
+static int
+adjust_pc_after_break (struct thread_info *thread,
+ struct target_waitstatus *ws)
{
struct regcache *regcache;
struct gdbarch *gdbarch;
struct address_space *aspace;
CORE_ADDR breakpoint_pc;
+ int ret = 0;
/* If we've hit a breakpoint, we'll normally be stopped with SIGTRAP. If
we aren't, just return.
@@ -2926,11 +3334,11 @@ adjust_pc_after_break (struct execution_control_state *ecs)
target with both of these set in GDB history, and it seems unlikely to be
correct, so gdbarch_have_nonsteppable_watchpoint is not checked here. */
- if (ecs->ws.kind != TARGET_WAITKIND_STOPPED)
- return;
+ if (ws->kind != TARGET_WAITKIND_STOPPED)
+ return 0;
- if (ecs->ws.value.sig != TARGET_SIGNAL_TRAP)
- return;
+ if (ws->value.sig != TARGET_SIGNAL_TRAP)
+ return 0;
/* In reverse execution, when a breakpoint is hit, the instruction
under it has already been de-executed. The reported PC always
@@ -2959,14 +3367,14 @@ adjust_pc_after_break (struct execution_control_state *ecs)
INSN1 hadn't been de-executed yet. Doing nothing is the correct
behaviour. */
if (execution_direction == EXEC_REVERSE)
- return;
+ return 0;
/* If this target does not decrement the PC after breakpoints, then
we have nothing to do. */
- regcache = get_thread_regcache (ecs->ptid);
+ regcache = get_thread_regcache (thread->ptid);
gdbarch = get_regcache_arch (regcache);
if (gdbarch_decr_pc_after_break (gdbarch) == 0)
- return;
+ return 0;
aspace = get_regcache_aspace (regcache);
@@ -2984,7 +3392,8 @@ adjust_pc_after_break (struct execution_control_state *ecs)
SIGTRAPs, we keep a list of such breakpoint locations for a bit,
and retire them after a number of stop events are reported. */
if (software_breakpoint_inserted_here_p (aspace, breakpoint_pc)
- || (non_stop && moribund_breakpoint_here_p (aspace, breakpoint_pc)))
+ || (target_is_non_stop_p ()
+ && moribund_breakpoint_here_p (aspace, breakpoint_pc)))
{
struct cleanup *old_cleanups = NULL;
@@ -3010,14 +3419,19 @@ adjust_pc_after_break (struct execution_control_state *ecs)
we also need to back up to the breakpoint address. */
if (singlestep_breakpoints_inserted_p
- || !ptid_equal (ecs->ptid, inferior_ptid)
- || !currently_stepping (ecs->event_thread)
- || ecs->event_thread->prev_pc == breakpoint_pc)
- regcache_write_pc (regcache, breakpoint_pc);
+ || !ptid_equal (thread->ptid, inferior_ptid)
+ || !currently_stepping (thread)
+ || thread->prev_pc == breakpoint_pc)
+ {
+ regcache_write_pc (regcache, breakpoint_pc);
+ ret = 1;
+ }
if (RECORD_IS_USED)
do_cleanups (old_cleanups);
}
+
+ return ret;
}
void
@@ -3136,6 +3550,165 @@ fill_in_stop_func (struct gdbarch *gdbarch,
}
}
+static ptid_t
+wait_one (ptid_t wait_ptid, struct target_waitstatus *ws)
+{
+ struct cleanup *old_chain;
+ ptid_t event_ptid;
+
+ /* We have to invalidate the registers BEFORE calling target_wait
+ because they can be loaded from the target while in target_wait.
+ This makes remote debugging a bit more efficient for those
+ targets that provide critical registers as part of their normal
+ status mechanism. */
+
+ overlay_cache_invalid = 1;
+ registers_changed ();
+
+ if (deprecated_target_wait_hook)
+ event_ptid = deprecated_target_wait_hook (wait_ptid, ws, 0);
+ else
+ event_ptid = target_wait (wait_ptid, ws, 0);
+
+ if (debug_infrun)
+ print_target_wait_results (wait_ptid, event_ptid, ws);
+
+ if (ws->kind == TARGET_WAITKIND_SYSCALL_ENTRY
+ || ws->kind == TARGET_WAITKIND_SYSCALL_RETURN)
+ ws->value.syscall_number = UNKNOWN_SYSCALL;
+
+ return event_ptid;
+}
+
+static void
+stop_all_threads (void)
+{
+ /* We may need multiple passes to discover all threads. */
+ int pass;
+ int iterations = 0;
+
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog, "infrun.c: stop_all_threads\n");
+
+ /* Stop threads in two passes since threads could be spawning as we
+ go through the first pass. In the second pass, we will stop such
+ spawned threads. */
+ for (pass = 0; pass < 2; pass++, iterations++)
+ {
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun.c: stop_all_threads, pass=%d, iterations=%d\n",
+ pass, iterations);
+ while (1)
+ {
+ ptid_t event_ptid;
+ struct target_waitstatus ws;
+ int need_wait = 0;
+ struct thread_info *t = NULL;
+
+ update_thread_list ();
+
+ /* Go through all threads looking for threads that we need
+ to tell the target to stop. */
+ ALL_LIVE_THREADS (t)
+ {
+ if (t->executing)
+ {
+ /* If already stopping, don't request a stop again.
+ We just haven't seen the notification yet. */
+ if (!t->stop_requested)
+ {
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun.c: %s executing, need stop\n",
+ target_pid_to_str (t->ptid));
+ target_stop (t->ptid);
+ t->stop_requested = 1;
+ }
+ else
+ {
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun.c: %s executing, already stopping\n",
+ target_pid_to_str (t->ptid));
+ }
+
+ if (t->stop_requested)
+ need_wait = 1;
+ }
+ else
+ {
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun.c: %s not executing\n",
+ target_pid_to_str (t->ptid));
+ }
+ }
+
+ if (!need_wait)
+ break;
+
+ /* If we find new threads on the second iteration, restart
+ over. We want to see two iterations in a row with all
+ threads stopped. */
+ if (pass > 0)
+ pass = -1;
+
+ event_ptid = wait_one (minus_one_ptid, &ws);
+ if (ws.kind == TARGET_WAITKIND_NO_RESUMED)
+ /* All resumed threads exited. */
+ ;
+ else if (ws.kind == TARGET_WAITKIND_EXITED
+ || ws.kind == TARGET_WAITKIND_SIGNALLED)
+ warning ("exited while stopping threads\n");
+ else
+ {
+ if (!in_thread_list (event_ptid))
+ t = add_thread (event_ptid);
+ else
+ t = find_thread_ptid (event_ptid);
+
+ t->stop_requested = 0;
+ t->executing = 0;
+
+ if (ws.kind == TARGET_WAITKIND_STOPPED && ws.value.sig == TARGET_SIGNAL_0)
+ {
+ /* We caught the event that we intended to catch, so
+ there's no event pending. */
+ t->suspend.waitstatus.kind = TARGET_WAITKIND_IGNORE;
+ t->suspend.waitstatus_pending_p = 0;
+ }
+ else
+ {
+ if (debug_infrun)
+ {
+ char *statstr;
+
+ statstr = target_waitstatus_to_string (&ws);
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: target_wait %s, saving "
+ "status for %d.%ld.%ld\n",
+ statstr,
+ ptid_get_pid (t->ptid),
+ ptid_get_lwp (t->ptid),
+ ptid_get_tid (t->ptid));
+ xfree (statstr);
+ }
+
+ /* Record for later. */
+ t->suspend.waitstatus = ws;
+ t->suspend.waitstatus_pending_p = 1;
+ }
+
+ set_running (event_ptid, 0);
+ }
+ }
+ }
+
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog, "infrun.c: stop_all_threads done\n");
+}
+
/* Given an execution control state that has been freshly filled in
by an event from the inferior, figure out what it means and take
appropriate action. */
@@ -3226,7 +3799,7 @@ handle_inferior_event (struct execution_control_state *ecs)
ecs->event_thread = find_thread_ptid (ecs->ptid);
/* Dependent on valid ECS->EVENT_THREAD. */
- adjust_pc_after_break (ecs);
+ adjust_pc_after_break (ecs->event_thread, &ecs->ws);
/* Dependent on the current PC value modified by adjust_pc_after_break. */
reinit_frame_cache ();
@@ -3266,7 +3839,7 @@ handle_inferior_event (struct execution_control_state *ecs)
we're handling a process exit in non-stop mode, there's nothing
to do, as threads of the dead process are gone, and threads of
any other process were left running. */
- if (!non_stop)
+ if (!non_stop && !(target_is_non_stop_p () && stop_only_if_needed))
set_executing (minus_one_ptid, 0);
else if (ecs->ws.kind != TARGET_WAITKIND_SIGNALLED
&& ecs->ws.kind != TARGET_WAITKIND_EXITED)
@@ -3698,7 +4271,7 @@ handle_inferior_event (struct execution_control_state *ecs)
if (ecs->new_thread_event)
{
- if (non_stop)
+ if (target_is_non_stop_p ())
/* Non-stop assumes that the target handles adding new threads
to the thread list. */
internal_error (__FILE__, __LINE__,
@@ -5583,6 +6156,23 @@ stop_stepping (struct execution_control_state *ecs)
/* Let callers know we don't want to wait for the inferior anymore. */
ecs->wait_some_more = 0;
+
+ if (!non_stop
+ && target_is_non_stop_p ()
+ && stop_only_if_needed)
+ {
+ struct thread_info *t;
+
+ stop_all_threads ();
+
+ cancel_breakpoints ();
+
+ /* In all-stop, from the core's perspective, all threads
+ are now stopped until a new resume action is sent
+ over. */
+ ALL_LIVE_THREADS (t)
+ t->control.resumed = 0;
+ }
}
/* This function handles various cases where we need to continue
@@ -5634,10 +6224,23 @@ keep_going (struct execution_control_state *ecs)
struct regcache *thread_regcache = get_thread_regcache (ecs->ptid);
if (!use_displaced_stepping (get_regcache_arch (thread_regcache)))
- /* Since we can't do a displaced step, we have to remove
- the breakpoint while we step it. To keep things
- simple, we remove them all. */
- remove_breakpoints ();
+ {
+ struct thread_info *t;
+
+ /* Since we can't do a displaced step, we have to remove
+ the breakpoint while we step it. To keep things
+ simple, we remove them all. */
+ stop_all_threads ();
+
+ cancel_breakpoints ();
+ /* In all-stop, from the core's perspective, all threads
+ are now stopped until a new resume action is sent
+ over. */
+ ALL_LIVE_THREADS (t)
+ t->control.resumed = 0;
+
+ remove_breakpoints ();
+ }
}
else
{
@@ -7060,12 +7663,22 @@ show_schedule_multiple (struct ui_file *file, int from_tty,
"of all processes is %s.\n"), value);
}
+static void
+infrun_async_inferior_event_handler (gdb_client_data data)
+{
+ inferior_event_handler (INF_REG_EVENT, NULL);
+}
+
void
_initialize_infrun (void)
{
int i;
int numsigs;
+ /* Register extra event sources in the event loop. */
+ infrun_async_inferior_event_token
+ = create_async_event_handler (infrun_async_inferior_event_handler, NULL);
+
add_info ("signals", signals_info, _("\
What debugger does when program gets various signals.\n\
Specify a signal as argument to print info on that signal only."));
diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index d54f303..f28cfe2 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -1640,13 +1640,13 @@ get_pending_status (struct lwp_info *lp, int *status)
signo = TARGET_SIGNAL_0; /* a pending ptrace event, not a real signal. */
else if (lp->status)
signo = target_signal_from_host (WSTOPSIG (lp->status));
- else if (non_stop && !is_executing (lp->ptid))
+ else if (target_is_non_stop_p () && !is_executing (lp->ptid))
{
struct thread_info *tp = find_thread_ptid (lp->ptid);
signo = tp->suspend.stop_signal;
}
- else if (!non_stop)
+ else if (!target_is_non_stop_p ())
{
struct target_waitstatus last;
ptid_t last_ptid;
@@ -1794,7 +1794,7 @@ linux_nat_detach (struct target_ops *ops, char *args, int from_tty)
available. */
linux_fork_detach (args, from_tty);
- if (non_stop && target_can_async_p ())
+ if (target_is_non_stop_p ())
target_async (inferior_event_handler, 0);
}
else
@@ -2266,7 +2266,7 @@ linux_handle_extended_wait (struct lwp_info *lp, int status,
status = 0;
}
- if (non_stop)
+ if (target_is_non_stop_p ())
{
/* Add the new thread to GDB's lists as soon as possible
so that:
@@ -2290,6 +2290,7 @@ linux_handle_extended_wait (struct lwp_info *lp, int status,
{
set_running (new_lp->ptid, 1);
set_executing (new_lp->ptid, 1);
+ find_thread_ptid (new_lp->ptid)->control.resumed = 1;
/* thread_db_attach_lwp -> lin_lwp_attach_lwp forced
resume_stop. */
new_lp->last_resume_kind = resume_continue;
@@ -3645,7 +3646,7 @@ retry:
core before this one is handled. All-stop
always cancels breakpoint hits in all
threads. */
- if (non_stop
+ if (target_is_non_stop_p ()
&& linux_nat_lp_status_is_event (lp)
&& cancel_breakpoint (lp))
{
@@ -3802,7 +3803,7 @@ retry:
goto retry;
}
- if (!non_stop)
+ if (!target_is_non_stop_p ())
{
/* Only do the below in all-stop, as we currently use SIGINT
to implement target_stop (see linux_nat_stop) in
@@ -3830,7 +3831,7 @@ retry:
fprintf_unfiltered (gdb_stdlog, "LLW: Candidate event %s in %s.\n",
status_to_str (status), target_pid_to_str (lp->ptid));
- if (!non_stop)
+ if (!target_is_non_stop_p ())
{
/* Now stop all other LWP's ... */
iterate_over_lwps (minus_one_ptid, stop_callback, NULL);
@@ -3971,7 +3972,7 @@ linux_nat_wait (struct target_ops *ops,
specific_process, for example, see linux_nat_wait_1), and
meanwhile the event became uninteresting. Don't bother resuming
LWPs we're not going to wait for if they'd stop immediately. */
- if (non_stop)
+ if (target_is_non_stop_p ())
iterate_over_lwps (minus_one_ptid, resume_stopped_resumed_lwps, &ptid);
event_ptid = linux_nat_wait_1 (ops, ptid, ourstatus, target_options);
@@ -5430,6 +5431,12 @@ linux_nat_supports_non_stop (void)
return 1;
}
+static int
+linux_nat_is_non_stop_p (void)
+{
+ return 1;
+}
+
/* True if we want to support multi-process. To be removed when GDB
supports multi-exec. */
@@ -5652,7 +5659,7 @@ linux_nat_stop_lwp (struct lwp_info *lwp, void *data)
static void
linux_nat_stop (ptid_t ptid)
{
- if (non_stop)
+ if (target_is_non_stop_p ())
iterate_over_lwps (ptid, linux_nat_stop_lwp, NULL);
else
linux_ops->to_stop (ptid);
@@ -5810,6 +5817,7 @@ linux_nat_add_target (struct target_ops *t)
t->to_can_async_p = linux_nat_can_async_p;
t->to_is_async_p = linux_nat_is_async_p;
t->to_supports_non_stop = linux_nat_supports_non_stop;
+ t->to_is_non_stop_p = linux_nat_is_non_stop_p;
t->to_async = linux_nat_async;
t->to_terminal_inferior = linux_nat_terminal_inferior;
t->to_terminal_ours = linux_nat_terminal_ours;
diff --git a/gdb/remote.c b/gdb/remote.c
index 6e37f69..10ce014 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -3254,6 +3254,8 @@ remote_start_remote (int from_tty, struct target_ops *target, int extended_p)
if (!non_stop)
{
+ struct thread_info *tp;
+
if (rs->buf[0] == 'W' || rs->buf[0] == 'X')
{
if (!extended_p)
@@ -3288,7 +3290,8 @@ remote_start_remote (int from_tty, struct target_ops *target, int extended_p)
remote_add_inferior (ptid_get_pid (inferior_ptid), -1);
/* Always add the main thread. */
- add_thread_silent (inferior_ptid);
+ tp = add_thread_silent (inferior_ptid);
+ tp->control.resumed = 1;
/* init_wait_for_inferior should be called before get_offsets in order
to manage `inserted' flag in bp loc in a correct state.
@@ -4299,11 +4302,14 @@ extended_remote_attach_1 (struct target_ops *target, char *args, int from_tty)
}
else
{
+ struct thread_info *tp;
+
/* Now, if we have thread information, update inferior_ptid. */
inferior_ptid = remote_current_thread (inferior_ptid);
/* Add the main thread to the thread list. */
- add_thread_silent (inferior_ptid);
+ tp = add_thread_silent (inferior_ptid);
+ tp->control.resumed = 1;
}
/* Next, if the target can specify a description, read it. We do
diff --git a/gdb/target.c b/gdb/target.c
index c2aaa7e..eb283e1 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -659,6 +659,8 @@ update_current_target (void)
INHERIT (to_can_async_p, t);
INHERIT (to_is_async_p, t);
INHERIT (to_async, t);
+ /* Do not inherit to_supports_non_stop. */
+ INHERIT (to_is_non_stop_p, t);
INHERIT (to_find_memory_regions, t);
INHERIT (to_make_corefile_notes, t);
INHERIT (to_get_bookmark, t);
@@ -927,6 +929,7 @@ update_current_target (void)
(struct traceframe_info * (*) (void))
tcomplain);
de_fault (to_execution_direction, default_execution_direction);
+ de_fault (to_is_non_stop_p, return_zero);
#undef de_fault
diff --git a/gdb/target.h b/gdb/target.h
index 25e833f..d3deaf2 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -532,6 +532,7 @@ struct target_ops
int (*to_is_async_p) (void);
void (*to_async) (void (*) (enum inferior_event_type, void *), void *);
int (*to_supports_non_stop) (void);
+ int (*to_is_non_stop_p) (void);
/* find_memory_regions support method for gcore */
int (*to_find_memory_regions) (find_memory_region_ftype func, void *data);
/* make_corefile_notes support method for gcore */
@@ -1290,6 +1291,9 @@ extern int target_async_permitted;
int target_supports_non_stop (void);
+/* Is the target in non-stop mode? */
+#define target_is_non_stop_p() (current_target.to_is_non_stop_p ())
+
/* Put the target in async mode with the specified callback function. */
#define target_async(CALLBACK,CONTEXT) \
(current_target.to_async ((CALLBACK), (CONTEXT)))
diff --git a/gdb/thread.c b/gdb/thread.c
index 8cca83d..0777887 100644
--- a/gdb/thread.c
+++ b/gdb/thread.c
@@ -55,7 +55,7 @@ void _initialize_thread (void);
/* Prototypes for local functions. */
-static struct thread_info *thread_list = NULL;
+struct thread_info *thread_list = NULL;
static int highest_thread_num;
static void thread_command (char *tidstr, int from_tty);
@@ -174,6 +174,7 @@ new_thread (ptid_t ptid)
/* Nothing to follow yet. */
tp->pending_follow.kind = TARGET_WAITKIND_SPURIOUS;
tp->state = THREAD_STOPPED;
+ tp->suspend.waitstatus.kind = TARGET_WAITKIND_IGNORE;
return tp;
}
^ permalink raw reply [flat|nested] 53+ messages in thread
* [RFC/WIP PATCH 13/14] Make "thread apply all" only loop over threads in the current set
2011-11-28 15:39 [RFC/WIP PATCH 00/14] I/T sets Pedro Alves
` (8 preceding siblings ...)
2011-11-28 15:40 ` [RFC/WIP PATCH 04/14] Implement all-stop on top of a target running non-stop mode Pedro Alves
@ 2011-11-28 15:40 ` Pedro Alves
2011-11-28 18:40 ` Eli Zaretskii
2011-11-29 21:47 ` Tom Tromey
2011-11-28 15:40 ` [RFC/WIP PATCH 08/14] Add support for the '@' core operator Pedro Alves
` (7 subsequent siblings)
17 siblings, 2 replies; 53+ messages in thread
From: Pedro Alves @ 2011-11-28 15:40 UTC (permalink / raw)
To: gdb-patches
This makes "thread apply all" only loop over threads in the current
set instead of always all threads. That is,
[all]> thread apply all printf "current thread is %d\n", $_thread
Thread 3 (Thread 0x7ffff7028700 (LWP 30304)):
current thread is 3
Thread 2 (Thread 0x7ffff7829700 (LWP 30303)):
current thread is 2
Thread 1 (Thread 0x7ffff7fcb720 (LWP 30300)):
current thread is 1
vs
[all]> [1.1] thread apply all printf "current thread is %d\n", $_thread
Thread 1 (Thread 0x7ffff7fcb720 (LWP 30300)):
current thread is 1
I think it might make sense to make "info threads" only list threads
of the current focus too. WDYT?
---
gdb/thread.c | 5 +++--
1 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/gdb/thread.c b/gdb/thread.c
index 846d2d3..55a64ac 100644
--- a/gdb/thread.c
+++ b/gdb/thread.c
@@ -46,6 +46,7 @@
#include "gdb_regex.h"
#include "cli/cli-utils.h"
#include "continuations.h"
+#include "itset.h"
/* Definition of struct thread_info exported to gdbthread.h. */
@@ -1199,8 +1200,8 @@ thread_apply_all_command (char *cmd, int from_tty)
execute_command. */
saved_cmd = xstrdup (cmd);
make_cleanup (xfree, saved_cmd);
- for (tp = thread_list; tp; tp = tp->next)
- if (thread_alive (tp))
+ ALL_THREADS (tp)
+ if (itset_contains_thread (current_itset, tp) && thread_alive (tp))
{
switch_to_thread (tp->ptid);
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [RFC/WIP PATCH 13/14] Make "thread apply all" only loop over threads in the current set
2011-11-28 15:40 ` [RFC/WIP PATCH 13/14] Make "thread apply all" only loop over threads in the current set Pedro Alves
@ 2011-11-28 18:40 ` Eli Zaretskii
2011-11-28 18:56 ` Pedro Alves
2011-11-29 21:47 ` Tom Tromey
1 sibling, 1 reply; 53+ messages in thread
From: Eli Zaretskii @ 2011-11-28 18:40 UTC (permalink / raw)
To: Pedro Alves; +Cc: gdb-patches
> From: Pedro Alves <pedro@codesourcery.com>
> Date: Mon, 28 Nov 2011 15:40:09 +0000
>
> This makes "thread apply all" only loop over threads in the current
> set instead of always all threads. That is,
>
> [all]> thread apply all printf "current thread is %d\n", $_thread
>
> Thread 3 (Thread 0x7ffff7028700 (LWP 30304)):
> current thread is 3
>
> Thread 2 (Thread 0x7ffff7829700 (LWP 30303)):
> current thread is 2
>
> Thread 1 (Thread 0x7ffff7fcb720 (LWP 30300)):
> current thread is 1
>
> vs
>
> [all]> [1.1] thread apply all printf "current thread is %d\n", $_thread
>
> Thread 1 (Thread 0x7ffff7fcb720 (LWP 30300)):
> current thread is 1
>
>
> I think it might make sense to make "info threads" only list threads
> of the current focus too. WDYT?
Shouldn't this be under user control? Or are you sure no one will
ever want to have the former?
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [RFC/WIP PATCH 13/14] Make "thread apply all" only loop over threads in the current set
2011-11-28 18:40 ` Eli Zaretskii
@ 2011-11-28 18:56 ` Pedro Alves
0 siblings, 0 replies; 53+ messages in thread
From: Pedro Alves @ 2011-11-28 18:56 UTC (permalink / raw)
To: Eli Zaretskii; +Cc: gdb-patches
On Monday 28 November 2011 18:40:14, Eli Zaretskii wrote:
> > From: Pedro Alves <pedro@codesourcery.com>
> > Date: Mon, 28 Nov 2011 15:40:09 +0000
> >
> > This makes "thread apply all" only loop over threads in the current
> > set instead of always all threads. That is,
> >
> > [all]> thread apply all printf "current thread is %d\n", $_thread
> >
> > Thread 3 (Thread 0x7ffff7028700 (LWP 30304)):
> > current thread is 3
> >
> > Thread 2 (Thread 0x7ffff7829700 (LWP 30303)):
> > current thread is 2
> >
> > Thread 1 (Thread 0x7ffff7fcb720 (LWP 30300)):
> > current thread is 1
> >
> > vs
> >
> > [all]> [1.1] thread apply all printf "current thread is %d\n", $_thread
> >
> > Thread 1 (Thread 0x7ffff7fcb720 (LWP 30300)):
> > current thread is 1
> >
> >
> > I think it might make sense to make "info threads" only list threads
> > of the current focus too. WDYT?
>
> Shouldn't this be under user control? Or are you sure no one will
> ever want to have the former?
The idea is that you can always do "(gdb) [all] info threads"
(the [all] prefix overrides the current set for the duration
of the command) to see all threads. I just realized
that if we choose to default to a set that only includes the
current inferior instead of defaulting to [all], "info threads" no
longer behaves the same as currently, as it'd then only show
the threads of the current inferior by default. But then again,
that may still be a desirable change anyway. Hmm, choices, choices.
--
Pedro Alves
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [RFC/WIP PATCH 13/14] Make "thread apply all" only loop over threads in the current set
2011-11-28 15:40 ` [RFC/WIP PATCH 13/14] Make "thread apply all" only loop over threads in the current set Pedro Alves
2011-11-28 18:40 ` Eli Zaretskii
@ 2011-11-29 21:47 ` Tom Tromey
2011-12-16 18:47 ` Pedro Alves
1 sibling, 1 reply; 53+ messages in thread
From: Tom Tromey @ 2011-11-29 21:47 UTC (permalink / raw)
To: Pedro Alves; +Cc: gdb-patches
>>>>> "Pedro" == Pedro Alves <pedro@codesourcery.com> writes:
Pedro> This makes "thread apply all" only loop over threads in the current
Pedro> set instead of always all threads.
It seems to me that "thread apply" already has a thread argument, which
could be syntactically extended to cope with sets:
thread apply [1.*] print x
Then I think the existing syntax can just be mapped to a set:
thread apply 1 2 3 print x
=> thread apply [1,2,3] print x
thread apply all print x
=> thread apply [*] print x
(I didn't read the itset patch yet so I don't know if this is still
the right syntax, but you get the idea.)
If you considered this and rejected it, I would be interested in your
reason.
I am not strongly wedded to this idea.
I wonder if your idea might be confusing for users, since "all" is an
absolute word, but this patch makes it not so.
Pedro> I think it might make sense to make "info threads" only list threads
Pedro> of the current focus too. WDYT?
If a command can determine whether it has an explicit prefix (and TBH I
am not sure it is a good idea to allow this -- and I didn't read that
patch yet either) then you could have it use the current focus if and
only if the focus is explicit.
That is:
[1.*]> info thread
=> all threads
[1.*]> [1.*] info thread
=> just threads in inferior 1
If there is a set meaning "the current focus set" you could:
[1.*]> [$] info thread
... giving the current set some short moniker like "$" makes it easier
to use.
"info thread" takes thread arguments, so perhaps the same rewriting idea
used above applies. This approach would work even without a command
knowing whether it has explicit context:
(gdb) info thread 1 2 3
=> info thread [1,2,3]
(gdb) info thread [$]
=> focused threads
(gdb) info thread [1.*]
=> threads of inferior 1
In any case if these commands respect the focus set, then we also have
to define what happens if they are passed explicit threads.
Tom
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [RFC/WIP PATCH 13/14] Make "thread apply all" only loop over threads in the current set
2011-11-29 21:47 ` Tom Tromey
@ 2011-12-16 18:47 ` Pedro Alves
0 siblings, 0 replies; 53+ messages in thread
From: Pedro Alves @ 2011-12-16 18:47 UTC (permalink / raw)
To: gdb-patches; +Cc: Tom Tromey
On Tuesday 29 November 2011 21:47:09, Tom Tromey wrote:
> >>>>> "Pedro" == Pedro Alves <pedro@codesourcery.com> writes:
>
> Pedro> This makes "thread apply all" only loop over threads in the current
> Pedro> set instead of always all threads.
>
> It seems to me that "thread apply" already has a thread argument, which
> could be syntactically extended to cope with sets:
>
> thread apply [1.*] print x
>
> Then I think the existing syntax can just be mapped to a set:
>
> thread apply 1 2 3 print x
> => thread apply [1,2,3] print x
>
> thread apply all print x
> => thread apply [*] print x
> (I didn't read the itset patch yet so I don't know if this is still
> the right syntax, but you get the idea.)
>
> If you considered this and rejected it, I would be interested in your
> reason.
>
> I am not strongly wedded to this idea.
>
> I wonder if your idea might be confusing for users, since "all" is an
> absolute word, but this patch makes it not so.
Yeah. In a way, it's still "all" something. But instead of
all in the debug session, it's "all" in current focus. It's
a similar change as done to, e.g., "continue -a", where -a means "all
in current focus". I actually tried out the change after finding this:
http://sourceware.org/ml/gdb/2010-09/msg00035.html
"(...) a "upcmode" of operation where commands like breakpoint, info threads, .. apply
only to UPC threads (a set in your example). Also, "c -a" like commands apply only
to UPC threads. Same with "thread apply all". In essence you are focusing your
debugging to a group of threads. Switching off "upcmode" makes everything go back
to normal (in your case selecting a thread set "all" turns off this feature)."
and thinking that such upcmode could easily be reimplemented this way.
But I only now notice that they also change "info threads". Hmm.
I'm dropping this patch for now.
>
> Pedro> I think it might make sense to make "info threads" only list threads
> Pedro> of the current focus too. WDYT?
>
> If a command can determine whether it has an explicit prefix (and TBH I
> am not sure it is a good idea to allow this -- and I didn't read that
> patch yet either)
I don't think that is a good idea either.
> If there is a set meaning "the current focus set" you could:
>
> [1.*]> [$] info thread
>
> ... giving the current set some short moniker like "$" makes it easier
> to use.
That one I think makes sense. I implemented it for experimentation.
> "info thread" takes thread arguments, so perhaps the same rewriting idea
> used above applies. This approach would work even without a command
> knowing whether it has explicit context:
>
> (gdb) info thread 1 2 3
> => info thread [1,2,3]
>
> (gdb) info thread [$]
> => focused threads
>
> (gdb) info thread [1.*]
> => threads of inferior 1
This may be what makes most sense.
--
Pedro Alves
^ permalink raw reply [flat|nested] 53+ messages in thread
* [RFC/WIP PATCH 08/14] Add support for the '@' core operator
2011-11-28 15:39 [RFC/WIP PATCH 00/14] I/T sets Pedro Alves
` (9 preceding siblings ...)
2011-11-28 15:40 ` [RFC/WIP PATCH 13/14] Make "thread apply all" only loop over threads in the current set Pedro Alves
@ 2011-11-28 15:40 ` Pedro Alves
2011-11-30 17:29 ` Tom Tromey
2011-11-28 15:45 ` [RFC/WIP PATCH 14/14] Fix manythreads.exp test Pedro Alves
` (6 subsequent siblings)
17 siblings, 1 reply; 53+ messages in thread
From: Pedro Alves @ 2011-11-28 15:40 UTC (permalink / raw)
To: gdb-patches
This adds support for the '@' operator as described in the series
description. I've left this split into a separate patch, as I have
tend to think the syntax for filtering per core will end up different.
In any case, this adds support for set intersection, so I went ahead
with it anyway.
---
gdb/itset.c | 298 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 291 insertions(+), 7 deletions(-)
diff --git a/gdb/itset.c b/gdb/itset.c
index 81a4911..bd2c74a 100644
--- a/gdb/itset.c
+++ b/gdb/itset.c
@@ -439,6 +439,213 @@ create_range_itset (int inf_first, int inf_last, int thr_first, int thr_last)
\f
+/* An I/T set element representing a range of cores. */
+
+struct itset_elt_core_range
+{
+ struct itset_elt base;
+
+ /* The first and last cores in this range. If CORE_FIRST is
+ WILDCARD, then CORE_LAST is unused. */
+ int core_first, core_last;
+};
+
+/* Implementation of `contains_inferior' method. */
+
+static int
+core_range_contains_thread (struct itset_elt *base, struct thread_info *thr)
+{
+ struct itset_elt_core_range *core_range = (struct itset_elt_core_range *) base;
+ int core;
+
+ if (core_range->core_first == WILDCARD)
+ return 1;
+
+ core = target_core_of_thread (thr->ptid);
+ if (core_range->core_first <= core && core <= core_range->core_last)
+ return 1;
+
+ return 0;
+}
+
+/* Implementation of `contains_inferior' method. */
+
+static int
+core_range_contains_inferior (struct itset_elt *base, struct inferior *inf)
+{
+ struct itset_elt_core_range *core_range = (struct itset_elt_core_range *) base;
+ struct thread_info *thr;
+
+ /* True if we find a thread of this inferior that is running on our
+ core range. */
+ ALL_THREADS(thr)
+ {
+ /* It's cheaper to check the core range first, because looking
+ up the a thread's inferior is O(n). */
+ if (core_range_contains_thread (base, thr))
+ {
+ struct inferior *thr_inf;
+
+ thr_inf = get_thread_inferior (thr);
+ if (thr_inf == inf)
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/* Implementation of `is_empty' method. */
+
+static int
+core_range_is_empty (struct itset_elt *base)
+{
+ struct itset_elt_core_range *core_range = (struct itset_elt_core_range *) base;
+ struct inferior *inf;
+ struct thread_info *thr;
+
+ ALL_THREADS(thr)
+ {
+ if (core_range_contains_thread (base, thr))
+ return 0;
+ }
+
+ return 1;
+}
+
+static const struct itset_elt_vtable core_range_vtable =
+{
+ NULL,
+ core_range_contains_inferior,
+ core_range_contains_thread,
+ core_range_is_empty
+};
+
+/* Create a new `core_range' I/T set element. */
+
+static struct itset_elt *
+create_core_range_itset (int core_first, int core_last)
+{
+ struct itset_elt_core_range *elt;
+
+ elt = XNEW (struct itset_elt_core_range);
+ elt->base.vtable = &core_range_vtable;
+ elt->core_first = core_first;
+ elt->core_last = core_last;
+
+ return (struct itset_elt *) elt;
+}
+
+\f
+
+/* An I/T set element representing an intersection of sets. */
+
+struct itset_elt_intersect
+{
+ struct itset_elt base;
+
+ /* The elements that will be intersected. */
+ VEC (itset_elt_ptr) *elements;
+};
+
+/* Implementation of `destroy' method. */
+
+static void
+intersect_destroy (struct itset_elt *base)
+{
+ struct itset_elt_intersect *set = (struct itset_elt_intersect *) base;
+
+ VEC_free (itset_elt_ptr, set->elements);
+}
+
+/* Implementation of `contains_inferior' method. */
+
+static int
+intersect_contains_inferior (struct itset_elt *base, struct inferior *inf)
+{
+ struct itset_elt_intersect *intersect = (struct itset_elt_intersect *) base;
+ struct itset_elt *elt;
+ int ix;
+
+ gdb_assert (!VEC_empty (itset_elt_ptr, intersect->elements));
+
+ for (ix = 0; VEC_iterate (itset_elt_ptr, intersect->elements, ix, elt); ++ix)
+ {
+ if (!elt->vtable->contains_inferior (elt, inf))
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Implementation of `contains_inferior' method. */
+
+static int
+intersect_contains_thread (struct itset_elt *base, struct thread_info *thr)
+{
+ struct itset_elt_intersect *intersect = (struct itset_elt_intersect *) base;
+ struct itset_elt *elt;
+ int ix;
+
+ gdb_assert (!VEC_empty (itset_elt_ptr, intersect->elements));
+
+ for (ix = 0; VEC_iterate (itset_elt_ptr, intersect->elements, ix, elt); ++ix)
+ {
+ if (!elt->vtable->contains_thread (elt, thr))
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Implementation of `is_empty' method. */
+
+static int
+intersect_is_empty (struct itset_elt *base)
+{
+ struct itset_elt_intersect *intersect = (struct itset_elt_intersect *) base;
+ struct inferior *inf;
+ struct thread_info *thr;
+
+ ALL_INFERIORS(inf)
+ {
+ if (intersect_contains_inferior (base, inf))
+ return 0;
+ }
+
+ ALL_THREADS(thr)
+ {
+ if (intersect_contains_thread (base, thr))
+ return 0;
+ }
+
+ return 1;
+}
+
+static const struct itset_elt_vtable intersect_vtable =
+{
+ intersect_destroy,
+ intersect_contains_inferior,
+ intersect_contains_thread,
+ intersect_is_empty
+};
+
+/* Create a new `intersect' I/T set element. */
+
+static struct itset_elt_intersect *
+create_intersect_itset (void)
+{
+ struct itset_elt_intersect *elt;
+
+ elt = XNEW (struct itset_elt_intersect);
+ elt->base.vtable = &intersect_vtable;
+ elt->elements = NULL;
+
+ return elt;
+}
+
+\f
+
/* An I/T set element representing all inferiors. */
struct itset_elt_all
@@ -655,19 +862,38 @@ struct itset_elt_state
/* Implementation of `contains_inferior' method. */
static int
-state_contains_inferior (struct itset_elt *base, struct inferior *inf)
+state_contains_thread (struct itset_elt *base, struct thread_info *thr)
{
- return 1;
+ struct itset_elt_state *state = (struct itset_elt_state *) base;
+
+ return thr->state == state->state;
}
/* Implementation of `contains_inferior' method. */
static int
-state_contains_thread (struct itset_elt *base, struct thread_info *thr)
+state_contains_inferior (struct itset_elt *base, struct inferior *inf)
{
struct itset_elt_state *state = (struct itset_elt_state *) base;
+ struct thread_info *thr;
- return thr->state == state->state;
+ /* True if we find a thread of this inferior that is in the state
+ we're interested in. */
+ ALL_THREADS(thr)
+ {
+ /* It's cheaper to check the state first, because looking up the
+ a thread's inferior is O(n). */
+ if (state_contains_thread (base, thr))
+ {
+ struct inferior *thr_inf;
+
+ thr_inf = get_thread_inferior (thr);
+ if (thr_inf == inf)
+ return 1;
+ }
+ }
+
+ return 0;
}
/* Implementation of `is_empty' method. */
@@ -991,6 +1217,46 @@ parse_range (char **textp)
return elt;
}
+\f
+/* Parse an I/T set range. A range has the form F[:L][.T], where F is
+ the starting inferior, L is the ending inferior, and T is the
+ thread. Updates RESULT with the new I/T set elements, and returns
+ an updated pointer into the spec. Throws an exception on
+ error. */
+
+struct itset_elt *
+parse_core_range (char **textp)
+{
+ int core_first, core_last;
+ char *text = *textp;
+
+ if (*text == '@')
+ ++text;
+
+ if (*text == '*')
+ {
+ core_first = WILDCARD;
+ core_last = WILDCARD;
+ ++text;
+ }
+ else
+ {
+ core_first = strtol (text, &text, 10);
+ if (*text == ':')
+ {
+ ++text;
+ if (!isdigit (*text))
+ error (_("Expected digit in I/T set, at `%s'"), text);
+ core_last = strtol (text, &text, 10);
+ }
+ else
+ core_last = core_first;
+ }
+
+ *textp = text;
+ return create_core_range_itset (core_first, core_last);
+}
+
/* Parse a named I/T set. Currently the only named sets which are
recognized are `exec (NAME)', and `current'. Updates RESULT with
the new I/T set elements, and returns an updated pointer into the
@@ -1089,16 +1355,34 @@ make_cleanup_itset_free (struct itset *itset)
static struct itset_elt *
parse_one_element (char **spec)
{
+ struct itset_elt *elt;
+
*spec = skip_spaces (*spec);
if (isdigit (**spec) || **spec == '*' || **spec == '.')
- return parse_range (spec);
+ elt = parse_range (spec);
else if (isalpha (**spec))
- return parse_named (spec);
+ elt = parse_named (spec);
else if (**spec == '~')
- return parse_negated (spec);
+ elt = parse_negated (spec);
else
error (_("Invalid I/T syntax at `%s'"), *spec);
+
+ if (**spec == '@')
+ {
+ struct itset_elt_intersect *intersect;
+ struct itset_elt *core_range;
+
+ core_range = parse_core_range (spec);
+
+ intersect = create_intersect_itset ();
+ VEC_safe_push (itset_elt_ptr, intersect->elements, elt);
+ VEC_safe_push (itset_elt_ptr, intersect->elements, core_range);
+
+ return (struct itset_elt *) intersect;
+ }
+
+ return elt;
}
/* Parse an I/T set specification and return a new I/T set. Throws an
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [RFC/WIP PATCH 08/14] Add support for the '@' core operator
2011-11-28 15:40 ` [RFC/WIP PATCH 08/14] Add support for the '@' core operator Pedro Alves
@ 2011-11-30 17:29 ` Tom Tromey
0 siblings, 0 replies; 53+ messages in thread
From: Tom Tromey @ 2011-11-30 17:29 UTC (permalink / raw)
To: Pedro Alves; +Cc: gdb-patches
>>>>> "Pedro" == Pedro Alves <pedro@codesourcery.com> writes:
Pedro> This adds support for the '@' operator as described in the series
Pedro> description. I've left this split into a separate patch, as I have
Pedro> tend to think the syntax for filtering per core will end up different.
Pedro> In any case, this adds support for set intersection, so I went ahead
Pedro> with it anyway.
Just some trivia...
Pedro> +/* Implementation of `contains_inferior' method. */
Pedro> +
Pedro> +static int
Pedro> +core_range_contains_thread (struct itset_elt *base, struct thread_info *thr)
Comment mentions the wrong method.
Pedro> +/* Implementation of `contains_inferior' method. */
Pedro> +
Pedro> +static int
Pedro> +intersect_contains_thread (struct itset_elt *base, struct thread_info *thr)
Here too.
Pedro> /* Implementation of `contains_inferior' method. */
Pedro> static int
Pedro> -state_contains_inferior (struct itset_elt *base, struct inferior *inf)
Pedro> +state_contains_thread (struct itset_elt *base, struct thread_info *thr)
Here too.
Tom
^ permalink raw reply [flat|nested] 53+ messages in thread
* [RFC/WIP PATCH 14/14] Fix manythreads.exp test
2011-11-28 15:39 [RFC/WIP PATCH 00/14] I/T sets Pedro Alves
` (10 preceding siblings ...)
2011-11-28 15:40 ` [RFC/WIP PATCH 08/14] Add support for the '@' core operator Pedro Alves
@ 2011-11-28 15:45 ` Pedro Alves
2011-11-28 15:45 ` [RFC/WIP PATCH 06/14] Add base itsets support Pedro Alves
` (5 subsequent siblings)
17 siblings, 0 replies; 53+ messages in thread
From: Pedro Alves @ 2011-11-28 15:45 UTC (permalink / raw)
To: gdb-patches
As infrun only decides to stop or not all threads after handling the
event the target reported, threads can now appear or disappear after
"Program received signal SIGFOO" and presenting the prompt, which
breaks the manythreads.exp test. This fixes it.
---
gdb/testsuite/gdb.threads/manythreads.exp | 52 +++++++++++++++++++++++------
1 files changed, 41 insertions(+), 11 deletions(-)
diff --git a/gdb/testsuite/gdb.threads/manythreads.exp b/gdb/testsuite/gdb.threads/manythreads.exp
index 12a25b9..168beac 100644
--- a/gdb/testsuite/gdb.threads/manythreads.exp
+++ b/gdb/testsuite/gdb.threads/manythreads.exp
@@ -112,10 +112,39 @@ gdb_test_multiple "continue" "second continue" {
}
}
+# Note that in the tests below, we can't suffix the "Program received
+# signal SIGINT" regexes with $gdb_prompt, as doing so would mean that
+# e.g. with,
+#
+# Program received signal SIGINT, Interrupt.
+# [New Thread FOO]
+# [Switching to Thread BAR]
+# 0xdeadbeef in bar () from bar.so
+# (gdb)
+#
+# the [New ...] or [... exited] regexes would eat the "Program
+# received ..." bit. [New FOO] and [FOO exited] may appear while GDB
+# is stopping threads.
+
# Wait another second. If the program stops on its own, GDB has failed
# to handle duplicate SIGINTs sent to multiple threads.
set failed 0
remote_expect host 1 {
+ -re "Program received signal SIGINT" {
+
+ # Eat the prompt.
+ gdb_expect {
+ -re "$gdb_prompt $" {
+ }
+ }
+
+ if { $failed == 0 } {
+ fail "check for duplicate SIGINT"
+ }
+ send_gdb "continue\n"
+ set failed 1
+ exp_continue
+ }
-re "\\\[New \[^\]\]*\\\]\r\n" {
exp_continue -continue_timer
}
@@ -125,14 +154,6 @@ remote_expect host 1 {
-re "Thread \[^\n\]* executing\r\n" {
exp_continue -continue_timer
}
- -re "Program received signal SIGINT.*$gdb_prompt $" {
- if { $failed == 0 } {
- fail "check for duplicate SIGINT"
- }
- send_gdb "continue\n"
- set failed 1
- exp_continue
- }
timeout {
if { $failed == 0 } {
pass "check for duplicate SIGINT"
@@ -144,6 +165,18 @@ remote_expect host 1 {
send_gdb "\003"
set message "stop threads 2"
gdb_test_multiple "" "stop threads 2" {
+ -re "Program received signal SIGINT" {
+
+ # Eat the prompt.
+ gdb_expect {
+ -re "$gdb_prompt $" {
+ pass "$message"
+ }
+ timeout {
+ fail "$message (timeout)"
+ }
+ }
+ }
-re "\\\[New \[^\]\]*\\\]\r\n" {
exp_continue
}
@@ -153,9 +186,6 @@ gdb_test_multiple "" "stop threads 2" {
-re "Thread \[^\n\]* executing\r\n" {
exp_continue
}
- -re "Program received signal SIGINT.*$gdb_prompt $" {
- pass "$message"
- }
timeout {
fail "$message (timeout)"
}
^ permalink raw reply [flat|nested] 53+ messages in thread
* [RFC/WIP PATCH 06/14] Add base itsets support
2011-11-28 15:39 [RFC/WIP PATCH 00/14] I/T sets Pedro Alves
` (11 preceding siblings ...)
2011-11-28 15:45 ` [RFC/WIP PATCH 14/14] Fix manythreads.exp test Pedro Alves
@ 2011-11-28 15:45 ` Pedro Alves
2011-11-28 18:47 ` Eli Zaretskii
` (2 more replies)
2011-11-28 15:46 ` [RFC/WIP PATCH 11/14] Add I/T set support to most execution commands Pedro Alves
` (4 subsequent siblings)
17 siblings, 3 replies; 53+ messages in thread
From: Pedro Alves @ 2011-11-28 15:45 UTC (permalink / raw)
To: gdb-patches
This started out by reusing the itsets parsing code in the old
multi-process branch in CVS, but was actually quite limited, and in
need of a full rewrite. I found that Tromey's struct itset & co from
the archer-tromey-multi-inferior arch branch was a good clean start
design-wise (thanks Tom! :-) ). So I picked it up and got it into
shape.
As I said in the patch intro, this currently borrows heavilly
influenced by the HPD proposed spec syntax. The final form will
naturally need to find its way to the manual, but until then, see at
least:
1.2.1 Naming Individual Processes and Threads
1.2.2 Process/Thread Sets
1.2.3 Named Sets
2.2 Process/Thread Sets
at <http://sourceware.org/frysk/documentation/hpd.html>.
Currently:
- Supports user-specified named sets, and built-in sets. The
built-in sets are currently: all (all threads, all inferiors);
running (set of running threads); stopped (set of stopped threads);
curinf (the current inferior), and a special exec(FILENAME) set,
that takes a program file name (exec) as argument, to match all
inferiors running a given program.
- An "itfocus" command. This is used to set the current focus. E.g.,
(gdb) itfocus [2.*]
sets the focus on inferior 2.
- A `[' command. This is a prefix command, used to execute a command
with a given focus, without having to flip the current focus with
itfocus. E.g., with
(gdb) [3.*] break foo
the breakpoint is set on inferior 3. This is the same as:
(gdb) itfocus [3.*]
(gdb) break foo
(gdb) itfocus [whateverwasthecurrentsetbefore]
- a "defset" command. This is used to defined named itsets. E.g.,
(gdb) defset firstthree [1.1, 1.2, 1.3]
defines the set [firstthree], as being the threads 1, 2 and 3. You
can then use the set wherever an itset is allowed. E.g., with a
later patch, you'll be able to do:
(gdb) interrupt [firstthree]
- an "undefset" command. Used to undefine a set defined by itset.
"undefset -all" undefines all user-defined named sets.
- an "info itsets" command. Lists all named sets, in GDB "info"
style.
- A "whichsets" command. This shows which sets does a thread belong
to.
- A "viewset" command. This shows which inferiors and threads are
included in a specified itsets. It's the reverse of "whichsets".
The current default current set is [all]. I'm thinking it may make
sense to default to [curinf] instead, and thus get rid of "set
schedule-multiple" (see the patch about execution commands further in
the series).
Examples:
[all]> defset test [.1,.2]
[all]> info threads
Id Target Id Frame
3 Thread 0x7ffff7028700 (LWP 6128) "threads" 0x00007ffff7bcc78e in __lll_lock_wait_private () from /lib/x86_64-linux-gnu/libpthread.so.0
* 2 Thread 0x7ffff7829700 (LWP 6127) "threads" thread_function0 (arg=0x0) at threads.c:63
1 Thread 0x7ffff7fcb720 (LWP 6124) "threads" 0x00007ffff7910011 in clone () from /lib/x86_64-linux-gnu/libc.so.6
[all]> info itsets
Num Name What
1 test [.1,.2]
[all]> viewset [test]
[test] contains:
inferiors: 1
threads: 1.2, 1.1
[all]> whichsets [.1]
1.1 (Thread 0x7ffff7fcb720 (LWP 6124)) is in: all, stopped, curinf, test
[all]>
[all]> undefset test
[all]
Having played with this a lot, I'm not sure anymore requiring []
everywhere is a good idea. It's a bit hard to type. We can get rid
of the need for '[]' if we forbid spaces in specs. Then the [] prefix
command could be replaced with the "itfocus" command taking an
optional command to execute, much like TotalView's dfocus command.
We could add an 'f' alias for itfocus. E.g.,
|-------------------------+-------------------------+--------------------------|
| Before | After | What |
|-------------------------+-------------------------+--------------------------|
| [all]> f [1.1] | all> f 1.1 | Switch focus to thread 1 |
| *focus switches to 1.1* | *focus switches to 1.1* | |
|-------------------------+-------------------------+--------------------------|
| [1.1]> [all] b foo | 1.1> f all b foo | Set a breakpoint at foo |
| | | that triggers everywhere |
|-------------------------+-------------------------+--------------------------|
This has implications on the execution commands patch later in the
series. There, we currently support e.g., "step [all]", but without
the []'s, we'll need a new flag "step -f all".
---
gdb/Makefile.in | 4
gdb/breakpoint.h | 3
gdb/cli/cli-decode.c | 3
gdb/inferior.h | 2
gdb/itset.c | 1956 ++++++++++++++++++++++++++++++++++++++++++++++++++
gdb/itset.h | 95 ++
6 files changed, 2058 insertions(+), 5 deletions(-)
create mode 100644 gdb/itset.c
create mode 100644 gdb/itset.h
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 48221f2..839f191 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -741,7 +741,7 @@ SFILES = ada-exp.y ada-lang.c ada-typeprint.c ada-valprint.c ada-tasks.c \
valarith.c valops.c valprint.c value.c varobj.c vec.c \
wrapper.c \
xml-tdesc.c xml-support.c \
- inferior.c gdb_usleep.c \
+ inferior.c itset.c gdb_usleep.c \
record.c gcore.c \
jit.c \
xml-syscall.c \
@@ -914,7 +914,7 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \
prologue-value.o memory-map.o memrange.o \
xml-support.o xml-syscall.o xml-utils.o \
target-descriptions.o target-memory.o xml-tdesc.o xml-builtin.o \
- inferior.o osdata.o gdb_usleep.o record.o gcore.o \
+ inferior.o itset.o osdata.o gdb_usleep.o record.o gcore.o \
jit.o progspace.o skip.o \
common-utils.o buffer.o ptid.o gdb-dlfcn.o
diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h
index 6b51137..df50438 100644
--- a/gdb/breakpoint.h
+++ b/gdb/breakpoint.h
@@ -508,9 +508,6 @@ enum watchpoint_triggered
watch_triggered_yes
};
-/* This is used to declare the VEC syscalls_to_be_caught. */
-DEF_VEC_I(int);
-
typedef struct bp_location *bp_location_p;
DEF_VEC_P(bp_location_p);
diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c
index 22ae6ec..3f96f19 100644
--- a/gdb/cli/cli-decode.c
+++ b/gdb/cli/cli-decode.c
@@ -1132,6 +1132,9 @@ find_command_name_length (const char *text)
if (*p == '!')
return 1;
+ if (*p == '[')
+ return 1; /* The temporary focus command. */
+
while (isalnum (*p) || *p == '-' || *p == '_'
/* Characters used by TUI specific commands. */
|| *p == '+' || *p == '<' || *p == '>' || *p == '$'
diff --git a/gdb/inferior.h b/gdb/inferior.h
index cfaea7f..559b4f1 100644
--- a/gdb/inferior.h
+++ b/gdb/inferior.h
@@ -45,6 +45,8 @@ struct terminal_info;
#include "progspace.h"
+DEF_VEC_I(int);
+
struct infcall_suspend_state;
struct infcall_control_state;
diff --git a/gdb/itset.c b/gdb/itset.c
new file mode 100644
index 0000000..81a4911
--- /dev/null
+++ b/gdb/itset.c
@@ -0,0 +1,1956 @@
+/* itset.c - Inferior/Thread sets.
+ Copyright (C) 2010, 2011 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "defs.h"
+#include "itset.h"
+#include "vec.h"
+#include "bfd.h"
+#include "inferior.h"
+#include "progspace.h"
+#include "gdb_string.h"
+#include "cli/cli-utils.h"
+#include "gdbthread.h"
+#include "command.h"
+#include <ctype.h>
+#include "gdbcmd.h"
+
+/* Rather than creating/destroying these dynamic itsets when
+ necessary, keep global copies around (itsets are refcounted). */
+static struct itset *all_itset;
+static struct itset *running_itset;
+static struct itset *stopped_itset;
+static struct itset *curinf_itset;
+
+/* Forward declaration of the base class. */
+
+struct itset_elt;
+
+/* An element of an I/T set is a class with some virtual methods,
+ defined here. */
+
+struct itset_elt_vtable
+{
+ /* Destroy the contents of this element. If the element does not
+ require any special cleanup, this can be NULL. This should not
+ free the element itself; that is done by the caller. */
+
+ void (*destroy) (struct itset_elt *);
+
+ /* Return true if the element contains the inferior. The element
+ and the inferior are passed as arguments. */
+
+ int (*contains_inferior) (struct itset_elt *elt, struct inferior *inf);
+
+ /* Return true if the element contains the thread. The element and
+ the thread are passed as arguments. */
+
+ int (*contains_thread) (struct itset_elt *elt, struct thread_info *thr);
+
+ /* Return true if the element is empty. */
+
+ int (*is_empty) (struct itset_elt *elt);
+};
+
+/* The base class of all I/T set elements. */
+
+struct itset_elt
+{
+ const struct itset_elt_vtable *vtable;
+};
+
+typedef struct itset_elt *itset_elt_ptr;
+DEF_VEC_P (itset_elt_ptr);
+
+struct itset
+{
+ /* The itset's name. May be NULL. */
+ char *name;
+
+ /* The original specification of the set. */
+ char *spec;
+
+ /* The reference count. */
+ int refc;
+
+ /* The elements making up the set. */
+ VEC (itset_elt_ptr) *elements;
+};
+
+const char *
+itset_name (const struct itset *itset)
+{
+ return itset->name;
+}
+
+const char *
+itset_spec (const struct itset *itset)
+{
+ return itset->spec;
+}
+
+\f
+
+/* An element in the list of named itsets, which can be either
+ debugger built-in (all, stopped, running, etc.), or user
+ defined. */
+
+struct named_itset
+{
+ /* Pointer to next in linked list. */
+ struct named_itset *next;
+
+ /* Unique identifier. Positive if user defined, negative if
+ internal and built-in. */
+ int number;
+
+ /* The I/T set. */
+ struct itset *set;
+};
+
+/* The head of the list of named I/T sets. */
+
+static struct named_itset *named_itsets;
+
+/* Number of last named itset made. */
+
+static int named_itset_count;
+
+/* Number of last internal named itset made. */
+
+static int internal_named_itset_count;
+
+/* Traverse all named itsets. */
+
+#define ALL_NAMED_ITSETS(E) \
+ for ((E) = named_itsets; (E); (E) = (E)->next)
+
+/* Add IT at the end of the named itset chain. */
+
+static void
+add_to_named_itset_chain (struct named_itset *it)
+{
+ struct named_itset *it1;
+
+ /* Add this itset to the end of the chain so that a list of
+ breakpoints will come out in order of increasing numbers. */
+
+ it1 = named_itsets;
+ if (it1 == 0)
+ named_itsets = it;
+ else
+ {
+ while (it1->next)
+ it1 = it1->next;
+ it1->next = it;
+ }
+}
+
+static struct named_itset *
+get_named_itset (char *name)
+{
+ struct named_itset *it;
+
+ for (it = named_itsets; it != NULL; it = it->next)
+ if (strcmp (it->set->name, name) == 0)
+ return it;
+ return NULL;
+}
+
+\f
+
+/* A helper function that returns true if all elements in the ELEMENTS
+ set are empty. */
+
+static int
+set_is_empty (VEC (itset_elt_ptr) *elements)
+{
+ int ix;
+ struct itset_elt *elt;
+
+ for (ix = 0; VEC_iterate (itset_elt_ptr, elements, ix, elt); ++ix)
+ if (!elt->vtable->is_empty (elt))
+ return 0;
+
+ return 1;
+}
+
+/* A helper function that returns true if the inferior INF is
+ contained by the set ELEMENTS. */
+
+static int
+set_contains_inferior (VEC (itset_elt_ptr) *elements, struct inferior *inf)
+{
+ int ix;
+ struct itset_elt *elt;
+
+ for (ix = 0; VEC_iterate (itset_elt_ptr, elements, ix, elt); ++ix)
+ {
+ if (elt->vtable->contains_inferior (elt, inf))
+ return 1;
+ }
+
+ return 0;
+}
+
+/* A helper function that returns true if the inferior INF is
+ contained by the set ELEMENTS. */
+
+static int
+set_contains_thread (VEC (itset_elt_ptr) *elements, struct thread_info *thr)
+{
+ int ix;
+ struct itset_elt *elt;
+
+ for (ix = 0; VEC_iterate (itset_elt_ptr, elements, ix, elt); ++ix)
+ {
+ if (elt->vtable->contains_thread (elt, thr))
+ return 1;
+ }
+
+ return 0;
+}
+
+/* A helper function to destroy all the elements in the set ELEMENTS.
+ This also destroys ELEMENTS itself. */
+
+static void
+set_free (VEC (itset_elt_ptr) *elements)
+{
+ int ix;
+ struct itset_elt *elt;
+
+ for (ix = 0; VEC_iterate (itset_elt_ptr, elements, ix, elt); ++ix)
+ {
+ if (elt->vtable->destroy)
+ elt->vtable->destroy (elt);
+ xfree (elt);
+ }
+
+ VEC_free (itset_elt_ptr, elements);
+}
+
+\f
+
+/* An I/T set element representing all inferiors using a certain
+ executable. */
+
+struct itset_elt_exec
+{
+ struct itset_elt base;
+
+ /* The name of the executable. */
+ char *exec_name;
+};
+
+/* Implementation of `destroy' method. */
+
+static void
+exec_destroy (struct itset_elt *base)
+{
+ struct itset_elt_exec *exec = (struct itset_elt_exec *) base;
+
+ xfree (exec->exec_name);
+}
+
+/* Implementation of `contains_inferior' method. */
+
+static int
+exec_contains_inferior (struct itset_elt *base, struct inferior *inf)
+{
+ struct itset_elt_exec *exec = (struct itset_elt_exec *) base;
+
+ /* FIXME: smarter compare. */
+ return (inf->pspace->ebfd != NULL
+ && strcmp (exec->exec_name,
+ bfd_get_filename (inf->pspace->ebfd)) == 0);
+}
+
+/* Implementation of `contains_inferior' method. */
+
+static int
+exec_contains_thread (struct itset_elt *base, struct thread_info *thr)
+{
+ struct itset_elt_exec *exec = (struct itset_elt_exec *) base;
+ struct inferior *inf = get_thread_inferior (thr);
+
+ /* FIXME: smarter compare. */
+ return (inf->pspace->ebfd != NULL
+ && strcmp (exec->exec_name,
+ bfd_get_filename (inf->pspace->ebfd)) == 0);
+}
+
+/* Implementation of `is_empty' method. */
+
+static int
+exec_is_empty (struct itset_elt *base)
+{
+ struct itset_elt_exec *exec = (struct itset_elt_exec *) base;
+ struct inferior *inf;
+
+ ALL_INFERIORS (inf)
+ if (exec_contains_inferior (base, inf))
+ return 0;
+
+ return 1;
+}
+
+static const struct itset_elt_vtable exec_vtable =
+{
+ exec_destroy,
+ exec_contains_inferior,
+ exec_contains_thread,
+ exec_is_empty
+};
+
+/* Create a new `exec' I/T set element. */
+
+static struct itset_elt *
+create_exec_itset (char *arg)
+{
+ struct itset_elt_exec *elt;
+
+ elt = XNEW (struct itset_elt_exec);
+ elt->base.vtable = &exec_vtable;
+ elt->exec_name = arg;
+
+ return (struct itset_elt *) elt;
+}
+
+\f
+
+/* The value representing any inferior or thread. */
+
+#define WILDCARD -1
+
+/* An I/T set element representing a range of inferiors. */
+
+struct itset_elt_range
+{
+ struct itset_elt base;
+
+ /* The first and last inferiors in this range. If INF_FIRST is
+ WILDCARD, then INF_LAST is unused. */
+ int inf_first, inf_last;
+
+ /* The first and last threads in this range. If THR_FIRST is
+ WILDCARD, then THR_LAST is unused. */
+ int thr_first, thr_last;
+};
+
+/* Implementation of `contains_inferior' method. */
+
+static int
+range_contains_inferior (struct itset_elt *base, struct inferior *inf)
+{
+ struct itset_elt_range *range = (struct itset_elt_range *) base;
+
+ if (range->inf_first == WILDCARD
+ || (range->inf_first <= inf->num && inf->num <= range->inf_last))
+ return 1;
+ return 0;
+}
+
+/* Implementation of `contains_inferior' method. */
+
+static int
+range_contains_thread (struct itset_elt *base, struct thread_info *thr)
+{
+ struct itset_elt_range *range = (struct itset_elt_range *) base;
+
+ if (range->inf_first != WILDCARD)
+ {
+ struct inferior *inf;
+
+ inf = get_thread_inferior (thr);
+ if (inf->num < range->inf_first || range->inf_last < inf->num)
+ return 0;
+ }
+
+ if (range->thr_first == WILDCARD
+ || (range->thr_first <= thr->num && thr->num <= range->thr_last))
+ return 1;
+
+ return 0;
+}
+
+/* Implementation of `is_empty' method. */
+
+static int
+range_is_empty (struct itset_elt *base)
+{
+ struct itset_elt_range *range = (struct itset_elt_range *) base;
+ struct inferior *inf;
+ struct thread_info *thr;
+
+ ALL_INFERIORS(inf)
+ {
+ if (range_contains_inferior (base, inf))
+ return 0;
+ }
+
+ ALL_THREADS(thr)
+ {
+ if (range_contains_thread (base, thr))
+ return 0;
+ }
+
+ return 1;
+}
+
+static const struct itset_elt_vtable range_vtable =
+{
+ NULL,
+ range_contains_inferior,
+ range_contains_thread,
+ range_is_empty
+};
+
+/* Create a new `range' I/T set element. */
+
+static struct itset_elt *
+create_range_itset (int inf_first, int inf_last, int thr_first, int thr_last)
+{
+ struct itset_elt_range *elt;
+
+ elt = XNEW (struct itset_elt_range);
+ elt->base.vtable = &range_vtable;
+ elt->inf_first = inf_first;
+ elt->inf_last = inf_last;
+ elt->thr_first = thr_first;
+ elt->thr_last = thr_last;
+
+ return (struct itset_elt *) elt;
+}
+
+\f
+
+/* An I/T set element representing all inferiors. */
+
+struct itset_elt_all
+{
+ struct itset_elt base;
+};
+
+/* Implementation of `contains_inferior' method. */
+
+static int
+all_contains_inferior (struct itset_elt *base, struct inferior *inf)
+{
+ return 1;
+}
+
+/* Implementation of `contains_inferior' method. */
+
+static int
+all_contains_thread (struct itset_elt *base, struct thread_info *thr)
+{
+ return 1;
+}
+
+/* Implementation of `is_empty' method. */
+
+static int
+all_is_empty (struct itset_elt *base)
+{
+ /* There's always at least one inferior. */
+ return 0;
+}
+
+static const struct itset_elt_vtable all_vtable =
+{
+ NULL,
+ all_contains_inferior,
+ all_contains_thread,
+ all_is_empty
+};
+
+static struct itset_elt *
+create_all_itset (void)
+{
+ struct itset_elt_all *elt;
+
+ elt = XNEW (struct itset_elt_all);
+ elt->base.vtable = &all_vtable;
+
+ return (struct itset_elt *) elt;
+}
+
+\f
+
+/* An I/T set element representing an itset. */
+
+struct itset_elt_itset
+{
+ struct itset_elt base;
+
+ /* The I/T set this element wraps. */
+ struct itset *set;
+};
+
+static void
+itset_elt_itset_destroy (struct itset_elt *base)
+{
+ struct itset_elt_itset *iiset = (struct itset_elt_itset *) base;
+
+ itset_free (iiset->set);
+}
+
+/* Implementation of `contains_inferior' method. */
+
+static int
+itset_elt_itset_contains_inferior (struct itset_elt *base, struct inferior *inf)
+{
+ struct itset_elt_itset *iiset = (struct itset_elt_itset *) base;
+ return itset_contains_inferior (iiset->set, inf);
+}
+
+/* Implementation of `contains_inferior' method. */
+
+static int
+itset_elt_itset_contains_thread (struct itset_elt *base, struct thread_info *thr)
+{
+ struct itset_elt_itset *iiset = (struct itset_elt_itset *) base;
+ return itset_contains_thread (iiset->set, thr);
+}
+
+/* Implementation of `is_empty' method. */
+
+static int
+itset_elt_itset_is_empty (struct itset_elt *base)
+{
+ struct itset_elt_itset *iiset = (struct itset_elt_itset *) base;
+ return itset_is_empty (iiset->set);
+}
+
+static const struct itset_elt_vtable itset_elt_itset_vtable =
+{
+ itset_elt_itset_destroy,
+ itset_elt_itset_contains_inferior,
+ itset_elt_itset_contains_thread,
+ itset_elt_itset_is_empty
+};
+
+static struct itset_elt *
+create_itset_elt_itset (struct itset *set)
+{
+ struct itset_elt_itset *elt;
+
+ elt = XNEW (struct itset_elt_itset);
+ elt->base.vtable = &itset_elt_itset_vtable;
+ elt->set = itset_reference (set);
+
+ return (struct itset_elt *) elt;
+}
+
+\f
+
+/* An I/T set element representing a negated set. */
+
+struct itset_elt_negated
+{
+ struct itset_elt base;
+
+ /* The negated element. */
+ struct itset_elt *negated;
+};
+
+static void
+itset_elt_negated_destroy (struct itset_elt *base)
+{
+ struct itset_elt_negated *elt = (struct itset_elt_negated *) base;
+
+ if (elt->negated->vtable->destroy)
+ elt->negated->vtable->destroy (elt->negated);
+ xfree (elt->negated);
+}
+
+/* Implementation of `contains_inferior' method. */
+
+static int
+itset_elt_negated_contains_inferior (struct itset_elt *base, struct inferior *inf)
+{
+ struct itset_elt_negated *elt = (struct itset_elt_negated *) base;
+ return !elt->negated->vtable->contains_inferior (elt->negated, inf);
+}
+
+/* Implementation of `contains_inferior' method. */
+
+static int
+itset_elt_negated_contains_thread (struct itset_elt *base, struct thread_info *thr)
+{
+ struct itset_elt_negated *elt = (struct itset_elt_negated *) base;
+ return !elt->negated->vtable->contains_thread (elt->negated, thr);
+}
+
+/* Implementation of `is_empty' method. */
+
+static int
+itset_elt_negated_is_empty (struct itset_elt *base)
+{
+ struct itset_elt_negated *elt = (struct itset_elt_negated *) base;
+ struct inferior *inf;
+ struct thread_info *thr;
+
+ ALL_INFERIORS(inf)
+ {
+ if (itset_elt_negated_contains_inferior (base, inf))
+ return 0;
+ }
+
+ ALL_THREADS(thr)
+ {
+ if (itset_elt_negated_contains_thread (base, thr))
+ return 0;
+ }
+
+ return 1;
+}
+
+static const struct itset_elt_vtable itset_elt_negated_vtable =
+{
+ itset_elt_negated_destroy,
+ itset_elt_negated_contains_inferior,
+ itset_elt_negated_contains_thread,
+ itset_elt_negated_is_empty
+};
+
+static struct itset_elt_negated *
+create_itset_elt_negated (void)
+{
+ struct itset_elt_negated *elt;
+
+ elt = XNEW (struct itset_elt_negated);
+ elt->base.vtable = &itset_elt_negated_vtable;
+ elt->negated = NULL;
+
+ return elt;
+}
+
+\f
+
+/* An I/T set element representing all inferiors of a given state. */
+
+struct itset_elt_state
+{
+ struct itset_elt base;
+
+ int state;
+};
+
+/* Implementation of `contains_inferior' method. */
+
+static int
+state_contains_inferior (struct itset_elt *base, struct inferior *inf)
+{
+ return 1;
+}
+
+/* Implementation of `contains_inferior' method. */
+
+static int
+state_contains_thread (struct itset_elt *base, struct thread_info *thr)
+{
+ struct itset_elt_state *state = (struct itset_elt_state *) base;
+
+ return thr->state == state->state;
+}
+
+/* Implementation of `is_empty' method. */
+
+static int
+state_is_empty (struct itset_elt *base)
+{
+ struct thread_info *thr;
+
+ ALL_THREADS(thr)
+ if (state_contains_thread (base, thr))
+ return 0;
+
+ return 1;
+}
+
+static const struct itset_elt_vtable state_vtable =
+{
+ NULL,
+ state_contains_inferior,
+ state_contains_thread,
+ state_is_empty
+};
+
+static struct itset_elt *
+create_state_itset (int thread_state)
+{
+ struct itset_elt_state *elt;
+
+ elt = XNEW (struct itset_elt_state);
+ elt->base.vtable = &state_vtable;
+ elt->state = thread_state;
+
+ return (struct itset_elt *) elt;
+}
+
+\f
+
+/* Implementation of `contains_inferior' method. */
+
+static int
+current_contains_inferior (struct itset_elt *base, struct inferior *inf)
+{
+ return current_inferior () == inf;
+}
+
+/* Implementation of `contains_inferior' method. */
+
+static int
+current_contains_thread (struct itset_elt *base, struct thread_info *thr)
+{
+ struct inferior *inf;
+
+ inf = get_thread_inferior (thr);
+ return current_inferior () == inf;
+}
+
+/* Implementation of `is_empty' method. */
+
+static int
+current_is_empty (struct itset_elt *base)
+{
+ /* There's always a current inferior. */
+ return 0;
+}
+
+static const struct itset_elt_vtable current_vtable =
+{
+ NULL,
+ current_contains_inferior,
+ current_contains_thread,
+ current_is_empty
+};
+
+/* Create a new I/T set element representing just the current
+ inferior. */
+
+static struct itset_elt *
+create_current_itset (void)
+{
+ struct itset_elt *elt;
+
+ elt = XNEW (struct itset_elt);
+ elt->vtable = ¤t_vtable;
+
+ return elt;
+}
+
+\f
+
+/* An I/T set element representing a static list of inferiors. */
+
+struct itset_elt_static
+{
+ struct itset_elt base;
+
+ /* The inferiors. */
+ VEC (int) *inferiors;
+
+ /* The threads. */
+ VEC (int) *threads;
+};
+
+/* Implementation of `destroy' method. */
+
+static void
+static_destroy (struct itset_elt *base)
+{
+ struct itset_elt_static *st = (struct itset_elt_static *) base;
+
+ VEC_free (int, st->inferiors);
+ VEC_free (int, st->threads);
+}
+
+/* Helper function to compare two ints. Returns true if the first
+ argument is strictly less than the second, useful for
+ VEC_lower_bound. */
+
+static int
+static_lessthan (const int a, const int b)
+{
+ return a < b;
+}
+
+/* Implementation of `contains_inferior' method. */
+
+static int
+static_contains_inferior (struct itset_elt *base, struct inferior *inf)
+{
+ struct itset_elt_static *st = (struct itset_elt_static *) base;
+ int idx;
+
+ idx = VEC_lower_bound (int, st->inferiors, inf->num, static_lessthan);
+ if (idx < VEC_length (int, st->inferiors)
+ && VEC_index (int, st->inferiors, idx) == inf->num)
+ return 1;
+ return 0;
+}
+
+/* Implementation of `contains_thread' method. */
+
+static int
+static_contains_thread (struct itset_elt *base, struct thread_info *thr)
+{
+ struct itset_elt_static *st = (struct itset_elt_static *) base;
+ int idx;
+
+ idx = VEC_lower_bound (int, st->threads, thr->num, static_lessthan);
+ if (idx < VEC_length (int, st->threads)
+ && VEC_index (int, st->threads, idx) == thr->num)
+ return 1;
+ return 0;
+}
+
+/* Implementation of `is_empty' method. */
+
+static int
+static_is_empty (struct itset_elt *base)
+{
+ struct itset_elt_static *st = (struct itset_elt_static *) base;
+ int idx;
+
+ return VEC_empty (int, st->inferiors);
+}
+
+static const struct itset_elt_vtable static_vtable =
+{
+ static_destroy,
+ static_contains_inferior,
+ static_contains_thread,
+ static_is_empty
+};
+
+/* Helper struct used to pass data through iterate_over_inferiors. */
+
+struct iter_data
+{
+ /* The I/T set we are constructing. */
+
+ struct itset_elt_static *st;
+
+ /* The elements of the original (dynamic) I/T set. */
+
+ VEC (itset_elt_ptr) *elements;
+};
+
+/* A callback for iterate_over_inferiors that adds an inferior to the
+ result set, if it is in the source set. */
+
+static int
+check_one_inferior (struct inferior *inf, void *datum)
+{
+ struct iter_data *id = datum;
+
+ if (set_contains_inferior (id->elements, inf))
+ VEC_safe_push (int, id->st->inferiors, inf->num);
+
+ /* Keep going. */
+ return 0;
+}
+
+/* A callback for iterate_over_threads that adds a thread to the
+ result set, if it is in the source set. */
+
+static int
+check_one_thread (struct thread_info *thr, void *datum)
+{
+ struct iter_data *id = datum;
+
+ if (set_contains_thread (id->elements, thr))
+ VEC_safe_push (int, id->st->threads, thr->num);
+
+ /* Keep going. */
+ return 0;
+}
+
+/* Create a new static I/T set from the list of elements. */
+
+static struct itset_elt *
+create_static_itset (VEC (itset_elt_ptr) *elements)
+{
+ struct itset_elt_static *elt;
+ struct iter_data datum;
+
+ elt = XNEW (struct itset_elt_static);
+ elt->base.vtable = &static_vtable;
+ elt->inferiors = NULL;
+ elt->threads = NULL;
+
+ datum.st = elt;
+ datum.elements = elements;
+
+ iterate_over_inferiors (check_one_inferior, &datum);
+ if (VEC_length (int, elt->inferiors) > 1)
+ qsort (VEC_address (int, elt->inferiors),
+ VEC_length (int, elt->inferiors),
+ sizeof (int), compare_positive_ints);
+
+ iterate_over_threads (check_one_thread, &datum);
+ if (VEC_length (int, elt->threads) > 1)
+ qsort (VEC_address (int, elt->threads),
+ VEC_length (int, elt->threads),
+ sizeof (int), compare_positive_ints);
+
+ return (struct itset_elt *) elt;
+}
+
+\f
+/* Parse an I/T set range. A range has the form F[:L][.T], where F is
+ the starting inferior, L is the ending inferior, and T is the
+ thread. Updates RESULT with the new I/T set elements, and returns
+ an updated pointer into the spec. Throws an exception on
+ error. */
+
+struct itset_elt *
+parse_range (char **textp)
+{
+ int inf_first, inf_last, thr_first, thr_last;
+ char *text = *textp;
+ struct itset_elt *elt;
+
+ if (*text == '*')
+ {
+ inf_first = WILDCARD;
+ inf_last = WILDCARD;
+ ++text;
+ }
+ else if (*text == '.')
+ {
+ /* We allow e.g., '.2' as shorthand convenience. */
+ inf_first = WILDCARD;
+ inf_last = WILDCARD;
+ }
+ else
+ {
+ inf_first = strtol (text, &text, 10);
+ if (*text == ':')
+ {
+ ++text;
+ if (!isdigit (*text))
+ error (_("Expected digit in I/T set, at `%s'"), text);
+ inf_last = strtol (text, &text, 10);
+ }
+ else
+ inf_last = inf_first;
+ }
+
+ if (*text == '.')
+ {
+ ++text;
+ if (*text == '*')
+ {
+ thr_first = WILDCARD;
+ thr_last = WILDCARD;
+ ++text;
+ }
+ else if (!isdigit (*text))
+ error (_("Expected digit in I/T set, at `%s'"), text);
+ else
+ {
+ thr_first = strtol (text, (char **) &text, 10);
+ if (*text == ':')
+ {
+ ++text;
+ if (!isdigit (*text))
+ error (_("Expected digit in I/T set, at `%s'"), text);
+ thr_last = strtol (text, (char **) &text, 10);
+ }
+ else
+ thr_last = thr_first;
+ }
+ }
+ else
+ {
+ thr_first = WILDCARD;
+ thr_last = WILDCARD;
+ }
+
+ elt = create_range_itset (inf_first, inf_last, thr_first, thr_last);
+ *textp = text;
+ return elt;
+}
+
+/* Parse a named I/T set. Currently the only named sets which are
+ recognized are `exec (NAME)', and `current'. Updates RESULT with
+ the new I/T set elements, and returns an updated pointer into the
+ spec. Throws an exception on error. */
+
+struct itset_elt *
+parse_named (char **textp)
+{
+ struct itset_elt *elt;
+ char *text = *textp;
+ char *name = text;
+
+ for (text = name + 1; isalnum (*text) || *text == '_'; ++text)
+ ;
+
+ if (strncmp ("all", name, text - name) == 0)
+ elt = create_all_itset ();
+ else if (strncmp ("stopped", name, text - name) == 0)
+ elt = create_state_itset (THREAD_STOPPED);
+ else if (strncmp ("running", name, text - name) == 0)
+ elt = create_state_itset (THREAD_RUNNING);
+ else if (strncmp ("curinf", name, text - name) == 0)
+ elt = create_current_itset ();
+ else if (strncmp ("exec", name, text - name) == 0)
+ {
+ char *tem;
+ char *arg;
+
+ text = skip_spaces (text);
+ if (*text != '(')
+ error (_("'(' expected in I/T set after `exec'"));
+ text = skip_spaces (text + 1);
+ tem = strchr (text, ')');
+ if (!tem)
+ error (_("No closing ')' in I/T set for `exec'"));
+ if (tem - text == 0)
+ error (_("Empty argument to `exec' in I/T set"));
+ arg = xstrndup (text, tem - text);
+ text = tem + 1;
+ elt = create_exec_itset (arg);
+ }
+ else
+ {
+ struct named_itset *named_itset;
+ char *tem;
+
+ tem = alloca (text - name + 1);
+
+ memcpy (tem, name, text - name);
+ tem[text - name] = '\0';
+
+ named_itset = get_named_itset (tem);
+ if (named_itset == NULL)
+ error (_("Unknown named I/T set: `%s'"), tem);
+ elt = create_itset_elt_itset (named_itset->set);
+ }
+
+ *textp = text;
+ return elt;
+}
+
+static struct itset_elt *parse_one_element (char **specp);
+
+/* Parse a negated I/T set. Updates RESULT with the new I/T set
+ elements, and returns an updated pointer into the spec. Throws an
+ exception on error. */
+
+struct itset_elt *
+parse_negated (char **textp)
+{
+ struct itset_elt_negated *elt;
+
+ (*textp)++;
+
+ elt = create_itset_elt_negated ();
+ elt->negated = parse_one_element (textp);
+
+ return (struct itset_elt *) elt;
+}
+
+/* A cleanup function that calls itset_free. */
+
+static void
+itset_free_cleanup (void *arg)
+{
+ struct itset *itset = arg;
+ itset_free (itset);
+}
+
+struct cleanup *
+make_cleanup_itset_free (struct itset *itset)
+{
+ return make_cleanup (itset_free_cleanup, itset);
+}
+
+static struct itset_elt *
+parse_one_element (char **spec)
+{
+ *spec = skip_spaces (*spec);
+
+ if (isdigit (**spec) || **spec == '*' || **spec == '.')
+ return parse_range (spec);
+ else if (isalpha (**spec))
+ return parse_named (spec);
+ else if (**spec == '~')
+ return parse_negated (spec);
+ else
+ error (_("Invalid I/T syntax at `%s'"), *spec);
+}
+
+/* Parse an I/T set specification and return a new I/T set. Throws an
+ exception on error. */
+
+struct itset *
+itset_create (char **specp)
+{
+ int is_static = 0;
+ struct itset *result;
+ struct cleanup *cleanups;
+ char *spec = *specp;
+ char *spec_start;
+
+ if (*spec != '[')
+ error (_("I/T set must start with `['"));
+
+ result = XCNEW (struct itset);
+ result->refc = 1;
+
+ cleanups = make_cleanup_itset_free (result);
+
+ /* Skip the '['. */
+ ++spec;
+ spec = skip_spaces (spec);
+ spec_start = spec;
+
+ if (*spec == '!')
+ {
+ is_static = 1;
+ ++spec;
+ spec = skip_spaces (spec);
+ }
+
+ if (*spec != ']')
+ {
+ while (1)
+ {
+ struct itset_elt *elt;
+
+ elt = parse_one_element (&spec);
+ VEC_safe_push (itset_elt_ptr, result->elements, elt);
+
+ spec = skip_spaces (spec);
+ if (*spec == ',')
+ ++spec;
+ else if (*spec == ']')
+ break;
+ else
+ error (_("',' or ']' expected in I/T set, at `%s'"), spec);
+ }
+ }
+
+ /* Canonicalize by removing brackets. */
+ result->spec = xstrndup (spec_start, spec - spec_start);
+
+ /* Skip the ']'. */
+ ++spec;
+ *specp = skip_spaces (spec);
+
+ if (is_static)
+ {
+ struct itset_elt *st = create_static_itset (result->elements);
+
+ set_free (result->elements);
+ result->elements = NULL;
+ VEC_safe_push (itset_elt_ptr, result->elements, st);
+ }
+
+ discard_cleanups (cleanups);
+
+ return result;
+}
+
+/* Create a new I/T set which represents the current inferior and all
+ its threads. */
+
+static struct itset *
+itset_create_curinf (void)
+{
+ char *spec = "[curinf]";
+
+ return itset_create (&spec);
+}
+
+static struct itset *
+itset_create_all (void)
+{
+ char *spec = "[all]";
+
+ return itset_create (&spec);
+}
+
+static struct itset *
+itset_create_running (void)
+{
+ char *spec = "[running]";
+
+ return itset_create (&spec);
+}
+
+static struct itset *
+itset_create_stopped (void)
+{
+ char *spec = "[stopped]";
+
+ return itset_create (&spec);
+}
+
+/* Return 1 if SET contains INF, 0 otherwise. */
+
+int
+itset_is_empty (const struct itset *set)
+{
+ return set_is_empty (set->elements);
+}
+
+/* Return 1 if SET contains INF, 0 otherwise. */
+
+int
+itset_contains_inferior (struct itset *set, struct inferior *inf)
+{
+ return set_contains_inferior (set->elements, inf);
+}
+
+/* Return 1 if SET contains THR, 0 otherwise. */
+
+int
+itset_contains_thread (struct itset *set, struct thread_info *thr)
+{
+ return set_contains_thread (set->elements, thr);
+}
+
+/* Acquire a new reference to an I/T set. */
+
+struct itset *
+itset_reference (struct itset *itset)
+{
+ ++itset->refc;
+ return itset;
+}
+
+/* Destroy SET. */
+
+void
+itset_free (struct itset *set)
+{
+ /* Like xfree, allow NULL. */
+ if (set == NULL)
+ return;
+
+ if (--set->refc == 0)
+ {
+ set_free (set->elements);
+ xfree (set->name);
+ xfree (set->spec);
+ xfree (set);
+ }
+}
+
+/* Helper struct for iterate_over_itset. */
+
+struct iterate_data
+{
+ /* The I/T set we are using. */
+ struct itset *itset;
+
+ /* The original callback */
+ int (*callback) (struct inferior *, void *);
+
+ /* The data passed in to iterate_over_itset. */
+ void *client_data;
+};
+
+/* Callback function for iterate_over_inferiors, used by
+ iterate_over_itset. */
+
+static int
+iter_callback (struct inferior *inf, void *d)
+{
+ struct iterate_data *data = d;
+
+ if (itset_contains_inferior (data->itset, inf))
+ return data->callback (inf, data->client_data);
+
+ /* Keep going. */
+ return 0;
+}
+
+/* Like iterate_over_inferiors, but iterate over only those inferiors
+ in ITSET. */
+
+struct inferior *
+iterate_over_itset_inferiors (struct itset *itset,
+ itset_inf_callback_func *callback,
+ void *datum)
+{
+ struct iterate_data data;
+
+ data.itset = itset;
+ data.callback = callback;
+ data.client_data = datum;
+
+ return iterate_over_inferiors (iter_callback, &data);
+}
+
+/* Helper struct for iterate_over_itset. */
+
+struct iterate_thr_data
+{
+ /* The I/T set we are using. */
+ struct itset *itset;
+
+ /* The original callback */
+ int (*callback) (struct thread_info *, void *);
+
+ /* The data passed in to iterate_over_itset_threads. */
+ void *client_data;
+};
+
+/* Callback function for iterate_over_inferiors, used by
+ iterate_over_itset. */
+
+static int
+iter_thr_callback (struct thread_info *thr, void *d)
+{
+ struct iterate_thr_data *data = d;
+
+ if (itset_contains_thread (data->itset, thr))
+ return data->callback (thr, data->client_data);
+
+ /* Keep going. */
+ return 0;
+}
+
+/* Like iterate_over_inferiors, but iterate over only those inferiors
+ in ITSET. */
+
+struct thread_info *
+iterate_over_itset_threads (struct itset *itset,
+ int (*callback) (struct thread_info *, void *),
+ void *datum)
+{
+ struct iterate_thr_data data;
+
+ data.itset = itset;
+ data.callback = callback;
+ data.client_data = datum;
+
+ return iterate_over_threads (iter_thr_callback, &data);
+}
+
+struct itset *current_itset = NULL;
+
+/* A cleanups callback, helper for save_current_itset
+ below. */
+
+static void
+restore_itset (void *arg)
+{
+ struct itset *saved_itset = arg;
+
+ current_itset = saved_itset;
+}
+
+/* Save the current itset so that it may be restored by a later call
+ to do_cleanups. Returns the struct cleanup pointer needed for
+ later doing the cleanup. */
+
+static struct cleanup *
+save_current_itset (void)
+{
+ struct cleanup *old_chain = make_cleanup (restore_itset,
+ current_itset);
+
+ return old_chain;
+}
+
+static int
+first_inferior (struct inferior *inf, void *data)
+{
+ return 1;
+}
+
+static int
+count_inferiors (struct inferior *inf, void *data)
+{
+ int *count_p = (int *) data;
+ (*count_p)++;
+ return 0;
+}
+
+static int
+first_thread_of (struct thread_info *thr, void *data)
+{
+ int inf_pid = * (int *) data;
+
+ if (ptid_get_pid (thr->ptid) != inf_pid)
+ return 0;
+
+ if (thr->state != THREAD_EXITED)
+ return 1;
+ return 0;
+}
+
+struct count_threads_arg
+{
+ int pid;
+ int count;
+};
+
+static int
+count_threads_of (struct thread_info *thr, void *data)
+{
+ struct count_threads_arg *c = data;
+
+ if (ptid_get_pid (thr->ptid) != c->pid)
+ return 0;
+
+ c->count++;
+ return 0;
+}
+
+/* Debugging dump for i/t sets. */
+
+void
+dump_itset (struct itset *itset)
+{
+}
+
+static void
+itfocus_command (char *spec, int from_tty)
+{
+ struct itset *itset;
+ struct itset_entry *entry;
+ struct inferior *inf;
+ struct cleanup *old_chain;
+ int inf_count;
+
+ if (spec == NULL)
+ {
+ if (current_itset)
+ printf_filtered (_("Focus is [%s] (current inferior is %d)"),
+ current_itset->spec,
+ current_inferior ()->num);
+ else
+ printf_filtered (_("No focus has been set. (current inferior is %d)"),
+ current_inferior ()->num);
+ printf_filtered ("\n");
+ return;
+ }
+
+ itset = itset_create (&spec);
+ old_chain = make_cleanup_itset_free (itset);
+
+ spec = skip_spaces (spec);
+ if (*spec != '\0')
+ error (_("Junk at end of I/T set: %s"), spec);
+
+ if (itset_is_empty (itset))
+ error (_("Cannot focus on an empty set, focus is unchanged"));
+
+ discard_cleanups (old_chain);
+
+ itset_free (current_itset);
+ current_itset = itset;
+
+ /* For now, set a current inferior from the first element of the
+ focus set. */
+ inf = iterate_over_itset_inferiors (itset, first_inferior, NULL);
+ inf_count = 0;
+ iterate_over_itset_inferiors (itset, count_inferiors, &inf_count);
+ if (inf_count > 1)
+ warning ("\
+%d inferiors in the current i/t set, using inf %d to get current exec",
+ inf_count, inf->num);
+
+ if (inf->pid != 0)
+ {
+ struct count_threads_arg count_threads_arg;
+
+ count_threads_arg.pid = inf->pid;
+ count_threads_arg.count = 0;
+ iterate_over_itset_threads (itset, count_threads_of, &count_threads_arg);
+ if (count_threads_arg.count != 0)
+ {
+ struct thread_info *thr;
+
+ if (count_threads_arg.count > 1)
+ warning (_("\
+%d threads for inferior %d in the current i/t set, switching to first"),
+ count_threads_arg.count, inf->num);
+ thr = iterate_over_itset_threads (itset, first_thread_of, &inf->pid);
+ switch_to_thread (thr->ptid);
+ }
+ }
+ else
+ {
+ set_current_inferior (inf);
+ switch_to_thread (null_ptid);
+ set_current_program_space (inf->pspace);
+ }
+
+ /* Confirm the choice of focus. */
+ printf_filtered (_("New focus: "));
+ dump_itset (current_itset);
+ printf_filtered (_("Current inferior is %d.\n"), current_inferior ()->num);
+}
+
+static struct named_itset *
+make_itset_named_itset (struct itset *set, char *name, int internal)
+{
+ struct named_itset *named_itset;
+
+ itset_reference (set);
+ set->name = name;
+
+ named_itset = XCNEW (struct named_itset);
+ named_itset->set = set;
+
+ if (internal)
+ named_itset->number = --internal_named_itset_count;
+ else
+ named_itset->number = ++named_itset_count;
+
+ return named_itset;
+}
+
+static int
+itset_elt_is_static (struct itset_elt *elt)
+{
+ return elt->vtable == &static_vtable;
+}
+
+static int
+itset_is_static (struct itset *itset)
+{
+ struct itset_elt *elt;
+ int ix;
+
+ /* True if all elements are static. */
+
+ if (VEC_empty (itset_elt_ptr, itset->elements))
+ return 0;
+
+ for (ix = 0; VEC_iterate (itset_elt_ptr, itset->elements, ix, elt); ++ix)
+ if (!itset_elt_is_static (elt))
+ return 0;
+
+ return 1;
+}
+
+static void
+defset_command (char *arg, int from_tty)
+{
+ char *endp;
+ char *name;
+ char *spec;
+ struct itset *itset;
+ struct named_itset *named_itset;
+ struct cleanup *old_chain;
+
+ if (arg == NULL || *arg == '\0')
+ error_no_arg (_("no args"));
+
+ arg = skip_spaces (arg);
+
+ endp = skip_to_space (arg);
+ spec = endp;
+
+ name = xstrndup (arg, endp - arg);
+ old_chain = make_cleanup (xfree, name);
+
+ named_itset = get_named_itset (name);
+ if (named_itset != NULL)
+ error (_("itset %s already exists"), name);
+
+ spec = skip_spaces (spec);
+
+ itset = itset_create (&spec);
+ make_cleanup_itset_free (itset);
+
+ if (itset_is_static (itset) && itset_is_empty (itset))
+ warning (_("static itset is empty"));
+
+ named_itset = make_itset_named_itset (itset, name, 0);
+ itset_free (itset);
+ discard_cleanups (old_chain);
+ add_to_named_itset_chain (named_itset);
+}
+
+static void
+free_named_itset (struct named_itset *it)
+{
+ itset_free (it->set);
+ xfree (it);
+}
+
+static void
+undefset_command (char *arg, int from_tty)
+{
+ char *name;
+ struct named_itset *it, **it_link;
+ int found;
+
+ if (arg == NULL || *arg == '\0')
+ error_no_arg (_("no args"));
+
+ name = skip_spaces (arg);
+
+ if (strcmp (name, "-all") == 0)
+ {
+ it = named_itsets;
+ it_link = &named_itsets;
+ while (it != NULL)
+ {
+ if (it->number > 0)
+ {
+ *it_link = it->next;
+ free_named_itset (it);
+ }
+ else
+ it_link = &it->next;
+ it = *it_link;
+ }
+ return;
+ }
+
+ found = 0;
+ it = named_itsets;
+ it_link = &named_itsets;
+ while (it != NULL)
+ {
+ if (strcmp (it->set->name, name) == 0)
+ {
+ if (it->number < 0)
+ error (_("cannot delete builtin I/T set"));
+
+ *it_link = it->next;
+ free_named_itset (it);
+ found = 1;
+ break;
+ }
+
+ it_link = &it->next;
+ it = *it_link;
+ }
+
+ if (!found)
+ warning (_("itset %s does not exist"), name);
+}
+
+static void
+itsets_info (char *arg, int from_tty)
+{
+ struct named_itset *e;
+ int num_printable_entries;
+ struct cleanup *tbl_chain;
+
+ /* Compute the number of rows in the table. */
+ num_printable_entries = 0;
+ ALL_NAMED_ITSETS (e)
+ if (e->number > 0
+ && (arg == NULL || number_is_in_list (arg, e->number)))
+ num_printable_entries++;
+
+ if (num_printable_entries == 0)
+ {
+ if (arg == NULL)
+ ui_out_message (current_uiout, 0, _("No named itsets.\n"));
+ else
+ ui_out_message (current_uiout, 0,
+ _("No named itset found with number %s.\n"), arg);
+
+ return;
+ }
+
+ tbl_chain
+ = make_cleanup_ui_out_table_begin_end (current_uiout, 3,
+ num_printable_entries,
+ "NamedItsetListTable");
+
+ ui_out_table_header (current_uiout, 7, ui_left, "number", "Num"); /* 1 */
+ ui_out_table_header (current_uiout, 14, ui_left, "name", "Name"); /* 2 */
+ ui_out_table_header (current_uiout, 40, ui_noalign, "what", "What"); /* 3 */
+ ui_out_table_body (current_uiout);
+
+ ALL_NAMED_ITSETS (e)
+ if (e->number > 0)
+ {
+ struct cleanup *entry_chain;
+
+ QUIT;
+
+ /* If we have an "args" string, it is a list of named itsets
+ to accept. Skip the others. */
+ if (arg != NULL && !number_is_in_list (arg, e->number))
+ continue;
+
+ entry_chain
+ = make_cleanup_ui_out_tuple_begin_end (current_uiout, "named-itset");
+ ui_out_field_int (current_uiout, "number", e->number); /* 1 */
+ ui_out_field_string (current_uiout, "name", e->set->name); /* 2 */
+ ui_out_text (current_uiout, "[");
+ ui_out_field_string (current_uiout, "what", e->set->spec); /* 3 */
+ ui_out_text (current_uiout, "]\n");
+ do_cleanups (entry_chain);
+ }
+
+ do_cleanups (tbl_chain);
+}
+
+static int
+whichsets_callback (struct thread_info *thr, void *data)
+{
+ struct named_itset *named_itset;
+ struct inferior *inf = get_thread_inferior (thr);
+ int printed = 0;
+
+ ALL_NAMED_ITSETS(named_itset)
+ {
+ QUIT;
+
+ if (itset_contains_thread (named_itset->set, thr))
+ {
+ if (!printed)
+ {
+ printf_filtered (_("%d.%d (%s) is in:"),
+ inf->num, thr->num,
+ target_pid_to_str (thr->ptid));
+ printf_filtered (" %s", itset_name (named_itset->set));
+ printed = 1;
+ }
+ else
+ printf_filtered (", %s", itset_name (named_itset->set));
+ }
+ }
+
+ if (printed)
+ printf_filtered ("\n");
+
+ return 0;
+}
+
+static void
+whichsets_command (char *arg, int from_tty)
+{
+ struct named_itset *named_itset;
+ struct itset *itset;
+ struct cleanup *old_chain;
+
+ if (arg == NULL)
+ {
+ /* No arg means all threads. */
+ itset = itset_reference (current_itset);
+ }
+ else
+ {
+ arg = skip_spaces (arg);
+ itset = itset_create (&arg);
+ }
+
+ old_chain = make_cleanup_itset_free (itset);
+ iterate_over_itset_threads (itset, whichsets_callback, NULL);
+ do_cleanups (old_chain);
+}
+
+static void
+viewset (struct itset *itset)
+{
+ struct inferior *inf;
+ struct thread_info *thr;
+ int printed;
+
+ printf_filtered ("[%s] contains:\n",
+ itset_name (itset) ? itset_name (itset) : itset_spec (itset));
+
+ printed = 0;
+ ALL_INFERIORS(inf)
+ {
+ if (itset_contains_inferior (itset, inf))
+ {
+ if (!printed)
+ {
+ printf_filtered (_(" inferiors: %d"), inf->num);
+ printed = 1;
+ }
+ else
+ printf_filtered (", %d", inf->num);
+
+ }
+ }
+ printf_filtered ("\n");
+
+ printed = 0;
+ ALL_THREADS(thr)
+ {
+ if (itset_contains_thread (itset, thr))
+ {
+ struct inferior *inf = get_thread_inferior (thr);
+ if (!printed)
+ {
+ printf_filtered (_(" threads: %d.%d"), inf->num, thr->num);
+ printed = 1;
+ }
+ else
+ printf_filtered (", %d.%d", inf->num, thr->num);
+ }
+ }
+ printf_filtered ("\n");
+
+#if 0
+ /* XXX dynamic vs static */
+ printf_unfiltered ("i/t set %s specified as '%s' (%s)",
+ itset->name, itset->spec,
+ /* itset->dynamic*/ 1 ? "dynamic" : "static");
+
+ printf_unfiltered (" {%di", VEC_length (itset_entry, itset->inferiors));
+ for (ix = 0; VEC_iterate (itset_entry, itset->inferiors, ix, entry); ++ix)
+ {
+ int iy, thr;
+
+ printf_unfiltered (",%d", entry->inferior);
+ printf_unfiltered (".{%dt", VEC_length (int, entry->threads));
+ for (iy = 0; VEC_iterate (int, entry->threads, iy, thr); ++iy)
+ printf_unfiltered (",%d", thr);
+ printf_unfiltered ("}");
+ }
+ printf_unfiltered ("}");
+ printf_unfiltered ("\n");
+#endif
+}
+
+static void
+viewset_command (char *arg, int from_tty)
+{
+ struct named_itset *named_itset;
+
+ if (arg == NULL)
+ {
+ struct named_itset *e;
+ struct itset *itset;
+
+ /* No arg means all debugger- and user-defined sets. */
+ ALL_NAMED_ITSETS (named_itset)
+ viewset (named_itset->set);
+ }
+ else
+ {
+ struct itset *itset;
+ struct cleanup *old_chain;
+
+ arg = skip_spaces (arg);
+ itset = itset_create (&arg);
+ old_chain = make_cleanup_itset_free (itset);
+ viewset (itset);
+ do_cleanups (old_chain);
+ }
+}
+
+static void
+temp_itfocus_command (char *spec, int from_tty)
+{
+ struct itset *itset;
+ struct cleanup *old_chain;
+
+ if (spec == NULL)
+ error_no_arg (_("i/t set"));
+
+ /* Evil hack. We know the command is `['. */
+ spec--;
+ itset = itset_create (&spec);
+ old_chain = make_cleanup_itset_free (itset);
+
+ if (itset_is_empty (itset))
+ {
+ warning (_("empty set, nothing to do"));
+ do_cleanups (old_chain);
+ return;
+ }
+
+ save_current_itset ();
+ current_itset = itset;
+
+ execute_command (spec, from_tty);
+
+ do_cleanups (old_chain);
+}
+
+static void
+make_internal_itset (struct itset *itset, const char *name)
+{
+ struct named_itset *named_itset;
+
+ named_itset = make_itset_named_itset (itset, xstrdup (name), 1);
+ add_to_named_itset_chain (named_itset);
+}
+
+void
+_initialize_itset (void)
+{
+ struct cmd_list_element *c = NULL;
+
+ all_itset = itset_create_all ();
+ running_itset = itset_create_running ();
+ stopped_itset = itset_create_stopped ();
+ curinf_itset = itset_create_curinf ();
+
+ make_internal_itset (all_itset, "all");
+ make_internal_itset (running_itset, "running");
+ make_internal_itset (stopped_itset, "stopped");
+ make_internal_itset (curinf_itset, "curinf");
+
+ current_itset = itset_reference (all_itset);
+
+ add_com ("itfocus", no_class, itfocus_command, _("\
+Change the set of current inferiors/threads."));
+
+ add_com ("[", no_class, temp_itfocus_command, _("\
+Change the set of current inferiors/threads."));
+
+ add_com ("defset", no_class, defset_command, _("\
+Add a new inferior.\n\
+Usage: add-inferior [-copies <N>] [-exec <FILENAME>]\n\
+N is the optional number of inferiors to add, default is 1.\n\
+FILENAME is the file name of the executable to use\n\
+as main program."));
+
+ add_com ("undefset", no_class, undefset_command, _("\
+Add a new inferior.\n\
+Usage: add-inferior [-copies <N>] [-exec <FILENAME>]\n\
+N is the optional number of inferiors to add, default is 1.\n\
+FILENAME is the file name of the executable to use\n\
+as main program."));
+
+ add_com ("whichsets", no_class, whichsets_command, _("\
+Add a new inferior.\n\
+Usage: add-inferior [-copies <N>] [-exec <FILENAME>]\n\
+N is the optional number of inferiors to add, default is 1.\n\
+FILENAME is the file name of the executable to use\n\
+as main program."));
+
+ add_com ("viewset", no_class, viewset_command, _("\
+Add a new inferior.\n\
+Usage: add-inferior [-copies <N>] [-exec <FILENAME>]\n\
+N is the optional number of inferiors to add, default is 1.\n\
+FILENAME is the file name of the executable to use\n\
+as main program."));
+
+ add_info ("itsets", itsets_info, _("\
+Display the list of defined named itsets. You can specify numbers (e.g. \"info itsets 1 3\"), \
+ranges (e.g. \"info itsets 4-8\"), or both (e.g. \"info itsets 1 3 4-8\").\n\n\
+If you don't specify any numbers or ranges, we'll show all itsets.\n\n\
+Usage: info itsets [NUMBERS AND/OR RANGES]\n"));
+}
diff --git a/gdb/itset.h b/gdb/itset.h
new file mode 100644
index 0000000..9574275
--- /dev/null
+++ b/gdb/itset.h
@@ -0,0 +1,95 @@
+/* itset.h - Inferior/Thread sets.
+ Copyright (C) 2010, 2011 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef ITSET_H
+#define ITSET_H
+
+struct inferior;
+struct thread_info;
+struct cleanup;
+
+/* This is an opaque type representing an I/T set. An I/T set is
+ simply a set of inferiors and/or threads. A set may be dynamic
+ (the members are enumerated at the time of use) or static (the
+ members are enumerated at the time of construction); but this
+ distinction is hidden from the callers. An I/T set object is
+ reference counted. */
+
+struct itset;
+
+/* Create a new I/T set from a user specification. The valid forms of
+ a specification are documented in the manual. *SPEC is the input
+ specification, and it is updated to point to the first non-space
+ character after the end of the specification. */
+
+struct itset *itset_create (char **spec);
+
+/* Create a new I/T set which represents the current inferior, at the
+ time that this call is made. */
+
+struct itset *itset_create_current (void);
+
+/* Like itset_create, but if *SPEC does not appear to be the start of
+ an I/T set, it will call itset_create_current and return the
+ result. */
+
+struct itset *itset_create_or_default (char **spec);
+
+/* Return true if the inferior is contained in the I/T set. */
+
+int itset_contains_inferior (struct itset *itset, struct inferior *inf);
+
+/* Return true if the thread is contained in the I/T set. */
+
+int itset_contains_thread (struct itset *itset, struct thread_info *inf);
+
+/* Return true if the inferior is contained in the I/T set. */
+
+int itset_member (struct itset *itset, int inf_id, int thread_id);
+
+const char *itset_name (const struct itset *itset);
+const char *itset_spec (const struct itset *itset);
+
+int itset_is_empty (const struct itset *itset);
+
+/* Acquire a new reference to an I/T set. Returns the I/T set, for
+ convenience. */
+
+struct itset *itset_reference (struct itset *itset);
+
+/* Release a reference to an I/T set. */
+
+void itset_free (struct itset *itset);
+
+struct cleanup *make_cleanup_itset_free (struct itset *itset);
+
+/* A cleanup function that calls itset_free. */
+
+void itset_cleanup (void *itset);
+
+/* Like iterate_over_inferiors, but iterate over only those inferiors
+ in ITSET. */
+
+typedef int (itset_inf_callback_func) (struct inferior *, void *);
+struct inferior *iterate_over_itset_inferiors (struct itset *itset,
+ itset_inf_callback_func *callback,
+ void *data);
+
+extern struct itset *current_itset;
+
+#endif /* ITSET_H */
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [RFC/WIP PATCH 06/14] Add base itsets support
2011-11-28 15:45 ` [RFC/WIP PATCH 06/14] Add base itsets support Pedro Alves
@ 2011-11-28 18:47 ` Eli Zaretskii
2011-11-28 18:56 ` Pedro Alves
2011-11-29 22:07 ` Tom Tromey
2011-11-30 18:54 ` Tom Tromey
2 siblings, 1 reply; 53+ messages in thread
From: Eli Zaretskii @ 2011-11-28 18:47 UTC (permalink / raw)
To: Pedro Alves; +Cc: gdb-patches
> From: Pedro Alves <pedro@codesourcery.com>
> Date: Mon, 28 Nov 2011 15:39:26 +0000
>
> + add_com ("defset", no_class, defset_command, _("\
> +Add a new inferior.\n\
No, you didn't mean that.
> + add_com ("undefset", no_class, undefset_command, _("\
> +Add a new inferior.\n\
Nor this.
> + add_com ("whichsets", no_class, whichsets_command, _("\
> +Add a new inferior.\n\
This, neither.
> + add_com ("viewset", no_class, viewset_command, _("\
> +Add a new inferior.\n\
Nor that.
> + add_info ("itsets", itsets_info, _("\
> +Display the list of defined named itsets. You can specify numbers (e.g. \"info itsets 1 3\"), \
You cannot have periods in the first line of a doc string, because all
the rest will be truncated by, e.g., `apropos'.
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [RFC/WIP PATCH 06/14] Add base itsets support
2011-11-28 18:47 ` Eli Zaretskii
@ 2011-11-28 18:56 ` Pedro Alves
0 siblings, 0 replies; 53+ messages in thread
From: Pedro Alves @ 2011-11-28 18:56 UTC (permalink / raw)
To: Eli Zaretskii; +Cc: gdb-patches
On Monday 28 November 2011 18:47:03, Eli Zaretskii wrote:
> > From: Pedro Alves <pedro@codesourcery.com>
> > Date: Mon, 28 Nov 2011 15:39:26 +0000
> >
> > + add_com ("defset", no_class, defset_command, _("\
> > +Add a new inferior.\n\
>
> No, you didn't mean that.
>
> > + add_com ("undefset", no_class, undefset_command, _("\
> > +Add a new inferior.\n\
>
> Nor this.
>
> > + add_com ("whichsets", no_class, whichsets_command, _("\
> > +Add a new inferior.\n\
>
> This, neither.
>
> > + add_com ("viewset", no_class, viewset_command, _("\
> > +Add a new inferior.\n\
>
> Nor that.
Thanks. Will fix all of this.
> > + add_info ("itsets", itsets_info, _("\
> > +Display the list of defined named itsets. You can specify numbers (e.g. \"info itsets 1 3\"), \
>
> You cannot have periods in the first line of a doc string, because all
> the rest will be truncated by, e.g., `apropos'.
Ah, forgot about that. Thanks.
--
Pedro Alves
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [RFC/WIP PATCH 06/14] Add base itsets support
2011-11-28 15:45 ` [RFC/WIP PATCH 06/14] Add base itsets support Pedro Alves
2011-11-28 18:47 ` Eli Zaretskii
@ 2011-11-29 22:07 ` Tom Tromey
2011-11-30 18:54 ` Tom Tromey
2 siblings, 0 replies; 53+ messages in thread
From: Tom Tromey @ 2011-11-29 22:07 UTC (permalink / raw)
To: Pedro Alves; +Cc: gdb-patches
>>>>> "Pedro" == Pedro Alves <pedro@codesourcery.com> writes:
More on this patch later, but:
Pedro> We could add an 'f' alias for itfocus. E.g.,
I think people already use this for "frame".
I've seen it on irc a few times.
Tom
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [RFC/WIP PATCH 06/14] Add base itsets support
2011-11-28 15:45 ` [RFC/WIP PATCH 06/14] Add base itsets support Pedro Alves
2011-11-28 18:47 ` Eli Zaretskii
2011-11-29 22:07 ` Tom Tromey
@ 2011-11-30 18:54 ` Tom Tromey
2011-12-16 17:26 ` Pedro Alves
2 siblings, 1 reply; 53+ messages in thread
From: Tom Tromey @ 2011-11-30 18:54 UTC (permalink / raw)
To: Pedro Alves; +Cc: gdb-patches
>>>>> "Pedro" == Pedro Alves <pedro@codesourcery.com> writes:
Pedro> This started out by reusing the itsets parsing code in the old
Pedro> multi-process branch in CVS, but was actually quite limited, and in
Pedro> need of a full rewrite. I found that Tromey's struct itset & co from
Pedro> the archer-tromey-multi-inferior arch branch was a good clean start
Pedro> design-wise (thanks Tom! :-) ).
My pleasure.
Pedro> - A `[' command. This is a prefix command, used to execute a command
Pedro> with a given focus, without having to flip the current focus with
Pedro> itfocus.
Cute implementation approach :)
I don't think completion will work right for this, even if you add a
completer for the new command. It would be good to have a way do
"command" completion but then further delegate to the command's
completer as appropriate.
Pedro> Having played with this a lot, I'm not sure anymore requiring []
Pedro> everywhere is a good idea. It's a bit hard to type. We can get rid
Pedro> of the need for '[]' if we forbid spaces in specs. Then the [] prefix
Pedro> command could be replaced with the "itfocus" command taking an
Pedro> optional command to execute, much like TotalView's dfocus command.
Pedro> We could add an 'f' alias for itfocus. E.g.,
It would be fine by me. Actually, since you have used it, I think you
should present it exactly as you would like it, then see what happens.
It is generally better, I think, to have a concrete proposal, as worked
out as possible. Otherwise the discussion can be speculative and veer
into the bikesheddy.
Pedro> + /* Add this itset to the end of the chain so that a list of
Pedro> + breakpoints will come out in order of increasing numbers. */
s/breakpoints/itsets/
Pedro> +static int
Pedro> +exec_contains_inferior (struct itset_elt *base, struct inferior *inf)
Pedro> +{
Pedro> + struct itset_elt_exec *exec = (struct itset_elt_exec *) base;
Pedro> +
Pedro> + /* FIXME: smarter compare. */
Pedro> + return (inf->pspace->ebfd != NULL
Pedro> + && strcmp (exec->exec_name,
Pedro> + bfd_get_filename (inf->pspace->ebfd)) == 0);
The FIXME is my fault; but still should be fixed.
I wonder whether the 'exec' style should be extended to:
1. Allow matching any objfile in the inferior, not just the main
executable. If not, it seems like a useful new sort of itset to
provide.
2. Match just the path components provided by the user, so that the BFD
/usr/bin/gdb will match user inputs "gdb" and "bin/gdb".
Pedro> +static void
Pedro> +restore_itset (void *arg)
Pedro> +{
Pedro> + struct itset *saved_itset = arg;
Pedro> +
Pedro> + current_itset = saved_itset;
Pedro> +}
Pedro> +
Pedro> +/* Save the current itset so that it may be restored by a later call
Pedro> + to do_cleanups. Returns the struct cleanup pointer needed for
Pedro> + later doing the cleanup. */
Pedro> +
Pedro> +static struct cleanup *
Pedro> +save_current_itset (void)
Pedro> +{
Pedro> + struct cleanup *old_chain = make_cleanup (restore_itset,
Pedro> + current_itset);
Pedro> +
Pedro> + return old_chain;
Pedro> +}
I think save_current_itset should acquire a reference, and restore_itset
should release it. Otherwise I think the right call to
temp_itfocus_command can free current_itset, causing a crash.
Pedro> +static int
Pedro> +exec_contains_thread (struct itset_elt *base, struct thread_info *thr)
I think this one should delegate to exec_contains_inferior for better
maintenance.
Pedro> +void
Pedro> +dump_itset (struct itset *itset)
Pedro> +{
Pedro> +}
:-)
Pedro> +static void
Pedro> +itfocus_command (char *spec, int from_tty)
I wonder if it would be better to put the guts of this into a utility
function and then call that from MI as well.
Also I suspect we'd want some kind of MI async notification for changes
to the current itset..?
Pedro> +static void
Pedro> +itsets_info (char *arg, int from_tty)
Pedro> +{
[...]
Pedro> + if (e->number > 0)
It would be nice to have a "maint info" variant that skips this check.
Pedro> +static void
Pedro> +temp_itfocus_command (char *spec, int from_tty)
Pedro> +{
Pedro> + struct itset *itset;
Pedro> + struct cleanup *old_chain;
Pedro> +
Pedro> + if (spec == NULL)
Pedro> + error_no_arg (_("i/t set"));
Pedro> +
Pedro> + /* Evil hack. We know the command is `['. */
Pedro> + spec--;
I forget, are leading spaces skipped by the CLI command processor?
Pedro> + add_com ("[", no_class, temp_itfocus_command, _("\
Pedro> +Change the set of current inferiors/threads."));
I'm a little surprised that this didn't need some change in the CLI
code, like "!" did.
Pedro> + add_com ("undefset", no_class, undefset_command, _("\
How about "delete itset ..." instead?
Pedro> + add_com ("whichsets", no_class, whichsets_command, _("\
[...]
Pedro> + add_com ("viewset", no_class, viewset_command, _("\
[...]
How about "info" subcommands for these instead?
As an aside, I have been thinking that some important (non-command)
concepts in gdb should be in the online help. For example, I think it
would be nice if "help linespec" described the possible linespecs; then
other things like "help break" could refer to that.
Anyway, "help itset" might be nice to have.
Pedro> +/* Create a new I/T set which represents the current inferior, at the
Pedro> + time that this call is made. */
Pedro> +
Pedro> +struct itset *itset_create_current (void);
Reading this again, I realize that the comment should specify whether a
static or dynamic set is returned.
Pedro> +/* Return true if the inferior is contained in the I/T set. */
Pedro> +
Pedro> +int itset_contains_inferior (struct itset *itset, struct inferior *inf);
Pedro> +/* Return true if the inferior is contained in the I/T set. */
Pedro> +
Pedro> +int itset_member (struct itset *itset, int inf_id, int thread_id);
Perhaps the names should be closer to indicate that these are just
overloads.
Pedro> +const char *itset_name (const struct itset *itset);
Pedro> +const char *itset_spec (const struct itset *itset);
Pedro> +
Pedro> +int itset_is_empty (const struct itset *itset);
These need comments. For this module I took the API comments in the
header file approach...
Pedro> +extern struct itset *current_itset;
Ditto.
Tom
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [RFC/WIP PATCH 06/14] Add base itsets support
2011-11-30 18:54 ` Tom Tromey
@ 2011-12-16 17:26 ` Pedro Alves
0 siblings, 0 replies; 53+ messages in thread
From: Pedro Alves @ 2011-12-16 17:26 UTC (permalink / raw)
To: gdb-patches; +Cc: Tom Tromey
On Wednesday 30 November 2011 18:53:52, Tom Tromey wrote:
> >>>>> "Pedro" == Pedro Alves <pedro@codesourcery.com> writes:
>
> Pedro> This started out by reusing the itsets parsing code in the old
> Pedro> multi-process branch in CVS, but was actually quite limited, and in
> Pedro> need of a full rewrite. I found that Tromey's struct itset & co from
> Pedro> the archer-tromey-multi-inferior arch branch was a good clean start
> Pedro> design-wise (thanks Tom! :-) ).
>
> My pleasure.
>
> Pedro> - A `[' command. This is a prefix command, used to execute a command
> Pedro> with a given focus, without having to flip the current focus with
> Pedro> itfocus.
>
> Cute implementation approach :)
>
> I don't think completion will work right for this, even if you add a
> completer for the new command. It would be good to have a way do
> "command" completion but then further delegate to the command's
> completer as appropriate.
>
> Pedro> Having played with this a lot, I'm not sure anymore requiring []
> Pedro> everywhere is a good idea. It's a bit hard to type. We can get rid
> Pedro> of the need for '[]' if we forbid spaces in specs. Then the [] prefix
> Pedro> command could be replaced with the "itfocus" command taking an
> Pedro> optional command to execute, much like TotalView's dfocus command.
> Pedro> We could add an 'f' alias for itfocus. E.g.,
>
> It would be fine by me. Actually, since you have used it, I think you
> should present it exactly as you would like it, then see what happens.
> It is generally better, I think, to have a concrete proposal, as worked
> out as possible. Otherwise the discussion can be speculative and veer
> into the bikesheddy.
Thing is I have been focused much more on making the inferior control
infrustructure actually work, and leaving the actual syntax etc. without
that much attention. I have adjusted everything to not need the []'s,
and I've rewriten itsets parsing and structures to support the
idea that each object kind needs its own prefix letter, and dot is
an intersection operator (e.g., i1.t2.c3), and I like the result. But I
think it will need a bit more experience and thought. It's missing some
important predicates. And I'm still wondering if we can / how can we unify
the selected thread/inferior and current focus concepts more tightly into
a single entity.
> Pedro> + /* Add this itset to the end of the chain so that a list of
> Pedro> + breakpoints will come out in order of increasing numbers. */
>
> s/breakpoints/itsets/
>
Thanks, fixed.
> Pedro> +static int
> Pedro> +exec_contains_inferior (struct itset_elt *base, struct inferior *inf)
> Pedro> +{
> Pedro> + struct itset_elt_exec *exec = (struct itset_elt_exec *) base;
> Pedro> +
> Pedro> + /* FIXME: smarter compare. */
> Pedro> + return (inf->pspace->ebfd != NULL
> Pedro> + && strcmp (exec->exec_name,
> Pedro> + bfd_get_filename (inf->pspace->ebfd)) == 0);
>
> The FIXME is my fault; but still should be fixed.
>
> I wonder whether the 'exec' style should be extended to:
>
> 1. Allow matching any objfile in the inferior, not just the main
> executable. If not, it seems like a useful new sort of itset to
> provide.
Yeah, though I think this would be a separate predicate. I mean,
"do something whenever the current thread is in DSO with objfile named
FOO" is a different test to "do something whenever the current thread
is part of any inferior running program "EXEC".
>
> 2. Match just the path components provided by the user, so that the BFD
> /usr/bin/gdb will match user inputs "gdb" and "bin/gdb".
To be honest, I really haven't really played much with 'exec' sets.
I think I only tried it once. :-) I was even thinking of removing it
from the first iteration, but ended up leaving it in place, since it
was already there.
>
> Pedro> +static void
> Pedro> +restore_itset (void *arg)
> Pedro> +{
> Pedro> + struct itset *saved_itset = arg;
> Pedro> +
> Pedro> + current_itset = saved_itset;
> Pedro> +}
> Pedro> +
> Pedro> +/* Save the current itset so that it may be restored by a later call
> Pedro> + to do_cleanups. Returns the struct cleanup pointer needed for
> Pedro> + later doing the cleanup. */
> Pedro> +
> Pedro> +static struct cleanup *
> Pedro> +save_current_itset (void)
> Pedro> +{
> Pedro> + struct cleanup *old_chain = make_cleanup (restore_itset,
> Pedro> + current_itset);
> Pedro> +
> Pedro> + return old_chain;
> Pedro> +}
>
> I think save_current_itset should acquire a reference, and restore_itset
> should release it. Otherwise I think the right call to
> temp_itfocus_command can free current_itset, causing a crash.
I don't think so, because the callers are expected to do:
save_current_itset ();
current_itset = itset;
That is, the reference was transfered to the cleanup. But I
did the change just in case anyway. Thanks.
>
> Pedro> +static int
> Pedro> +exec_contains_thread (struct itset_elt *base, struct thread_info *thr)
>
> I think this one should delegate to exec_contains_inferior for better
> maintenance.
>
> Pedro> +void
> Pedro> +dump_itset (struct itset *itset)
> Pedro> +{
> Pedro> +}
>
> :-)
>
> Pedro> +static void
> Pedro> +itfocus_command (char *spec, int from_tty)
>
> I wonder if it would be better to put the guts of this into a utility
> function and then call that from MI as well.
> Also I suspect we'd want some kind of MI async notification for changes
> to the current itset..?
Most probably. MI support is a TODO yet.
>
> Pedro> +static void
> Pedro> +itsets_info (char *arg, int from_tty)
> Pedro> +{
> [...]
> Pedro> + if (e->number > 0)
>
> It would be nice to have a "maint info" variant that skips this check.
Yeah, done.
>
> Pedro> +static void
> Pedro> +temp_itfocus_command (char *spec, int from_tty)
> Pedro> +{
> Pedro> + struct itset *itset;
> Pedro> + struct cleanup *old_chain;
> Pedro> +
> Pedro> + if (spec == NULL)
> Pedro> + error_no_arg (_("i/t set"));
> Pedro> +
> Pedro> + /* Evil hack. We know the command is `['. */
> Pedro> + spec--;
>
> I forget, are leading spaces skipped by the CLI command processor?
Looks like it. In any case, this is gone. `[' is no longer used.
>
> Pedro> + add_com ("[", no_class, temp_itfocus_command, _("\
> Pedro> +Change the set of current inferiors/threads."));
>
> I'm a little surprised that this didn't need some change in the CLI
> code, like "!" did.
It actually needed. You missed the cli-decode.c change. :-)
--- a/gdb/cli/cli-decode.c
+++ b/gdb/cli/cli-decode.c
@@ -1132,6 +1132,9 @@ find_command_name_length (const char *text)
if (*p == '!')
return 1;
+ if (*p == '[')
+ return 1; /* The temporary focus command. */
+
But in any case, this is gone.
>
> Pedro> + add_com ("undefset", no_class, undefset_command, _("\
>
> How about "delete itset ..." instead?
Yeah, it's more gdb-ish. I added these because they are what HPD specifies.
I'm going to post out a WIP v2 series with that not done yet though.
Sorry about that.
>
> Pedro> + add_com ("whichsets", no_class, whichsets_command, _("\
> [...]
> Pedro> + add_com ("viewset", no_class, viewset_command, _("\
> [...]
>
> How about "info" subcommands for these instead?
Makes sense too.
>
>
> As an aside, I have been thinking that some important (non-command)
> concepts in gdb should be in the online help. For example, I think it
> would be nice if "help linespec" described the possible linespecs; then
> other things like "help break" could refer to that.
>
> Anyway, "help itset" might be nice to have.
Indeed. Heck, most of the manual could be online.
I'll have to leave this for v3 though.
>
> Pedro> +/* Create a new I/T set which represents the current inferior, at the
> Pedro> + time that this call is made. */
> Pedro> +
> Pedro> +struct itset *itset_create_current (void);
>
> Reading this again, I realize that the comment should specify whether a
> static or dynamic set is returned.
Yeah. It's actually dynamic, and the comment is misleading. I've
adjusted it.
>
> Pedro> +/* Return true if the inferior is contained in the I/T set. */
> Pedro> +
> Pedro> +int itset_contains_inferior (struct itset *itset, struct inferior *inf);
>
> Pedro> +/* Return true if the inferior is contained in the I/T set. */
> Pedro> +
> Pedro> +int itset_member (struct itset *itset, int inf_id, int thread_id);
>
> Perhaps the names should be closer to indicate that these are just
> overloads.
itset_member is actually a relic of the old itsets implementation. It
doesn't exist anymore, but the declaration was left behind. Removed now.
>
> Pedro> +const char *itset_name (const struct itset *itset);
> Pedro> +const char *itset_spec (const struct itset *itset);
> Pedro> +
> Pedro> +int itset_is_empty (const struct itset *itset);
>
> These need comments. For this module I took the API comments in the
> header file approach...
>
> Pedro> +extern struct itset *current_itset;
>
> Ditto.
That's what WIP means. :-) There are a lot of comments missing
throughout the whole series. Fixed this instance now.
Thanks!
--
Pedro Alves
^ permalink raw reply [flat|nested] 53+ messages in thread
* [RFC/WIP PATCH 11/14] Add I/T set support to most execution commands
2011-11-28 15:39 [RFC/WIP PATCH 00/14] I/T sets Pedro Alves
` (12 preceding siblings ...)
2011-11-28 15:45 ` [RFC/WIP PATCH 06/14] Add base itsets support Pedro Alves
@ 2011-11-28 15:46 ` Pedro Alves
2011-11-30 19:27 ` Tom Tromey
2011-11-28 18:10 ` [RFC/WIP PATCH 00/14] I/T sets Pedro Alves
` (3 subsequent siblings)
17 siblings, 1 reply; 53+ messages in thread
From: Pedro Alves @ 2011-11-28 15:46 UTC (permalink / raw)
To: gdb-patches
This adds I/T set support to most execution commands. For execution
commands, there are actually two sets in effect:
- The "apply set":
This is the set of threads the execution command is applied to. For
example, for "step", the apply set is the set of threads that will be
stepped; for "finish", the apply set is the set of threads that will
the finished.
- The "run free set":
This is the set of threads that is allowed to run free while the
execution command is applied to the all threads in the "apply set".
The run free set is derived from the current set (as set by itfocus,
or by the [SET] prefix), and to remain backwards compatible, from "set
scheduler-locking on/step", "set schedule-multiple" and "set non-stop
on" too. Example, if the current set is [all] (all threads), by
default the run free set is the [all] set too. But if either
scheduler-locking is on/step or "set non-stop" is on, the run free set
is set to the empty set, meaning no thread is let run free.
With these two sets, a user that is not aware of GDBs new
capabilities, does not notice any user interface change. For example,
by default, "(gdb) step" will still step the current thread, and let
all other threads in the current inferior run free.
The pointer them comes from being able to override these new sets to
do more complicated commands.
For example, making use of the %ITSET% prompt substitution patch,
$ gdb -ex "set prompt %ITSET%> " ~/gdb/tests/threads
[all]> start
Temporary breakpoint 1 at 0x40068c: file threads.c, line 35.
Starting program: /home/pedro/gdb/tests/threads
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Temporary breakpoint 1,
main () at threads.c:35
35 long i = 0;
[all]> info threads
Id Target Id Frame
3 Thread 0x7ffff7028700 (LWP 22403) "threads" 0x00007ffff7bcc78e in __lll_lock_wait_private () from /lib/x86_64-linux-gnu/libpthread.so.0
* 2 Thread 0x7ffff7829700 (LWP 22402) "threads" thread_function0 (arg=0x0) at threads.c:63
1 Thread 0x7ffff7fcb720 (LWP 22323) "threads" 0x00007ffff7910011 in clone () from /lib/x86_64-linux-gnu/libc.so.6
[all]> itfocus [1.*]
warning: 3 threads for inferior 1 in the current i/t set, switching to first
New focus: Current inferior is 1.
[1.*]> info threads
Id Target Id Frame
* 3 Thread 0x7ffff7028700 (LWP 22403) "threads" 0x00007ffff7bcc78e in __lll_lock_wait_private () from /lib/x86_64-linux-gnu/libpthread.so.0
2 Thread 0x7ffff7829700 (LWP 22402) "threads" thread_function0 (arg=0x0) at threads.c:63
1 Thread 0x7ffff7fcb720 (LWP 22323) "threads" 0x00007ffff7910011 in clone () from /lib/x86_64-linux-gnu/libc.so.6
[1.*]> step
This last "step" commands steps thread 3 (the apply set, derived from
the selected thread), and, lets all other threads of inferior 1 run
free (derived from the current focus).
You can step more than one thread in parallel. For that, you pass an
explicit apply set as argument to the execution command. For example:
[1.*]> step [.1, .2]
That steps threads 1 and 2, and lets all other threads in the current
focus run free. Currently each of thread in the apply set step
independently and unaware of other threads in the same apply set.
That is, say, thread 1 finishes stepping first, and we'll give back
the prompt to the user immediately, while thread 2 continues stepping
in the background (as if "thread 1; step&; thread 2; step&" had been
issued). Instead, I think it'd make sense to wait for all threads in
the apply set to finish what they were told to do before considering
the whole command done with, and giving the prompt back to the user.
If you want to step all threads in the current focus in parallel,
although you could, you don't need to spell out the current focus set
as the apply set explicitly as in
[1.*]> step [1.*]
Instead, you can pass the -a (from "all") flag to the execution
command, as in:
[1.*]> step -a
That is, this makes the apply set be equal to the current set, thus
stepping all threads in parallel. The "run free set" is still the
current set, but, naturally, there are no threads left to run free.
Note you can make use of the [] prefix command to temporarily switch
the current set, so you can for example have defined a set named
[workers], and then step them all with:
[1.*]> [workers] step -a
Often you'll want to step only a given thread or set of threads, and
not let any other thread run free while stepping. That is, run a step
as if "set scheduler-locking" was step or on. You can do that already
with what I've presented above, with e.g.,
[1.*]> [1.2] step [1.2]
That is, the [1.2] prefix sets the current set temporarily to thread
2, and the apply set is also set to thread 2.
Or, you may more simply do:
[1.*]> [] step [1.2]
That is, the [] prefix sets the current set temporarily to the empty
set, and the apply set is thread 2.
Or, you may also do:
[1.*]> [] step
where the current set is set to the empty set, and the apply set is
derived from the current thread. I've added yet a third option (-l,
from lock) to get the same explicit locking. E.g., if the current
thread is thread 2, then these are equivalent:
[1.2]> step
[1.*]> [1.2] step
[1.*]> [] step
[1.*]> step -l
they all step thread 2, with scheduler locking on.
Lastly, it'd be useful to have a way to override the global "set
scheduler-locking on/step" or "set non-stop on" options. That is done
with the "-c" (from current set) option. Maybe that that instead be
"-u" for unlocked?
So this:
[all]> set scheduler-locking on
[all]> step
steps the currently selected thread, and doesn't let any other thread
run free.
And this:
[all]> set scheduler-locking on
[all]> step -c
steps the currently selected thread, and let's all threads in [all]
run free. That is, it's the equivalent of:
[all]> set scheduler-locking off
[all]> step
[all]> set scheduler-locking on
Lastly, all these switches are currently implemented in the following commands:
step, next, stepi, nexti, continue, until, jump, signal, finish and interrupt.
You'll note that several of the commands' implementations have split
in two passes - the first pass loops over all threads in the apply
set, validating if everything is okay to apply the command, thus
avoiding most cases of resuming thread 1..3, and then later throwing
an error resuming thread N.
A lot is missing:
- This whole series is not regression free yet.
- "step [set]" should wait for all thread in the set to finish the
step.
- make sure that we're not regressing against a target that doesn't
support non-stop/async. I haven't tried that yet.
- MI support.
- We're always resuming all threads individually. This is not really
a problem for native targets, but if you're debugging 1000 threads
against the remote target, and you do a "s", we'll send 1
vCont;sTID packet, plus 999 vCont;cTID packets, instead of a single
vCont;sTID;c". We could make remote_resume not really resume, but
collect resume requests in a vector, and add a new
target_apply_resume method that actually applies all pending resume
requests, so we could compress the requests. Native targets would
still resume from within target_resume, and would implement
target_apply_resume as doing nothing. The trouble is in knowing
whether the 999 continue requests do indeed map to "all threads",
and so can be compressed. vCont;c is not really the same as
resuming all threads individually. The former means that we'll get
the pending events for threads we hadn't heard yet too. This needs
a bit more thought and experimentation. I'm not sure I'll be able
to get this far in this first round though, so I may end up leaving
all this native only at first somehow.
- Probably many other things I'm not remembering right now.
---
gdb/breakpoint.c | 166 ++++++--
gdb/gdbthread.h | 5
gdb/infcall.c | 23 +
gdb/infcmd.c | 831 +++++++++++++++++++++++++++++++--------
gdb/inferior.h | 7
gdb/infrun.c | 100 +++--
gdb/itset.c | 8
gdb/itset.h | 2
gdb/testsuite/gdb.base/jump.exp | 2
gdb/thread.c | 2
10 files changed, 899 insertions(+), 247 deletions(-)
diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index 75badf9..d642651 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -9799,64 +9799,149 @@ until_break_command_continuation (void *arg, int err)
delete_longjmp_breakpoint (a->thread_num);
}
+struct until_break_cmd_data
+{
+ struct symtab_and_line sal;
+ struct frame_id breakpoint_frame_id;
+ struct frame_id selected_frame_id;
+};
+
+struct until_break_aec_callback_data
+{
+ int from_tty;
+};
+
+static void until_break_aec_callback (struct thread_info *thread, void *data);
+
+static void
+itset_free_p (void *arg)
+{
+ struct itset **itset_p = arg;
+
+ if (*itset_p)
+ itset_free (*itset_p);
+}
+
+char *parse_execution_args (char *args, int step,
+ struct itset **apply_itset,
+ struct itset **run_free_itset);
+
+typedef void (*aec_callback_func) (struct thread_info *thr, void *data);
+void apply_execution_command (struct itset *apply_itset,
+ struct itset *run_free_itset,
+ aec_callback_func callback, void *callback_data);
+
+void ensure_runnable (struct thread_info *thr);
+
void
until_break_command (char *arg, int from_tty, int anywhere)
{
- struct symtabs_and_lines sals;
- struct symtab_and_line sal;
- struct frame_info *frame = get_selected_frame (NULL);
- struct breakpoint *breakpoint;
- struct breakpoint *breakpoint2 = NULL;
+ struct itset *apply_itset = NULL;
+ struct itset *run_free_itset = NULL;
+ struct thread_info *thr;
struct cleanup *old_chain;
- int thread;
- struct thread_info *tp;
+ int thr_count = 0;
+ struct until_break_aec_callback_data cb_data;
- clear_proceed_status ();
+ old_chain = make_cleanup (itset_free_p, &apply_itset);
+ make_cleanup (itset_free_p, &run_free_itset);
- /* Set a breakpoint where the user wants it and at return from
- this function. */
+ arg = parse_execution_args (arg, 0, &apply_itset, &run_free_itset);
- if (last_displayed_sal_is_valid ())
- sals = decode_line_1 (&arg, 1,
- get_last_displayed_symtab (),
- get_last_displayed_line (),
- NULL);
- else
- sals = decode_line_1 (&arg, 1, (struct symtab *) NULL, 0, NULL);
+ ALL_THREADS (thr)
+ if (itset_contains_thread (apply_itset, thr))
+ {
+ struct symtabs_and_lines sals;
+ struct symtab_and_line sal;
+ struct frame_info *frame;
+ struct frame_id breakpoint_frame_id;
+ struct until_break_cmd_data *cmd_data;
- if (sals.nelts != 1)
- error (_("Couldn't get information on specified line."));
+ ++thr_count;
- sal = sals.sals[0];
- xfree (sals.sals); /* malloc'd, so freed. */
+ ensure_runnable (thr);
- if (*arg)
- error (_("Junk at end of arguments."));
+ if (!ptid_equal (inferior_ptid, thr->ptid))
+ switch_to_thread (thr->ptid);
- resolve_sal_pc (&sal);
+ frame = get_selected_frame (NULL);
- if (anywhere)
- /* If the user told us to continue until a specified location,
- we don't specify a frame at which we need to stop. */
- breakpoint = set_momentary_breakpoint (get_frame_arch (frame), sal,
- null_frame_id, bp_until);
- else
- /* Otherwise, specify the selected frame, because we want to stop
- only at the very same frame. */
- breakpoint = set_momentary_breakpoint (get_frame_arch (frame), sal,
- get_stack_frame_id (frame),
- bp_until);
+ /* Set a breakpoint where the user wants it and at return from
+ this function. */
+
+ if (last_displayed_sal_is_valid ())
+ sals = decode_line_1 (&arg, 1,
+ get_last_displayed_symtab (),
+ get_last_displayed_line (),
+ NULL);
+ else
+ sals = decode_line_1 (&arg, 1, (struct symtab *) NULL, 0, NULL);
+
+ if (sals.nelts != 1)
+ error (_("Couldn't get information on specified line."));
+
+ sal = sals.sals[0];
+ xfree (sals.sals); /* malloc'd, so freed. */
+
+ if (*arg)
+ error (_("Junk at end of arguments."));
+
+ resolve_sal_pc (&sal);
+
+ if (anywhere)
+ /* If the user told us to continue until a specified
+ location, we don't specify a frame at which we need to
+ stop. */
+ breakpoint_frame_id = null_frame_id;
+ else
+ /* Otherwise, specify the selected frame, because we want to
+ stop only at the very same frame. */
+ breakpoint_frame_id = get_stack_frame_id (frame);
+
+ cmd_data = XNEW (struct until_break_cmd_data);
+ cmd_data->sal = sal;
+ cmd_data->breakpoint_frame_id = breakpoint_frame_id;
+ cmd_data->selected_frame_id = get_frame_id (frame);
+ thr->cmd_data = cmd_data;
+ }
+
+ cb_data.from_tty = from_tty;
+ apply_execution_command (apply_itset, run_free_itset,
+ until_break_aec_callback, NULL);
+
+ do_cleanups (old_chain);
+}
+
+static void
+until_break_aec_callback (struct thread_info *thread, void *data)
+{
+ struct breakpoint *breakpoint;
+ struct breakpoint *breakpoint2 = NULL;
+ int thread_num;
+ struct until_break_aec_callback_data *d = data;
+ struct frame_info *frame;
+ struct until_break_cmd_data *cmd_data = thread->cmd_data;
+ int ix;
+ struct cleanup *old_chain;
+ frame = frame_find_by_id (cmd_data->selected_frame_id);
+ select_frame (frame);
+
+ breakpoint = set_momentary_breakpoint (get_frame_arch (frame),
+ cmd_data->sal,
+ cmd_data->breakpoint_frame_id,
+ bp_until);
old_chain = make_cleanup_delete_breakpoint (breakpoint);
- tp = inferior_thread ();
- thread = tp->num;
+ thread_num = thread->num;
/* Keep within the current frame, or in frames called by the current
one. */
if (frame_id_p (frame_unwind_caller_id (frame)))
{
+ struct symtab_and_line sal;
+
sal = find_pc_line (frame_unwind_caller_pc (frame), 0);
sal.pc = frame_unwind_caller_pc (frame);
breakpoint2 = set_momentary_breakpoint (frame_unwind_caller_arch (frame),
@@ -9865,10 +9950,11 @@ until_break_command (char *arg, int from_tty, int anywhere)
bp_until);
make_cleanup_delete_breakpoint (breakpoint2);
- set_longjmp_breakpoint (tp, frame_unwind_caller_id (frame));
- make_cleanup (delete_longjmp_breakpoint_cleanup, &thread);
+ set_longjmp_breakpoint (thread, frame_unwind_caller_id (frame));
+ make_cleanup (delete_longjmp_breakpoint_cleanup, &thread_num);
}
+ clear_proceed_status ();
proceed (-1, TARGET_SIGNAL_DEFAULT, 0);
/* If we are running asynchronously, and proceed call above has
@@ -9883,7 +9969,7 @@ until_break_command (char *arg, int from_tty, int anywhere)
args->breakpoint = breakpoint;
args->breakpoint2 = breakpoint2;
- args->thread_num = thread;
+ args->thread_num = thread_num;
discard_cleanups (old_chain);
add_continuation (inferior_thread (),
diff --git a/gdb/gdbthread.h b/gdb/gdbthread.h
index 0135219..7598f6a 100644
--- a/gdb/gdbthread.h
+++ b/gdb/gdbthread.h
@@ -188,6 +188,8 @@ struct thread_info
call. See `struct thread_suspend_state'. */
struct thread_suspend_state suspend;
+ int reported_event;
+
int current_line;
struct symtab *current_symtab;
@@ -241,6 +243,9 @@ struct thread_info
which exceptions to intercept. */
struct frame_id initiating_frame;
+ /* Data used by the execution command in effect. */
+ void *cmd_data;
+
/* Private data used by the target vector implementation. */
struct private_thread_info *private;
diff --git a/gdb/infcall.c b/gdb/infcall.c
index 50f1289..7149db4 100644
--- a/gdb/infcall.c
+++ b/gdb/infcall.c
@@ -38,6 +38,14 @@
#include "ada-lang.h"
#include "gdbthread.h"
#include "exceptions.h"
+#include "itset.h"
+
+typedef void (*aec_callback_func) (struct thread_info *thr, void *data);
+void apply_execution_command (struct itset *apply_itset,
+ struct itset *run_free_itset,
+ aec_callback_func callback, void *callback_data);
+
+struct itset *default_run_free_itset (struct itset *apply_itset, int step);
/* If we can't find a function's name from its address,
we print this instead. */
@@ -413,6 +421,21 @@ run_inferior_call (struct thread_info *call_thread, CORE_ADDR real_pc)
{
proceed (real_pc, TARGET_SIGNAL_0, 0);
+ if (target_is_non_stop_p ())
+ {
+ struct itset *apply_itset = itset_create_empty ();
+ struct itset *run_free_itset
+ = default_run_free_itset (apply_itset, 0);
+
+ apply_execution_command (apply_itset, current_itset,
+ NULL, NULL);
+
+ itset_free (apply_itset);
+ itset_free (run_free_itset);
+
+ switch_to_thread (call_thread->ptid);
+ }
+
/* Inferior function calls are always synchronous, even if the
target supports asynchronous execution. Do here what
`proceed' itself does in sync mode. */
diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index 7b935fe..e22c87f 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -57,6 +57,102 @@
#include "tracepoint.h"
#include "inf-loop.h"
#include "continuations.h"
+#include "itset.h"
+#include "cli/cli-utils.h"
+
+
+struct itset *
+current_thread_set (void)
+{
+ struct itset *set;
+ struct inferior *inf;
+ struct thread_info *tp;
+ char *b;
+
+ inf = current_inferior ();
+ tp = inferior_thread ();
+
+ b = alloca (256);
+ sprintf (b, "[%d.%d]", inf->num, tp->num);
+ return itset_create (&b);
+}
+
+void do_target_resume (ptid_t ptid, int step, enum target_signal signo);
+
+typedef void (*aec_callback_func) (struct thread_info *thr, void *data);
+
+int follow_fork (int should_resume);
+
+void
+apply_execution_command (struct itset *apply_itset,
+ struct itset *run_free_itset,
+ aec_callback_func callback, void *callback_data)
+{
+ if (target_is_non_stop_p ())
+ {
+ struct thread_info *t;
+ int followed_fork = 0;
+
+ /* See if there are threads we'd run free that are stopped at
+ forks. If so, follow the fork, and refuse to apply the
+ execution command further. */
+ ALL_THREADS (t)
+ {
+ if (t->state == THREAD_STOPPED
+ && itset_contains_thread (run_free_itset, t)
+ && !itset_contains_thread (apply_itset, t))
+ {
+ if (t->suspend.waitstatus.kind == TARGET_WAITKIND_FORKED
+ || t->suspend.waitstatus.kind == TARGET_WAITKIND_VFORKED)
+ {
+ switch_to_thread (t->ptid);
+ follow_fork (0);
+ followed_fork = 1;
+ }
+ }
+ }
+
+ if (followed_fork)
+ {
+ normal_stop ();
+ if (target_can_async_p ())
+ inferior_event_handler (INF_EXEC_COMPLETE, NULL);
+ return;
+ }
+
+ ALL_THREADS (t)
+ {
+ if (t->state == THREAD_STOPPED
+ && itset_contains_thread (apply_itset, t))
+ {
+ switch_to_thread (t->ptid);
+ (*callback) (t, callback_data);
+ }
+ else if (t->state == THREAD_STOPPED
+ && itset_contains_thread (run_free_itset, t))
+ {
+ /* If T has reported an event before (rather than having
+ been forced-suspended by GDB, then have it step over
+ any breakpoint its stopped at. Otherwise, resume it
+ as is, and let it hit any breakpoint it may be
+ stopped at (or report it's already pending event), so
+ the event is reported. */
+ if (t->reported_event)
+ {
+ switch_to_thread (t->ptid);
+ clear_proceed_status_thread (t);
+ proceed ((CORE_ADDR) -1, TARGET_SIGNAL_DEFAULT, 0);
+ }
+ else
+ do_target_resume (t->ptid, 0, TARGET_SIGNAL_0);
+ }
+ }
+ }
+ else
+ {
+ (*callback) (inferior_thread (), callback_data);
+ }
+}
/* Functions exported for general use, in inferior.h: */
@@ -79,7 +175,7 @@ static void nofp_registers_info (char *, int);
static void print_return_value (struct type *func_type,
struct type *value_type);
-static void until_next_command (int);
+static void until_next_command (char *, int);
static void until_command (char *, int);
@@ -596,7 +692,7 @@ run_command_1 (char *args, int from_tty, int tbreak_at_main)
events --- the frontend shouldn't see them as stopped. In
all-stop, always finish the state of all threads, as we may be
resuming more than just the new process. */
- if (non_stop)
+ if (target_is_non_stop_p ())
ptid = pid_to_ptid (ptid_get_pid (inferior_ptid));
else
ptid = minus_one_ptid;
@@ -656,11 +752,11 @@ proceed_thread_callback (struct thread_info *thread, void *arg)
much. If/when GDB gains a way to tell the target `hold this
thread stopped until I say otherwise', then we can optimize
this. */
- if (!is_stopped (thread->ptid))
+ if (thread->state != THREAD_STOPPED)
return 0;
switch_to_thread (thread->ptid);
- clear_proceed_status ();
+ clear_proceed_status_thread (thread);
proceed ((CORE_ADDR) -1, TARGET_SIGNAL_DEFAULT, 0);
return 0;
}
@@ -713,23 +809,182 @@ continue_1 (int all_threads)
}
}
+static void
+itset_free_p (void *arg)
+{
+ struct itset **itset_p = arg;
+
+ if (*itset_p)
+ itset_free (*itset_p);
+}
+
+struct itset *
+default_run_free_itset (struct itset *apply_itset, int step)
+{
+ if (non_stop)
+ {
+ /* In non-stop mode, threads are always handled
+ individually. */
+ return itset_create_empty ();
+ }
+ else if (scheduler_mode == schedlock_on
+ || (scheduler_mode == schedlock_step && step))
+ {
+ /* User-settable 'scheduler' mode requires solo thread
+ resume. */
+ return itset_create_empty ();
+ }
+ else if (!sched_multi)
+ {
+ struct inferior *inf;
+ char *set_spec;
+ char *p;
+ int first = 1;
+ struct itset *set;
+
+ /* Resume only threads of the current inferior process. */
+ set_spec = xstrdup ("[");
+ ALL_INFERIORS (inf)
+ if (itset_contains_inferior (apply_itset, inf))
+ {
+ char buf[128];
+
+ if (first)
+ {
+ first = 0;
+ sprintf (buf, "%d.*", inf->num);
+ }
+ else
+ sprintf (buf, ",%d.*", inf->num);
+
+ set_spec = reconcat (set_spec, set_spec, buf, (char *) NULL);
+ }
+ set_spec = reconcat (set_spec, set_spec, "]", (char *) NULL);
+
+ p = set_spec;
+ set = itset_create (&p);
+ xfree (set_spec);
+ return set;
+ }
+ else
+ {
+ /* By default, resume all threads in the current set. */
+ return itset_reference (current_itset);
+ }
+}
+
+char *
+parse_execution_args (char *args, int step,
+ struct itset **apply_itset,
+ struct itset **run_free_itset)
+{
+ if (args != NULL)
+ {
+ while (*args)
+ {
+ char *p;
+
+ args = skip_spaces (args);
+ p = skip_to_space (args);
+
+ if (strncmp (args, "-a", p - args) == 0)
+ {
+ if (*apply_itset)
+ itset_free (*apply_itset);
+ *apply_itset = itset_reference (current_itset);
+ args = p;
+ }
+ else if (strncmp (args, "-c", p - args) == 0)
+ {
+ if (*run_free_itset)
+ itset_free (*run_free_itset);
+ *run_free_itset = itset_reference (current_itset);
+ args = p;
+ }
+ else if (strncmp (args, "-l", p - args) == 0)
+ {
+ if (*run_free_itset)
+ itset_free (*run_free_itset);
+ *run_free_itset = itset_create_empty ();
+ args = p;
+ }
+ else if (strcmp (args, "--") == 0)
+ {
+ args += 2;
+ break;
+ }
+ else
+ break;
+ }
+
+ args = skip_spaces (args);
+
+ if (*args == '[')
+ {
+ if (*apply_itset)
+ itset_free (*apply_itset);
+ *apply_itset = itset_create (&args);
+ args = skip_spaces (args);
+ }
+ }
+
+ if (*apply_itset == NULL)
+ *apply_itset = current_thread_set ();
+
+ if (*run_free_itset == NULL)
+ *run_free_itset = default_run_free_itset (*apply_itset, step);
+
+ if (args && *args == '\0')
+ return NULL;
+ else
+ return args;
+}
+
+static void
+continue_aec_callback (struct thread_info *thread, void *data)
+{
+ proceed_thread_callback (thread, NULL);
+}
+
/* continue [-a] [proceed-count] [&] */
void
continue_command (char *args, int from_tty)
{
int async_exec = 0;
int all_threads = 0;
+ int ignore_count = 0;
+ int ignore_count_p = 0;
+ struct itset *apply_itset = NULL;
+ struct itset *run_free_itset = NULL;
+ struct cleanup *old_chain;
ERROR_NO_INFERIOR;
/* Find out whether we must run in the background. */
if (args != NULL)
async_exec = strip_bg_char (&args);
+ old_chain = make_cleanup (itset_free_p, &apply_itset);
+ make_cleanup (itset_free_p, &run_free_itset);
+
+ args = parse_execution_args (args, 0, &apply_itset, &run_free_itset);
+ if (args)
+ {
+ args = skip_spaces (args);
+ if (*args != '\0')
+ {
+ ignore_count = parse_and_eval_long (args);
+ ignore_count_p = 1;
+ }
+ }
+
/* If we must run in the background, but the target can't do it,
error out. */
if (async_exec && !target_can_async_p ())
error (_("Asynchronous execution not supported on this target."));
+ if (itset_is_empty (apply_itset))
+ error (_("Set of threads to continue is empty."));
+
/* If we are not asked to run in the bg, then prepare to run in the
foreground, synchronously. */
if (!async_exec && target_can_async_p ())
@@ -738,17 +993,6 @@ continue_command (char *args, int from_tty)
async_disable_stdin ();
}
- if (args != NULL)
- {
- if (strncmp (args, "-a", sizeof ("-a") - 1) == 0)
- {
- all_threads = 1;
- args += sizeof ("-a") - 1;
- if (*args == '\0')
- args = NULL;
- }
- }
-
if (!non_stop && all_threads)
error (_("`-a' is meaningless in all-stop mode."));
@@ -756,9 +1000,9 @@ continue_command (char *args, int from_tty)
error (_("Can't resume all threads and specify "
"proceed count simultaneously."));
- /* If we have an argument left, set proceed count of breakpoint we
- stopped at. */
- if (args != NULL)
+ /* Set proceed count of breakpoint we stopped at, if the user
+ requested it. */
+ if (ignore_count_p)
{
bpstat bs = NULL;
int num, stat;
@@ -781,9 +1025,7 @@ continue_command (char *args, int from_tty)
while ((stat = bpstat_num (&bs, &num)) != 0)
if (stat > 0)
{
- set_ignore_count (num,
- parse_and_eval_long (args) - 1,
- from_tty);
+ set_ignore_count (num, ignore_count - 1, from_tty);
/* set_ignore_count prints a message ending with a period.
So print two spaces before "Continuing.". */
if (from_tty)
@@ -801,7 +1043,10 @@ continue_command (char *args, int from_tty)
if (from_tty)
printf_filtered (_("Continuing.\n"));
- continue_1 (all_threads);
+ apply_execution_command (apply_itset, run_free_itset,
+ continue_aec_callback, NULL);
+
+ do_cleanups (old_chain);
}
\f
/* Record the starting point of a "step" or "next" command. */
@@ -852,36 +1097,106 @@ delete_longjmp_breakpoint_cleanup (void *arg)
delete_longjmp_breakpoint (thread);
}
+struct step_1_args
+{
+ int count;
+ int skip_subroutines;
+ int single_inst;
+ int thread;
+};
+
+static void step_1_1 (int skip_subroutines, int single_inst, int count);
+
static void
-step_1 (int skip_subroutines, int single_inst, char *count_string)
+step_1_aec_callback (struct thread_info *thread, void *data)
+{
+ struct step_1_args *args = data;
+
+ switch_to_thread (thread->ptid);
+ step_1_1 (args->skip_subroutines, args->single_inst, args->count);
+}
+
+void
+ensure_runnable (struct thread_info *thr)
+{
+ if (thr->state == THREAD_EXITED)
+ error (_("Thread %d (%s) has exited."),
+ thr->num, target_pid_to_str (thr->ptid));
+ else if (thr->state == THREAD_EXITED)
+ error (_("Thread %d (%s) is running."),
+ thr->num, target_pid_to_str (thr->ptid));
+}
+
+static void
+step_1 (int skip_subroutines, int single_inst, char *args)
{
int count = 1;
- struct cleanup *cleanups = make_cleanup (null_cleanup, NULL);
int async_exec = 0;
- int thread = -1;
+ struct cleanup *old_chain;
+ struct itset *apply_itset = NULL;
+ struct itset *run_free_itset = NULL;
+ struct step_1_args step_args;
+ struct thread_info *thr;
+ int thr_count = 0;
ERROR_NO_INFERIOR;
ensure_not_tfind_mode ();
- ensure_valid_thread ();
- ensure_not_running ();
- if (count_string)
- async_exec = strip_bg_char (&count_string);
+ if (args)
+ async_exec = strip_bg_char (&args);
+
+ old_chain = make_cleanup (itset_free_p, &apply_itset);
+ make_cleanup (itset_free_p, &run_free_itset);
+
+ args = parse_execution_args (args, 1, &apply_itset, &run_free_itset);
+ if (args)
+ {
+ args = skip_spaces (args);
+ if (*args != '\0')
+ count = parse_and_eval_long (args);
+ }
/* If we get a request for running in the bg but the target
doesn't support it, error out. */
if (async_exec && !target_can_async_p ())
error (_("Asynchronous execution not supported on this target."));
+ ALL_THREADS (thr)
+ if (itset_contains_thread (apply_itset, thr))
+ {
+ ++thr_count;
+
+ ensure_runnable (thr);
+ }
+
+ if (thr_count == 0)
+ error (_("Set of threads to step is empty."));
+
/* If we don't get a request of running in the bg, then we need
to simulate synchronous (fg) execution. */
+ /* FIXME: should only do this is actually about to resume. */
if (!async_exec && target_can_async_p ())
{
/* Simulate synchronous execution. */
async_disable_stdin ();
}
- count = count_string ? parse_and_eval_long (count_string) : 1;
+ step_args.skip_subroutines = skip_subroutines;
+ step_args.single_inst = single_inst;
+ step_args.count = count;
+ step_args.thread = -1;
+
+ apply_execution_command (apply_itset, run_free_itset,
+ step_1_aec_callback, &step_args);
+
+ do_cleanups (old_chain);
+}
+
+static void
+step_1_1 (int skip_subroutines, int single_inst, int count)
+{
+ int thread = -1;
+ struct cleanup *cleanups = make_cleanup (null_cleanup, NULL);
if (!single_inst || skip_subroutines) /* Leave si command alone. */
{
@@ -935,14 +1250,6 @@ step_1 (int skip_subroutines, int single_inst, char *count_string)
}
}
-struct step_1_continuation_args
-{
- int count;
- int skip_subroutines;
- int single_inst;
- int thread;
-};
-
/* Called after we are done with one step operation, to check whether
we need to step again, before we print the prompt and return control
to the user. If count is > 1, we will need to do one more call to
@@ -951,7 +1258,7 @@ struct step_1_continuation_args
static void
step_1_continuation (void *args, int err)
{
- struct step_1_continuation_args *a = args;
+ struct step_1_args *a = args;
if (target_has_execution)
{
@@ -1072,7 +1379,7 @@ step_once (int skip_subroutines, int single_inst, int count, int thread)
further stepping. */
if (target_can_async_p ())
{
- struct step_1_continuation_args *args;
+ struct step_1_args *args;
args = xmalloc (sizeof (*args));
args->skip_subroutines = skip_subroutines;
@@ -1086,23 +1393,53 @@ step_once (int skip_subroutines, int single_inst, int count, int thread)
}
\f
+
+struct jump_cmd_data
+{
+ CORE_ADDR addr;
+};
+
+struct jump_aec_callback_data
+{
+ int from_tty;
+};
+
+static void
+jump_aec_callback (struct thread_info *thread, void *data)
+{
+ struct jump_aec_callback_data *arg = data;
+ struct jump_map_entry *jme;
+ int ix;
+ struct gdbarch *gdbarch = get_current_arch ();
+ int from_tty = arg->from_tty;
+ struct jump_cmd_data *cmd_data = thread->cmd_data;
+ CORE_ADDR addr = cmd_data->addr;
+
+ if (from_tty)
+ printf_filtered (_("Continuing %d (%s) at %s.\n"),
+ thread->num, target_pid_to_str (thread->ptid),
+ paddress (gdbarch, addr));
+
+ clear_proceed_status ();
+ proceed (addr, TARGET_SIGNAL_0, 0);
+ return;
+}
+
/* Continue program at specified address. */
static void
jump_command (char *arg, int from_tty)
{
- struct gdbarch *gdbarch = get_current_arch ();
- CORE_ADDR addr;
- struct symtabs_and_lines sals;
- struct symtab_and_line sal;
- struct symbol *fn;
- struct symbol *sfn;
int async_exec = 0;
+ struct itset *apply_itset = NULL;
+ struct itset *run_free_itset = NULL;
+ struct cleanup *old_chain;
+ struct thread_info *thr;
+ int thr_count = 0;
+ struct jump_aec_callback_data cb_data;
ERROR_NO_INFERIOR;
ensure_not_tfind_mode ();
- ensure_valid_thread ();
- ensure_not_running ();
/* Find out whether we must run in the background. */
if (arg != NULL)
@@ -1113,59 +1450,71 @@ jump_command (char *arg, int from_tty)
if (async_exec && !target_can_async_p ())
error (_("Asynchronous execution not supported on this target."));
+ old_chain = make_cleanup (itset_free_p, &apply_itset);
+ make_cleanup (itset_free_p, &run_free_itset);
+
+ arg = parse_execution_args (arg, 0, &apply_itset, &run_free_itset);
if (!arg)
error_no_arg (_("starting address"));
- sals = decode_line_spec_1 (arg, 1);
- if (sals.nelts != 1)
- {
- error (_("Unreasonable jump request"));
- }
+ ALL_THREADS (thr)
+ if (itset_contains_thread (apply_itset, thr))
+ {
+ struct symtabs_and_lines sals;
+ struct symtab_and_line sal;
+ struct symbol *fn;
+ struct symbol *sfn;
+ struct jump_cmd_data *cmd_data;
- sal = sals.sals[0];
- xfree (sals.sals);
+ ++thr_count;
- if (sal.symtab == 0 && sal.pc == 0)
- error (_("No source file has been specified."));
+ ensure_runnable (thr);
- resolve_sal_pc (&sal); /* May error out. */
+ if (!ptid_equal (inferior_ptid, thr->ptid))
+ switch_to_thread (thr->ptid);
- /* See if we are trying to jump to another function. */
- fn = get_frame_function (get_current_frame ());
- sfn = find_pc_function (sal.pc);
- if (fn != NULL && sfn != fn)
- {
- if (!query (_("Line %d is not in `%s'. Jump anyway? "), sal.line,
- SYMBOL_PRINT_NAME (fn)))
- {
- error (_("Not confirmed."));
- /* NOTREACHED */
- }
- }
+ sals = decode_line_spec_1 (arg, 1);
+ if (sals.nelts != 1)
+ error (_("Unreasonable jump request for thread %s"),
+ target_pid_to_str (thr->ptid));
- if (sfn != NULL)
- {
- fixup_symbol_section (sfn, 0);
- if (section_is_overlay (SYMBOL_OBJ_SECTION (sfn)) &&
- !section_is_mapped (SYMBOL_OBJ_SECTION (sfn)))
- {
- if (!query (_("WARNING!!! Destination is in "
- "unmapped overlay! Jump anyway? ")))
- {
+ sal = sals.sals[0];
+ xfree (sals.sals);
+
+ if (sal.symtab == 0 && sal.pc == 0)
+ error (_("No source file has been specified."));
+
+ resolve_sal_pc (&sal); /* May error out. */
+
+ /* See if we are trying to jump to another function. */
+ fn = get_frame_function (get_current_frame ());
+ sfn = find_pc_function (sal.pc);
+ if (fn != NULL && sfn != fn)
+ {
+ if (!query (_("Line %d is not in `%s'. Jump anyway? "), sal.line,
+ SYMBOL_PRINT_NAME (fn)))
error (_("Not confirmed."));
- /* NOTREACHED */
- }
- }
- }
+ }
- addr = sal.pc;
+ if (sfn != NULL)
+ {
+ fixup_symbol_section (sfn, 0);
+ if (section_is_overlay (SYMBOL_OBJ_SECTION (sfn)) &&
+ !section_is_mapped (SYMBOL_OBJ_SECTION (sfn)))
+ {
+ if (!query (_("WARNING!!! Destination is in "
+ "unmapped overlay! Jump anyway? ")))
+ error (_("Not confirmed."));
+ }
+ }
- if (from_tty)
- {
- printf_filtered (_("Continuing at "));
- fputs_filtered (paddress (gdbarch, addr), gdb_stdout);
- printf_filtered (".\n");
- }
+ cmd_data = XNEW (struct jump_cmd_data);
+ cmd_data->addr = sal.pc;
+ thr->cmd_data = cmd_data;
+ }
+
+ if (thr_count == 0)
+ error (_("Set of threads to jump is empty."));
/* If we are not asked to run in the bg, then prepare to run in the
foreground, synchronously. */
@@ -1175,8 +1524,11 @@ jump_command (char *arg, int from_tty)
async_disable_stdin ();
}
- clear_proceed_status ();
- proceed (addr, TARGET_SIGNAL_0, 0);
+ cb_data.from_tty = from_tty;
+ apply_execution_command (apply_itset, run_free_itset,
+ jump_aec_callback, &cb_data);
+
+ do_cleanups (old_chain);
}
\f
@@ -1196,27 +1548,43 @@ go_command (char *line_no, int from_tty)
/* Continue program giving it specified signal. */
+struct signal_aec_callback_data
+{
+ enum target_signal oursig;
+};
+
+static void signal_aec_callback (struct thread_info *thread, void *data);
+
static void
-signal_command (char *signum_exp, int from_tty)
+signal_command (char *arg, int from_tty)
{
enum target_signal oursig;
int async_exec = 0;
+ struct itset *apply_itset = NULL;
+ struct itset *run_free_itset = NULL;
+ struct cleanup *old_chain;
+ struct signal_aec_callback_data cb_data;
+ struct thread_info *thr;
+ int thr_count = 0;
dont_repeat (); /* Too dangerous. */
ERROR_NO_INFERIOR;
ensure_not_tfind_mode ();
- ensure_valid_thread ();
- ensure_not_running ();
/* Find out whether we must run in the background. */
- if (signum_exp != NULL)
- async_exec = strip_bg_char (&signum_exp);
+ if (arg != NULL)
+ async_exec = strip_bg_char (&arg);
/* If we must run in the background, but the target can't do it,
error out. */
if (async_exec && !target_can_async_p ())
error (_("Asynchronous execution not supported on this target."));
+ old_chain = make_cleanup (itset_free_p, &apply_itset);
+ make_cleanup (itset_free_p, &run_free_itset);
+
+ arg = parse_execution_args (arg, 0, &apply_itset, &run_free_itset);
+
/* If we are not asked to run in the bg, then prepare to run in the
foreground, synchronously. */
if (!async_exec && target_can_async_p ())
@@ -1225,18 +1593,29 @@ signal_command (char *signum_exp, int from_tty)
async_disable_stdin ();
}
- if (!signum_exp)
+ if (!arg)
error_no_arg (_("signal number"));
+ ALL_THREADS (thr)
+ if (itset_contains_thread (apply_itset, thr))
+ {
+ ++thr_count;
+
+ ensure_runnable (thr);
+ }
+
+ if (thr_count == 0)
+ error (_("Set of threads to signal is empty."));
+
/* It would be even slicker to make signal names be valid expressions,
(the type could be "enum $signal" or some such), then the user could
assign them to convenience variables. */
- oursig = target_signal_from_name (signum_exp);
+ oursig = target_signal_from_name (arg);
if (oursig == TARGET_SIGNAL_UNKNOWN)
{
/* No, try numeric. */
- int num = parse_and_eval_long (signum_exp);
+ int num = parse_and_eval_long (arg);
if (num == 0)
oursig = TARGET_SIGNAL_0;
@@ -1253,8 +1632,20 @@ signal_command (char *signum_exp, int from_tty)
target_signal_to_name (oursig));
}
- clear_proceed_status ();
- proceed ((CORE_ADDR) -1, oursig, 0);
+ cb_data.oursig = oursig;
+ apply_execution_command (apply_itset, run_free_itset,
+ signal_aec_callback, &cb_data);
+
+ do_cleanups (old_chain);
+}
+
+static void
+signal_aec_callback (struct thread_info *thread, void *data)
+{
+ struct signal_aec_callback_data *d = data;
+
+ clear_proceed_status_thread (thread);
+ proceed ((CORE_ADDR) -1, d->oursig, 0);
}
/* Continuation args to be passed to the "until" command
@@ -1283,51 +1674,91 @@ until_next_continuation (void *arg, int err)
we set. This may involve changes to wait_for_inferior and the
proceed status code. */
+static void until_next_aec_callback (struct thread_info *thread, void *data);
+
static void
-until_next_command (int from_tty)
+until_next_command (char *arg, int from_tty)
{
- struct frame_info *frame;
- CORE_ADDR pc;
- struct symbol *func;
- struct symtab_and_line sal;
- struct thread_info *tp = inferior_thread ();
- int thread = tp->num;
+ struct itset *apply_itset = NULL;
+ struct itset *run_free_itset = NULL;
struct cleanup *old_chain;
+ struct thread_info *thr;
+ int thr_count = 0;
- clear_proceed_status ();
- set_step_frame ();
+ old_chain = make_cleanup (itset_free_p, &apply_itset);
+ make_cleanup (itset_free_p, &run_free_itset);
- frame = get_current_frame ();
+ arg = parse_execution_args (arg, 0, &apply_itset, &run_free_itset);
- /* Step until either exited from this function or greater
- than the current line (if in symbolic section) or pc (if
- not). */
+ ALL_THREADS (thr)
+ if (itset_contains_thread (apply_itset, thr))
+ {
+ struct frame_info *frame;
+ CORE_ADDR pc;
+ struct symbol *func;
+ struct symtab_and_line sal;
- pc = get_frame_pc (frame);
- func = find_pc_function (pc);
+ ++thr_count;
- if (!func)
- {
- struct minimal_symbol *msymbol = lookup_minimal_symbol_by_pc (pc);
+ ensure_runnable (thr);
- if (msymbol == NULL)
- error (_("Execution is not within a known function."));
+ if (!ptid_equal (inferior_ptid, thr->ptid))
+ switch_to_thread (thr->ptid);
- tp->control.step_range_start = SYMBOL_VALUE_ADDRESS (msymbol);
- tp->control.step_range_end = pc;
- }
- else
- {
- sal = find_pc_line (pc, 0);
+ clear_proceed_status_thread (thr);
+ set_step_frame ();
- tp->control.step_range_start = BLOCK_START (SYMBOL_BLOCK_VALUE (func));
- tp->control.step_range_end = sal.end;
- }
+ frame = get_current_frame ();
+
+ /* Step until either exited from this function or greater than
+ the current line (if in symbolic section) or pc (if
+ not). */
+
+ pc = get_frame_pc (frame);
+ func = find_pc_function (pc);
+
+ if (!func)
+ {
+ struct minimal_symbol *msymbol = lookup_minimal_symbol_by_pc (pc);
+
+ if (msymbol == NULL)
+ error (_("Execution is not within a known function."));
+
+ thr->control.step_range_start = SYMBOL_VALUE_ADDRESS (msymbol);
+ thr->control.step_range_end = pc;
+ }
+ else
+ {
+ sal = find_pc_line (pc, 0);
+
+ thr->control.step_range_start = BLOCK_START (SYMBOL_BLOCK_VALUE (func));
+ thr->control.step_range_end = sal.end;
+ }
+
+ thr->control.step_over_calls = STEP_OVER_ALL;
+
+ thr->step_multi = 0; /* Only one call to proceed */
+ }
- tp->control.step_over_calls = STEP_OVER_ALL;
+ if (thr_count == 0)
+ error (_("Set of threads to until is empty."));
- tp->step_multi = 0; /* Only one call to proceed */
+ apply_execution_command (apply_itset, run_free_itset,
+ until_next_aec_callback, NULL);
+ do_cleanups (old_chain);
+}
+
+\f
+
+static void
+until_next_aec_callback (struct thread_info *tp, void *data)
+{
+ int thread = tp->num;
+ struct cleanup *old_chain;
+ struct frame_info *frame;
+
+ frame = get_current_frame ();
set_longjmp_breakpoint (tp, get_frame_id (frame));
old_chain = make_cleanup (delete_longjmp_breakpoint_cleanup, &thread);
@@ -1354,8 +1785,6 @@ until_command (char *arg, int from_tty)
ERROR_NO_INFERIOR;
ensure_not_tfind_mode ();
- ensure_valid_thread ();
- ensure_not_running ();
/* Find out whether we must run in the background. */
if (arg != NULL)
@@ -1377,7 +1806,7 @@ until_command (char *arg, int from_tty)
if (arg)
until_break_command (arg, from_tty, 0);
else
- until_next_command (from_tty);
+ until_next_command (arg, from_tty);
}
static void
@@ -1387,8 +1816,6 @@ advance_command (char *arg, int from_tty)
ERROR_NO_INFERIOR;
ensure_not_tfind_mode ();
- ensure_valid_thread ();
- ensure_not_running ();
if (arg == NULL)
error_no_arg (_("a location"));
@@ -1648,21 +2075,34 @@ finish_forward (struct symbol *function, struct frame_info *frame)
do_all_continuations (0);
}
+struct finish_cmd_data
+{
+ struct frame_id selected_frame_id;
+};
+
+struct finish_aec_callback_data
+{
+ int from_tty;
+};
+
+static void finish_aec_callback (struct thread_info *thread, void *data);
+
/* "finish": Set a temporary breakpoint at the place the selected
frame will return to, then continue. */
static void
finish_command (char *arg, int from_tty)
{
- struct frame_info *frame;
- struct symbol *function;
-
+ struct itset *apply_itset = NULL;
+ struct itset *run_free_itset = NULL;
+ struct cleanup *old_chain;
+ struct thread_info *thr;
+ int thr_count = 0;
+ struct finish_aec_callback_data cb_data;
int async_exec = 0;
ERROR_NO_INFERIOR;
ensure_not_tfind_mode ();
- ensure_valid_thread ();
- ensure_not_running ();
/* Find out whether we must run in the background. */
if (arg != NULL)
@@ -1673,6 +2113,11 @@ finish_command (char *arg, int from_tty)
if (async_exec && !target_can_async_p ())
error (_("Asynchronous execution not supported on this target."));
+ old_chain = make_cleanup (itset_free_p, &apply_itset);
+ make_cleanup (itset_free_p, &run_free_itset);
+
+ arg = parse_execution_args (arg, 0, &apply_itset, &run_free_itset);
+
/* If we are not asked to run in the bg, then prepare to run in the
foreground, synchronously. */
if (!async_exec && target_can_async_p ())
@@ -1684,29 +2129,73 @@ finish_command (char *arg, int from_tty)
if (arg)
error (_("The \"finish\" command does not take any arguments."));
- frame = get_prev_frame (get_selected_frame (_("No selected frame.")));
- if (frame == 0)
- error (_("\"finish\" not meaningful in the outermost frame."));
+ ALL_THREADS (thr)
+ if (itset_contains_thread (apply_itset, thr))
+ {
+ struct frame_info *frame;
+ struct frame_info *prev;
+ struct finish_cmd_data *cmd_data;
+
+ ++thr_count;
+
+ ensure_runnable (thr);
+
+ if (!ptid_equal (inferior_ptid, thr->ptid))
+ switch_to_thread (thr->ptid);
+
+ frame = get_selected_frame (_("No selected frame."));
+ prev = get_prev_frame (frame);
+ if (prev == NULL)
+ error (_("\"finish\" not meaningful in the outermost frame."));
+
+ cmd_data = XNEW (struct finish_cmd_data);
+ cmd_data->selected_frame_id = get_frame_id (frame);
+ thr->cmd_data = cmd_data;
+ }
+
+ if (thr_count == 0)
+ error (_("Set of threads to finish is empty."));
+
+ cb_data.from_tty = from_tty;
+ apply_execution_command (apply_itset, run_free_itset,
+ finish_aec_callback, &cb_data);
+
+ do_cleanups (old_chain);
+}
+
+static void
+finish_aec_callback (struct thread_info *tp, void *data)
+{
+ struct finish_aec_callback_data *d = data;
+ int from_tty = d->from_tty;
+ struct finish_cmd_data *cmd_data = tp->cmd_data;
+ int ix;
+ struct frame_info *frame, *prev;
+ struct symbol *function;
clear_proceed_status ();
+ frame = frame_find_by_id (cmd_data->selected_frame_id);
+ gdb_assert (frame != NULL);
+ select_frame (frame);
+ prev = get_prev_frame (frame);
+ gdb_assert (prev != NULL);
+
/* Finishing from an inline frame is completely different. We don't
try to show the "return value" - no way to locate it. So we do
not need a completion. */
- if (get_frame_type (get_selected_frame (_("No selected frame.")))
- == INLINE_FRAME)
+ if (get_frame_type (frame) == INLINE_FRAME)
{
/* Claim we are stepping in the calling frame. An empty step
range means that we will stop once we aren't in a function
called by that frame. We don't use the magic "1" value for
step_range_end, because then infrun will think this is nexti,
and not step over the rest of this inlined function call. */
- struct thread_info *tp = inferior_thread ();
struct symtab_and_line empty_sal;
init_sal (&empty_sal);
- set_step_info (frame, empty_sal);
- tp->control.step_range_start = get_frame_pc (frame);
+ set_step_info (prev, empty_sal);
+ tp->control.step_range_start = get_frame_pc (prev);
tp->control.step_range_end = tp->control.step_range_start;
tp->control.step_over_calls = STEP_OVER_ALL;
@@ -1715,7 +2204,7 @@ finish_command (char *arg, int from_tty)
if (from_tty)
{
printf_filtered (_("Run till exit from "));
- print_stack_frame (get_selected_frame (NULL), 1, LOCATION);
+ print_stack_frame (frame, 1, LOCATION);
}
proceed ((CORE_ADDR) -1, TARGET_SIGNAL_DEFAULT, 1);
@@ -1724,7 +2213,7 @@ finish_command (char *arg, int from_tty)
/* Find the function we will return from. */
- function = find_pc_function (get_frame_pc (get_selected_frame (NULL)));
+ function = find_pc_function (get_frame_pc (frame));
/* Print info on the selected frame, including level number but not
source. */
@@ -1735,13 +2224,13 @@ finish_command (char *arg, int from_tty)
else
printf_filtered (_("Run till exit from "));
- print_stack_frame (get_selected_frame (NULL), 1, LOCATION);
+ print_stack_frame (frame, 1, LOCATION);
}
if (execution_direction == EXEC_REVERSE)
finish_backward (function);
else
- finish_forward (function, frame);
+ finish_forward (function, prev);
}
\f
@@ -2693,10 +3182,31 @@ interrupt_target_1 (int all_threads)
ptid_t ptid;
if (all_threads)
- ptid = minus_one_ptid;
+ {
+ if (target_is_non_stop_p ())
+ {
+ struct thread_info *t;
+
+ ALL_LIVE_THREADS (t)
+ if (itset_contains_thread (current_itset, t))
+ {
+ target_stop (t->ptid);
+ set_stop_requested (t->ptid, 1);
+ }
+
+ return;
+ }
+ else
+ {
+ ptid = minus_one_ptid;
+ target_stop (ptid);
+ }
+ }
else
- ptid = inferior_ptid;
- target_stop (ptid);
+ {
+ ptid = inferior_ptid;
+ target_stop (ptid);
+ }
/* Tag the thread as having been explicitly requested to stop, so
other parts of gdb know not to resume this thread automatically,
@@ -2704,7 +3214,7 @@ interrupt_target_1 (int all_threads)
non-stop mode, as when debugging a multi-threaded application in
all-stop mode, we will only get one stop event --- it's undefined
which thread will report the event. */
- if (non_stop)
+ if (target_is_non_stop_p ())
set_stop_requested (ptid, 1);
}
@@ -2727,9 +3237,6 @@ interrupt_target_command (char *args, int from_tty)
&& strncmp (args, "-a", sizeof ("-a") - 1) == 0)
all_threads = 1;
- if (!non_stop && all_threads)
- error (_("-a is meaningless in all-stop mode."));
-
interrupt_target_1 (all_threads);
}
}
diff --git a/gdb/inferior.h b/gdb/inferior.h
index 559b4f1..add97e7 100644
--- a/gdb/inferior.h
+++ b/gdb/inferior.h
@@ -105,6 +105,8 @@ extern int sync_execution;
extern void clear_proceed_status (void);
+extern void clear_proceed_status_thread (struct thread_info *tp);
+
extern void proceed (CORE_ADDR, enum target_signal, int);
extern int sched_multi;
@@ -168,6 +170,11 @@ extern void resume (int, enum target_signal);
extern ptid_t user_visible_resume_ptid (int step);
+extern const char schedlock_off[];
+extern const char schedlock_on[];
+extern const char schedlock_step[];
+extern const char *scheduler_mode;
+
extern void insert_step_resume_breakpoint_at_sal (struct gdbarch *,
struct symtab_and_line ,
struct frame_id);
diff --git a/gdb/infrun.c b/gdb/infrun.c
index c279508..6b16184 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -81,7 +81,7 @@ static int hook_stop_stub (void *);
static int restore_selected_frame (void *);
-static int follow_fork (void);
+int follow_fork (int);
static void set_schedlock_func (char *args, int from_tty,
struct cmd_list_element *c);
@@ -416,28 +416,14 @@ show_follow_fork_mode_string (struct ui_file *file, int from_tty,
}
\f
-/* Tell the target to follow the fork we're stopped at. Returns true
- if the inferior should be resumed; false, if the target for some
- reason decided it's best not to resume. */
+/* Check if we lost stopped at a fork event, but switched over to
+ another thread since. If so, switch back to the event thread, and
+ return false. Otherwise, return true. */
static int
-follow_fork (void)
+prepare_to_follow_fork (void)
{
- int follow_child = (follow_fork_mode_string == follow_fork_mode_child);
- int should_resume = 1;
- struct thread_info *tp;
-
- /* Copy user stepping state to the new inferior thread. FIXME: the
- followed fork child thread should have a copy of most of the
- parent thread structure's run control related fields, not just these.
- Initialized to avoid "may be used uninitialized" warnings from gcc. */
- struct breakpoint *step_resume_breakpoint = NULL;
- struct breakpoint *exception_resume_breakpoint = NULL;
- CORE_ADDR step_range_start = 0;
- CORE_ADDR step_range_end = 0;
- struct frame_id step_frame_id = { 0 };
-
- if (!non_stop)
+ if (!target_is_non_stop_p ())
{
ptid_t wait_ptid;
struct target_waitstatus wait_status;
@@ -461,10 +447,32 @@ follow_fork (void)
afterwards refuse to resume, and inform the user what
happened. */
switch_to_thread (wait_ptid);
- should_resume = 0;
+ return 0;
}
}
+ return 1;
+}
+
+/* Tell the target to follow the fork we're stopped at. Returns true
+ if the inferior should be resumed; false, if the target for some
+ reason decided it's best not to resume. */
+
+int
+follow_fork (int should_resume)
+{
+ int follow_child = (follow_fork_mode_string == follow_fork_mode_child);
+ struct thread_info *tp;
+ /* Copy user stepping state to the new inferior thread. FIXME: the
+ followed fork child thread should have a copy of most of the
+ parent thread structure's run control related fields, not just these.
+ Initialized to avoid "may be used uninitialized" warnings from gcc. */
+ struct breakpoint *step_resume_breakpoint = NULL;
+ struct breakpoint *exception_resume_breakpoint = NULL;
+ CORE_ADDR step_range_start = 0;
+ CORE_ADDR step_range_end = 0;
+ struct frame_id step_frame_id = { 0 };
+
tp = inferior_thread ();
/* If there were any forks/vforks that were caught and are now to be
@@ -1420,7 +1428,7 @@ displaced_step_restore (struct displaced_step_inferior_state *displaced,
displaced->step_copy));
}
-static void do_target_resume (ptid_t ptid, int step, enum target_signal signo);
+void do_target_resume (ptid_t ptid, int step, enum target_signal signo);
static void
displaced_step_fixup (ptid_t event_ptid, enum target_signal signal)
@@ -1587,16 +1595,16 @@ resume_cleanups (void *ignore)
normal_stop ();
}
-static const char schedlock_off[] = "off";
-static const char schedlock_on[] = "on";
-static const char schedlock_step[] = "step";
+const char schedlock_off[] = "off";
+const char schedlock_on[] = "on";
+const char schedlock_step[] = "step";
static const char *scheduler_enums[] = {
schedlock_off,
schedlock_on,
schedlock_step,
NULL
};
-static const char *scheduler_mode = schedlock_off;
+const char *scheduler_mode = schedlock_off;
static void
show_scheduler_mode (struct ui_file *file, int from_tty,
struct cmd_list_element *c, const char *value)
@@ -1669,9 +1677,9 @@ user_visible_resume_ptid (int step)
}
/* Maybe resume a single thread after all. */
- if (non_stop)
+ if (target_is_non_stop_p ())
{
- /* With non-stop mode on, threads are always handled
+ /* In non-stop mode, threads are always handled
individually. */
resume_ptid = inferior_ptid;
}
@@ -1686,7 +1694,7 @@ user_visible_resume_ptid (int step)
return resume_ptid;
}
-static void
+void
do_target_resume (ptid_t ptid, int step, enum target_signal signo)
{
int resume_many;
@@ -1734,6 +1742,7 @@ do_target_resume (ptid_t ptid, int step, enum target_signal signo)
happens to apply to another thread. */
tp->suspend.stop_signal = TARGET_SIGNAL_0;
tp->control.resumed = 1;
+ tp->reported_event = 0;
if (tp->suspend.waitstatus_pending_p)
{
@@ -2074,7 +2083,7 @@ a command like `return' or `jump' to continue execution."));
/* Clear out all variables saying what to do when inferior is continued.
First do this, then set the ones you want, then call `proceed'. */
-static void
+void
clear_proceed_status_thread (struct thread_info *tp)
{
if (tp->state == THREAD_RUNNING)
@@ -2120,7 +2129,7 @@ clear_proceed_status_callback (struct thread_info *tp, void *data)
void
clear_proceed_status (void)
{
- if (!non_stop)
+ if (!target_is_non_stop_p ())
{
/* In all-stop mode, delete the per-thread status of all
threads, even if inferior_ptid is null_ptid, there may be
@@ -2133,7 +2142,7 @@ clear_proceed_status (void)
{
struct inferior *inferior;
- if (non_stop)
+ if (target_is_non_stop_p ())
{
/* If in non-stop mode, only delete the per-thread status of
the current thread. */
@@ -2255,7 +2264,7 @@ proceed (CORE_ADDR addr, enum target_signal siggnal, int step)
/* If we're stopped at a fork/vfork, follow the branch set by the
"set follow-fork-mode" command; otherwise, we'll just proceed
resuming the current thread. */
- if (!follow_fork ())
+ if (prepare_to_follow_fork () && !follow_fork (1))
{
/* The target for some reason decided not to resume. */
normal_stop ();
@@ -2308,7 +2317,7 @@ proceed (CORE_ADDR addr, enum target_signal siggnal, int step)
"infrun: proceed (addr=%s, signal=%d, step=%d)\n",
paddress (gdbarch, addr), siggnal, step);
- if (non_stop)
+ if (target_is_non_stop_p ())
/* In non-stop, each thread is handled individually. The context
must already be set to the right thread here. */
;
@@ -2676,7 +2685,7 @@ delete_step_thread_step_resume_breakpoint (void)
resume breakpoints out of GDB's lists. */
return;
- if (non_stop)
+ if (target_is_non_stop_p ())
{
/* If in non-stop mode, only delete the step-resume or
longjmp-resume breakpoint of the thread that just stopped
@@ -2879,7 +2888,7 @@ prepare_for_detach (void)
/* In non-stop mode, each thread is handled individually.
Switch early, so the global state is set correctly for this
thread. */
- if (non_stop
+ if (target_is_non_stop_p ()
&& ecs->ws.kind != TARGET_WAITKIND_EXITED
&& ecs->ws.kind != TARGET_WAITKIND_SIGNALLED)
context_switch (ecs->ptid);
@@ -3181,20 +3190,18 @@ fetch_inferior_event (void *client_data)
/* cancel breakpoints */
}
- if (non_stop
- && ecs->ws.kind != TARGET_WAITKIND_IGNORE
+ if (ecs->ws.kind != TARGET_WAITKIND_IGNORE
&& ecs->ws.kind != TARGET_WAITKIND_NO_RESUMED
&& ecs->ws.kind != TARGET_WAITKIND_EXITED
&& ecs->ws.kind != TARGET_WAITKIND_SIGNALLED)
- /* In non-stop mode, each thread is handled individually. Switch
- early, so the global state is set correctly for this
+ /* Switch early, so the global state is set correctly for this
thread. */
context_switch (ecs->ptid);
/* If an error happens while handling the event, propagate GDB's
knowledge of the executing state to the frontend/user running
state. */
- if (!non_stop)
+ if (!target_is_non_stop_p ())
ts_old_chain = make_cleanup (finish_thread_state_cleanup, &minus_one_ptid);
else
ts_old_chain = make_cleanup (finish_thread_state_cleanup, &ecs->ptid);
@@ -4158,7 +4165,7 @@ handle_inferior_event (struct execution_control_state *ecs)
ecs->event_thread->suspend.stop_signal = TARGET_SIGNAL_0;
- should_resume = follow_fork ();
+ should_resume = prepare_to_follow_fork() && follow_fork (1);
parent = ecs->ptid;
child = ecs->ws.value.related_pid;
@@ -4567,7 +4574,7 @@ handle_inferior_event (struct execution_control_state *ecs)
error (_("Cannot step over breakpoint hit in wrong thread"));
else
{ /* Single step */
- if (!non_stop)
+ if (!target_is_non_stop_p ())
{
/* Only need to require the next event from this
thread in all-stop mode. */
@@ -5207,7 +5214,7 @@ process_event_stop_test:
/* In all-stop mode, if we're currently stepping but have stopped in
some other thread, we need to switch back to the stepped thread. */
- if (!non_stop)
+ if (!target_is_non_stop_p ())
{
struct thread_info *tp;
@@ -6191,6 +6198,11 @@ stop_stepping (struct execution_control_state *ecs)
/* Let callers know we don't want to wait for the inferior anymore. */
ecs->wait_some_more = 0;
+ if (ecs->event_thread)
+ {
+ ecs->event_thread->reported_event = 1;
+ }
+
if (target_is_non_stop_p ()
&& stop_only_if_needed)
{
diff --git a/gdb/itset.c b/gdb/itset.c
index bd2c74a..717d261 100644
--- a/gdb/itset.c
+++ b/gdb/itset.c
@@ -1492,6 +1492,14 @@ itset_create_stopped (void)
return itset_create (&spec);
}
+struct itset *
+itset_create_empty (void)
+{
+ char *spec = "[]";
+
+ return itset_create (&spec);
+}
+
/* Return 1 if SET contains INF, 0 otherwise. */
int
diff --git a/gdb/itset.h b/gdb/itset.h
index 9574275..da08477 100644
--- a/gdb/itset.h
+++ b/gdb/itset.h
@@ -67,6 +67,8 @@ const char *itset_spec (const struct itset *itset);
int itset_is_empty (const struct itset *itset);
+struct itset *itset_create_empty (void);
+
/* Acquire a new reference to an I/T set. Returns the I/T set, for
convenience. */
diff --git a/gdb/testsuite/gdb.base/jump.exp b/gdb/testsuite/gdb.base/jump.exp
index adf50c6..a9b8711 100644
--- a/gdb/testsuite/gdb.base/jump.exp
+++ b/gdb/testsuite/gdb.base/jump.exp
@@ -116,7 +116,7 @@ gdb_test "jump 12" \
"n"
gdb_test "jump 12" \
- "Continuing at.*" \
+ "Continuing .* \(.*\) at.*" \
"jump out of current function" \
"Line 12 is not in `main'. Jump anyway.*y or n. $" \
"y"
diff --git a/gdb/thread.c b/gdb/thread.c
index b42c8c3..846d2d3 100644
--- a/gdb/thread.c
+++ b/gdb/thread.c
@@ -134,6 +134,8 @@ clear_thread_inferior_resources (struct thread_info *tp)
static void
free_thread (struct thread_info *tp)
{
+ xfree (tp->cmd_data);
+
if (tp->private)
{
if (tp->private_dtor)
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [RFC/WIP PATCH 11/14] Add I/T set support to most execution commands
2011-11-28 15:46 ` [RFC/WIP PATCH 11/14] Add I/T set support to most execution commands Pedro Alves
@ 2011-11-30 19:27 ` Tom Tromey
0 siblings, 0 replies; 53+ messages in thread
From: Tom Tromey @ 2011-11-30 19:27 UTC (permalink / raw)
To: Pedro Alves; +Cc: gdb-patches
>>>>> "Pedro" == Pedro Alves <pedro@codesourcery.com> writes:
Pedro> This adds I/T set support to most execution commands.
Very nice.
Pedro> Lastly, all these switches are currently implemented in the
Pedro> following commands:
Pedro> step, next, stepi, nexti, continue, until, jump, signal, finish
Pedro> and interrupt.
I thought there was a PR open about "continue -a" not working when there
are multiple inferiors, but I couldn't find it just now. Anyway I guess
this series would fix it.
Pedro> - make sure that we're not regressing against a target that doesn't
Pedro> support non-stop/async. I haven't tried that yet.
A user on irc specifically asked about making sure that this doesn't
regress Windows.
Tom
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [RFC/WIP PATCH 00/14] I/T sets
2011-11-28 15:39 [RFC/WIP PATCH 00/14] I/T sets Pedro Alves
` (13 preceding siblings ...)
2011-11-28 15:46 ` [RFC/WIP PATCH 11/14] Add I/T set support to most execution commands Pedro Alves
@ 2011-11-28 18:10 ` Pedro Alves
2011-11-30 19:35 ` Tom Tromey
` (2 subsequent siblings)
17 siblings, 0 replies; 53+ messages in thread
From: Pedro Alves @ 2011-11-28 18:10 UTC (permalink / raw)
To: gdb-patches
On Monday 28 November 2011 15:38:53, Pedro Alves wrote:
> This series is also available at http://github.com/palves/gdb/tree/itsets-v1
> for convenience.
Whoops, I've just noticed that the series I posted is badly
broken due to a last minute change. I've just pushed the commit
below there to fix it, and will fold it into the proper
place in for v2.
Sorry about that.
--
Pedro Alves
commit 0103de5e1e88bab2050a19abc51c92fd3d3898d0
Author: Pedro Alves <pedro@codesourcery.com>
Date: Mon Nov 28 18:02:14 2011 +0000
Allow calling itset_reference on a NULL itset. We'll just return a
NULL itset then.
(top-gdb) bt
#0 0x00000000006a4811 in itset_reference (itset=0x0) at ../../gdb/gdb/itset.c:1532
#1 0x000000000052f87d in bpstat_stop_set (bs_head=0x1461ad0) at ../../gdb/gdb/breakpoint.c:4559
#2 0x00000000005a09eb in handle_inferior_event (ecs=0x7fff568abc00) at ../../gdb/gdb/infrun.c:5183
diff --git a/gdb/itset.c b/gdb/itset.c
index 717d261..25e40b6 100644
--- a/gdb/itset.c
+++ b/gdb/itset.c
@@ -1529,7 +1529,8 @@ itset_contains_thread (struct itset *set, struct thread_info *thr)
struct itset *
itset_reference (struct itset *itset)
{
- ++itset->refc;
+ if (itset)
+ ++itset->refc;
return itset;
}
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [RFC/WIP PATCH 00/14] I/T sets
2011-11-28 15:39 [RFC/WIP PATCH 00/14] I/T sets Pedro Alves
` (14 preceding siblings ...)
2011-11-28 18:10 ` [RFC/WIP PATCH 00/14] I/T sets Pedro Alves
@ 2011-11-30 19:35 ` Tom Tromey
2011-12-16 19:40 ` Pedro Alves
2012-02-09 7:51 ` Tomas Östlund
2012-02-09 8:19 ` [RFC/WIP PATCH 00/14] I/T sets (resend) Tomas Östlund
17 siblings, 1 reply; 53+ messages in thread
From: Tom Tromey @ 2011-11-30 19:35 UTC (permalink / raw)
To: Pedro Alves; +Cc: gdb-patches
>>>>> "Pedro" == Pedro Alves <pedro@codesourcery.com> writes:
Pedro> Following up on <http://sourceware.org/ml/gdb/2011-11/msg00013.html>.
Pedro> This patch set seeds an implementation of a general
Pedro> inferior/process/thread/core set construct, or inferior/thread set
Pedro> (I/T set, or just itset) for short, which should allow users to refer
Pedro> to large numbers of inferiors, processes, threads, and cores in a
Pedro> consistent way and using a flexible syntax.
Ok, I read through the series and sent all my comments. Some patches I
did not review in detail, since I don't feel competent to do so.
Pedro> IPTC sets are a collection of arbitrarily many processes,
Pedro> threads, and cores.
I wish we had a better name.
The current name seems to gain a letter with every new thread on the
topic :-)
Pedro> Along the idea that we
Pedro> need an intersection operator somehow, an idea I've been kicking in
Pedro> the background, is to make all kinds of objects have the same stand,
Pedro> and require, say, a one letter prefix to identify what kind of object
Pedro> we're specifying. E.g, i for inferior, p for process, t for thread, c
Pedro> for core and a for Ada task. In this scheme, the '.' is really a set
Pedro> intersection operator, and the ',' is the union operator. I'm very
Pedro> much inclined to try this route, but I'm also very interested in
Pedro> learning other's opinions.
I like this idea.
Tom
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [RFC/WIP PATCH 00/14] I/T sets
2011-11-30 19:35 ` Tom Tromey
@ 2011-12-16 19:40 ` Pedro Alves
0 siblings, 0 replies; 53+ messages in thread
From: Pedro Alves @ 2011-12-16 19:40 UTC (permalink / raw)
To: gdb-patches; +Cc: Tom Tromey
On Wednesday 30 November 2011 19:35:38, Tom Tromey wrote:
> >>>>> "Pedro" == Pedro Alves <pedro@codesourcery.com> writes:
>
> Pedro> Following up on <http://sourceware.org/ml/gdb/2011-11/msg00013.html>.
> Pedro> This patch set seeds an implementation of a general
> Pedro> inferior/process/thread/core set construct, or inferior/thread set
> Pedro> (I/T set, or just itset) for short, which should allow users to refer
> Pedro> to large numbers of inferiors, processes, threads, and cores in a
> Pedro> consistent way and using a flexible syntax.
>
> Ok, I read through the series and sent all my comments. Some patches I
> did not review in detail, since I don't feel competent to do so.
>
> Pedro> IPTC sets are a collection of arbitrarily many processes,
> Pedro> threads, and cores.
>
> I wish we had a better name.
>
> The current name seems to gain a letter with every new thread on the
> topic :-)
That was kind of on purpose. :-)
The best name I could think of is "scope". If we call these things
scopes, we can also rename the itfocus command to "scope", which can
be abbreviated as "sc". ("s" is already step, obviously). It's spelled
itfocus currently because "focus" was already taken by the TUI.
It looks like this:
curinf> scope t3 break -stop i1 main
Breakpoint 2 at 0x40068c: file threads.c, line 35.
curinf> scope i1
Current inferior is 1.
i1> sc all
Current inferior is 1.
all>
WDYT?
> Pedro> Along the idea that we
> Pedro> need an intersection operator somehow, an idea I've been kicking in
> Pedro> the background, is to make all kinds of objects have the same stand,
> Pedro> and require, say, a one letter prefix to identify what kind of object
> Pedro> we're specifying. E.g, i for inferior, p for process, t for thread, c
> Pedro> for core and a for Ada task. In this scheme, the '.' is really a set
> Pedro> intersection operator, and the ',' is the union operator. I'm very
> Pedro> much inclined to try this route, but I'm also very interested in
> Pedro> learning other's opinions.
>
> I like this idea.
I've implemented it. I'll post a WIP v2 series with that change (and
many others) shortly.
--
Pedro Alves
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [RFC/WIP PATCH 00/14] I/T sets
2011-11-28 15:39 [RFC/WIP PATCH 00/14] I/T sets Pedro Alves
` (15 preceding siblings ...)
2011-11-30 19:35 ` Tom Tromey
@ 2012-02-09 7:51 ` Tomas Östlund
2012-02-09 8:19 ` [RFC/WIP PATCH 00/14] I/T sets (resend) Tomas Östlund
17 siblings, 0 replies; 53+ messages in thread
From: Tomas Östlund @ 2012-02-09 7:51 UTC (permalink / raw)
To: Pedro Alves; +Cc: gdb-patches
Hi!
I hope it is not too late to give feedback and perhaps influence this
proposed feature. Let me first say that I think this is a very good
initiative and I see a lot of potential for it.
DISCLAIMER: This will be a long and somewhat unstructured feedback.
Please bear with me :-).
First some background:
Where I work at Ericsson we have long experience of debugging a
many-core architecture. It is an architecture developed in-house that is
proprietary and somewhat resembles Tilera's many-core architecture.
We have also developed a debugger that is able to control all cores in
the same debugging session. The debugger could be seen as a "bare-metal"
debugger in the sense that we do not operate on processes and threads
but only on cores. The hardware supports instruction-level stepping when
running the target debugger and furthermore in a synchronous fashion.
To give some examples of what is possible:
----------
cluster1/dsp0> step
steps 1 machine code instruction on dsp0 in cluster 1
----------
cluster1/dsp0> step all
synchronously step 1 machine code instruction on all cores in cluster 1.
----------
cluster1/dsp0> step dsp1 dsp2 dsp4
synchronously step 1 machine code instruction on dsp1, dsp2 and dsp4.
Because the debugger supports synchronous operations the concept of a
"group leader" is needed for some operations.
For example, if we do a high-level stepping (stepping past a C statement
for example) of several cores, the first specified core will be the one
that decides when the operation has finished:
-------
cluster1/dsp0> cstep dsp1 dsp2 dsp4
The above command will result in dsp1 stepping past the next C statement
with dsp2 and dsp4 running along synchronously, stopping whenever dsp1
stops.
--------
cluster1/dsp0> cfinish dsp2 dsp1 dsp4
The above command will result in dsp2 running until it returns from the
current function with dsp1 and dsp4 running along synchronously,
stopping whenever dsp2 stops.
This was only some examples to give you a quick overview from where we
are coming.
Now some actual feedback:
On 01/-10/37 20:59, Pedro Alves wrote:
[...]
> Along the idea that we
> need an intersection operator somehow, an idea I've been kicking in
> the background, is to make all kinds of objects have the same stand,
> and require, say, a one letter prefix to identify what kind of object
> we're specifying. E.g, i for inferior, p for process, t for thread, c
> for core and a for Ada task. In this scheme, the '.' is really a set
> intersection operator, and the ',' is the union operator. I'm very
> much inclined to try this route, but I'm also very interested in
> learning other's opinions. Here are examples:
>
> | Syntax | Meaning |
> |-------------------------+---------------------------------------------------|
> | i1.t1 | inferior 1, thread 1 |
> |-------------------------+---------------------------------------------------|
> | p123 | all (threads cores, etc.) of process with pid 123 |
> |-------------------------+---------------------------------------------------|
> | i1.t[1-100].c1-3 | threads 1 to 100 of iinferior 1, running on |
> | | cores 1 to 3 |
> |-------------------------+---------------------------------------------------|
> | i1.t*.c1-3,workers | same as above, union with all in the user |
> | | defined set "workers" |
> |-------------------------+---------------------------------------------------|
> | workers.~c1-3 | All in the user defined set "workers", except |
> | | those on cores 1 to 3 |
> |-------------------------+---------------------------------------------------|
> | i1.a1-3 | Ada tasks 1 to 3 of inferior 1 |
> |-------------------------+---------------------------------------------------|
> | workers.i1,crunchers.i2 | All workers of inferior 1, and |
> | | all crunchers of inferior 2 |
> |-------------------------+---------------------------------------------------|
>
> WDYT?
I think this is a very good suggestion and a must in order to make all
objects have the same stand as you say.
>
> The current implementation, borrowing heavilly from the HPD and the
> syntax we first thought out (see Stan's link above), currently
> implements the INF.TID form, and as a separate patch, the @core
> extension.
After some googling I finally found what HPD refers to. I had never
heard of it before and I'm curious what happened to the initiative?
Anyway, I've read the specification and think it has a lot of good
concepts, although it misses (intentionally?) to consider processor
cores as first class elements. One thing I dislike though is the syntax
for specifying sets using brackets ([]). Using the command line should
not be harder that it has to be and using brackets is very awkward,
especially on many keyboards with non-US layout. For example, on a
Swedish keyboard, you have to use a combination of ALT GR + 8 to produce
"[". Also I don't see the point of putting the set specification before
the actual command. For example compare the operation of doing "step
dsp0, dsp1 and dsp4" with our debugger
> step dsp0 dsp1 dsp4
and using the HPD syntax:
> [dsp0,dsp1,dsp4] step
Which one is more natural and easier to type?
Quite often in debugging, you reuse earlier debugger commands from
command history and modify them to apply to some other set of cores or
threads. This is also more easily done, if what commands should apply
to, are placed at the end of a command.
We think syntax-wise can spaces or commas be used as separators between
union members. It is easier to type, will cause fewer typing mistakes
and it is also easier to visually distinguish between ' ' and '.' than
between ',' and '.'.
If there are historical reasons for supporting the 'postfix' HPD syntax
we would like to extend the command syntax to support 'infix' as well as
we have given examples of above.
The HPD syntax does not specify a rationale for putting the set
specification before the command. What is the reason for that?
>
> The following examples exhibit some possible uses of sets:
>
> |-------------------------------------------+----------------------------------|
> | Commands | Behaviour |
> |-------------------------------------------+----------------------------------|
> | (gdb) step [.34-59] | Single-step the current |
> |<lines stepped over> | process's threads whose |
> | (gdb) | numbers are in the range 34 |
> | | through 59, inclusive |
Here you are not using the HPD syntax with putting the set before the
command. Why? Although I like this one better. Now let's just skip the
brackets :-).
> |-------------------------------------------+----------------------------------|
> | (gdb) step [@2] | Single-step all threads on |
> |<line stepped over> | core 2 |
> | (gdb) | |
Hmm, if this means step *all* threads on core 2, how would you express
the scenario in our debugger, where you can step a single machine code
instruction on a specified core?
With your proposed syntax using a letter prefix, should it not instead be
(gdb) step [t*.c2]
> However, what if the user does:
>
> [foo]> itset [1.2]
> [1.2]> step
>
> Here the user has explicitly said it only wants to focus on thread 2.
> What should the step above resume freely? HPD seems to suggest that
> the global setting should also be respected, which by default means
> the whole process is resumed.
>
> Let's get back to focusing on the whole process:
>
> [1.2]> itset [1.*]
> focus set to [1.*]
> [1.*]> step
>
> And imagine even that we're only debugging inferior 1.
> Now, according to HPD, this would step each and every thread of the
> process. It's equivalent to issuing:
>
> [some_default_focus] [1.*] step
> ^^^^^^^^^^^^^^^^ ^^^
> prompt explicit set cmd
>
>
> But, this conflicts with GDB's concept of a selected thread, and
> will be counter to all of our user's expectations... Worse, it's
> unimplementable on targets that don't do non-stop, so we'd end up
> with yet another incompatible mode. I'd very much like to merge
> all modes, not add another!
We totally agree! It is much better to define a flexible command syntax
that directly supports executing in the execution modes without having
to do any mode switching.
From our experience we have not seen the need for specifying multiple
'execution masters' nor are the semantics of such execution operations
clear. So if we for example express that simultaneously step 3 cores and
let all other cores run for that duration, does execution stop when the
first 'master core' stops ?. Or do we run until all master cores stop
(but that would mean they are no longer in sync i.e. have run the same
amount of cycles)? What happens if some master core
never stops? But by specifying a single execution master, the behavior
is much more predicable and synchronous.
--
Tomas Ãstlund
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [RFC/WIP PATCH 00/14] I/T sets (resend)
2011-11-28 15:39 [RFC/WIP PATCH 00/14] I/T sets Pedro Alves
` (16 preceding siblings ...)
2012-02-09 7:51 ` Tomas Östlund
@ 2012-02-09 8:19 ` Tomas Östlund
2012-02-09 14:36 ` Pedro Alves
17 siblings, 1 reply; 53+ messages in thread
From: Tomas Östlund @ 2012-02-09 8:19 UTC (permalink / raw)
To: palves; +Cc: gdb-patches
Resending since Pedro changed e-mail
--------
Hi!
I hope it is not too late to give feedback and perhaps influence this
proposed feature. Let me first say that I think this is a very good
initiative and I see a lot of potential for it.
DISCLAIMER: This will be a long and somewhat unstructured feedback.
Please bear with me :-).
First some background:
Where I work at Ericsson we have long experience of debugging a
many-core architecture. It is an architecture developed in-house that
is proprietary and somewhat resembles Tilera's many-core architecture.
We have also developed a debugger that is able to control all cores in
the same debugging session. The debugger could be seen as a "bare-
metal" debugger in the sense that we do not operate on processes and
threads but only on cores. The hardware supports instruction-level
stepping when running the target debugger and furthermore in a
synchronous fashion.
To give some examples of what is possible:
----------
cluster1/dsp0> step
steps 1 machine code instruction on dsp0 in cluster 1
----------
cluster1/dsp0> step all
synchronously step 1 machine code instruction on all cores in cluster 1.
----------
cluster1/dsp0> step dsp1 dsp2 dsp4
synchronously step 1 machine code instruction on dsp1, dsp2 and dsp4.
Because the debugger supports synchronous operations the concept of a
"group leader" is needed for some operations.
For example, if we do a high-level stepping (stepping past a C
statement for example) of several cores, the first specified core will
be the one that decides when the operation has finished:
-------
cluster1/dsp0> cstep dsp1 dsp2 dsp4
The above command will result in dsp1 stepping past the next C
statement with dsp2 and dsp4 running along synchronously, stopping
whenever dsp1 stops.
--------
cluster1/dsp0> cfinish dsp2 dsp1 dsp4
The above command will result in dsp2 running until it returns from the
current function with dsp1 and dsp4 running along synchronously,
stopping whenever dsp2 stops.
This was only some examples to give you a quick overview from where we
are coming.
Now some actual feedback:
On 01/-10/37 20:59, Pedro Alves wrote:
[...]
> Along the idea that we
> need an intersection operator somehow, an idea I've been kicking in
> the background, is to make all kinds of objects have the same stand,
> and require, say, a one letter prefix to identify what kind of object
> we're specifying. E.g, i for inferior, p for process, t for thread, c
> for core and a for Ada task. In this scheme, the '.' is really a set
> intersection operator, and the ',' is the union operator. I'm very
> much inclined to try this route, but I'm also very interested in
> learning other's opinions. Here are examples:
>
> | Syntax | Meaning |
> |-------------------------+---------------------------------------------------|
> | i1.t1 | inferior 1, thread 1 |
> |-------------------------+---------------------------------------------------|
> | p123 | all (threads cores, etc.) of process with pid 123 |
> |-------------------------+---------------------------------------------------|
> | i1.t[1-100].c1-3 | threads 1 to 100 of iinferior 1, running on |
> | | cores 1 to 3 |
> |-------------------------+---------------------------------------------------|
> | i1.t*.c1-3,workers | same as above, union with all in the user |
> | | defined set "workers" |
> |-------------------------+---------------------------------------------------|
> | workers.~c1-3 | All in the user defined set "workers", except |
> | | those on cores 1 to 3 |
> |-------------------------+---------------------------------------------------|
> | i1.a1-3 | Ada tasks 1 to 3 of inferior 1 |
> |-------------------------+---------------------------------------------------|
> | workers.i1,crunchers.i2 | All workers of inferior 1, and |
> | | all crunchers of inferior 2 |
> |-------------------------+---------------------------------------------------|
>
> WDYT?
I think this is a very good suggestion and a must in order to make all
objects have the same stand as you say.
>
> The current implementation, borrowing heavilly from the HPD and the
> syntax we first thought out (see Stan's link above), currently
> implements the INF.TID form, and as a separate patch, the @core
> extension.
After some googling I finally found what HPD refers to. I had never
heard of it before and I'm curious what happened to the initiative?
Anyway, I've read the specification and think it has a lot of good
concepts, although it misses (intentionally?) to consider processor
cores as first class elements. One thing I dislike though is the syntax
for specifying sets using brackets ([]). Using the command line should
not be harder that it has to be and using brackets is very awkward,
especially on many keyboards with non-US layout. For example, on a
Swedish keyboard, you have to use a combination of ALT GR + 8 to
produce "[". Also I don't see the point of putting the set
specification before the actual command. For example compare the
operation of doing "step dsp0, dsp1 and dsp4" with our debugger
> step dsp0 dsp1 dsp4
and using the HPD syntax:
> [dsp0,dsp1,dsp4] step
Which one is more natural and easier to type?
Quite often in debugging, you reuse earlier debugger commands from
command history and modify them to apply to some other set of cores or
threads. This is also more easily done, if what commands should apply
to, are placed at the end of a command.
We think syntax-wise can spaces or commas be used as separators between
union members. It is easier to type, will cause fewer typing mistakes
and it is also easier to visually distinguish between ' ' and '.' than
between ',' and '.'.
If there are historical reasons for supporting the 'postfix' HPD syntax
we would like to extend the command syntax to support 'infix' as well
as we have given examples of above.
The HPD syntax does not specify a rationale for putting the set
specification before the command. What is the reason for that?
>
> The following examples exhibit some possible uses of sets:
>
> |-------------------------------------------+----------------------------------|
> | Commands | Behaviour |
> |-------------------------------------------+----------------------------------|
> | (gdb) step [.34-59] | Single-step the current |
> |<lines stepped over> | process's threads whose |
> | (gdb) | numbers are in the range 34 |
> | | through 59, inclusive |
Here you are not using the HPD syntax with putting the set before the
command. Why? Although I like this one better. Now let's just skip the
brackets :-).
> |-------------------------------------------+----------------------------------|
> | (gdb) step [@2] | Single-step all threads on |
> |<line stepped over> | core 2 |
> | (gdb) | |
Hmm, if this means step *all* threads on core 2, how would you express
the scenario in our debugger, where you can step a single machine code
instruction on a specified core?
With your proposed syntax using a letter prefix, should it not instead
be
(gdb) step [t*.c2]
> However, what if the user does:
>
> [foo]> itset [1.2]
> [1.2]> step
>
> Here the user has explicitly said it only wants to focus on thread 2.
> What should the step above resume freely? HPD seems to suggest that
> the global setting should also be respected, which by default means
> the whole process is resumed.
>
> Let's get back to focusing on the whole process:
>
> [1.2]> itset [1.*]
> focus set to [1.*]
> [1.*]> step
>
> And imagine even that we're only debugging inferior 1.
> Now, according to HPD, this would step each and every thread of the
> process. It's equivalent to issuing:
>
> [some_default_focus] [1.*] step
> ^^^^^^^^^^^^^^^^ ^^^
> prompt explicit set cmd
>
>
> But, this conflicts with GDB's concept of a selected thread, and
> will be counter to all of our user's expectations... Worse, it's
> unimplementable on targets that don't do non-stop, so we'd end up
> with yet another incompatible mode. I'd very much like to merge
> all modes, not add another!
We totally agree! It is much better to define a flexible command syntax
that directly supports executing in the execution modes without having
to do any mode switching.
From our experience we have not seen the need for specifying multiple
'execution masters' nor are the semantics of such execution operations
clear. So if we for example express that simultaneously step 3 cores
and let all other cores run for that duration, does execution stop when
the first 'master core' stops ?. Or do we run until all master cores
stop (but that would mean they are no longer in sync i.e. have run the
same amount of cycles)? What happens if some master core
never stops? But by specifying a single execution master, the behavior
is much more predicable and synchronous.
--
Tomas Ãstlund
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [RFC/WIP PATCH 00/14] I/T sets (resend)
2012-02-09 8:19 ` [RFC/WIP PATCH 00/14] I/T sets (resend) Tomas Östlund
@ 2012-02-09 14:36 ` Pedro Alves
2012-02-15 9:48 ` Tomas Östlund
0 siblings, 1 reply; 53+ messages in thread
From: Pedro Alves @ 2012-02-09 14:36 UTC (permalink / raw)
To: Tomas Östlund; +Cc: gdb-patches
On 02/09/2012 08:18 AM, Tomas Ãstlund wrote:
> Resending since Pedro changed e-mail
>
> --------
>
> Hi!
>
> I hope it is not too late to give feedback and perhaps influence this
> proposed feature. Let me first say that I think this is a very good
> initiative and I see a lot of potential for it.
It's not. :-)
> This was only some examples to give you a quick overview from where we
> are coming.
Thanks! This is largely in line with the work's original requirements.
>
> Now some actual feedback:
>
> On 01/-10/37 20:59, Pedro Alves wrote:
> [...]
>> Along the idea that we
>> need an intersection operator somehow, an idea I've been kicking in
>> the background, is to make all kinds of objects have the same stand,
>> and require, say, a one letter prefix to identify what kind of object
>> we're specifying. E.g, i for inferior, p for process, t for thread, c
>> for core and a for Ada task. In this scheme, the '.' is really a set
>> intersection operator, and the ',' is the union operator. I'm very
>> much inclined to try this route, but I'm also very interested in
>> learning other's opinions. Here are examples:
>>
>> | Syntax | Meaning |
>> |-------------------------+---------------------------------------------------|
>> | i1.t1 | inferior 1, thread 1 |
>> |-------------------------+---------------------------------------------------|
>> | p123 | all (threads cores, etc.) of process with pid 123 |
>> |-------------------------+---------------------------------------------------|
>> | i1.t[1-100].c1-3 | threads 1 to 100 of iinferior 1, running on |
>> | | cores 1 to 3 |
>> |-------------------------+---------------------------------------------------|
>> | i1.t*.c1-3,workers | same as above, union with all in the user |
>> | | defined set "workers" |
>> |-------------------------+---------------------------------------------------|
>> | workers.~c1-3 | All in the user defined set "workers", except |
>> | | those on cores 1 to 3 |
>> |-------------------------+---------------------------------------------------|
>> | i1.a1-3 | Ada tasks 1 to 3 of inferior 1 |
>> |-------------------------+---------------------------------------------------|
>> | workers.i1,crunchers.i2 | All workers of inferior 1, and |
>> | | all crunchers of inferior 2 |
>> |-------------------------+---------------------------------------------------|
>>
>> WDYT?
>
> I think this is a very good suggestion and a must in order to make all
> objects have the same stand as you say.
Great. I actually implemented most of this.
See the v2 (latest version) of the series at:
http://sourceware.org/ml/gdb-patches/2011-12/msg00550.html
>> The current implementation, borrowing heavilly from the HPD and the
>> syntax we first thought out (see Stan's link above), currently
>> implements the INF.TID form, and as a separate patch, the @core
>> extension.
>
> After some googling I finally found what HPD refers to. I had never
> heard of it before and I'm curious what happened to the initiative?
> Anyway, I've read the specification and think it has a lot of good
> concepts, although it misses (intentionally?) to consider processor
> cores as first class elements. One thing I dislike though is the syntax
> for specifying sets using brackets ([]). Using the command line should
> not be harder that it has to be and using brackets is very awkward,
> especially on many keyboards with non-US layout. For example, on a
> Swedish keyboard, you have to use a combination of ALT GR + 8 to
> produce "[". Also I don't see the point of putting the set
> specification before the actual command.
A agree. On a Portuguese keyboard it's also painful. And that's why
I got rid of []'s in v2.
For example compare the
> operation of doing "step dsp0, dsp1 and dsp4" with our debugger
>
>> step dsp0 dsp1 dsp4
>
> and using the HPD syntax:
>
>> [dsp0,dsp1,dsp4] step
>
> Which one is more natural and easier to type?
See v2. :-)
>
> Quite often in debugging, you reuse earlier debugger commands from
> command history and modify them to apply to some other set of cores or
> threads. This is also more easily done, if what commands should apply
> to, are placed at the end of a command.
>
> We think syntax-wise can spaces or commas be used as separators between
> union members. It is easier to type, will cause fewer typing mistakes
> and it is also easier to visually distinguish between ' ' and '.' than
> between ',' and '.'.
The problem is one of ambiguity when you want to be able to specify
sets as an option, and support other options. E.g.,
break -stop foo bar qux
-stop takes a set as argument. Is the set "foo", or "foo bar"? Perhaps
even "foo bar qux"? You'd need to quote the set somehow, like
break -stop 'foo bar' qux.
which we were trying to avoid (just like []'s)
We could allow optional quoting. Another alternative would be to use some
other characters. I don't know, '.', and ',' seemed quite intuitive to me. Maybe
you could try v2 out a bit. It's still easily accessible in my git, as pointed
out in the v2 series intro.
> If there are historical reasons for supporting the 'postfix' HPD syntax
> we would like to extend the command syntax to support 'infix' as well
> as we have given examples of above.
>
> The HPD syntax does not specify a rationale for putting the set
> specification before the command. What is the reason for that?
It kind of follows from the idea that you can also switch the
set focus permanently. With "itfocus" in v1, or "scope" in v2.
You define your commands to apply to the current
scope. So:
(gdb) itfocus foo / scope foo
(gdb) step
is the same as
(gdb) [foo] step (v1)
or
(gdb) scope foo step (v2)
IOW, you can think of prefix scope as a shorthand. BTW, the v2 syntax is
quite like TotalView's (they call theirs "dfocus", IIRC).
>
>>
>> The following examples exhibit some possible uses of sets:
>>
>> |-------------------------------------------+----------------------------------|
>> | Commands | Behaviour |
>> |-------------------------------------------+----------------------------------|
>> | (gdb) step [.34-59] | Single-step the current |
>> |<lines stepped over> | process's threads whose |
>> | (gdb) | numbers are in the range 34 |
>> | | through 59, inclusive |
>
> Here you are not using the HPD syntax with putting the set before the
> command. Why? Although I like this one better. Now let's just skip the
> brackets :-).
:-) That's what you get while your mind isn't completely sorted out yet.
Please do see the v2 series.
>
>
>> |-------------------------------------------+----------------------------------|
>> | (gdb) step [@2] | Single-step all threads on |
>> |<line stepped over> | core 2 |
>> | (gdb) | |
>
> Hmm, if this means step *all* threads on core 2, how would you express
> the scenario in our debugger, where you can step a single machine code
> instruction on a specified core?
> With your proposed syntax using a letter prefix, should it not instead
> be
>
> (gdb) step [t*.c2]
Yes. The proposed syntax hadn't been implemented yet on the v1. I had written
the new syntax as a proposal of something to be changed after v1. And I did it for v2.
>
>> However, what if the user does:
>>
>> [foo]> itset [1.2]
>> [1.2]> step
>>
>> Here the user has explicitly said it only wants to focus on thread 2.
>> What should the step above resume freely? HPD seems to suggest that
>> the global setting should also be respected, which by default means
>> the whole process is resumed.
>>
>> Let's get back to focusing on the whole process:
>>
>> [1.2]> itset [1.*]
>> focus set to [1.*]
>> [1.*]> step
>>
>> And imagine even that we're only debugging inferior 1.
>> Now, according to HPD, this would step each and every thread of the
>> process. It's equivalent to issuing:
>>
>> [some_default_focus] [1.*] step
>> ^^^^^^^^^^^^^^^^ ^^^
>> prompt explicit set cmd
>>
>>
>> But, this conflicts with GDB's concept of a selected thread, and
>> will be counter to all of our user's expectations... Worse, it's
>> unimplementable on targets that don't do non-stop, so we'd end up
>> with yet another incompatible mode. I'd very much like to merge
>> all modes, not add another!
>
> We totally agree! It is much better to define a flexible command syntax
> that directly supports executing in the execution modes without having
> to do any mode switching.
>
> From our experience we have not seen the need for specifying multiple
> 'execution masters' nor are the semantics of such execution operations
> clear. So if we for example express that simultaneously step 3 cores
> and let all other cores run for that duration, does execution stop when
> the first 'master core' stops ?. Or do we run until all master cores
> stop (but that would mean they are no longer in sync i.e. have run the
> same amount of cycles)? What happens if some master core
> never stops? But by specifying a single execution master, the behavior
> is much more predicable and synchronous.
That's interesting. Thanks!
--
Pedro Alves
^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: [RFC/WIP PATCH 00/14] I/T sets (resend)
2012-02-09 14:36 ` Pedro Alves
@ 2012-02-15 9:48 ` Tomas Östlund
0 siblings, 0 replies; 53+ messages in thread
From: Tomas Östlund @ 2012-02-15 9:48 UTC (permalink / raw)
To: Pedro Alves; +Cc: gdb-patches, ERAMMFN, stanshebs
On 02/09/12 15:35, Pedro Alves wrote:
[...]
>
> Great. I actually implemented most of this.
>
> See the v2 (latest version) of the series at:
>
> http://sourceware.org/ml/gdb-patches/2011-12/msg00550.html
Thanks, I will check that out.
>
> The problem is one of ambiguity when you want to be able to specify
> sets as an option, and support other options. E.g.,
>
> break -stop foo bar qux
>
> -stop takes a set as argument. Is the set "foo", or "foo bar"? Perhaps
> even "foo bar qux"? You'd need to quote the set somehow, like
>
> break -stop 'foo bar' qux.
>
> which we were trying to avoid (just like []'s)
>
> We could allow optional quoting. Another alternative would be to use some
> other characters. I don't know, '.', and ',' seemed quite intuitive to me. Maybe
> you could try v2 out a bit. It's still easily accessible in my git, as pointed
> out in the v2 series intro.
I'm not sure I see what the problem is here. If you compare to a UNIX shell, you have
the same kind of issue that you sometimes need to quote in order to be unambiguous.
For example, using the grep command as an example:
> grep foo bar froz
will grep for the string "foo" in the files bar and froz.
On the other hand, in order to search for the string "foo bar" in the file froz you
need to quote like
> grep "foo bar" froz.
The idea is that you do not need to *always* quote as was the case with the brackets
in the HPD spec, only when the command is ambiguous. So I think it is perfectly fine
to expect the user to somehow quote the argument to the -stop flag like
break -stop 'foo bar' qux
or
break -stop foo,bar qux
>
> It kind of follows from the idea that you can also switch the
> set focus permanently. With "itfocus" in v1, or "scope" in v2.
>
> You define your commands to apply to the current
> scope. So:
>
> (gdb) itfocus foo / scope foo
> (gdb) step
>
> is the same as
>
> (gdb) [foo] step (v1)
>
> or
>
> (gdb) scope foo step (v2)
>
> IOW, you can think of prefix scope as a shorthand. BTW, the v2 syntax is
> quite like TotalView's (they call theirs "dfocus", IIRC).
I think the semantics of this is confusing.
The command "scope foo" and "scope foo step" looks quite similar but has completely
different semantics. If you compare again with a UNIX shell (which I think is a
very good analogy and one that maps very well to the PCT concept)it would mean that
you would write
> cd bar ls
instead of simply
> ls bar
As you can see, I like the semantics of the Unix shell ;-)
The reason I use the shell as example is that our debugger shell is very much
influenced by it and as I said, it maps very nicely.
Both the UNIX shell and a debugger shell has a concept of a "current context".
In the UNIX shell, it is the current working directory and in the debug shell it is
the "current working set".
In the Unix shell many commands operates on files and directories and in the debug
shell it operates on cores, processes and threads.
We have even implemented shell things like ";" to execute more than one command like
cluster1/dsp0> step; print foo; go; print bar
will step the current core dsp0, then print the variable foo, then start execution
and finally print the variable bar when we stop for some reason.
Another shell features we have is redirect of output, so that you can write things like
cluster1/dsp0> showregisters > my_regs.txt
The above command will store the contents of all registers on dsp0 in the file
my_reg.txt. The nice thing about mimicking a real shell is that you have a lot of
knowledge that can be directly transferred to the debug shell.
If you know how to use a UNIX shell then many of the same concepts can be reused.
Cheers,
Tomas
^ permalink raw reply [flat|nested] 53+ messages in thread