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