1174199Srwatson/*-
2174199Srwatson * Copyright (c) 2007 Robert N. M. Watson
3174199Srwatson * All rights reserved.
4174199Srwatson *
5174199Srwatson * Redistribution and use in source and binary forms, with or without
6174199Srwatson * modification, are permitted provided that the following conditions
7174199Srwatson * are met:
8174199Srwatson * 1. Redistributions of source code must retain the above copyright
9174199Srwatson *    notice, this list of conditions and the following disclaimer.
10174199Srwatson * 2. Redistributions in binary form must reproduce the above copyright
11174199Srwatson *    notice, this list of conditions and the following disclaimer in the
12174199Srwatson *    documentation and/or other materials provided with the distribution.
13174199Srwatson *
14174199Srwatson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15174199Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16174199Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17174199Srwatson * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18174199Srwatson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19174199Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20174199Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21174199Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22174199Srwatson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23174199Srwatson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24174199Srwatson * SUCH DAMAGE.
25174199Srwatson *
26174199Srwatson * $FreeBSD$
27174199Srwatson */
28174199Srwatson
29186567Srwatson#include <sys/param.h>
30174199Srwatson#include <sys/sysctl.h>
31174199Srwatson#include <sys/user.h>
32174199Srwatson
33174199Srwatson#include <err.h>
34174199Srwatson#include <errno.h>
35221807Sstas#include <libprocstat.h>
36174199Srwatson#include <stdio.h>
37174199Srwatson#include <stdlib.h>
38174199Srwatson#include <string.h>
39174199Srwatson
40174199Srwatson#include "procstat.h"
41174199Srwatson
42174199Srwatson/*
43174199Srwatson * Walk the stack trace provided by the kernel and reduce it to what we
44174199Srwatson * actually want to print.  This involves stripping true instruction pointers,
45174199Srwatson * frame numbers, and carriage returns as generated by stack(9).  If -kk is
46174199Srwatson * specified, print the function and offset, otherwise just the function.
47174199Srwatson */
48174199Srwatsonenum trace_state { TS_FRAMENUM, TS_PC, TS_AT, TS_FUNC, TS_OFF };
49174199Srwatson
50174199Srwatsonstatic enum trace_state
51174199Srwatsonkstack_nextstate(enum trace_state ts)
52174199Srwatson{
53174199Srwatson
54174199Srwatson	switch (ts) {
55174199Srwatson	case TS_FRAMENUM:
56174199Srwatson		return (TS_PC);
57174199Srwatson
58174199Srwatson	case TS_PC:
59174199Srwatson		return (TS_AT);
60174199Srwatson
61174199Srwatson	case TS_AT:
62174199Srwatson		return (TS_FUNC);
63174199Srwatson
64174199Srwatson	case TS_FUNC:
65174199Srwatson		return (TS_OFF);
66174199Srwatson
67174199Srwatson	case TS_OFF:
68174199Srwatson		return TS_FRAMENUM;
69174199Srwatson
70174199Srwatson	default:
71174199Srwatson		errx(-1, "kstack_nextstate");
72174199Srwatson	}
73174199Srwatson}
74174199Srwatson
75174199Srwatsonstatic void
76174199Srwatsonkstack_cleanup(const char *old, char *new, int kflag)
77174199Srwatson{
78174199Srwatson	enum trace_state old_ts, ts;
79174199Srwatson	const char *cp_old;
80174199Srwatson	char *cp_new;
81174199Srwatson
82174199Srwatson	ts = TS_FRAMENUM;
83174199Srwatson	for (cp_old = old, cp_new = new; *cp_old != '\0'; cp_old++) {
84174199Srwatson		switch (*cp_old) {
85174199Srwatson		case ' ':
86174199Srwatson		case '\n':
87174199Srwatson		case '+':
88174199Srwatson			old_ts = ts;
89174199Srwatson			ts = kstack_nextstate(old_ts);
90174199Srwatson			if (old_ts == TS_OFF) {
91174199Srwatson				*cp_new = ' ';
92174199Srwatson				cp_new++;
93174199Srwatson			}
94174199Srwatson			if (kflag > 1 && old_ts == TS_FUNC) {
95174199Srwatson				*cp_new = '+';
96174199Srwatson				cp_new++;
97174199Srwatson			}
98174199Srwatson			continue;
99174199Srwatson		}
100174199Srwatson		if (ts == TS_FUNC || (kflag > 1 && ts == TS_OFF)) {
101174199Srwatson			*cp_new = *cp_old;
102174199Srwatson			cp_new++;
103174199Srwatson		}
104174199Srwatson	}
105174199Srwatson	*cp_new = '\0';
106174199Srwatson}
107174199Srwatson
108174199Srwatson/*
109174199Srwatson * Sort threads by tid.
110174199Srwatson */
111174199Srwatsonstatic int
112174199Srwatsonkinfo_kstack_compare(const void *a, const void *b)
113174199Srwatson{
114174199Srwatson
115176107Sdwmalone        return ((const struct kinfo_kstack *)a)->kkst_tid -
116176107Sdwmalone            ((const struct kinfo_kstack *)b)->kkst_tid;
117174199Srwatson}
118174199Srwatson
119174199Srwatsonstatic void
120174199Srwatsonkinfo_kstack_sort(struct kinfo_kstack *kkstp, int count)
121174199Srwatson{
122174199Srwatson
123174199Srwatson        qsort(kkstp, count, sizeof(*kkstp), kinfo_kstack_compare);
124174199Srwatson}
125174199Srwatson
126174199Srwatson
127174199Srwatsonvoid
128249685Strocinyprocstat_kstack(struct procstat *procstat, struct kinfo_proc *kipp, int kflag)
129174199Srwatson{
130174199Srwatson	struct kinfo_kstack *kkstp, *kkstp_free;
131174230Srwatson	struct kinfo_proc *kip, *kip_free;
132174199Srwatson	char trace[KKST_MAXLEN];
133176107Sdwmalone	unsigned int i, j;
134249685Strociny	unsigned int kip_count, kstk_count;
135174199Srwatson
136174199Srwatson	if (!hflag)
137174230Srwatson		printf("%5s %6s %-16s %-16s %-29s\n", "PID", "TID", "COMM",
138174230Srwatson		    "TDNAME", "KSTACK");
139174199Srwatson
140249685Strociny	kkstp = kkstp_free = procstat_getkstack(procstat, kipp, &kstk_count);
141174199Srwatson	if (kkstp == NULL)
142174199Srwatson		return;
143174199Srwatson
144174230Srwatson	/*
145174230Srwatson	 * We need to re-query for thread information, so don't use *kipp.
146174230Srwatson	 */
147249685Strociny	kip = kip_free = procstat_getprocs(procstat,
148249685Strociny	    KERN_PROC_PID | KERN_PROC_INC_THREAD, kipp->ki_pid, &kip_count);
149174230Srwatson
150249685Strociny	if (kip == NULL) {
151249685Strociny		procstat_freekstack(procstat, kkstp_free);
152174230Srwatson		return;
153174230Srwatson	}
154174230Srwatson
155249685Strociny	kinfo_kstack_sort(kkstp, kstk_count);
156249685Strociny	for (i = 0; i < kstk_count; i++) {
157174199Srwatson		kkstp = &kkstp_free[i];
158174230Srwatson
159174230Srwatson		/*
160174230Srwatson		 * Look up the specific thread using its tid so we can
161174230Srwatson		 * display the per-thread command line.
162174230Srwatson		 */
163174230Srwatson		kipp = NULL;
164249685Strociny		for (j = 0; j < kip_count; j++) {
165174230Srwatson			kipp = &kip_free[j];
166174230Srwatson			if (kkstp->kkst_tid == kipp->ki_tid)
167174230Srwatson				break;
168174230Srwatson		}
169174230Srwatson		if (kipp == NULL)
170174230Srwatson			continue;
171174230Srwatson
172221807Sstas		printf("%5d ", kipp->ki_pid);
173174199Srwatson		printf("%6d ", kkstp->kkst_tid);
174174230Srwatson		printf("%-16s ", kipp->ki_comm);
175224199Sbz		printf("%-16s ", (strlen(kipp->ki_tdname) &&
176224199Sbz		    (strcmp(kipp->ki_comm, kipp->ki_tdname) != 0)) ?
177224199Sbz		    kipp->ki_tdname : "-");
178174199Srwatson
179174199Srwatson		switch (kkstp->kkst_state) {
180174199Srwatson		case KKST_STATE_RUNNING:
181174230Srwatson			printf("%-29s\n", "<running>");
182174199Srwatson			continue;
183174199Srwatson
184174199Srwatson		case KKST_STATE_SWAPPED:
185174230Srwatson			printf("%-29s\n", "<swapped>");
186174199Srwatson			continue;
187174199Srwatson
188174199Srwatson		case KKST_STATE_STACKOK:
189174199Srwatson			break;
190174199Srwatson
191174199Srwatson		default:
192174230Srwatson			printf("%-29s\n", "<unknown>");
193174199Srwatson			continue;
194174199Srwatson		}
195174199Srwatson
196174199Srwatson		/*
197174199Srwatson		 * The kernel generates a trace with carriage returns between
198174199Srwatson		 * entries, but for a more compact view, we convert carriage
199174199Srwatson		 * returns to spaces.
200174199Srwatson		 */
201174199Srwatson		kstack_cleanup(kkstp->kkst_trace, trace, kflag);
202174230Srwatson		printf("%-29s\n", trace);
203174199Srwatson	}
204249685Strociny	procstat_freekstack(procstat, kkstp_free);
205249685Strociny	procstat_freeprocs(procstat, kip_free);
206174199Srwatson}
207