]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - cddl/contrib/opensolaris/lib/libdtrace/common/dt_work.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.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 error, r;
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         r = dt_ioctl(dtp, DTRACEIOC_ENABLE, &args);
210         error = errno;
211         dtrace_dof_destroy(dtp, dof);
212
213         if (r == -1 && (error != ENOTTY || dtp->dt_vector == NULL))
214                 return (dt_set_errno(dtp, error));
215
216         if (dt_ioctl(dtp, DTRACEIOC_GO, &dtp->dt_beganon) == -1) {
217                 if (errno == EACCES)
218                         return (dt_set_errno(dtp, EDT_DESTRUCTIVE));
219
220                 if (errno == EALREADY)
221                         return (dt_set_errno(dtp, EDT_ISANON));
222
223                 if (errno == ENOENT)
224                         return (dt_set_errno(dtp, EDT_NOANON));
225
226                 if (errno == E2BIG)
227                         return (dt_set_errno(dtp, EDT_ENDTOOBIG));
228
229                 if (errno == ENOSPC)
230                         return (dt_set_errno(dtp, EDT_BUFTOOSMALL));
231
232                 return (dt_set_errno(dtp, errno));
233         }
234
235         dtp->dt_active = 1;
236
237         if (dt_options_load(dtp) == -1)
238                 return (dt_set_errno(dtp, errno));
239
240         return (dt_aggregate_go(dtp));
241 }
242
243 int
244 dtrace_stop(dtrace_hdl_t *dtp)
245 {
246         int gen = dtp->dt_statusgen;
247
248         if (dtp->dt_stopped)
249                 return (0);
250
251         if (dt_ioctl(dtp, DTRACEIOC_STOP, &dtp->dt_endedon) == -1)
252                 return (dt_set_errno(dtp, errno));
253
254         dtp->dt_stopped = 1;
255
256         /*
257          * Now that we're stopped, we're going to get status one final time.
258          */
259         if (dt_ioctl(dtp, DTRACEIOC_STATUS, &dtp->dt_status[gen]) == -1)
260                 return (dt_set_errno(dtp, errno));
261
262         if (dt_handle_status(dtp, &dtp->dt_status[gen ^ 1],
263             &dtp->dt_status[gen]) == -1)
264                 return (-1);
265
266         return (0);
267 }
268
269
270 dtrace_workstatus_t
271 dtrace_work(dtrace_hdl_t *dtp, FILE *fp,
272     dtrace_consume_probe_f *pfunc, dtrace_consume_rec_f *rfunc, void *arg)
273 {
274         int status = dtrace_status(dtp);
275         dtrace_optval_t policy = dtp->dt_options[DTRACEOPT_BUFPOLICY];
276         dtrace_workstatus_t rval;
277
278         switch (status) {
279         case DTRACE_STATUS_EXITED:
280         case DTRACE_STATUS_FILLED:
281         case DTRACE_STATUS_STOPPED:
282                 /*
283                  * Tracing is stopped.  We now want to force dtrace_consume()
284                  * and dtrace_aggregate_snap() to proceed, regardless of
285                  * switchrate and aggrate.  We do this by clearing the times.
286                  */
287                 dtp->dt_lastswitch = 0;
288                 dtp->dt_lastagg = 0;
289                 rval = DTRACE_WORKSTATUS_DONE;
290                 break;
291
292         case DTRACE_STATUS_NONE:
293         case DTRACE_STATUS_OKAY:
294                 rval = DTRACE_WORKSTATUS_OKAY;
295                 break;
296
297         case -1:
298                 return (DTRACE_WORKSTATUS_ERROR);
299         }
300
301         if ((status == DTRACE_STATUS_NONE || status == DTRACE_STATUS_OKAY) &&
302             policy != DTRACEOPT_BUFPOLICY_SWITCH) {
303                 /*
304                  * There either isn't any status or things are fine -- and
305                  * this is a "ring" or "fill" buffer.  We don't want to consume
306                  * any of the trace data or snapshot the aggregations; we just
307                  * return.
308                  */
309                 assert(rval == DTRACE_WORKSTATUS_OKAY);
310                 return (rval);
311         }
312
313         if (dtrace_aggregate_snap(dtp) == -1)
314                 return (DTRACE_WORKSTATUS_ERROR);
315
316         if (dtrace_consume(dtp, fp, pfunc, rfunc, arg) == -1)
317                 return (DTRACE_WORKSTATUS_ERROR);
318
319         return (rval);
320 }