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.
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.
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]
22 * Copyright (c) 2008-2009, Intel Corporation.
23 * All Rights Reserved.
34 #include "latencytop.h"
36 static dtrace_hdl_t
*g_dtp
= NULL
; /* dtrace handle */
37 static pid_t pid_self
= -1; /* PID of our own process */
40 * Ignore sched if sched is not tracked.
41 * Also ignore ourselves (i.e., latencytop).
43 #define SHOULD_IGNORE(pid) \
44 ((!g_config.lt_cfg_trace_sched && 0 == (pid)) || pid_self == (pid))
47 * Get an integer value from dtrace record.
50 rec_get_value(void *a
, size_t b
)
55 case sizeof (uint64_t):
56 ret
= *((uint64_t *)(a
));
58 case sizeof (uint32_t):
59 ret
= *((uint32_t *)(a
));
61 case sizeof (uint16_t):
62 ret
= *((uint16_t *)(a
));
64 case sizeof (uint8_t):
65 ret
= *((uint8_t *)(a
));
75 * Callback to process aggregation lt_call_* (related to on/off cpu
76 * activities) in the snapshot.
79 aggwalk_call(const dtrace_aggdata_t
*data
, lt_stat_type_t stat_type
)
81 dtrace_aggdesc_t
*aggdesc
= data
->dtada_desc
;
87 unsigned int stack_depth
;
94 unsigned int buffersize
;
96 unsigned int priority
;
97 enum { REC_PID
= 1, REC_TID
, REC_STACK
, REC_TAG
, REC_PRIO
, REC_AGG
,
100 /* Check action type */
101 if ((aggdesc
->dtagd_nrecs
< NREC
) ||
102 (aggdesc
->dtagd_rec
[REC_PID
].dtrd_action
!= DTRACEACT_DIFEXPR
) ||
103 (aggdesc
->dtagd_rec
[REC_TID
].dtrd_action
!= DTRACEACT_DIFEXPR
) ||
104 (aggdesc
->dtagd_rec
[REC_TAG
].dtrd_action
!= DTRACEACT_DIFEXPR
) ||
105 (aggdesc
->dtagd_rec
[REC_PRIO
].dtrd_action
!= DTRACEACT_DIFEXPR
) ||
106 (!DTRACEACT_ISAGG(aggdesc
->dtagd_rec
[REC_AGG
].dtrd_action
)) ||
107 (aggdesc
->dtagd_rec
[REC_STACK
].dtrd_action
!= DTRACEACT_STACK
)) {
113 data
->dtada_data
+ aggdesc
->dtagd_rec
[REC_PID
].dtrd_offset
,
114 aggdesc
->dtagd_rec
[REC_PID
].dtrd_size
);
116 if (SHOULD_IGNORE(pid
)) {
121 data
->dtada_data
+ aggdesc
->dtagd_rec
[REC_TID
].dtrd_offset
,
122 aggdesc
->dtagd_rec
[REC_TID
].dtrd_size
);
124 /* Parse stack array from dtagd_rec */
125 stack_depth
= aggdesc
->dtagd_rec
[REC_STACK
].dtrd_arg
;
126 pc_size
= aggdesc
->dtagd_rec
[REC_STACK
].dtrd_size
/ stack_depth
;
127 addr
= data
->dtada_data
+ aggdesc
->dtagd_rec
[REC_STACK
].dtrd_offset
;
128 buffersize
= (stack_depth
* (2 * PATH_MAX
+ 2) + 1) * sizeof (char);
129 buffer
= (char *)lt_malloc(buffersize
);
131 ptrsize
= buffersize
;
133 /* Print the stack */
134 while (stack_depth
> 0) {
135 pc
= rec_get_value(addr
, pc_size
);
143 if (dtrace_lookup_by_addr(g_dtp
, pc
, &sym
, &dts
) == 0) {
145 len
= snprintf(ptr
, ptrsize
,
146 "%s`%s ", dts
.dts_object
, dts
.dts_name
);
151 * snprintf returns "desired" length, so
152 * reaching here means our buffer is full.
153 * Move ptr to the last byte of the buffer and
156 ptr
= &buffer
[buffersize
-1];
166 * We have printed something, so it is safe to remove
172 tag
= (char *)data
->dtada_data
+
173 aggdesc
->dtagd_rec
[REC_TAG
].dtrd_offset
;
175 priority
= rec_get_value(
176 data
->dtada_data
+ aggdesc
->dtagd_rec
[REC_PRIO
].dtrd_offset
,
177 aggdesc
->dtagd_rec
[REC_PRIO
].dtrd_size
);
179 agg_value
= rec_get_value(
180 data
->dtada_data
+ aggdesc
->dtagd_rec
[REC_AGG
].dtrd_offset
,
181 aggdesc
->dtagd_rec
[REC_AGG
].dtrd_size
);
183 lt_stat_update(pid
, tid
, buffer
, tag
, priority
, stat_type
, agg_value
);
185 if (buffer
!= NULL
) {
193 * Callback to process aggregation lt_named_* (related to lock spinning etc.),
197 aggwalk_named(const dtrace_aggdata_t
*data
, lt_stat_type_t stat_type
)
199 dtrace_aggdesc_t
*aggdesc
= data
->dtada_desc
;
205 enum { REC_PID
= 1, REC_TID
, REC_TYPE
, REC_AGG
, NREC
};
207 /* Check action type */
208 if ((aggdesc
->dtagd_nrecs
< NREC
) ||
209 (aggdesc
->dtagd_rec
[REC_PID
].dtrd_action
!= DTRACEACT_DIFEXPR
) ||
210 (aggdesc
->dtagd_rec
[REC_TID
].dtrd_action
!= DTRACEACT_DIFEXPR
) ||
211 (aggdesc
->dtagd_rec
[REC_TYPE
].dtrd_action
!= DTRACEACT_DIFEXPR
) ||
212 (!DTRACEACT_ISAGG(aggdesc
->dtagd_rec
[REC_AGG
].dtrd_action
))) {
218 data
->dtada_data
+ aggdesc
->dtagd_rec
[REC_PID
].dtrd_offset
,
219 aggdesc
->dtagd_rec
[REC_PID
].dtrd_size
);
221 if (SHOULD_IGNORE(pid
)) {
226 data
->dtada_data
+ aggdesc
->dtagd_rec
[REC_TID
].dtrd_offset
,
227 aggdesc
->dtagd_rec
[REC_TID
].dtrd_size
);
229 type
= (char *)data
->dtada_data
230 + aggdesc
->dtagd_rec
[REC_TYPE
].dtrd_offset
;
231 cause_id
= lt_table_cause_from_name(type
, 1, CAUSE_FLAG_SPECIAL
);
233 agg_value
= rec_get_value(
234 data
->dtada_data
+ aggdesc
->dtagd_rec
[REC_AGG
].dtrd_offset
,
235 aggdesc
->dtagd_rec
[REC_AGG
].dtrd_size
);
237 lt_stat_update_cause(pid
, tid
, cause_id
, stat_type
, agg_value
);
244 * Callback to process aggregation lt_sync_* (related to synchronization
245 * objects), in the snapshot.
248 aggwalk_sync(const dtrace_aggdata_t
*data
, lt_stat_type_t stat_type
)
250 dtrace_aggdesc_t
*aggdesc
= data
->dtada_desc
;
255 unsigned long long wchan
;
256 enum { REC_PID
= 1, REC_TID
, REC_STYPE
, REC_WCHAN
, REC_AGG
, NREC
};
258 /* Check action type */
259 if ((aggdesc
->dtagd_nrecs
< NREC
) ||
260 (aggdesc
->dtagd_rec
[REC_PID
].dtrd_action
!= DTRACEACT_DIFEXPR
) ||
261 (aggdesc
->dtagd_rec
[REC_TID
].dtrd_action
!= DTRACEACT_DIFEXPR
) ||
262 (aggdesc
->dtagd_rec
[REC_STYPE
].dtrd_action
!= DTRACEACT_DIFEXPR
) ||
263 (aggdesc
->dtagd_rec
[REC_WCHAN
].dtrd_action
!= DTRACEACT_DIFEXPR
) ||
264 (!DTRACEACT_ISAGG(aggdesc
->dtagd_rec
[REC_AGG
].dtrd_action
))) {
270 data
->dtada_data
+ aggdesc
->dtagd_rec
[REC_PID
].dtrd_offset
,
271 aggdesc
->dtagd_rec
[REC_PID
].dtrd_size
);
273 if (SHOULD_IGNORE(pid
)) {
278 data
->dtada_data
+ aggdesc
->dtagd_rec
[REC_TID
].dtrd_offset
,
279 aggdesc
->dtagd_rec
[REC_TID
].dtrd_size
);
281 stype
= rec_get_value(
282 data
->dtada_data
+ aggdesc
->dtagd_rec
[REC_STYPE
].dtrd_offset
,
283 aggdesc
->dtagd_rec
[REC_STYPE
].dtrd_size
);
285 wchan
= rec_get_value(
286 data
->dtada_data
+ aggdesc
->dtagd_rec
[REC_WCHAN
].dtrd_offset
,
287 aggdesc
->dtagd_rec
[REC_WCHAN
].dtrd_size
);
289 agg_value
= rec_get_value(
290 data
->dtada_data
+ aggdesc
->dtagd_rec
[REC_AGG
].dtrd_offset
,
291 aggdesc
->dtagd_rec
[REC_AGG
].dtrd_size
);
293 lt_stat_update_sobj(pid
, tid
, stype
, wchan
, stat_type
, agg_value
);
299 * Callback to process various aggregations in the snapshot. Called by
300 * different aggwalk_* functions.
304 aggwalk(const dtrace_aggdata_t
*data
, void *arg
)
308 lt_stat_type_t stat_type
;
309 int (*func
)(const dtrace_aggdata_t
*, lt_stat_type_t
);
311 (void) strncpy(buffer
, data
->dtada_desc
->dtagd_name
, sizeof (buffer
));
312 buffer
[sizeof (buffer
) - 1] = '\0';
313 tmp
= strtok(buffer
, "_");
315 if (tmp
== NULL
|| strcmp(tmp
, "lt") != 0) {
319 tmp
= strtok(NULL
, "_");
323 } else if (strcmp(tmp
, "call") == 0) {
325 } else if (strcmp(tmp
, "named") == 0) {
326 func
= aggwalk_named
;
327 } else if (strcmp(tmp
, "sync") == 0) {
333 tmp
= strtok(NULL
, "_");
337 } else if (strcmp(tmp
, "count") == 0) {
338 stat_type
= LT_STAT_COUNT
;
339 } else if (strcmp(tmp
, "sum") == 0) {
340 stat_type
= LT_STAT_SUM
;
341 } else if (strcmp(tmp
, "max") == 0) {
342 stat_type
= LT_STAT_MAX
;
347 (void) func(data
, stat_type
);
350 /* We have our data, so remove it from DTrace now */
351 return (DTRACE_AGGWALK_REMOVE
);
355 * Callback to handle event caused by DTrace dropping data.
359 drop_handler(const dtrace_dropdata_t
*data
, void *user
)
361 lt_display_error("Drop: %s\n", data
->dtdda_msg
);
362 lt_drop_detected
= B_TRUE
;
364 /* Pretend nothing happened, so just continue */
365 return (DTRACE_HANDLE_OK
);
368 #ifndef EMBED_CONFIGS
370 * Copy the content from a "real" file into a temp file.
373 copy_tmp_file(const char *src
, FILE *dst
)
379 if ((tmp
= fopen(src
, "r")) == NULL
) {
383 while ((bytes
= fread(buffer
, 1, sizeof (buffer
), tmp
)) > 0) {
384 if (fwrite(buffer
, bytes
, 1, dst
) != 1) {
396 * DTrace initialization. D script starts running when this function returns.
402 dtrace_proginfo_t info
;
404 FILE *fp_script
= NULL
;
409 if ((g_dtp
= dtrace_open(DTRACE_VERSION
, 0, &err
)) == NULL
) {
410 lt_display_error("Cannot open dtrace library: %s\n",
411 dtrace_errmsg(NULL
, err
));
415 if (dtrace_handle_drop(g_dtp
, &drop_handler
, NULL
) == -1) {
416 lt_display_error("Cannot install DTrace handle: %s\n",
417 dtrace_errmsg(NULL
, err
));
421 if (g_config
.lt_cfg_enable_filter
) {
422 if ((err
= dtrace_setopt(g_dtp
, "define",
423 "ENABLE_FILTER")) != 0) {
425 "Failed to set option ENABLE_FILTER.\n");
430 if (g_config
.lt_cfg_trace_syncobj
) {
431 if ((err
= dtrace_setopt(g_dtp
, "define",
432 "ENABLE_SYNCOBJ")) != 0) {
434 "Failed to set option ENABLE_SYNCOBJ.\n");
439 if (g_config
.lt_cfg_trace_sched
) {
440 if ((err
= dtrace_setopt(g_dtp
, "define",
441 "ENABLE_SCHED")) != 0) {
443 "Failed to set option ENABLE_SCHED.\n");
448 if (g_config
.lt_cfg_trace_pid
!= 0) {
449 (void) snprintf(tmp
, sizeof (tmp
), "TRACE_PID=%u",
450 g_config
.lt_cfg_trace_pid
);
451 if ((err
= dtrace_setopt(g_dtp
, "define", tmp
)) != 0) {
453 "Failed to set option TRACE_PID.\n");
458 if (g_config
.lt_cfg_trace_pgid
!= 0) {
459 (void) snprintf(tmp
, sizeof (tmp
), "TRACE_PGID=%u",
460 g_config
.lt_cfg_trace_pgid
);
461 if ((err
= dtrace_setopt(g_dtp
, "define", tmp
)) != 0) {
463 "Failed to set option TRACE_PGID.\n");
468 if (g_config
.lt_cfg_low_overhead_mode
) {
469 if ((err
= dtrace_setopt(g_dtp
, "define",
470 "ENABLE_LOW_OVERHEAD")) != 0) {
472 "Failed to set option ENABLE_LOW_OVERHEAD.\n");
477 /* Create a temp file; libdtrace needs it for cpp(1) */
478 if ((fp_script
= tmpfile()) == NULL
) {
479 lt_display_error("Cannot create tmp file\n");
483 /* Copy the main D script into the temp file */
485 if (fwrite(&latencytop_d_start
,
486 (size_t)(&latencytop_d_end
- &latencytop_d_start
), 1, fp_script
)
488 lt_display_error("Could not copy D script, fwrite() failed\n");
489 (void) fclose(fp_script
);
493 if (copy_tmp_file(DEFAULT_D_SCRIPT_NAME
, fp_script
) != 0) {
494 lt_display_error("Cannot open script file %s\n",
495 DEFAULT_D_SCRIPT_NAME
);
496 (void) fclose(fp_script
);
499 #endif /* EMBED_CONFIGS */
501 if (lt_table_append_trans(fp_script
) != 0) {
502 (void) fclose(fp_script
);
506 (void) fseek(fp_script
, 0, SEEK_SET
);
508 if ((prog
= dtrace_program_fcompile(g_dtp
, fp_script
,
509 DTRACE_C_CPP
, 0, NULL
)) == NULL
) {
510 lt_display_error("Failed to compile D script.\n");
511 (void) fclose(fp_script
);
512 return (dtrace_errno(g_dtp
));
515 (void) fclose(fp_script
);
517 /* Execute the D script */
518 if (dtrace_program_exec(g_dtp
, prog
, &info
) == -1) {
519 lt_display_error("Failed to enable probes.\n");
520 return (dtrace_errno(g_dtp
));
523 if (dtrace_go(g_dtp
) != 0) {
524 lt_display_error("Failed to run D script.\n");
525 return (dtrace_errno(g_dtp
));
532 * Worker function to move aggregate data to user space. Called periodically
533 * to prevent the kernel from running out of memory.
536 lt_dtrace_work(int force
)
538 static uint64_t last_snap
= 0;
539 uint64_t now
= lt_millisecond();
541 if (!force
&& now
- last_snap
< g_config
.lt_cfg_snap_interval
) {
542 return (last_snap
+ g_config
.lt_cfg_snap_interval
- now
);
545 if (dtrace_status(g_dtp
) == -1) {
546 lt_display_error("Failed when getting status: %s\n",
547 dtrace_errmsg(g_dtp
, dtrace_errno(g_dtp
)));
551 if (dtrace_aggregate_snap(g_dtp
) != 0) {
552 lt_display_error("Failed to snap aggregate: %s\n",
553 dtrace_errmsg(g_dtp
, dtrace_errno(g_dtp
)));
562 * Walk through dtrace aggregator and collect data for latencytop to display.
563 * Called immediately before UI update.
566 lt_dtrace_collect(void)
568 if (lt_dtrace_work(1) != 0) {
572 if (dtrace_aggregate_walk(g_dtp
, aggwalk
, NULL
) != 0) {
573 lt_display_error("Failed to sort aggregate: %s\n",
574 dtrace_errmsg(g_dtp
, dtrace_errno(g_dtp
)));
579 * Probably we don't need to clear again, because we have removed
580 * everything. Paranoid ?
582 dtrace_aggregate_clear(g_dtp
);
591 lt_dtrace_deinit(void)
595 if (dtrace_stop(g_dtp
) != 0) {
596 lt_display_error("dtrace_stop failed: %s\n",
597 dtrace_errmsg(g_dtp
, dtrace_errno(g_dtp
)));