diff options
-rw-r--r-- | gdb-helpers/libcr.py | 142 | ||||
-rw-r--r-- | libcr/tests/test_matrix.c | 6 | ||||
-rw-r--r-- | libcr_ipc/tests/test_chan.c | 8 | ||||
-rw-r--r-- | libcr_ipc/tests/test_mutex.c | 6 | ||||
-rw-r--r-- | libcr_ipc/tests/test_rpc.c | 12 | ||||
-rw-r--r-- | libcr_ipc/tests/test_select.c | 12 | ||||
-rw-r--r-- | libcr_ipc/tests/test_sema.c | 18 |
7 files changed, 113 insertions, 91 deletions
diff --git a/gdb-helpers/libcr.py b/gdb-helpers/libcr.py index fcfd86e..6f95a81 100644 --- a/gdb-helpers/libcr.py +++ b/gdb-helpers/libcr.py @@ -89,6 +89,7 @@ def gdb_longjmp(buf: gdb_JmpBuf) -> None: class CrGlobals: + main: "CrMain" coroutines: list["CrCoroutine"] _breakpoint: "CrBreakpoint" _known_threads: set[gdb.InferiorThread] @@ -98,6 +99,7 @@ class CrGlobals: gdb.parse_and_eval("sizeof(coroutine_table)/sizeof(coroutine_table[0])") ) + self.main = CrMain(self) self.coroutines = [CrCoroutine(self, i + 1) for i in range(num)] self._breakpoint = CrBreakpoint() @@ -126,35 +128,37 @@ class CrGlobals: # Ignore thread creation events. self._known_threads = cur_threads return - if self.coroutine_running: - if not self.coroutine_running.is_selected(): - if gdb_bug_32428: - print("Must return to running coroutine before continuing.") - print("Hit ^C twice then run:") - print(f" cr select {self.coroutine_running.id}") - while True: - time.sleep(1) - assert self.coroutine_running.cont_env - gdb_longjmp(self.coroutine_running.cont_env) + if not self.coroutine_running.is_selected(): + if gdb_bug_32428: + print("Must return to running coroutine before continuing.") + print("Hit ^C twice then run:") + print(f" cr select {self.coroutine_running.cid}") + while True: + time.sleep(1) + assert self.coroutine_running.cont_env + gdb_longjmp(self.coroutine_running.cont_env) + self.main.cont_env = None for cr in self.coroutines: cr.cont_env = None def is_valid_cid(self, cid: int) -> bool: - return 0 < cid <= len(self.coroutines) + return (0 < cid <= len(self.coroutines)) and ( + self.coroutines[cid - 1].state != self.CR_NONE + ) @property - def coroutine_running(self) -> "CrCoroutine | None": + def coroutine_running(self) -> "CrMain | CrCoroutine": cid = int(gdb.parse_and_eval("coroutine_running")) if not self.is_valid_cid(cid): - return None + return self.main return self.coroutines[cid - 1] @property - def coroutine_selected(self) -> "CrCoroutine | None": + def coroutine_selected(self) -> "CrMain | CrCoroutine": for cr in self.coroutines: if cr.is_selected(): return cr - return None + return self.main @property def CR_NONE(self) -> gdb.Value: @@ -164,6 +168,35 @@ class CrGlobals: def CR_RUNNING(self) -> gdb.Value: return gdb.parse_and_eval("CR_RUNNING") + def select(self, cr: "CrMain | CrCoroutine", level: int = -1) -> None: + self.coroutine_selected.cont_env = gdb_setjmp() + + if cr.cont_env: + gdb_longjmp(cr.cont_env) + else: + env: gdb_JmpBuf + if cr == self.coroutine_running: + assert False # cr.cont_env should have been set + match cr: + case CrMain(): + env = self.readjmp("&coroutine_main_env") + case CrCoroutine(): + if cr.state == self.CR_RUNNING: + env = self.readjmp("&coroutine_add_env") + else: + env = self.readjmp(f"&coroutine_table[{cr.cid-1}].env") + gdb_longjmp(env) + cr_select_top_frame() + + @contextlib.contextmanager + def with_selected(self, cr: "CrMain | CrCoroutine") -> typing.Iterator[None]: + saved_env = gdb_setjmp() + self.select(cr) + try: + yield + finally: + gdb_longjmp(saved_env) + class CrBreakpointUnwinder(gdb.unwinder.Unwinder): """Used to temporarily disable unwinding so that @@ -245,6 +278,22 @@ def cr_select_top_frame() -> None: break +class CrMain: + cr_globals: CrGlobals + cont_env: gdb_JmpBuf | None + + def __init__(self, cr_globals: CrGlobals) -> None: + self.cr_globals = cr_globals + self.cont_env = None + + @property + def cid(self) -> int: + return 0 + + def is_selected(self) -> bool: + return not any(cr.is_selected() for cr in self.cr_globals.coroutines) + + class CrCoroutine: cr_globals: CrGlobals cid: int @@ -256,10 +305,6 @@ class CrCoroutine: self.cont_env = None @property - def id(self) -> int: - return self.cid - - @property def state(self) -> gdb.Value: return gdb.parse_and_eval(f"coroutine_table[{self.cid-1}].state") @@ -272,36 +317,10 @@ class CrCoroutine: def is_selected(self) -> bool: sp = int(gdb.parse_and_eval("$sp")) - lo = int(gdb.parse_and_eval(f"coroutine_table[{self.id-1}].stack")) - hi = lo + int(gdb.parse_and_eval(f"coroutine_table[{self.id-1}].stack_size")) + lo = int(gdb.parse_and_eval(f"coroutine_table[{self.cid-1}].stack")) + hi = lo + int(gdb.parse_and_eval(f"coroutine_table[{self.cid-1}].stack_size")) return lo <= sp < hi - def select(self, level: int = -1) -> None: - if self.cr_globals.coroutine_selected: - self.cr_globals.coroutine_selected.cont_env = gdb_setjmp() - - if self.cont_env: - gdb_longjmp(self.cont_env) - else: - env: gdb_JmpBuf - if self == self.cr_globals.coroutine_running: - assert False # self.cont_env should have been set - elif self.state == self.cr_globals.CR_RUNNING: - env = self.cr_globals.readjmp("&coroutine_add_env") - else: - env = self.cr_globals.readjmp(f"&coroutine_table[{self.id-1}].env") - gdb_longjmp(env) - cr_select_top_frame() - - @contextlib.contextmanager - def with_selected(self) -> typing.Iterator[None]: - saved_env = gdb_setjmp() - self.select() - try: - yield - finally: - gdb_longjmp(saved_env) - # User-facing commands ######################################################### @@ -342,8 +361,11 @@ class CrListCommand(gdb.Command): rows: list[tuple[str, str, str, str, str]] = [ ("", "Id", "Name", "State", "Frame") ] - for cr in self.cr_globals.coroutines: - if cr.state == self.cr_globals.CR_NONE: + for cid in range(len(self.cr_globals.coroutines) + 1): + cr: CrMain | CrCoroutine = ( + self.cr_globals.coroutines[cid - 1] if cid else self.cr_globals.main + ) + if isinstance(cr, CrCoroutine) and cr.state == self.cr_globals.CR_NONE: continue rows += [ ( @@ -353,9 +375,9 @@ class CrListCommand(gdb.Command): "G" if cr.is_selected() else " ", ] ), - str(cr.id), - repr(cr.name), - str(cr.state), + str(cr.cid), + repr(cr.name) if isinstance(cr, CrCoroutine) else "-", + str(cr.state) if isinstance(cr, CrCoroutine) else "-", self._pretty_frame(cr, from_tty), ) ] @@ -382,9 +404,9 @@ class CrListCommand(gdb.Command): l = l[:maxline] print(l) - def _pretty_frame(self, cr: CrCoroutine, from_tty: bool) -> str: + def _pretty_frame(self, cr: CrMain | CrCoroutine, from_tty: bool) -> str: try: - with cr.with_selected(): + with self.cr_globals.with_selected(cr): saved_level = gdb.selected_frame().level() cr_select_top_frame() full = gdb.execute("frame", from_tty=from_tty, to_string=True) @@ -411,16 +433,15 @@ class CrSelectCommand(gdb.Command): if len(argv) != 1: raise gdb.GdbError("Usage: cr select COROUTINE") cr = self._find(argv[0]) - cr.select() + self.cr_globals.select(cr) gdb.execute("frame") - def _find(self, name: str) -> CrCoroutine: + def _find(self, name: str) -> CrMain | CrCoroutine: if name.isnumeric(): cid = int(name) - if ( - self.cr_globals.is_valid_cid(cid) - and self.cr_globals.coroutines[cid - 1].state != self.cr_globals.CR_NONE - ): + if cid == 0: + return self.cr_globals.main + if self.cr_globals.is_valid_cid(cid): return self.cr_globals.coroutines[cid - 1] crs: list[CrCoroutine] = [] for cr in self.cr_globals.coroutines: @@ -445,6 +466,7 @@ def cr_initialize() -> None: if _cr_globals: old = _cr_globals new = CrGlobals() + new.main.cont_env = old.main.cont_env for i in range(min(len(old.coroutines), len(new.coroutines))): new.coroutines[i].cont_env = old.coroutines[i].cont_env old.delete() diff --git a/libcr/tests/test_matrix.c b/libcr/tests/test_matrix.c index 1f23455..eaa4bdc 100644 --- a/libcr/tests/test_matrix.c +++ b/libcr/tests/test_matrix.c @@ -1,6 +1,6 @@ /* libcr/tests/test_matrix.c - Tests for libcr * - * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> + * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> * SPDX-License-Identifier: AGPL-3.0-or-later */ @@ -8,14 +8,14 @@ int a = 1; -COROUTINE cr_init(void *) { +COROUTINE init_cr(void *) { cr_begin(); a = 2; cr_end(); } int main() { - coroutine_add("init", cr_init, NULL); + coroutine_add("init", init_cr, NULL); coroutine_main(); if (a != 2) return 1; diff --git a/libcr_ipc/tests/test_chan.c b/libcr_ipc/tests/test_chan.c index e5d9dc8..4788dd4 100644 --- a/libcr_ipc/tests/test_chan.c +++ b/libcr_ipc/tests/test_chan.c @@ -11,7 +11,7 @@ CR_CHAN_DECLARE(intchan, int); -COROUTINE cr_producer(void *_ch) { +COROUTINE producer_cr(void *_ch) { intchan_t *ch = _ch; cr_begin(); @@ -26,7 +26,7 @@ COROUTINE cr_producer(void *_ch) { cr_end(); } -COROUTINE cr_consumer(void *_ch) { +COROUTINE consumer_cr(void *_ch) { int x; intchan_t *ch = _ch; cr_begin(); @@ -42,8 +42,8 @@ COROUTINE cr_consumer(void *_ch) { int main() { intchan_t ch = {0}; - coroutine_add("producer", cr_producer, &ch); - coroutine_add("consumer", cr_consumer, &ch); + coroutine_add("producer", producer_cr, &ch); + coroutine_add("consumer", consumer_cr, &ch); coroutine_main(); return 0; } diff --git a/libcr_ipc/tests/test_mutex.c b/libcr_ipc/tests/test_mutex.c index 43714c9..d08315d 100644 --- a/libcr_ipc/tests/test_mutex.c +++ b/libcr_ipc/tests/test_mutex.c @@ -11,7 +11,7 @@ int counter = 0; -COROUTINE cr_worker(void *_mu) { +COROUTINE worker_cr(void *_mu) { cr_mutex_t *mu = _mu; cr_begin(); @@ -29,8 +29,8 @@ COROUTINE cr_worker(void *_mu) { int main() { cr_mutex_t mu = {}; - coroutine_add("a", cr_worker, &mu); - coroutine_add("b", cr_worker, &mu); + coroutine_add("a", worker_cr, &mu); + coroutine_add("b", worker_cr, &mu); coroutine_main(); test_assert(counter == 200); return 0; diff --git a/libcr_ipc/tests/test_rpc.c b/libcr_ipc/tests/test_rpc.c index 910b738..1461450 100644 --- a/libcr_ipc/tests/test_rpc.c +++ b/libcr_ipc/tests/test_rpc.c @@ -14,7 +14,7 @@ CR_RPC_DECLARE(intrpc, int, int); /* Test that the RPC is fair, have worker1 start waiting first, and * ensure that it gets the first request. */ -COROUTINE cr_caller(void *_ch) { +COROUTINE caller_cr(void *_ch) { intrpc_t *ch = _ch; cr_begin(); @@ -27,7 +27,7 @@ COROUTINE cr_caller(void *_ch) { cr_exit(); } -COROUTINE cr_worker1(void *_ch) { +COROUTINE worker1_cr(void *_ch) { intrpc_t *ch = _ch; cr_begin(); @@ -38,7 +38,7 @@ COROUTINE cr_worker1(void *_ch) { cr_exit(); } -COROUTINE cr_worker2(void *_ch) { +COROUTINE worker2_cr(void *_ch) { intrpc_t *ch = _ch; cr_begin(); @@ -51,9 +51,9 @@ COROUTINE cr_worker2(void *_ch) { int main() { intrpc_t ch = {0}; - coroutine_add("worker1", cr_worker1, &ch); - coroutine_add("caller", cr_caller, &ch); - coroutine_add("worker2", cr_worker2, &ch); + coroutine_add("worker1", worker1_cr, &ch); + coroutine_add("caller", caller_cr, &ch); + coroutine_add("worker2", worker2_cr, &ch); coroutine_main(); return 0; } diff --git a/libcr_ipc/tests/test_select.c b/libcr_ipc/tests/test_select.c index 3da1c78..9b5d117 100644 --- a/libcr_ipc/tests/test_select.c +++ b/libcr_ipc/tests/test_select.c @@ -14,7 +14,7 @@ CR_CHAN_DECLARE(intchan, int); intchan_t ch[10] = {0}; intchan_t fch = {0}; -COROUTINE cr_consumer(void *) { +COROUTINE consumer_cr(void *) { cr_begin(); struct cr_select_arg args[11]; @@ -60,7 +60,7 @@ COROUTINE cr_consumer(void *) { cr_end(); } -COROUTINE cr_producer(void *_n) { +COROUTINE producer_cr(void *_n) { int n = *(int *)_n; cr_begin(); @@ -69,7 +69,7 @@ COROUTINE cr_producer(void *_n) { cr_end(); } -COROUTINE cr_final(void *) { +COROUTINE final_cr(void *) { cr_begin(); int ret = cr_chan_recv(&fch); @@ -81,9 +81,9 @@ COROUTINE cr_final(void *) { int main() { for (int i = 0; i < 10; i++) - coroutine_add("producer", cr_producer, &i); - coroutine_add("consumer", cr_consumer, NULL); - coroutine_add("final", cr_final, NULL); + coroutine_add("producer", producer_cr, &i); + coroutine_add("consumer", consumer_cr, NULL); + coroutine_add("final", final_cr, NULL); coroutine_main(); return 0; } diff --git a/libcr_ipc/tests/test_sema.c b/libcr_ipc/tests/test_sema.c index e5b22a5..435c01a 100644 --- a/libcr_ipc/tests/test_sema.c +++ b/libcr_ipc/tests/test_sema.c @@ -13,7 +13,7 @@ int counter = 0; -COROUTINE cr_first(void *_sema) { +COROUTINE first_cr(void *_sema) { cr_sema_t *sema = _sema; cr_begin(); @@ -24,7 +24,7 @@ COROUTINE cr_first(void *_sema) { cr_exit(); } -COROUTINE cr_second(void *_sema) { +COROUTINE second_cr(void *_sema) { cr_sema_t *sema = _sema; cr_begin(); @@ -35,7 +35,7 @@ COROUTINE cr_second(void *_sema) { cr_exit(); } -COROUTINE cr_producer(void *_sema) { +COROUTINE producer_cr(void *_sema) { cr_sema_t *sema = _sema; cr_begin(); @@ -45,7 +45,7 @@ COROUTINE cr_producer(void *_sema) { cr_end(); } -COROUTINE cr_consumer(void *_sema) { +COROUTINE consumer_cr(void *_sema) { cr_sema_t *sema = _sema; cr_begin(); @@ -59,16 +59,16 @@ int main() { cr_sema_t sema = {}; printf("== test 1 =========================================\n"); - coroutine_add("first", cr_first, &sema); - coroutine_add("second", cr_second, &sema); + coroutine_add("first", first_cr, &sema); + coroutine_add("second", second_cr, &sema); coroutine_main(); test_assert(sema.cnt == 0); printf("== test 2 =========================================\n"); - coroutine_add("consumer", cr_consumer, &sema); - coroutine_add("producer", cr_producer, &sema); + coroutine_add("consumer", consumer_cr, &sema); + coroutine_add("producer", producer_cr, &sema); coroutine_main(); - coroutine_add("consumer", cr_consumer, &sema); + coroutine_add("consumer", consumer_cr, &sema); coroutine_main(); test_assert(sema.cnt == 0); |