]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - cddl/contrib/opensolaris/lib/libdtrace/common/dt_work.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / cddl / contrib / opensolaris / lib / libdtrace / common / dt_work.c
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 #pragma ident   "%Z%%M% %I%     %E% SMI"
29
30 #include <dt_impl.h>
31 #include <stddef.h>
32 #include <errno.h>
33 #include <assert.h>
34 #include <time.h>
35
36 static const struct {
37         int dtslt_option;
38         size_t dtslt_offs;
39 } _dtrace_sleeptab[] = {
40         { DTRACEOPT_STATUSRATE, offsetof(dtrace_hdl_t, dt_laststatus) },
41         { DTRACEOPT_AGGRATE, offsetof(dtrace_hdl_t, dt_lastagg) },
42         { DTRACEOPT_SWITCHRATE, offsetof(dtrace_hdl_t, dt_lastswitch) },
43         { DTRACEOPT_MAX, 0 }
44 };
45
46 void
47 dtrace_sleep(dtrace_hdl_t *dtp)
48 {
49         dt_proc_hash_t *dph = dtp->dt_procs;
50         dtrace_optval_t policy = dtp->dt_options[DTRACEOPT_BUFPOLICY];
51         dt_proc_notify_t *dprn;
52
53         hrtime_t earliest = INT64_MAX;
54         struct timespec tv;
55         hrtime_t now;
56         int i;
57
58         for (i = 0; _dtrace_sleeptab[i].dtslt_option < DTRACEOPT_MAX; i++) {
59                 uintptr_t a = (uintptr_t)dtp + _dtrace_sleeptab[i].dtslt_offs;
60                 int opt = _dtrace_sleeptab[i].dtslt_option;
61                 dtrace_optval_t interval = dtp->dt_options[opt];
62
63                 /*
64                  * If the buffering policy is set to anything other than
65                  * "switch", we ignore the aggrate and switchrate -- they're
66                  * meaningless.
67                  */
68                 if (policy != DTRACEOPT_BUFPOLICY_SWITCH &&
69                     _dtrace_sleeptab[i].dtslt_option != DTRACEOPT_STATUSRATE)
70                         continue;
71
72                 if (*((hrtime_t *)a) + interval < earliest)
73                         earliest = *((hrtime_t *)a) + interval;
74         }
75
76         (void) pthread_mutex_lock(&dph->dph_lock);
77
78         now = gethrtime();
79
80         if (earliest < now) {
81                 (void) pthread_mutex_unlock(&dph->dph_lock);
82                 return; /* sleep duration has already past */
83         }
84
85 #if defined(sun)
86         tv.tv_sec = (earliest - now) / NANOSEC;
87         tv.tv_nsec = (earliest - now) % NANOSEC;
88
89         /*
90          * Wait for either 'tv' nanoseconds to pass or to receive notification
91          * that a process is in an interesting state.  Regardless of why we
92          * awaken, iterate over any pending notifications and process them.
93          */
94         (void) pthread_cond_reltimedwait_np(&dph->dph_cv, &dph->dph_lock, &tv);
95 #else
96         earliest -= now;
97         clock_gettime(CLOCK_REALTIME,&tv);
98         tv.tv_sec += earliest / NANOSEC;
99         tv.tv_nsec += earliest % NANOSEC;
100         while (tv.tv_nsec > NANOSEC) {
101                 tv.tv_sec += 1;
102                 tv.tv_nsec -= NANOSEC;
103         }
104
105         /*
106          * Wait for either 'tv' nanoseconds to pass or to receive notification
107          * that a process is in an interesting state.  Regardless of why we
108          * awaken, iterate over any pending notifications and process them.
109          */
110         (void) pthread_cond_timedwait(&dph->dph_cv, &dph->dph_lock, &tv);
111 #endif
112
113         while ((dprn = dph->dph_notify) != NULL) {
114                 if (dtp->dt_prochdlr != NULL) {
115                         char *err = dprn->dprn_errmsg;
116                         if (*err == '\0')
117                                 err = NULL;
118
119                         dtp->dt_prochdlr(dprn->dprn_dpr->dpr_proc, err,
120                             dtp->dt_procarg);
121                 }
122
123                 dph->dph_notify = dprn->dprn_next;
124                 dt_free(dtp, dprn);
125         }
126
127         (void) pthread_mutex_unlock(&dph->dph_lock);
128 }
129
130 int
131 dtrace_status(dtrace_hdl_t *dtp)
132 {
133         int gen = dtp->dt_statusgen;
134         dtrace_optval_t interval = dtp->dt_options[DTRACEOPT_STATUSRATE];
135         hrtime_t now = gethrtime();
136
137         if (!dtp->dt_active)
138                 return (DTRACE_STATUS_NONE);
139
140         if (dtp->dt_stopped)
141                 return (DTRACE_STATUS_STOPPED);
142
143         if (dtp->dt_laststatus != 0) {
144                 if (now - dtp->dt_laststatus < interval)
145                         return (DTRACE_STATUS_NONE);
146
147                 dtp->dt_laststatus += interval;
148         } else {
149                 dtp->dt_laststatus = now;
150         }
151
152         if (dt_ioctl(dtp, DTRACEIOC_STATUS, &dtp->dt_status[gen]) == -1)
153                 return (dt_set_errno(dtp, errno));
154
155         dtp->dt_statusgen ^= 1;
156
157         if (dt_handle_status(dtp, &dtp->dt_status[dtp->dt_statusgen],
158             &dtp->dt_status[gen]) == -1)
159                 return (-1);
160
161         if (dtp->dt_status[gen].dtst_exiting) {
162                 if (!dtp->dt_stopped)
163                         (void) dtrace_stop(dtp);
164
165                 return (DTRACE_STATUS_EXITED);
166         }
167
168         if (dtp->dt_status[gen].dtst_filled == 0)
169                 return (DTRACE_STATUS_OKAY);
170
171         if (dtp->dt_options[DTRACEOPT_BUFPOLICY] != DTRACEOPT_BUFPOLICY_FILL)
172                 return (DTRACE_STATUS_OKAY);
173
174         if (!dtp->dt_stopped) {
175                 if (dtrace_stop(dtp) == -1)
176                         return (-1);
177         }
178
179         return (DTRACE_STATUS_FILLED);
180 }
181
182 int
183 dtrace_go(dtrace_hdl_t *dtp)
184 {
185         dtrace_enable_io_t args;
186         void *dof;
187         int err;
188
189         if (dtp->dt_active)
190                 return (dt_set_errno(dtp, EINVAL));
191
192         /*
193          * If a dtrace:::ERROR program and callback are registered, enable the
194          * program before we start tracing.  If this fails for a vector open
195          * with ENOTTY, we permit dtrace_go() to succeed so that vector clients
196          * such as mdb's dtrace module can execute the rest of dtrace_go() even
197          * though they do not provide support for the DTRACEIOC_ENABLE ioctl.
198          */
199         if (dtp->dt_errprog != NULL &&
200             dtrace_program_exec(dtp, dtp->dt_errprog, NULL) == -1 && (
201             dtp->dt_errno != ENOTTY || dtp->dt_vector == NULL))
202                 return (-1); /* dt_errno has been set for us */
203
204         if ((dof = dtrace_getopt_dof(dtp)) == NULL)
205                 return (-1); /* dt_errno has been set for us */
206
207         args.dof = dof;
208         args.n_matched = 0;
209         err = dt_ioctl(dtp, DTRACEIOC_ENABLE, &args);
210         dtrace_dof_destroy(dtp, dof);
211
212         if (err == -1 && (errno != ENOTTY || dtp->dt_vector == NULL))
213                 return (dt_set_errno(dtp, errno));
214
215         if (dt_ioctl(dtp, DTRACEIOC_GO, &dtp->dt_beganon) == -1) {
216                 if (errno == EACCES)
217                         return (dt_set_errno(dtp, EDT_DESTRUCTIVE));
218
219                 if (errno == EALREADY)
220                         return (dt_set_errno(dtp, EDT_ISANON));
221
222                 if (errno == ENOENT)
223                         return (dt_set_errno(dtp, EDT_NOANON));
224
225                 if (errno == E2BIG)
226                         return (dt_set_errno(dtp, EDT_ENDTOOBIG));
227
228                 if (errno == ENOSPC)
229                         return (dt_set_errno(dtp, EDT_BUFTOOSMALL));
230
231                 return (dt_set_errno(dtp, errno));
232         }
233
234         dtp->dt_active = 1;
235
236         if (dt_options_load(dtp) == -1)
237                 return (dt_set_errno(dtp, errno));
238
239         return (dt_aggregate_go(dtp));
240 }
241
242 int
243 dtrace_stop(dtrace_hdl_t *dtp)
244 {
245         int gen = dtp->dt_statusgen;
246
247         if (dtp->dt_stopped)
248                 return (0);
249
250         if (dt_ioctl(dtp, DTRACEIOC_STOP, &dtp->dt_endedon) == -1)
251                 return (dt_set_errno(dtp, errno));
252
253         dtp->dt_stopped = 1;
254
255         /*
256          * Now that we're stopped, we're going to get status one final time.
257          */
258         if (dt_ioctl(dtp, DTRACEIOC_STATUS, &dtp->dt_status[gen]) == -1)
259                 return (dt_set_errno(dtp, errno));
260
261         if (dt_handle_status(dtp, &dtp->dt_status[gen ^ 1],
262             &dtp->dt_status[gen]) == -1)
263                 return (-1);
264
265         return (0);
266 }
267
268
269 dtrace_workstatus_t
270 dtrace_work(dtrace_hdl_t *dtp, FILE *fp,
271     dtrace_consume_probe_f *pfunc, dtrace_consume_rec_f *rfunc, void *arg)
272 {
273         int status = dtrace_status(dtp);
274         dtrace_optval_t policy = dtp->dt_options[DTRACEOPT_BUFPOLICY];
275         dtrace_workstatus_t rval;
276
277         switch (status) {
278         case DTRACE_STATUS_EXITED:
279         case DTRACE_STATUS_FILLED:
280         case DTRACE_STATUS_STOPPED:
281                 /*
282                  * Tracing is stopped.  We now want to force dtrace_consume()
283                  * and dtrace_aggregate_snap() to proceed, regardless of
284                  * switchrate and aggrate.  We do this by clearing the times.
285                  */
286                 dtp->dt_lastswitch = 0;
287                 dtp->dt_lastagg = 0;
288                 rval = DTRACE_WORKSTATUS_DONE;
289                 break;
290
291         case DTRACE_STATUS_NONE:
292         case DTRACE_STATUS_OKAY:
293                 rval = DTRACE_WORKSTATUS_OKAY;
294                 break;
295
296         case -1:
297                 return (DTRACE_WORKSTATUS_ERROR);
298         }
299
300         if ((status == DTRACE_STATUS_NONE || status == DTRACE_STATUS_OKAY) &&
301             policy != DTRACEOPT_BUFPOLICY_SWITCH) {
302                 /*
303                  * There either isn't any status or things are fine -- and
304                  * this is a "ring" or "fill" buffer.  We don't want to consume
305                  * any of the trace data or snapshot the aggregations; we just
306                  * return.
307                  */
308                 assert(rval == DTRACE_WORKSTATUS_OKAY);
309                 return (rval);
310         }
311
312         if (dtrace_aggregate_snap(dtp) == -1)
313                 return (DTRACE_WORKSTATUS_ERROR);
314
315         if (dtrace_consume(dtp, fp, pfunc, rfunc, arg) == -1)
316                 return (DTRACE_WORKSTATUS_ERROR);
317
318         return (rval);
319 }