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