xref: /freebsd/sys/cddl/dev/dtmalloc/dtmalloc.c (revision 3494f7c0)
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  */
24 
25 #include <sys/param.h>
26 #include <sys/systm.h>
27 #include <sys/conf.h>
28 #include <sys/ctype.h>
29 #include <sys/kernel.h>
30 #include <sys/malloc.h>
31 #include <sys/module.h>
32 
33 #include <sys/dtrace.h>
34 #include <sys/dtrace_bsd.h>
35 
36 extern bool dtrace_malloc_enabled;
37 static uint32_t dtrace_malloc_enabled_count;
38 
39 static int	dtmalloc_unload(void);
40 static void	dtmalloc_getargdesc(void *, dtrace_id_t, void *, dtrace_argdesc_t *);
41 static void	dtmalloc_provide(void *, dtrace_probedesc_t *);
42 static void	dtmalloc_destroy(void *, dtrace_id_t, void *);
43 static void	dtmalloc_enable(void *, dtrace_id_t, void *);
44 static void	dtmalloc_disable(void *, dtrace_id_t, void *);
45 static void	dtmalloc_load(void *);
46 
47 static dtrace_pattr_t dtmalloc_attr = {
48 { DTRACE_STABILITY_STABLE, DTRACE_STABILITY_STABLE, DTRACE_CLASS_COMMON },
49 { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
50 { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
51 { DTRACE_STABILITY_STABLE, DTRACE_STABILITY_STABLE, DTRACE_CLASS_COMMON },
52 { DTRACE_STABILITY_STABLE, DTRACE_STABILITY_STABLE, DTRACE_CLASS_COMMON },
53 };
54 
55 static dtrace_pops_t dtmalloc_pops = {
56 	.dtps_provide =		dtmalloc_provide,
57 	.dtps_provide_module =	NULL,
58 	.dtps_enable =		dtmalloc_enable,
59 	.dtps_disable =		dtmalloc_disable,
60 	.dtps_suspend =		NULL,
61 	.dtps_resume =		NULL,
62 	.dtps_getargdesc =	dtmalloc_getargdesc,
63 	.dtps_getargval =	NULL,
64 	.dtps_usermode =	NULL,
65 	.dtps_destroy =		dtmalloc_destroy
66 };
67 
68 static dtrace_provider_id_t	dtmalloc_id;
69 
70 static void
71 dtmalloc_getargdesc(void *arg, dtrace_id_t id, void *parg, dtrace_argdesc_t *desc)
72 {
73 	const char *p = NULL;
74 
75 	switch (desc->dtargd_ndx) {
76 	case 0:
77 		p = "struct malloc_type *";
78 		break;
79 	case 1:
80 		p = "struct malloc_type_internal *";
81 		break;
82 	case 2:
83 		p = "struct malloc_type_stats *";
84 		break;
85 	case 3:
86 		p = "unsigned long";
87 		break;
88 	case 4:
89 		p = "int";
90 		break;
91 	default:
92 		desc->dtargd_ndx = DTRACE_ARGNONE;
93 		break;
94 	}
95 
96 	if (p != NULL)
97 		strlcpy(desc->dtargd_native, p, sizeof(desc->dtargd_native));
98 
99 	return;
100 }
101 
102 static void
103 dtmalloc_type_cb(struct malloc_type *mtp, void *arg __unused)
104 {
105 	char name[DTRACE_FUNCNAMELEN];
106 	struct malloc_type_internal *mtip = &mtp->ks_mti;
107 	int i;
108 
109 	/*
110 	 * malloc_type descriptions are allowed to contain whitespace, but
111 	 * DTrace probe identifiers are not, so replace the whitespace with
112 	 * underscores.
113 	 */
114 	strlcpy(name, mtp->ks_shortdesc, sizeof(name));
115 	for (i = 0; name[i] != 0; i++)
116 		if (isspace(name[i]))
117 			name[i] = '_';
118 
119 	if (dtrace_probe_lookup(dtmalloc_id, NULL, name, "malloc") != 0)
120 		return;
121 
122 	(void) dtrace_probe_create(dtmalloc_id, NULL, name, "malloc", 0,
123 	    &mtip->mti_probes[DTMALLOC_PROBE_MALLOC]);
124 	(void) dtrace_probe_create(dtmalloc_id, NULL, name, "free", 0,
125 	    &mtip->mti_probes[DTMALLOC_PROBE_FREE]);
126 }
127 
128 static void
129 dtmalloc_provide(void *arg, dtrace_probedesc_t *desc)
130 {
131 	if (desc != NULL)
132 		return;
133 
134 	malloc_type_list(dtmalloc_type_cb, desc);
135 }
136 
137 static void
138 dtmalloc_destroy(void *arg, dtrace_id_t id, void *parg)
139 {
140 }
141 
142 static void
143 dtmalloc_enable(void *arg, dtrace_id_t id, void *parg)
144 {
145 	uint32_t *p = parg;
146 	*p = id;
147 	dtrace_malloc_enabled_count++;
148 	if (dtrace_malloc_enabled_count == 1)
149 		dtrace_malloc_enabled = true;
150 }
151 
152 static void
153 dtmalloc_disable(void *arg, dtrace_id_t id, void *parg)
154 {
155 	uint32_t *p = parg;
156 	*p = 0;
157 	dtrace_malloc_enabled_count--;
158 	if (dtrace_malloc_enabled_count == 0)
159 		dtrace_malloc_enabled = false;
160 }
161 
162 static void
163 dtmalloc_load(void *dummy)
164 {
165 	if (dtrace_register("dtmalloc", &dtmalloc_attr, DTRACE_PRIV_USER,
166 	    NULL, &dtmalloc_pops, NULL, &dtmalloc_id) != 0)
167 		return;
168 
169 	dtrace_malloc_probe = dtrace_probe;
170 }
171 
172 
173 static int
174 dtmalloc_unload(void)
175 {
176 	int error = 0;
177 
178 	dtrace_malloc_probe = NULL;
179 
180 	if ((error = dtrace_unregister(dtmalloc_id)) != 0)
181 		return (error);
182 
183 	return (error);
184 }
185 
186 static int
187 dtmalloc_modevent(module_t mod __unused, int type, void *data __unused)
188 {
189 	int error = 0;
190 
191 	switch (type) {
192 	case MOD_LOAD:
193 		break;
194 
195 	case MOD_UNLOAD:
196 		break;
197 
198 	case MOD_SHUTDOWN:
199 		break;
200 
201 	default:
202 		error = EOPNOTSUPP;
203 		break;
204 
205 	}
206 
207 	return (error);
208 }
209 
210 SYSINIT(dtmalloc_load, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, dtmalloc_load, NULL);
211 SYSUNINIT(dtmalloc_unload, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, dtmalloc_unload, NULL);
212 
213 DEV_MODULE(dtmalloc, dtmalloc_modevent, NULL);
214 MODULE_VERSION(dtmalloc, 1);
215 MODULE_DEPEND(dtmalloc, dtrace, 1, 1, 1);
216 MODULE_DEPEND(dtmalloc, opensolaris, 1, 1, 1);
217