1179237Sjb/*
2179237Sjb * CDDL HEADER START
3179237Sjb *
4179237Sjb * The contents of this file are subject to the terms of the
5179237Sjb * Common Development and Distribution License (the "License").
6179237Sjb * You may not use this file except in compliance with the License.
7179237Sjb *
8179237Sjb * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9179237Sjb * or http://www.opensolaris.org/os/licensing.
10179237Sjb * See the License for the specific language governing permissions
11179237Sjb * and limitations under the License.
12179237Sjb *
13179237Sjb * When distributing Covered Code, include this CDDL HEADER in each
14179237Sjb * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15179237Sjb * If applicable, add the following below this CDDL HEADER, with the
16179237Sjb * fields enclosed by brackets "[]" replaced with your own identifying
17179237Sjb * information: Portions Copyright [yyyy] [name of copyright owner]
18179237Sjb *
19179237Sjb * CDDL HEADER END
20179237Sjb *
21179237Sjb * Portions Copyright 2006-2008 John Birrell jb@freebsd.org
22179237Sjb *
23179237Sjb * $FreeBSD$
24179237Sjb *
25179237Sjb */
26179237Sjb
27179237Sjb#include <sys/cdefs.h>
28179237Sjb#include <sys/param.h>
29179237Sjb#include <sys/systm.h>
30179237Sjb#include <sys/conf.h>
31252325Smarkj#include <sys/ctype.h>
32179237Sjb#include <sys/kernel.h>
33179237Sjb#include <sys/malloc.h>
34179237Sjb#include <sys/module.h>
35179237Sjb
36179237Sjb#include <sys/dtrace.h>
37179237Sjb#include <sys/dtrace_bsd.h>
38179237Sjb
39179237Sjbstatic d_open_t	dtmalloc_open;
40179237Sjbstatic int	dtmalloc_unload(void);
41179237Sjbstatic void	dtmalloc_getargdesc(void *, dtrace_id_t, void *, dtrace_argdesc_t *);
42179237Sjbstatic void	dtmalloc_provide(void *, dtrace_probedesc_t *);
43179237Sjbstatic void	dtmalloc_destroy(void *, dtrace_id_t, void *);
44179237Sjbstatic void	dtmalloc_enable(void *, dtrace_id_t, void *);
45179237Sjbstatic void	dtmalloc_disable(void *, dtrace_id_t, void *);
46179237Sjbstatic void	dtmalloc_load(void *);
47179237Sjb
48179237Sjbstatic struct cdevsw dtmalloc_cdevsw = {
49179237Sjb	.d_version	= D_VERSION,
50179237Sjb	.d_open		= dtmalloc_open,
51179237Sjb	.d_name		= "dtmalloc",
52179237Sjb};
53179237Sjb
54179237Sjbstatic dtrace_pattr_t dtmalloc_attr = {
55179237Sjb{ DTRACE_STABILITY_STABLE, DTRACE_STABILITY_STABLE, DTRACE_CLASS_COMMON },
56179237Sjb{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
57179237Sjb{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
58179237Sjb{ DTRACE_STABILITY_STABLE, DTRACE_STABILITY_STABLE, DTRACE_CLASS_COMMON },
59179237Sjb{ DTRACE_STABILITY_STABLE, DTRACE_STABILITY_STABLE, DTRACE_CLASS_COMMON },
60179237Sjb};
61179237Sjb
62179237Sjbstatic dtrace_pops_t dtmalloc_pops = {
63179237Sjb	dtmalloc_provide,
64179237Sjb	NULL,
65179237Sjb	dtmalloc_enable,
66179237Sjb	dtmalloc_disable,
67179237Sjb	NULL,
68179237Sjb	NULL,
69179237Sjb	dtmalloc_getargdesc,
70179237Sjb	NULL,
71179237Sjb	NULL,
72179237Sjb	dtmalloc_destroy
73179237Sjb};
74179237Sjb
75179237Sjbstatic struct cdev		*dtmalloc_cdev;
76179237Sjbstatic dtrace_provider_id_t	dtmalloc_id;
77179237Sjb
78179237Sjbstatic void
79179237Sjbdtmalloc_getargdesc(void *arg, dtrace_id_t id, void *parg, dtrace_argdesc_t *desc)
80179237Sjb{
81179237Sjb	const char *p = NULL;
82179237Sjb
83179237Sjb	switch (desc->dtargd_ndx) {
84179237Sjb	case 0:
85179237Sjb		p = "struct malloc_type *";
86179237Sjb		break;
87179237Sjb	case 1:
88179237Sjb		p = "struct malloc_type_internal *";
89179237Sjb		break;
90179237Sjb	case 2:
91179237Sjb		p = "struct malloc_type_stats *";
92179237Sjb		break;
93179237Sjb	case 3:
94179237Sjb		p = "unsigned long";
95179237Sjb		break;
96179237Sjb	case 4:
97179237Sjb		p = "int";
98179237Sjb		break;
99179237Sjb	default:
100179237Sjb		desc->dtargd_ndx = DTRACE_ARGNONE;
101179237Sjb		break;
102179237Sjb	}
103179237Sjb
104179237Sjb	if (p != NULL)
105179237Sjb		strlcpy(desc->dtargd_native, p, sizeof(desc->dtargd_native));
106179237Sjb
107179237Sjb	return;
108179237Sjb}
109179237Sjb
110179237Sjbstatic void
111179237Sjbdtmalloc_type_cb(struct malloc_type *mtp, void *arg __unused)
112179237Sjb{
113179237Sjb	char name[DTRACE_FUNCNAMELEN];
114179237Sjb	struct malloc_type_internal *mtip = mtp->ks_handle;
115252325Smarkj	int i;
116179237Sjb
117252325Smarkj	/*
118252325Smarkj	 * malloc_type descriptions are allowed to contain whitespace, but
119252325Smarkj	 * DTrace probe identifiers are not, so replace the whitespace with
120252325Smarkj	 * underscores.
121252325Smarkj	 */
122179237Sjb	strlcpy(name, mtp->ks_shortdesc, sizeof(name));
123252325Smarkj	for (i = 0; name[i] != 0; i++)
124252325Smarkj		if (isspace(name[i]))
125252325Smarkj			name[i] = '_';
126179237Sjb
127179237Sjb	if (dtrace_probe_lookup(dtmalloc_id, NULL, name, "malloc") != 0)
128179237Sjb		return;
129179237Sjb
130179237Sjb	(void) dtrace_probe_create(dtmalloc_id, NULL, name, "malloc", 0,
131179237Sjb	    &mtip->mti_probes[DTMALLOC_PROBE_MALLOC]);
132179237Sjb	(void) dtrace_probe_create(dtmalloc_id, NULL, name, "free", 0,
133179237Sjb	    &mtip->mti_probes[DTMALLOC_PROBE_FREE]);
134179237Sjb}
135179237Sjb
136179237Sjbstatic void
137179237Sjbdtmalloc_provide(void *arg, dtrace_probedesc_t *desc)
138179237Sjb{
139179237Sjb	if (desc != NULL)
140179237Sjb		return;
141179237Sjb
142179237Sjb	malloc_type_list(dtmalloc_type_cb, desc);
143179237Sjb}
144179237Sjb
145179237Sjbstatic void
146179237Sjbdtmalloc_destroy(void *arg, dtrace_id_t id, void *parg)
147179237Sjb{
148179237Sjb}
149179237Sjb
150179237Sjbstatic void
151179237Sjbdtmalloc_enable(void *arg, dtrace_id_t id, void *parg)
152179237Sjb{
153179237Sjb	uint32_t *p = parg;
154179237Sjb	*p = id;
155179237Sjb}
156179237Sjb
157179237Sjbstatic void
158179237Sjbdtmalloc_disable(void *arg, dtrace_id_t id, void *parg)
159179237Sjb{
160179237Sjb	uint32_t *p = parg;
161179237Sjb	*p = 0;
162179237Sjb}
163179237Sjb
164179237Sjbstatic void
165179237Sjbdtmalloc_load(void *dummy)
166179237Sjb{
167179237Sjb	/* Create the /dev/dtrace/dtmalloc entry. */
168179237Sjb	dtmalloc_cdev = make_dev(&dtmalloc_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600,
169179237Sjb	    "dtrace/dtmalloc");
170179237Sjb
171179237Sjb	if (dtrace_register("dtmalloc", &dtmalloc_attr, DTRACE_PRIV_USER,
172179237Sjb	    NULL, &dtmalloc_pops, NULL, &dtmalloc_id) != 0)
173179237Sjb		return;
174179237Sjb
175179237Sjb	dtrace_malloc_probe = dtrace_probe;
176179237Sjb}
177179237Sjb
178179237Sjb
179179237Sjbstatic int
180179237Sjbdtmalloc_unload()
181179237Sjb{
182179237Sjb	int error = 0;
183179237Sjb
184179237Sjb	dtrace_malloc_probe = NULL;
185179237Sjb
186179237Sjb	if ((error = dtrace_unregister(dtmalloc_id)) != 0)
187179237Sjb		return (error);
188179237Sjb
189179237Sjb	destroy_dev(dtmalloc_cdev);
190179237Sjb
191179237Sjb	return (error);
192179237Sjb}
193179237Sjb
194179237Sjbstatic int
195179237Sjbdtmalloc_modevent(module_t mod __unused, int type, void *data __unused)
196179237Sjb{
197179237Sjb	int error = 0;
198179237Sjb
199179237Sjb	switch (type) {
200179237Sjb	case MOD_LOAD:
201179237Sjb		break;
202179237Sjb
203179237Sjb	case MOD_UNLOAD:
204179237Sjb		break;
205179237Sjb
206179237Sjb	case MOD_SHUTDOWN:
207179237Sjb		break;
208179237Sjb
209179237Sjb	default:
210179237Sjb		error = EOPNOTSUPP;
211179237Sjb		break;
212179237Sjb
213179237Sjb	}
214179237Sjb
215179237Sjb	return (error);
216179237Sjb}
217179237Sjb
218179237Sjbstatic int
219179237Sjbdtmalloc_open(struct cdev *dev __unused, int oflags __unused, int devtype __unused, struct thread *td __unused)
220179237Sjb{
221179237Sjb	return (0);
222179237Sjb}
223179237Sjb
224179237SjbSYSINIT(dtmalloc_load, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, dtmalloc_load, NULL);
225179237SjbSYSUNINIT(dtmalloc_unload, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, dtmalloc_unload, NULL);
226179237Sjb
227179237SjbDEV_MODULE(dtmalloc, dtmalloc_modevent, NULL);
228179237SjbMODULE_VERSION(dtmalloc, 1);
229179237SjbMODULE_DEPEND(dtmalloc, dtrace, 1, 1, 1);
230179237SjbMODULE_DEPEND(dtmalloc, opensolaris, 1, 1, 1);
231