1 /*	$NetBSD: dtmalloc.c,v 1.3 2018/05/28 21:05:03 chs Exp $	*/
2 
3 /*
4  * CDDL HEADER START
5  *
6  * The contents of this file are subject to the terms of the
7  * Common Development and Distribution License (the "License").
8  * You may not use this file except in compliance with the License.
9  *
10  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
11  * or http://www.opensolaris.org/os/licensing.
12  * See the License for the specific language governing permissions
13  * and limitations under the License.
14  *
15  * When distributing Covered Code, include this CDDL HEADER in each
16  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
17  * If applicable, add the following below this CDDL HEADER, with the
18  * fields enclosed by brackets "[]" replaced with your own identifying
19  * information: Portions Copyright [yyyy] [name of copyright owner]
20  *
21  * CDDL HEADER END
22  *
23  * Portions Copyright 2006-2008 John Birrell jb@freebsd.org
24  *
25  * $FreeBSD: head/sys/cddl/dev/dtmalloc/dtmalloc.c 252325 2013-06-28 03:14:40Z markj $
26  *
27  */
28 
29 #include <sys/cdefs.h>
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/conf.h>
33 #include <sys/ctype.h>
34 #include <sys/kernel.h>
35 #include <sys/malloc.h>
36 #include <sys/module.h>
37 
38 #include <sys/dtrace.h>
39 #include <sys/dtrace_bsd.h>
40 
41 static d_open_t	dtmalloc_open;
42 static int	dtmalloc_unload(void);
43 static void	dtmalloc_getargdesc(void *, dtrace_id_t, void *, dtrace_argdesc_t *);
44 static void	dtmalloc_provide(void *, dtrace_probedesc_t *);
45 static void	dtmalloc_destroy(void *, dtrace_id_t, void *);
46 static void	dtmalloc_enable(void *, dtrace_id_t, void *);
47 static void	dtmalloc_disable(void *, dtrace_id_t, void *);
48 static void	dtmalloc_load(void *);
49 
50 static struct cdevsw dtmalloc_cdevsw = {
51 	.d_version	= D_VERSION,
52 	.d_open		= dtmalloc_open,
53 	.d_name		= "dtmalloc",
54 };
55 
56 static dtrace_pattr_t dtmalloc_attr = {
57 { DTRACE_STABILITY_STABLE, DTRACE_STABILITY_STABLE, DTRACE_CLASS_COMMON },
58 { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
59 { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
60 { DTRACE_STABILITY_STABLE, DTRACE_STABILITY_STABLE, DTRACE_CLASS_COMMON },
61 { DTRACE_STABILITY_STABLE, DTRACE_STABILITY_STABLE, DTRACE_CLASS_COMMON },
62 };
63 
64 static dtrace_pops_t dtmalloc_pops = {
65 	dtmalloc_provide,
66 	NULL,
67 	dtmalloc_enable,
68 	dtmalloc_disable,
69 	NULL,
70 	NULL,
71 	dtmalloc_getargdesc,
72 	NULL,
73 	NULL,
74 	dtmalloc_destroy
75 };
76 
77 static struct cdev		*dtmalloc_cdev;
78 static dtrace_provider_id_t	dtmalloc_id;
79 
80 static void
dtmalloc_getargdesc(void * arg,dtrace_id_t id,void * parg,dtrace_argdesc_t * desc)81 dtmalloc_getargdesc(void *arg, dtrace_id_t id, void *parg, dtrace_argdesc_t *desc)
82 {
83 	const char *p = NULL;
84 
85 	switch (desc->dtargd_ndx) {
86 	case 0:
87 		p = "struct malloc_type *";
88 		break;
89 	case 1:
90 		p = "struct malloc_type_internal *";
91 		break;
92 	case 2:
93 		p = "struct malloc_type_stats *";
94 		break;
95 	case 3:
96 		p = "unsigned long";
97 		break;
98 	case 4:
99 		p = "int";
100 		break;
101 	default:
102 		desc->dtargd_ndx = DTRACE_ARGNONE;
103 		break;
104 	}
105 
106 	if (p != NULL)
107 		strlcpy(desc->dtargd_native, p, sizeof(desc->dtargd_native));
108 
109 	return;
110 }
111 
112 static void
dtmalloc_type_cb(struct malloc_type * mtp,void * arg __unused)113 dtmalloc_type_cb(struct malloc_type *mtp, void *arg __unused)
114 {
115 	char name[DTRACE_FUNCNAMELEN];
116 	struct malloc_type_internal *mtip = mtp->ks_handle;
117 	int i;
118 
119 	/*
120 	 * malloc_type descriptions are allowed to contain whitespace, but
121 	 * DTrace probe identifiers are not, so replace the whitespace with
122 	 * underscores.
123 	 */
124 	strlcpy(name, mtp->ks_shortdesc, sizeof(name));
125 	for (i = 0; name[i] != 0; i++)
126 		if (isspace(name[i]))
127 			name[i] = '_';
128 
129 	if (dtrace_probe_lookup(dtmalloc_id, NULL, name, "malloc") != 0)
130 		return;
131 
132 	(void) dtrace_probe_create(dtmalloc_id, NULL, name, "malloc", 0,
133 	    &mtip->mti_probes[DTMALLOC_PROBE_MALLOC]);
134 	(void) dtrace_probe_create(dtmalloc_id, NULL, name, "free", 0,
135 	    &mtip->mti_probes[DTMALLOC_PROBE_FREE]);
136 }
137 
138 static void
dtmalloc_provide(void * arg,dtrace_probedesc_t * desc)139 dtmalloc_provide(void *arg, dtrace_probedesc_t *desc)
140 {
141 	if (desc != NULL)
142 		return;
143 
144 	malloc_type_list(dtmalloc_type_cb, desc);
145 }
146 
147 static void
dtmalloc_destroy(void * arg,dtrace_id_t id,void * parg)148 dtmalloc_destroy(void *arg, dtrace_id_t id, void *parg)
149 {
150 }
151 
152 static void
dtmalloc_enable(void * arg,dtrace_id_t id,void * parg)153 dtmalloc_enable(void *arg, dtrace_id_t id, void *parg)
154 {
155 	uint32_t *p = parg;
156 	*p = id;
157 }
158 
159 static void
dtmalloc_disable(void * arg,dtrace_id_t id,void * parg)160 dtmalloc_disable(void *arg, dtrace_id_t id, void *parg)
161 {
162 	uint32_t *p = parg;
163 	*p = 0;
164 }
165 
166 static void
dtmalloc_load(void * dummy)167 dtmalloc_load(void *dummy)
168 {
169 	/* Create the /dev/dtrace/dtmalloc entry. */
170 	dtmalloc_cdev = make_dev(&dtmalloc_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600,
171 	    "dtrace/dtmalloc");
172 
173 	if (dtrace_register("dtmalloc", &dtmalloc_attr, DTRACE_PRIV_USER,
174 	    NULL, &dtmalloc_pops, NULL, &dtmalloc_id) != 0)
175 		return;
176 
177 	dtrace_malloc_probe = dtrace_probe;
178 }
179 
180 
181 static int
dtmalloc_unload()182 dtmalloc_unload()
183 {
184 	int error = 0;
185 
186 	dtrace_malloc_probe = NULL;
187 
188 	if ((error = dtrace_unregister(dtmalloc_id)) != 0)
189 		return (error);
190 
191 	destroy_dev(dtmalloc_cdev);
192 
193 	return (error);
194 }
195 
196 static int
dtmalloc_modevent(module_t mod __unused,int type,void * data __unused)197 dtmalloc_modevent(module_t mod __unused, int type, void *data __unused)
198 {
199 	int error = 0;
200 
201 	switch (type) {
202 	case MOD_LOAD:
203 		break;
204 
205 	case MOD_UNLOAD:
206 		break;
207 
208 	case MOD_SHUTDOWN:
209 		break;
210 
211 	default:
212 		error = EOPNOTSUPP;
213 		break;
214 
215 	}
216 
217 	return (error);
218 }
219 
220 static int
dtmalloc_open(struct cdev * dev __unused,int oflags __unused,int devtype __unused,struct thread * td __unused)221 dtmalloc_open(struct cdev *dev __unused, int oflags __unused, int devtype __unused, struct thread *td __unused)
222 {
223 	return (0);
224 }
225 
226 SYSINIT(dtmalloc_load, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, dtmalloc_load, NULL);
227 SYSUNINIT(dtmalloc_unload, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, dtmalloc_unload, NULL);
228 
229 DEV_MODULE(dtmalloc, dtmalloc_modevent, NULL);
230 MODULE_VERSION(dtmalloc, 1);
231 MODULE_DEPEND(dtmalloc, dtrace, 1, 1, 1);
232 MODULE_DEPEND(dtmalloc, opensolaris, 1, 1, 1);
233