1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright (c) 2000 by Sun Microsystems, Inc.
24  * All rights reserved.
25  */
26 /*
27  * Copyright 2018 Joyent, Inc.
28  */
29 
30 #include <mdb/mdb_modapi.h>
31 #include <mdb/mdb_ctf.h>
32 #include "ctxop.h"
33 
34 struct ctxop_walk_state {
35 	uintptr_t	cws_head;
36 	uint_t		cws_next_offset;
37 };
38 
39 int
ctxop_walk_init(mdb_walk_state_t * wsp)40 ctxop_walk_init(mdb_walk_state_t *wsp)
41 {
42 	struct ctxop_walk_state *priv;
43 	int offset;
44 	uintptr_t addr;
45 
46 	if (wsp->walk_addr == 0) {
47 		mdb_warn("must specify thread for ctxop walk\n");
48 		return (WALK_ERR);
49 	}
50 
51 	offset = mdb_ctf_offsetof_by_name("kthread_t", "t_ctx");
52 	if (offset == -1)
53 		return (WALK_ERR);
54 
55 	if (mdb_vread(&addr, sizeof (addr),
56 	    wsp->walk_addr + offset) != sizeof (addr)) {
57 		mdb_warn("failed to read thread %p", wsp->walk_addr);
58 		return (WALK_ERR);
59 	}
60 
61 	/* No further work for threads with a NULL t_ctx */
62 	if (addr == 0) {
63 		wsp->walk_data = NULL;
64 		return (WALK_DONE);
65 	}
66 
67 	/* rely on CTF for the offset of the 'next' pointer */
68 	offset = mdb_ctf_offsetof_by_name("struct ctxop", "next");
69 	if (offset == -1)
70 		return (WALK_ERR);
71 
72 	priv = mdb_alloc(sizeof (*priv), UM_SLEEP);
73 	priv->cws_head = addr;
74 	priv->cws_next_offset = (uint_t)offset;
75 
76 	wsp->walk_data = priv;
77 	wsp->walk_addr = addr;
78 	return (WALK_NEXT);
79 }
80 
81 int
ctxop_walk_step(mdb_walk_state_t * wsp)82 ctxop_walk_step(mdb_walk_state_t *wsp)
83 {
84 	struct ctxop_walk_state *priv = wsp->walk_data;
85 	uintptr_t next;
86 	int status;
87 
88 	if (mdb_vread(&next, sizeof (next),
89 	    wsp->walk_addr + priv->cws_next_offset) == -1) {
90 		mdb_warn("failed to read ctxop`next at %p",
91 		    wsp->walk_addr + priv->cws_next_offset);
92 		return (WALK_DONE);
93 	}
94 
95 	status = wsp->walk_callback(wsp->walk_addr, NULL, wsp->walk_cbdata);
96 
97 	if (status == WALK_NEXT) {
98 		/*
99 		 * If a NULL terminator or a loop back to the head element is
100 		 * encountered, the walk is done.
101 		 */
102 		if (next == 0 || next == priv->cws_head) {
103 			status = WALK_DONE;
104 		}
105 	}
106 
107 	wsp->walk_addr = next;
108 	return (status);
109 }
110 
111 void
ctxop_walk_fini(mdb_walk_state_t * wsp)112 ctxop_walk_fini(mdb_walk_state_t *wsp)
113 {
114 	struct ctxop_walk_state *priv = wsp->walk_data;
115 
116 	if (priv != NULL) {
117 		mdb_free(priv, sizeof (*priv));
118 	}
119 }
120