xref: /onnv-gate/usr/src/cmd/mdb/common/modules/genunix/combined.c (revision 10609:f1cdb59c41b4)
16712Stomee /*
26712Stomee  * CDDL HEADER START
36712Stomee  *
46712Stomee  * The contents of this file are subject to the terms of the
56712Stomee  * Common Development and Distribution License (the "License").
66712Stomee  * You may not use this file except in compliance with the License.
76712Stomee  *
86712Stomee  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
96712Stomee  * or http://www.opensolaris.org/os/licensing.
106712Stomee  * See the License for the specific language governing permissions
116712Stomee  * and limitations under the License.
126712Stomee  *
136712Stomee  * When distributing Covered Code, include this CDDL HEADER in each
146712Stomee  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
156712Stomee  * If applicable, add the following below this CDDL HEADER, with the
166712Stomee  * fields enclosed by brackets "[]" replaced with your own identifying
176712Stomee  * information: Portions Copyright [yyyy] [name of copyright owner]
186712Stomee  *
196712Stomee  * CDDL HEADER END
206712Stomee  */
216712Stomee /*
22*10609SJonathan.Adams@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
236712Stomee  * Use is subject to license terms.
246712Stomee  */
256712Stomee 
266712Stomee #include <mdb/mdb_modapi.h>
276712Stomee 
286712Stomee typedef struct combined_walk {
296712Stomee 	int (*cw_init)(mdb_walk_state_t *);
306712Stomee 	int (*cw_step)(mdb_walk_state_t *);
316712Stomee 	void (*cw_fini)(mdb_walk_state_t *);
326712Stomee 	struct combined_walk *cw_next;
336712Stomee 	void *cw_data;
346712Stomee 	boolean_t cw_initialized;
356712Stomee } combined_walk_t;
366712Stomee 
376712Stomee typedef struct combined_walk_data {
386712Stomee 	uintptr_t cwd_initial_walk_addr;	/* to init each walk */
396712Stomee 	combined_walk_t *cwd_current_walk;
406712Stomee 	combined_walk_t *cwd_final_walk;	/* tail pointer */
417849STom.Erickson@Sun.COM 
427849STom.Erickson@Sun.COM 	struct combined_walk_data *cwd_next;
437849STom.Erickson@Sun.COM 	struct combined_walk_data *cwd_prev;
447849STom.Erickson@Sun.COM 	void *cwd_tag;				/* used to find this data */
456712Stomee } combined_walk_data_t;
466712Stomee 
476712Stomee /*
486712Stomee  * Initialize a combined walk to
496712Stomee  * A) present a single concatenated series of elements from different
506712Stomee  *    structures, or
516712Stomee  * B) select from several possible walks at runtime.
526712Stomee  * Multiple walks are done in the same order passed to combined_walk_add(). Each
536712Stomee  * walk is initialized with the same wsp->walk_addr.
546712Stomee  */
556712Stomee void
combined_walk_init(mdb_walk_state_t * wsp)566712Stomee combined_walk_init(mdb_walk_state_t *wsp)
576712Stomee {
586712Stomee 	combined_walk_data_t *cwd;
596712Stomee 
606712Stomee 	cwd = mdb_alloc(sizeof (combined_walk_data_t), UM_SLEEP);
616712Stomee 
626712Stomee 	cwd->cwd_initial_walk_addr = wsp->walk_addr;
636712Stomee 	cwd->cwd_current_walk = cwd->cwd_final_walk = NULL;
647849STom.Erickson@Sun.COM 	cwd->cwd_next = cwd->cwd_prev = NULL;
657849STom.Erickson@Sun.COM 	cwd->cwd_tag = NULL;
666712Stomee 	wsp->walk_data = cwd;
676712Stomee }
686712Stomee 
697849STom.Erickson@Sun.COM /*
707849STom.Erickson@Sun.COM  * If a sub-walker's walk_step() is interrupted (by Ctrl-C or entering 'q' when
717849STom.Erickson@Sun.COM  * prompted for the next screenful of data), there won't be an opportunity to
727849STom.Erickson@Sun.COM  * switch wsp->walk_data from the sub-walker's data back to the combined walk
737849STom.Erickson@Sun.COM  * data, since control will not return from walk_step(). Since mdb is
747849STom.Erickson@Sun.COM  * single-threaded, we can save the combined walk data for combined_walk_fini()
757849STom.Erickson@Sun.COM  * to use in case it was reached from an interrupted walk_step(). To allow for
767849STom.Erickson@Sun.COM  * the possibility of nested combined walks, we'll save them on a list tagged by
777849STom.Erickson@Sun.COM  * the sub-walker's data.
787849STom.Erickson@Sun.COM  */
797849STom.Erickson@Sun.COM static combined_walk_data_t *cwd_saved;
807849STom.Erickson@Sun.COM 
817849STom.Erickson@Sun.COM static void
combined_walk_data_save(combined_walk_data_t * cwd,void * tag)827849STom.Erickson@Sun.COM combined_walk_data_save(combined_walk_data_t *cwd, void *tag)
837849STom.Erickson@Sun.COM {
847849STom.Erickson@Sun.COM 	cwd->cwd_next = cwd_saved;
857849STom.Erickson@Sun.COM 	cwd->cwd_prev = NULL;
867849STom.Erickson@Sun.COM 	if (cwd_saved != NULL) {
877849STom.Erickson@Sun.COM 		cwd_saved->cwd_prev = cwd;
887849STom.Erickson@Sun.COM 	}
897849STom.Erickson@Sun.COM 	cwd_saved = cwd;
907849STom.Erickson@Sun.COM 	cwd->cwd_tag = tag;
917849STom.Erickson@Sun.COM }
927849STom.Erickson@Sun.COM 
937849STom.Erickson@Sun.COM static void
combined_walk_data_drop(combined_walk_data_t * cwd)947849STom.Erickson@Sun.COM combined_walk_data_drop(combined_walk_data_t *cwd)
957849STom.Erickson@Sun.COM {
967849STom.Erickson@Sun.COM 	if (cwd->cwd_prev == NULL) {
977849STom.Erickson@Sun.COM 		cwd_saved = cwd->cwd_next;
987849STom.Erickson@Sun.COM 	} else {
997849STom.Erickson@Sun.COM 		cwd->cwd_prev->cwd_next = cwd->cwd_next;
1007849STom.Erickson@Sun.COM 	}
1017849STom.Erickson@Sun.COM 	if (cwd->cwd_next != NULL) {
1027849STom.Erickson@Sun.COM 		cwd->cwd_next->cwd_prev = cwd->cwd_prev;
1037849STom.Erickson@Sun.COM 	}
1047849STom.Erickson@Sun.COM 	cwd->cwd_next = cwd->cwd_prev = NULL;
1057849STom.Erickson@Sun.COM 	cwd->cwd_tag = NULL;
1067849STom.Erickson@Sun.COM }
1077849STom.Erickson@Sun.COM 
1087849STom.Erickson@Sun.COM static combined_walk_data_t *
combined_walk_data_find(void * tag)1097849STom.Erickson@Sun.COM combined_walk_data_find(void *tag)
1107849STom.Erickson@Sun.COM {
1117849STom.Erickson@Sun.COM 	combined_walk_data_t *cwd;
1127849STom.Erickson@Sun.COM 
1137849STom.Erickson@Sun.COM 	if (tag == NULL) {
1147849STom.Erickson@Sun.COM 		return (NULL);
1157849STom.Erickson@Sun.COM 	}
1167849STom.Erickson@Sun.COM 
1177849STom.Erickson@Sun.COM 	for (cwd = cwd_saved; cwd != NULL; cwd = cwd->cwd_next) {
1187849STom.Erickson@Sun.COM 		if (cwd->cwd_tag == tag) {
1197849STom.Erickson@Sun.COM 			return (cwd);
1207849STom.Erickson@Sun.COM 		}
1217849STom.Erickson@Sun.COM 	}
1227849STom.Erickson@Sun.COM 
1237849STom.Erickson@Sun.COM 	return (NULL);
1247849STom.Erickson@Sun.COM }
1257849STom.Erickson@Sun.COM 
1266712Stomee static void
combined_walk_append(combined_walk_data_t * cwd,combined_walk_t * cw)1276712Stomee combined_walk_append(combined_walk_data_t *cwd, combined_walk_t *cw)
1286712Stomee {
1296712Stomee 	if (cwd->cwd_final_walk == NULL) {
1306712Stomee 		cwd->cwd_current_walk = cwd->cwd_final_walk = cw;
1316712Stomee 	} else {
1326712Stomee 		cwd->cwd_final_walk->cw_next = cw;
1336712Stomee 		cwd->cwd_final_walk = cw;
1346712Stomee 	}
1356712Stomee }
1366712Stomee 
1376712Stomee static combined_walk_t *
combined_walk_remove_current(combined_walk_data_t * cwd)1386712Stomee combined_walk_remove_current(combined_walk_data_t *cwd)
1396712Stomee {
1406712Stomee 	combined_walk_t *cw = cwd->cwd_current_walk;
1416712Stomee 	if (cw == NULL) {
1426712Stomee 		return (NULL);
1436712Stomee 	}
1446712Stomee 	if (cw == cwd->cwd_final_walk) {
1456712Stomee 		cwd->cwd_final_walk = cw->cw_next;
1466712Stomee 	}
1476712Stomee 	cwd->cwd_current_walk = cw->cw_next;
1486712Stomee 	cw->cw_next = NULL;
1496712Stomee 	return (cw);
1506712Stomee }
1516712Stomee 
1526712Stomee void
combined_walk_add(mdb_walk_state_t * wsp,int (* walk_init)(mdb_walk_state_t *),int (* walk_step)(mdb_walk_state_t *),void (* walk_fini)(mdb_walk_state_t *))1536712Stomee combined_walk_add(mdb_walk_state_t *wsp,
1546712Stomee 	int (*walk_init)(mdb_walk_state_t *),
1556712Stomee 	int (*walk_step)(mdb_walk_state_t *),
1566712Stomee 	void (*walk_fini)(mdb_walk_state_t *))
1576712Stomee {
1586712Stomee 	combined_walk_data_t *cwd = wsp->walk_data;
1596712Stomee 	combined_walk_t *cw;
1606712Stomee 
1616712Stomee 	cw = mdb_alloc(sizeof (combined_walk_t), UM_SLEEP);
1626712Stomee 
1636712Stomee 	cw->cw_init = walk_init;
1646712Stomee 	cw->cw_step = walk_step;
1656712Stomee 	cw->cw_fini = walk_fini;
1666712Stomee 	cw->cw_next = NULL;
1676712Stomee 	cw->cw_data = NULL;
1686712Stomee 	cw->cw_initialized = B_FALSE;
1696712Stomee 
1706712Stomee 	combined_walk_append(cwd, cw);
1716712Stomee }
1726712Stomee 
1736712Stomee int
combined_walk_step(mdb_walk_state_t * wsp)1746712Stomee combined_walk_step(mdb_walk_state_t *wsp)
1756712Stomee {
1766712Stomee 	combined_walk_data_t *cwd = wsp->walk_data;
1776712Stomee 	combined_walk_t *cw = cwd->cwd_current_walk;
1786712Stomee 	int status;
1796712Stomee 
1806712Stomee 	if (cw == NULL) {
1816712Stomee 		return (WALK_DONE);
1826712Stomee 	}
1836712Stomee 
1846712Stomee 	if (cw->cw_initialized) {
1856712Stomee 		wsp->walk_data = cw->cw_data;
1866712Stomee 	} else {
1876712Stomee 		wsp->walk_addr = cwd->cwd_initial_walk_addr;
1886712Stomee 		status = cw->cw_init(wsp);
1896712Stomee 		cw->cw_data = wsp->walk_data;
190*10609SJonathan.Adams@Sun.COM 		if (status != WALK_NEXT)
191*10609SJonathan.Adams@Sun.COM 			goto done;
1926712Stomee 		cw->cw_initialized = B_TRUE;
1936712Stomee 	}
1946712Stomee 
1957849STom.Erickson@Sun.COM 	/* save cwd for fini() in case step() is interrupted */
1967849STom.Erickson@Sun.COM 	combined_walk_data_save(cwd, cw->cw_data);
1976712Stomee 	status = cw->cw_step(wsp);
1987849STom.Erickson@Sun.COM 	/* control may never reach here */
1997849STom.Erickson@Sun.COM 	combined_walk_data_drop(cwd);
2006712Stomee 
201*10609SJonathan.Adams@Sun.COM 	if (status == WALK_DONE)
202*10609SJonathan.Adams@Sun.COM 		goto done;
203*10609SJonathan.Adams@Sun.COM 	wsp->walk_data = cwd;
204*10609SJonathan.Adams@Sun.COM 	return (status);
205*10609SJonathan.Adams@Sun.COM 
206*10609SJonathan.Adams@Sun.COM done:
207*10609SJonathan.Adams@Sun.COM 	(void) combined_walk_remove_current(cwd);
208*10609SJonathan.Adams@Sun.COM 	if (cw->cw_initialized)
2096712Stomee 		cw->cw_fini(wsp);
210*10609SJonathan.Adams@Sun.COM 	mdb_free(cw, sizeof (combined_walk_t));
211*10609SJonathan.Adams@Sun.COM 	wsp->walk_data = cwd;
212*10609SJonathan.Adams@Sun.COM 	if (status == WALK_DONE)
2136712Stomee 		return (combined_walk_step(wsp));
2146712Stomee 	return (status);
2156712Stomee }
2166712Stomee 
2176712Stomee void
combined_walk_fini(mdb_walk_state_t * wsp)2186712Stomee combined_walk_fini(mdb_walk_state_t *wsp)
2196712Stomee {
2207849STom.Erickson@Sun.COM 	combined_walk_data_t *cwd;
2216712Stomee 	combined_walk_t *cw;
2226712Stomee 
2237849STom.Erickson@Sun.COM 	/*
2247849STom.Erickson@Sun.COM 	 * If walk_step() was interrupted, wsp->walk_data will be the
2257849STom.Erickson@Sun.COM 	 * sub-walker's data, not the combined walker's data, so first check to
2267849STom.Erickson@Sun.COM 	 * see if there is saved combined walk data tagged by the presumed
2277849STom.Erickson@Sun.COM 	 * sub-walker's walk data.
2287849STom.Erickson@Sun.COM 	 */
2297849STom.Erickson@Sun.COM 	cwd = combined_walk_data_find(wsp->walk_data);
2307849STom.Erickson@Sun.COM 	if (cwd == NULL) {
2317849STom.Erickson@Sun.COM 		/*
2327849STom.Erickson@Sun.COM 		 * walk_step() was not interrupted, so wsp->walk_data is
2337849STom.Erickson@Sun.COM 		 * actually the combined walk data.
2347849STom.Erickson@Sun.COM 		 */
2357849STom.Erickson@Sun.COM 		cwd = wsp->walk_data;
2367849STom.Erickson@Sun.COM 	} else {
2377849STom.Erickson@Sun.COM 		combined_walk_data_drop(cwd);
2387849STom.Erickson@Sun.COM 	}
2397849STom.Erickson@Sun.COM 
2406712Stomee 	while ((cw = combined_walk_remove_current(cwd)) != NULL) {
2416712Stomee 		if (cw->cw_initialized) {
2426712Stomee 			wsp->walk_data = cw->cw_data;
2436712Stomee 			cw->cw_fini(wsp);
2446712Stomee 		}
2456712Stomee 		mdb_free(cw, sizeof (combined_walk_t));
2466712Stomee 	}
2476712Stomee 
2486712Stomee 	mdb_free(cwd, sizeof (combined_walk_data_t));
2496712Stomee }
250