procstat_kstack.c revision 310121
1/*-
2 * Copyright (c) 2007 Robert N. M. Watson
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * $FreeBSD: stable/10/usr.bin/procstat/procstat_kstack.c 310121 2016-12-15 16:52:17Z vangyzen $
27 */
28
29#include <sys/param.h>
30#include <sys/sysctl.h>
31#include <sys/user.h>
32
33#include <err.h>
34#include <errno.h>
35#include <libprocstat.h>
36#include <stdio.h>
37#include <stdlib.h>
38#include <string.h>
39
40#include "procstat.h"
41
42/*
43 * Walk the stack trace provided by the kernel and reduce it to what we
44 * actually want to print.  This involves stripping true instruction pointers,
45 * frame numbers, and carriage returns as generated by stack(9).  If -kk is
46 * specified, print the function and offset, otherwise just the function.
47 */
48enum trace_state { TS_FRAMENUM, TS_PC, TS_AT, TS_FUNC, TS_OFF };
49
50static enum trace_state
51kstack_nextstate(enum trace_state ts)
52{
53
54	switch (ts) {
55	case TS_FRAMENUM:
56		return (TS_PC);
57
58	case TS_PC:
59		return (TS_AT);
60
61	case TS_AT:
62		return (TS_FUNC);
63
64	case TS_FUNC:
65		return (TS_OFF);
66
67	case TS_OFF:
68		return TS_FRAMENUM;
69
70	default:
71		errx(-1, "kstack_nextstate");
72	}
73}
74
75static void
76kstack_cleanup(const char *old, char *new, int kflag)
77{
78	enum trace_state old_ts, ts;
79	const char *cp_old;
80	char *cp_new;
81
82	ts = TS_FRAMENUM;
83	for (cp_old = old, cp_new = new; *cp_old != '\0'; cp_old++) {
84		switch (*cp_old) {
85		case ' ':
86		case '\n':
87		case '+':
88			old_ts = ts;
89			ts = kstack_nextstate(old_ts);
90			if (old_ts == TS_OFF) {
91				*cp_new = ' ';
92				cp_new++;
93			}
94			if (kflag > 1 && old_ts == TS_FUNC) {
95				*cp_new = '+';
96				cp_new++;
97			}
98			continue;
99		}
100		if (ts == TS_FUNC || (kflag > 1 && ts == TS_OFF)) {
101			*cp_new = *cp_old;
102			cp_new++;
103		}
104	}
105	*cp_new = '\0';
106}
107
108/*
109 * Sort threads by tid.
110 */
111static int
112kinfo_kstack_compare(const void *a, const void *b)
113{
114
115        return ((const struct kinfo_kstack *)a)->kkst_tid -
116            ((const struct kinfo_kstack *)b)->kkst_tid;
117}
118
119static void
120kinfo_kstack_sort(struct kinfo_kstack *kkstp, int count)
121{
122
123        qsort(kkstp, count, sizeof(*kkstp), kinfo_kstack_compare);
124}
125
126
127void
128procstat_kstack(struct procstat *procstat, struct kinfo_proc *kipp, int kflag)
129{
130	struct kinfo_kstack *kkstp, *kkstp_free;
131	struct kinfo_proc *kip, *kip_free;
132	char trace[KKST_MAXLEN];
133	unsigned int i, j;
134	unsigned int kip_count, kstk_count;
135
136	if (!hflag)
137		printf("%5s %6s %-19s %-19s %-29s\n", "PID", "TID", "COMM",
138		    "TDNAME", "KSTACK");
139
140	kkstp = kkstp_free = procstat_getkstack(procstat, kipp, &kstk_count);
141	if (kkstp == NULL)
142		return;
143
144	/*
145	 * We need to re-query for thread information, so don't use *kipp.
146	 */
147	kip = kip_free = procstat_getprocs(procstat,
148	    KERN_PROC_PID | KERN_PROC_INC_THREAD, kipp->ki_pid, &kip_count);
149
150	if (kip == NULL) {
151		procstat_freekstack(procstat, kkstp_free);
152		return;
153	}
154
155	kinfo_kstack_sort(kkstp, kstk_count);
156	for (i = 0; i < kstk_count; i++) {
157		kkstp = &kkstp_free[i];
158
159		/*
160		 * Look up the specific thread using its tid so we can
161		 * display the per-thread command line.
162		 */
163		kipp = NULL;
164		for (j = 0; j < kip_count; j++) {
165			kipp = &kip_free[j];
166			if (kkstp->kkst_tid == kipp->ki_tid)
167				break;
168		}
169		if (kipp == NULL)
170			continue;
171
172		printf("%5d ", kipp->ki_pid);
173		printf("%6d ", kkstp->kkst_tid);
174		printf("%-19s ", kipp->ki_comm);
175		printf("%-19s ", kinfo_proc_thread_name(kipp));
176
177		switch (kkstp->kkst_state) {
178		case KKST_STATE_RUNNING:
179			printf("%-29s\n", "<running>");
180			continue;
181
182		case KKST_STATE_SWAPPED:
183			printf("%-29s\n", "<swapped>");
184			continue;
185
186		case KKST_STATE_STACKOK:
187			break;
188
189		default:
190			printf("%-29s\n", "<unknown>");
191			continue;
192		}
193
194		/*
195		 * The kernel generates a trace with carriage returns between
196		 * entries, but for a more compact view, we convert carriage
197		 * returns to spaces.
198		 */
199		kstack_cleanup(kkstp->kkst_trace, trace, kflag);
200		printf("%-29s\n", trace);
201	}
202	procstat_freekstack(procstat, kkstp_free);
203	procstat_freeprocs(procstat, kip_free);
204}
205