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
39static d_open_t	dtmalloc_open;
40static int	dtmalloc_unload(void);
41static void	dtmalloc_getargdesc(void *, dtrace_id_t, void *, dtrace_argdesc_t *);
42static void	dtmalloc_provide(void *, dtrace_probedesc_t *);
43static void	dtmalloc_destroy(void *, dtrace_id_t, void *);
44static void	dtmalloc_enable(void *, dtrace_id_t, void *);
45static void	dtmalloc_disable(void *, dtrace_id_t, void *);
46static void	dtmalloc_load(void *);
47
48static struct cdevsw dtmalloc_cdevsw = {
49	.d_version	= D_VERSION,
50	.d_open		= dtmalloc_open,
51	.d_name		= "dtmalloc",
52};
53
54static dtrace_pattr_t dtmalloc_attr = {
55{ DTRACE_STABILITY_STABLE, DTRACE_STABILITY_STABLE, DTRACE_CLASS_COMMON },
56{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
57{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
58{ DTRACE_STABILITY_STABLE, DTRACE_STABILITY_STABLE, DTRACE_CLASS_COMMON },
59{ DTRACE_STABILITY_STABLE, DTRACE_STABILITY_STABLE, DTRACE_CLASS_COMMON },
60};
61
62static dtrace_pops_t dtmalloc_pops = {
63	dtmalloc_provide,
64	NULL,
65	dtmalloc_enable,
66	dtmalloc_disable,
67	NULL,
68	NULL,
69	dtmalloc_getargdesc,
70	NULL,
71	NULL,
72	dtmalloc_destroy
73};
74
75static struct cdev		*dtmalloc_cdev;
76static dtrace_provider_id_t	dtmalloc_id;
77
78static void
79dtmalloc_getargdesc(void *arg, dtrace_id_t id, void *parg, dtrace_argdesc_t *desc)
80{
81	const char *p = NULL;
82
83	switch (desc->dtargd_ndx) {
84	case 0:
85		p = "struct malloc_type *";
86		break;
87	case 1:
88		p = "struct malloc_type_internal *";
89		break;
90	case 2:
91		p = "struct malloc_type_stats *";
92		break;
93	case 3:
94		p = "unsigned long";
95		break;
96	case 4:
97		p = "int";
98		break;
99	default:
100		desc->dtargd_ndx = DTRACE_ARGNONE;
101		break;
102	}
103
104	if (p != NULL)
105		strlcpy(desc->dtargd_native, p, sizeof(desc->dtargd_native));
106
107	return;
108}
109
110static void
111dtmalloc_type_cb(struct malloc_type *mtp, void *arg __unused)
112{
113	char name[DTRACE_FUNCNAMELEN];
114	struct malloc_type_internal *mtip = mtp->ks_handle;
115	int i;
116
117	/*
118	 * malloc_type descriptions are allowed to contain whitespace, but
119	 * DTrace probe identifiers are not, so replace the whitespace with
120	 * underscores.
121	 */
122	strlcpy(name, mtp->ks_shortdesc, sizeof(name));
123	for (i = 0; name[i] != 0; i++)
124		if (isspace(name[i]))
125			name[i] = '_';
126
127	if (dtrace_probe_lookup(dtmalloc_id, NULL, name, "malloc") != 0)
128		return;
129
130	(void) dtrace_probe_create(dtmalloc_id, NULL, name, "malloc", 0,
131	    &mtip->mti_probes[DTMALLOC_PROBE_MALLOC]);
132	(void) dtrace_probe_create(dtmalloc_id, NULL, name, "free", 0,
133	    &mtip->mti_probes[DTMALLOC_PROBE_FREE]);
134}
135
136static void
137dtmalloc_provide(void *arg, dtrace_probedesc_t *desc)
138{
139	if (desc != NULL)
140		return;
141
142	malloc_type_list(dtmalloc_type_cb, desc);
143}
144
145static void
146dtmalloc_destroy(void *arg, dtrace_id_t id, void *parg)
147{
148}
149
150static void
151dtmalloc_enable(void *arg, dtrace_id_t id, void *parg)
152{
153	uint32_t *p = parg;
154	*p = id;
155}
156
157static void
158dtmalloc_disable(void *arg, dtrace_id_t id, void *parg)
159{
160	uint32_t *p = parg;
161	*p = 0;
162}
163
164static void
165dtmalloc_load(void *dummy)
166{
167	/* Create the /dev/dtrace/dtmalloc entry. */
168	dtmalloc_cdev = make_dev(&dtmalloc_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600,
169	    "dtrace/dtmalloc");
170
171	if (dtrace_register("dtmalloc", &dtmalloc_attr, DTRACE_PRIV_USER,
172	    NULL, &dtmalloc_pops, NULL, &dtmalloc_id) != 0)
173		return;
174
175	dtrace_malloc_probe = dtrace_probe;
176}
177
178
179static int
180dtmalloc_unload()
181{
182	int error = 0;
183
184	dtrace_malloc_probe = NULL;
185
186	if ((error = dtrace_unregister(dtmalloc_id)) != 0)
187		return (error);
188
189	destroy_dev(dtmalloc_cdev);
190
191	return (error);
192}
193
194static int
195dtmalloc_modevent(module_t mod __unused, int type, void *data __unused)
196{
197	int error = 0;
198
199	switch (type) {
200	case MOD_LOAD:
201		break;
202
203	case MOD_UNLOAD:
204		break;
205
206	case MOD_SHUTDOWN:
207		break;
208
209	default:
210		error = EOPNOTSUPP;
211		break;
212
213	}
214
215	return (error);
216}
217
218static int
219dtmalloc_open(struct cdev *dev __unused, int oflags __unused, int devtype __unused, struct thread *td __unused)
220{
221	return (0);
222}
223
224SYSINIT(dtmalloc_load, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, dtmalloc_load, NULL);
225SYSUNINIT(dtmalloc_unload, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, dtmalloc_unload, NULL);
226
227DEV_MODULE(dtmalloc, dtmalloc_modevent, NULL);
228MODULE_VERSION(dtmalloc, 1);
229MODULE_DEPEND(dtmalloc, dtrace, 1, 1, 1);
230MODULE_DEPEND(dtmalloc, opensolaris, 1, 1, 1);
231