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
36extern bool dtrace_malloc_enabled;
37static uint32_t dtrace_malloc_enabled_count;
38
39static int	dtmalloc_unload(void);
40static void	dtmalloc_getargdesc(void *, dtrace_id_t, void *, dtrace_argdesc_t *);
41static void	dtmalloc_provide(void *, dtrace_probedesc_t *);
42static void	dtmalloc_destroy(void *, dtrace_id_t, void *);
43static void	dtmalloc_enable(void *, dtrace_id_t, void *);
44static void	dtmalloc_disable(void *, dtrace_id_t, void *);
45static void	dtmalloc_load(void *);
46
47static 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
55static 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
68static dtrace_provider_id_t	dtmalloc_id;
69
70static void
71dtmalloc_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
102static void
103dtmalloc_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
128static void
129dtmalloc_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
137static void
138dtmalloc_destroy(void *arg, dtrace_id_t id, void *parg)
139{
140}
141
142static void
143dtmalloc_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
152static void
153dtmalloc_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
162static void
163dtmalloc_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
173static int
174dtmalloc_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
186static int
187dtmalloc_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
210SYSINIT(dtmalloc_load, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, dtmalloc_load, NULL);
211SYSUNINIT(dtmalloc_unload, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, dtmalloc_unload, NULL);
212
213DEV_MODULE(dtmalloc, dtmalloc_modevent, NULL);
214MODULE_VERSION(dtmalloc, 1);
215MODULE_DEPEND(dtmalloc, dtrace, 1, 1, 1);
216MODULE_DEPEND(dtmalloc, opensolaris, 1, 1, 1);
217