/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (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 */ /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" #include <stdlib.h> #include <strings.h> #include <errno.h> #include <unistd.h> #include <assert.h> #include <dt_impl.h> #include <dt_printf.h> static int dt_epid_add(dtrace_hdl_t *dtp, dtrace_epid_t id) { dtrace_id_t max; int rval, i, maxformat; dtrace_eprobedesc_t *enabled, *nenabled; dtrace_probedesc_t *probe; while (id >= (max = dtp->dt_maxprobe) || dtp->dt_pdesc == NULL) { dtrace_id_t new_max = max ? (max << 1) : 1; size_t nsize = new_max * sizeof (void *); dtrace_probedesc_t **new_pdesc; dtrace_eprobedesc_t **new_edesc; if ((new_pdesc = malloc(nsize)) == NULL || (new_edesc = malloc(nsize)) == NULL) { free(new_pdesc); return (dt_set_errno(dtp, EDT_NOMEM)); } bzero(new_pdesc, nsize); bzero(new_edesc, nsize); if (dtp->dt_pdesc != NULL) { size_t osize = max * sizeof (void *); bcopy(dtp->dt_pdesc, new_pdesc, osize); free(dtp->dt_pdesc); bcopy(dtp->dt_edesc, new_edesc, osize); free(dtp->dt_edesc); } dtp->dt_pdesc = new_pdesc; dtp->dt_edesc = new_edesc; dtp->dt_maxprobe = new_max; } if (dtp->dt_pdesc[id] != NULL) return (0); if ((enabled = malloc(sizeof (dtrace_eprobedesc_t))) == NULL) return (dt_set_errno(dtp, EDT_NOMEM)); bzero(enabled, sizeof (dtrace_eprobedesc_t)); enabled->dtepd_epid = id; enabled->dtepd_nrecs = 1; if (dt_ioctl(dtp, DTRACEIOC_EPROBE, enabled) == -1) { rval = dt_set_errno(dtp, errno); free(enabled); return (rval); } if (DTRACE_SIZEOF_EPROBEDESC(enabled) != sizeof (*enabled)) { /* * There must be more than one action. Allocate the * appropriate amount of space and try again. */ if ((nenabled = malloc(DTRACE_SIZEOF_EPROBEDESC(enabled))) != NULL) bcopy(enabled, nenabled, sizeof (*enabled)); free(enabled); if ((enabled = nenabled) == NULL) return (dt_set_errno(dtp, EDT_NOMEM)); rval = dt_ioctl(dtp, DTRACEIOC_EPROBE, enabled); if (rval == -1) { rval = dt_set_errno(dtp, errno); free(enabled); return (rval); } } if ((probe = malloc(sizeof (dtrace_probedesc_t))) == NULL) { free(enabled); return (dt_set_errno(dtp, EDT_NOMEM)); } probe->dtpd_id = enabled->dtepd_probeid; if (dt_ioctl(dtp, DTRACEIOC_PROBES, probe) == -1) { rval = dt_set_errno(dtp, errno); goto err; } for (i = 0; i < enabled->dtepd_nrecs; i++) { dtrace_fmtdesc_t fmt; dtrace_recdesc_t *rec = &enabled->dtepd_rec[i]; if (!DTRACEACT_ISPRINTFLIKE(rec->dtrd_action)) continue; if (rec->dtrd_format == 0) continue; if (rec->dtrd_format <= dtp->dt_maxformat && dtp->dt_formats[rec->dtrd_format - 1] != NULL) continue; bzero(&fmt, sizeof (fmt)); fmt.dtfd_format = rec->dtrd_format; fmt.dtfd_string = NULL; fmt.dtfd_length = 0; if (dt_ioctl(dtp, DTRACEIOC_FORMAT, &fmt) == -1) { rval = dt_set_errno(dtp, errno); goto err; } if ((fmt.dtfd_string = malloc(fmt.dtfd_length)) == NULL) { rval = dt_set_errno(dtp, EDT_NOMEM); goto err; } if (dt_ioctl(dtp, DTRACEIOC_FORMAT, &fmt) == -1) { rval = dt_set_errno(dtp, errno); free(fmt.dtfd_string); goto err; } while (rec->dtrd_format > (maxformat = dtp->dt_maxformat)) { int new_max = maxformat ? (maxformat << 1) : 1; size_t nsize = new_max * sizeof (void *); size_t osize = maxformat * sizeof (void *); void **new_formats = malloc(nsize); if (new_formats == NULL) { rval = dt_set_errno(dtp, EDT_NOMEM); free(fmt.dtfd_string); goto err; } bzero(new_formats, nsize); bcopy(dtp->dt_formats, new_formats, osize); free(dtp->dt_formats); dtp->dt_formats = new_formats; dtp->dt_maxformat = new_max; } dtp->dt_formats[rec->dtrd_format - 1] = rec->dtrd_action == DTRACEACT_PRINTA ? dtrace_printa_create(dtp, fmt.dtfd_string) : dtrace_printf_create(dtp, fmt.dtfd_string); free(fmt.dtfd_string); if (dtp->dt_formats[rec->dtrd_format - 1] == NULL) { rval = -1; /* dt_errno is set for us */ goto err; } } dtp->dt_pdesc[id] = probe; dtp->dt_edesc[id] = enabled; return (0); err: /* * If we failed, free our allocated probes. Note that if we failed * while allocating formats, we aren't going to free formats that * we have already allocated. This is okay; these formats are * hanging off of dt_formats and will therefore not be leaked. */ free(enabled); free(probe); return (rval); } int dt_epid_lookup(dtrace_hdl_t *dtp, dtrace_epid_t epid, dtrace_eprobedesc_t **epdp, dtrace_probedesc_t **pdp) { int rval; if (epid >= dtp->dt_maxprobe || dtp->dt_pdesc[epid] == NULL) { if ((rval = dt_epid_add(dtp, epid)) != 0) return (rval); } assert(epid < dtp->dt_maxprobe); assert(dtp->dt_edesc[epid] != NULL); assert(dtp->dt_pdesc[epid] != NULL); *epdp = dtp->dt_edesc[epid]; *pdp = dtp->dt_pdesc[epid]; return (0); } void dt_epid_destroy(dtrace_hdl_t *dtp) { size_t i; assert((dtp->dt_pdesc != NULL && dtp->dt_edesc != NULL && dtp->dt_maxprobe > 0) || (dtp->dt_pdesc == NULL && dtp->dt_edesc == NULL && dtp->dt_maxprobe == 0)); if (dtp->dt_pdesc == NULL) return; for (i = 0; i < dtp->dt_maxprobe; i++) { if (dtp->dt_edesc[i] == NULL) { assert(dtp->dt_pdesc[i] == NULL); continue; } assert(dtp->dt_pdesc[i] != NULL); free(dtp->dt_edesc[i]); free(dtp->dt_pdesc[i]); } free(dtp->dt_pdesc); dtp->dt_pdesc = NULL; free(dtp->dt_edesc); dtp->dt_edesc = NULL; dtp->dt_maxprobe = 0; } void * dt_format_lookup(dtrace_hdl_t *dtp, int format) { if (format == 0 || format > dtp->dt_maxformat) return (NULL); if (dtp->dt_formats == NULL) return (NULL); return (dtp->dt_formats[format - 1]); } void dt_format_destroy(dtrace_hdl_t *dtp) { int i; for (i = 0; i < dtp->dt_maxformat; i++) { if (dtp->dt_formats[i] != NULL) dt_printf_destroy(dtp->dt_formats[i]); } free(dtp->dt_formats); dtp->dt_formats = NULL; } static int dt_aggid_add(dtrace_hdl_t *dtp, dtrace_aggid_t id) { dtrace_id_t max; dtrace_epid_t epid; int rval; while (id >= (max = dtp->dt_maxagg) || dtp->dt_aggdesc == NULL) { dtrace_id_t new_max = max ? (max << 1) : 1; size_t nsize = new_max * sizeof (void *); dtrace_aggdesc_t **new_aggdesc; if ((new_aggdesc = malloc(nsize)) == NULL) return (dt_set_errno(dtp, EDT_NOMEM)); bzero(new_aggdesc, nsize); if (dtp->dt_aggdesc != NULL) { bcopy(dtp->dt_aggdesc, new_aggdesc, max * sizeof (void *)); free(dtp->dt_aggdesc); } dtp->dt_aggdesc = new_aggdesc; dtp->dt_maxagg = new_max; } if (dtp->dt_aggdesc[id] == NULL) { dtrace_aggdesc_t *agg, *nagg; if ((agg = malloc(sizeof (dtrace_aggdesc_t))) == NULL) return (dt_set_errno(dtp, EDT_NOMEM)); bzero(agg, sizeof (dtrace_aggdesc_t)); agg->dtagd_id = id; agg->dtagd_nrecs = 1; if (dt_ioctl(dtp, DTRACEIOC_AGGDESC, agg) == -1) { rval = dt_set_errno(dtp, errno); free(agg); return (rval); } if (DTRACE_SIZEOF_AGGDESC(agg) != sizeof (*agg)) { /* * There must be more than one action. Allocate the * appropriate amount of space and try again. */ if ((nagg = malloc(DTRACE_SIZEOF_AGGDESC(agg))) != NULL) bcopy(agg, nagg, sizeof (*agg)); free(agg); if ((agg = nagg) == NULL) return (dt_set_errno(dtp, EDT_NOMEM)); rval = dt_ioctl(dtp, DTRACEIOC_AGGDESC, agg); if (rval == -1) { rval = dt_set_errno(dtp, errno); free(agg); return (rval); } } /* * If we have a uarg, it's a pointer to the compiler-generated * statement; we'll use this value to get the name and * compiler-generated variable ID for the aggregation. If * we're grabbing an anonymous enabling, this pointer value * is obviously meaningless -- and in this case, we can't * provide the compiler-generated aggregation information. */ if (dtp->dt_options[DTRACEOPT_GRABANON] == DTRACEOPT_UNSET) { dtrace_stmtdesc_t *sdp; dt_ident_t *aid; sdp = (dtrace_stmtdesc_t *)(uintptr_t) agg->dtagd_rec[0].dtrd_uarg; aid = sdp->dtsd_aggdata; agg->dtagd_name = aid->di_name; agg->dtagd_varid = aid->di_id; } else { agg->dtagd_varid = DTRACE_AGGVARIDNONE; } if ((epid = agg->dtagd_epid) >= dtp->dt_maxprobe || dtp->dt_pdesc[epid] == NULL) { if ((rval = dt_epid_add(dtp, epid)) != 0) { free(agg); return (rval); } } dtp->dt_aggdesc[id] = agg; } return (0); } int dt_aggid_lookup(dtrace_hdl_t *dtp, dtrace_aggid_t aggid, dtrace_aggdesc_t **adp) { int rval; if (aggid >= dtp->dt_maxagg || dtp->dt_aggdesc[aggid] == NULL) { if ((rval = dt_aggid_add(dtp, aggid)) != 0) return (rval); } assert(aggid < dtp->dt_maxagg); assert(dtp->dt_aggdesc[aggid] != NULL); *adp = dtp->dt_aggdesc[aggid]; return (0); } void dt_aggid_destroy(dtrace_hdl_t *dtp) { size_t i; assert((dtp->dt_aggdesc != NULL && dtp->dt_maxagg != 0) || (dtp->dt_aggdesc == NULL && dtp->dt_maxagg == 0)); if (dtp->dt_aggdesc == NULL) return; for (i = 0; i < dtp->dt_maxagg; i++) { if (dtp->dt_aggdesc[i] != NULL) free(dtp->dt_aggdesc[i]); } free(dtp->dt_aggdesc); dtp->dt_aggdesc = NULL; dtp->dt_maxagg = 0; }