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, Version 1.0 only
   6  * (the "License").  You may not use this file except in compliance
   7  * with the License.
   8  *
   9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  10  * or http://www.opensolaris.org/os/licensing.
  11  * See the License for the specific language governing permissions
  12  * and limitations under the License.
  13  *
  14  * When distributing Covered Code, include this CDDL HEADER in each
  15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  16  * If applicable, add the following below this CDDL HEADER, with the
  17  * fields enclosed by brackets "[]" replaced with your own identifying
  18  * information: Portions Copyright [yyyy] [name of copyright owner]
  19  *
  20  * CDDL HEADER END
  21  */
  22 
  23 /*
  24  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
  25  * Use is subject to license terms.
  26  */
  27 
  28 /*
  29  * Copyright (c) 2011, Joyent, Inc. All rights reserved.
  30  */
  31 
  32 #include <dt_impl.h>
  33 #include <stddef.h>
  34 #include <errno.h>
  35 #include <assert.h>
  36 #include <time.h>
  37 
  38 static const struct {
  39         int dtslt_option;
  40         size_t dtslt_offs;
  41 } _dtrace_sleeptab[] = {
  42         { DTRACEOPT_STATUSRATE, offsetof(dtrace_hdl_t, dt_laststatus) },
  43         { DTRACEOPT_AGGRATE, offsetof(dtrace_hdl_t, dt_lastagg) },
  44         { DTRACEOPT_SWITCHRATE, offsetof(dtrace_hdl_t, dt_lastswitch) },
  45         { DTRACEOPT_MAX, 0 }
  46 };
  47 
  48 void
  49 dtrace_sleep(dtrace_hdl_t *dtp)
  50 {
  51         dt_proc_hash_t *dph = dtp->dt_procs;
  52         dtrace_optval_t policy = dtp->dt_options[DTRACEOPT_BUFPOLICY];
  53         dt_proc_notify_t *dprn;
  54 
  55         hrtime_t earliest = INT64_MAX;
  56         struct timespec tv;
  57         hrtime_t now;
  58         int i;
  59 
  60         for (i = 0; _dtrace_sleeptab[i].dtslt_option < DTRACEOPT_MAX; i++) {
  61                 uintptr_t a = (uintptr_t)dtp + _dtrace_sleeptab[i].dtslt_offs;
  62                 int opt = _dtrace_sleeptab[i].dtslt_option;
  63                 dtrace_optval_t interval = dtp->dt_options[opt];
  64 
  65                 /*
  66                  * If the buffering policy is set to anything other than
  67                  * "switch", we ignore the aggrate and switchrate -- they're
  68                  * meaningless.
  69                  */
  70                 if (policy != DTRACEOPT_BUFPOLICY_SWITCH &&
  71                     _dtrace_sleeptab[i].dtslt_option != DTRACEOPT_STATUSRATE)
  72                         continue;
  73 
  74                 if (*((hrtime_t *)a) + interval < earliest)
  75                         earliest = *((hrtime_t *)a) + interval;
  76         }
  77 
  78         (void) pthread_mutex_lock(&dph->dph_lock);
  79 
  80         now = gethrtime();
  81 
  82         if (earliest < now) {
  83                 (void) pthread_mutex_unlock(&dph->dph_lock);
  84                 return; /* sleep duration has already past */
  85         }
  86 
  87         tv.tv_sec = (earliest - now) / NANOSEC;
  88         tv.tv_nsec = (earliest - now) % NANOSEC;
  89 
  90         /*
  91          * Wait for either 'tv' nanoseconds to pass or to receive notification
  92          * that a process is in an interesting state.  Regardless of why we
  93          * awaken, iterate over any pending notifications and process them.
  94          */
  95         (void) pthread_cond_reltimedwait_np(&dph->dph_cv, &dph->dph_lock, &tv);
  96 
  97         while ((dprn = dph->dph_notify) != NULL) {
  98                 if (dtp->dt_prochdlr != NULL) {
  99                         char *err = dprn->dprn_errmsg;
 100                         if (*err == '\0')
 101                                 err = NULL;
 102 
 103                         dtp->dt_prochdlr(dprn->dprn_dpr->dpr_proc, err,
 104                             dtp->dt_procarg);
 105                 }
 106 
 107                 dph->dph_notify = dprn->dprn_next;
 108                 dt_free(dtp, dprn);
 109         }
 110 
 111         (void) pthread_mutex_unlock(&dph->dph_lock);
 112 }
 113 
 114 int
 115 dtrace_status(dtrace_hdl_t *dtp)
 116 {
 117         int gen = dtp->dt_statusgen;
 118         dtrace_optval_t interval = dtp->dt_options[DTRACEOPT_STATUSRATE];
 119         hrtime_t now = gethrtime();
 120 
 121         if (!dtp->dt_active)
 122                 return (DTRACE_STATUS_NONE);
 123 
 124         if (dtp->dt_stopped)
 125                 return (DTRACE_STATUS_STOPPED);
 126 
 127         if (dtp->dt_laststatus != 0) {
 128                 if (now - dtp->dt_laststatus < interval)
 129                         return (DTRACE_STATUS_NONE);
 130 
 131                 dtp->dt_laststatus += interval;
 132         } else {
 133                 dtp->dt_laststatus = now;
 134         }
 135 
 136         if (dt_ioctl(dtp, DTRACEIOC_STATUS, &dtp->dt_status[gen]) == -1)
 137                 return (dt_set_errno(dtp, errno));
 138 
 139         dtp->dt_statusgen ^= 1;
 140 
 141         if (dt_handle_status(dtp, &dtp->dt_status[dtp->dt_statusgen],
 142             &dtp->dt_status[gen]) == -1)
 143                 return (-1);
 144 
 145         if (dtp->dt_status[gen].dtst_exiting) {
 146                 if (!dtp->dt_stopped)
 147                         (void) dtrace_stop(dtp);
 148 
 149                 return (DTRACE_STATUS_EXITED);
 150         }
 151 
 152         if (dtp->dt_status[gen].dtst_filled == 0)
 153                 return (DTRACE_STATUS_OKAY);
 154 
 155         if (dtp->dt_options[DTRACEOPT_BUFPOLICY] != DTRACEOPT_BUFPOLICY_FILL)
 156                 return (DTRACE_STATUS_OKAY);
 157 
 158         if (!dtp->dt_stopped) {
 159                 if (dtrace_stop(dtp) == -1)
 160                         return (-1);
 161         }
 162 
 163         return (DTRACE_STATUS_FILLED);
 164 }
 165 
 166 int
 167 dtrace_go(dtrace_hdl_t *dtp)
 168 {
 169         if (dtp->dt_active)
 170                 return (dt_set_errno(dtp, EINVAL));
 171 
 172         /*
 173          * If a dtrace:::ERROR program and callback are registered, enable the
 174          * program before we start tracing.  If this fails for a vector open
 175          * with ENOTTY, we permit dtrace_go() to succeed so that vector clients
 176          * such as mdb's dtrace module can execute the rest of dtrace_go() even
 177          * though they do not provide support for the DTRACEIOC_ENABLE ioctl.
 178          */
 179         if (dtp->dt_errprog != NULL &&
 180             dtrace_program_exec(dtp, dtp->dt_errprog, NULL) == -1 && (
 181             dtp->dt_errno != ENOTTY || dtp->dt_vector == NULL))
 182                 return (-1); /* dt_errno has been set for us */
 183 
 184         if (dt_ioctl(dtp, DTRACEIOC_GO, &dtp->dt_beganon) == -1) {
 185                 if (errno == EACCES)
 186                         return (dt_set_errno(dtp, EDT_DESTRUCTIVE));
 187 
 188                 if (errno == EALREADY)
 189                         return (dt_set_errno(dtp, EDT_ISANON));
 190 
 191                 if (errno == ENOENT)
 192                         return (dt_set_errno(dtp, EDT_NOANON));
 193 
 194                 if (errno == E2BIG)
 195                         return (dt_set_errno(dtp, EDT_ENDTOOBIG));
 196 
 197                 if (errno == ENOSPC)
 198                         return (dt_set_errno(dtp, EDT_BUFTOOSMALL));
 199 
 200                 return (dt_set_errno(dtp, errno));
 201         }
 202 
 203         dtp->dt_active = 1;
 204 
 205         if (dt_options_load(dtp) == -1)
 206                 return (dt_set_errno(dtp, errno));
 207 
 208         return (dt_aggregate_go(dtp));
 209 }
 210 
 211 int
 212 dtrace_stop(dtrace_hdl_t *dtp)
 213 {
 214         int gen = dtp->dt_statusgen;
 215 
 216         if (dtp->dt_stopped)
 217                 return (0);
 218 
 219         if (dt_ioctl(dtp, DTRACEIOC_STOP, &dtp->dt_endedon) == -1)
 220                 return (dt_set_errno(dtp, errno));
 221 
 222         dtp->dt_stopped = 1;
 223 
 224         /*
 225          * Now that we're stopped, we're going to get status one final time.
 226          */
 227         if (dt_ioctl(dtp, DTRACEIOC_STATUS, &dtp->dt_status[gen]) == -1)
 228                 return (dt_set_errno(dtp, errno));
 229 
 230         if (dt_handle_status(dtp, &dtp->dt_status[gen ^ 1],
 231             &dtp->dt_status[gen]) == -1)
 232                 return (-1);
 233 
 234         return (0);
 235 }
 236 
 237 
 238 dtrace_workstatus_t
 239 dtrace_work(dtrace_hdl_t *dtp, FILE *fp,
 240     dtrace_consume_probe_f *pfunc, dtrace_consume_rec_f *rfunc, void *arg)
 241 {
 242         int status = dtrace_status(dtp);
 243         dtrace_optval_t policy = dtp->dt_options[DTRACEOPT_BUFPOLICY];
 244         dtrace_workstatus_t rval;
 245 
 246         switch (status) {
 247         case DTRACE_STATUS_EXITED:
 248         case DTRACE_STATUS_FILLED:
 249         case DTRACE_STATUS_STOPPED:
 250                 /*
 251                  * Tracing is stopped.  We now want to force dtrace_consume()
 252                  * and dtrace_aggregate_snap() to proceed, regardless of
 253                  * switchrate and aggrate.  We do this by clearing the times.
 254                  */
 255                 dtp->dt_lastswitch = 0;
 256                 dtp->dt_lastagg = 0;
 257                 rval = DTRACE_WORKSTATUS_DONE;
 258                 break;
 259 
 260         case DTRACE_STATUS_NONE:
 261         case DTRACE_STATUS_OKAY:
 262                 rval = DTRACE_WORKSTATUS_OKAY;
 263                 break;
 264 
 265         case -1:
 266                 return (DTRACE_WORKSTATUS_ERROR);
 267         }
 268 
 269         if ((status == DTRACE_STATUS_NONE || status == DTRACE_STATUS_OKAY) &&
 270             policy != DTRACEOPT_BUFPOLICY_SWITCH) {
 271                 /*
 272                  * There either isn't any status or things are fine -- and
 273                  * this is a "ring" or "fill" buffer.  We don't want to consume
 274                  * any of the trace data or snapshot the aggregations; we just
 275                  * return.
 276                  */
 277                 assert(rval == DTRACE_WORKSTATUS_OKAY);
 278                 return (rval);
 279         }
 280 
 281         if (dtrace_aggregate_snap(dtp) == -1)
 282                 return (DTRACE_WORKSTATUS_ERROR);
 283 
 284         if (dtrace_consume(dtp, fp, pfunc, rfunc, arg) == -1)
 285                 return (DTRACE_WORKSTATUS_ERROR);
 286 
 287         return (rval);
 288 }