/* $NetBSD: dtmalloc.c,v 1.3 2018/05/28 21:05:03 chs Exp $ */
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
* Portions Copyright 2006-2008 John Birrell jb@freebsd.org
*
* $FreeBSD: head/sys/cddl/dev/dtmalloc/dtmalloc.c 252325 2013-06-28 03:14:40Z markj $
*
*/
#include <sys/cdefs.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/ctype.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/module.h>
#include <sys/dtrace.h>
#include <sys/dtrace_bsd.h>
static d_open_t dtmalloc_open;
static int dtmalloc_unload(void);
static void dtmalloc_getargdesc(void *, dtrace_id_t, void *, dtrace_argdesc_t *);
static void dtmalloc_provide(void *, dtrace_probedesc_t *);
static void dtmalloc_destroy(void *, dtrace_id_t, void *);
static void dtmalloc_enable(void *, dtrace_id_t, void *);
static void dtmalloc_disable(void *, dtrace_id_t, void *);
static void dtmalloc_load(void *);
static struct cdevsw dtmalloc_cdevsw = {
.d_version = D_VERSION,
.d_open = dtmalloc_open,
.d_name = "dtmalloc",
};
static dtrace_pattr_t dtmalloc_attr = {
{ DTRACE_STABILITY_STABLE, DTRACE_STABILITY_STABLE, DTRACE_CLASS_COMMON },
{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
{ DTRACE_STABILITY_STABLE, DTRACE_STABILITY_STABLE, DTRACE_CLASS_COMMON },
{ DTRACE_STABILITY_STABLE, DTRACE_STABILITY_STABLE, DTRACE_CLASS_COMMON },
};
static dtrace_pops_t dtmalloc_pops = {
dtmalloc_provide,
NULL,
dtmalloc_enable,
dtmalloc_disable,
NULL,
NULL,
dtmalloc_getargdesc,
NULL,
NULL,
dtmalloc_destroy
};
static struct cdev *dtmalloc_cdev;
static dtrace_provider_id_t dtmalloc_id;
static void
dtmalloc_getargdesc(void *arg, dtrace_id_t id, void *parg, dtrace_argdesc_t *desc)
{
const char *p = NULL;
switch (desc->dtargd_ndx) {
case 0:
p = "struct malloc_type *";
break;
case 1:
p = "struct malloc_type_internal *";
break;
case 2:
p = "struct malloc_type_stats *";
break;
case 3:
p = "unsigned long";
break;
case 4:
p = "int";
break;
default:
desc->dtargd_ndx = DTRACE_ARGNONE;
break;
}
if (p != NULL)
strlcpy(desc->dtargd_native, p, sizeof(desc->dtargd_native));
return;
}
static void
dtmalloc_type_cb(struct malloc_type *mtp, void *arg __unused)
{
char name[DTRACE_FUNCNAMELEN];
struct malloc_type_internal *mtip = mtp->ks_handle;
int i;
/*
* malloc_type descriptions are allowed to contain whitespace, but
* DTrace probe identifiers are not, so replace the whitespace with
* underscores.
*/
strlcpy(name, mtp->ks_shortdesc, sizeof(name));
for (i = 0; name[i] != 0; i++)
if (isspace(name[i]))
name[i] = '_';
if (dtrace_probe_lookup(dtmalloc_id, NULL, name, "malloc") != 0)
return;
(void) dtrace_probe_create(dtmalloc_id, NULL, name, "malloc", 0,
&mtip->mti_probes[DTMALLOC_PROBE_MALLOC]);
(void) dtrace_probe_create(dtmalloc_id, NULL, name, "free", 0,
&mtip->mti_probes[DTMALLOC_PROBE_FREE]);
}
static void
dtmalloc_provide(void *arg, dtrace_probedesc_t *desc)
{
if (desc != NULL)
return;
malloc_type_list(dtmalloc_type_cb, desc);
}
static void
dtmalloc_destroy(void *arg, dtrace_id_t id, void *parg)
{
}
static void
dtmalloc_enable(void *arg, dtrace_id_t id, void *parg)
{
uint32_t *p = parg;
*p = id;
}
static void
dtmalloc_disable(void *arg, dtrace_id_t id, void *parg)
{
uint32_t *p = parg;
*p = 0;
}
static void
dtmalloc_load(void *dummy)
{
/* Create the /dev/dtrace/dtmalloc entry. */
dtmalloc_cdev = make_dev(&dtmalloc_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600,
"dtrace/dtmalloc");
if (dtrace_register("dtmalloc", &dtmalloc_attr, DTRACE_PRIV_USER,
NULL, &dtmalloc_pops, NULL, &dtmalloc_id) != 0)
return;
dtrace_malloc_probe = dtrace_probe;
}
static int
dtmalloc_unload()
{
int error = 0;
dtrace_malloc_probe = NULL;
if ((error = dtrace_unregister(dtmalloc_id)) != 0)
return (error);
destroy_dev(dtmalloc_cdev);
return (error);
}
static int
dtmalloc_modevent(module_t mod __unused, int type, void *data __unused)
{
int error = 0;
switch (type) {
case MOD_LOAD:
break;
case MOD_UNLOAD:
break;
case MOD_SHUTDOWN:
break;
default:
error = EOPNOTSUPP;
break;
}
return (error);
}
static int
dtmalloc_open(struct cdev *dev __unused, int oflags __unused, int devtype __unused, struct thread *td __unused)
{
return (0);
}
SYSINIT(dtmalloc_load, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, dtmalloc_load, NULL);
SYSUNINIT(dtmalloc_unload, SI_SUB_DTRACE_PROVIDER, SI_ORDER_ANY, dtmalloc_unload, NULL);
DEV_MODULE(dtmalloc, dtmalloc_modevent, NULL);
MODULE_VERSION(dtmalloc, 1);
MODULE_DEPEND(dtmalloc, dtrace, 1, 1, 1);
MODULE_DEPEND(dtmalloc, opensolaris, 1, 1, 1);