diff options
Diffstat (limited to 'usr/src')
-rw-r--r-- | usr/src/cmd/mdb/common/modules/genunix/combined.c | 88 |
1 files changed, 85 insertions, 3 deletions
diff --git a/usr/src/cmd/mdb/common/modules/genunix/combined.c b/usr/src/cmd/mdb/common/modules/genunix/combined.c index 412761a7bd..be529ae40a 100644 --- a/usr/src/cmd/mdb/common/modules/genunix/combined.c +++ b/usr/src/cmd/mdb/common/modules/genunix/combined.c @@ -23,8 +23,6 @@ * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <mdb/mdb_modapi.h> typedef struct combined_walk { @@ -40,6 +38,10 @@ typedef struct combined_walk_data { uintptr_t cwd_initial_walk_addr; /* to init each walk */ combined_walk_t *cwd_current_walk; combined_walk_t *cwd_final_walk; /* tail pointer */ + + struct combined_walk_data *cwd_next; + struct combined_walk_data *cwd_prev; + void *cwd_tag; /* used to find this data */ } combined_walk_data_t; /* @@ -59,9 +61,68 @@ combined_walk_init(mdb_walk_state_t *wsp) cwd->cwd_initial_walk_addr = wsp->walk_addr; cwd->cwd_current_walk = cwd->cwd_final_walk = NULL; + cwd->cwd_next = cwd->cwd_prev = NULL; + cwd->cwd_tag = NULL; wsp->walk_data = cwd; } +/* + * If a sub-walker's walk_step() is interrupted (by Ctrl-C or entering 'q' when + * prompted for the next screenful of data), there won't be an opportunity to + * switch wsp->walk_data from the sub-walker's data back to the combined walk + * data, since control will not return from walk_step(). Since mdb is + * single-threaded, we can save the combined walk data for combined_walk_fini() + * to use in case it was reached from an interrupted walk_step(). To allow for + * the possibility of nested combined walks, we'll save them on a list tagged by + * the sub-walker's data. + */ +static combined_walk_data_t *cwd_saved; + +static void +combined_walk_data_save(combined_walk_data_t *cwd, void *tag) +{ + cwd->cwd_next = cwd_saved; + cwd->cwd_prev = NULL; + if (cwd_saved != NULL) { + cwd_saved->cwd_prev = cwd; + } + cwd_saved = cwd; + cwd->cwd_tag = tag; +} + +static void +combined_walk_data_drop(combined_walk_data_t *cwd) +{ + if (cwd->cwd_prev == NULL) { + cwd_saved = cwd->cwd_next; + } else { + cwd->cwd_prev->cwd_next = cwd->cwd_next; + } + if (cwd->cwd_next != NULL) { + cwd->cwd_next->cwd_prev = cwd->cwd_prev; + } + cwd->cwd_next = cwd->cwd_prev = NULL; + cwd->cwd_tag = NULL; +} + +static combined_walk_data_t * +combined_walk_data_find(void *tag) +{ + combined_walk_data_t *cwd; + + if (tag == NULL) { + return (NULL); + } + + for (cwd = cwd_saved; cwd != NULL; cwd = cwd->cwd_next) { + if (cwd->cwd_tag == tag) { + return (cwd); + } + } + + return (NULL); +} + static void combined_walk_append(combined_walk_data_t *cwd, combined_walk_t *cw) { @@ -133,7 +194,11 @@ combined_walk_step(mdb_walk_state_t *wsp) } } + /* save cwd for fini() in case step() is interrupted */ + combined_walk_data_save(cwd, cw->cw_data); status = cw->cw_step(wsp); + /* control may never reach here */ + combined_walk_data_drop(cwd); if (status == WALK_DONE) { (void) combined_walk_remove_current(cwd); @@ -150,9 +215,26 @@ combined_walk_step(mdb_walk_state_t *wsp) void combined_walk_fini(mdb_walk_state_t *wsp) { - combined_walk_data_t *cwd = wsp->walk_data; + combined_walk_data_t *cwd; combined_walk_t *cw; + /* + * If walk_step() was interrupted, wsp->walk_data will be the + * sub-walker's data, not the combined walker's data, so first check to + * see if there is saved combined walk data tagged by the presumed + * sub-walker's walk data. + */ + cwd = combined_walk_data_find(wsp->walk_data); + if (cwd == NULL) { + /* + * walk_step() was not interrupted, so wsp->walk_data is + * actually the combined walk data. + */ + cwd = wsp->walk_data; + } else { + combined_walk_data_drop(cwd); + } + while ((cw = combined_walk_remove_current(cwd)) != NULL) { if (cw->cw_initialized) { wsp->walk_data = cw->cw_data; |