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