summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src')
-rw-r--r--usr/src/cmd/mdb/common/modules/genunix/combined.c88
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;