xref: /illumos-gate/usr/src/cmd/mdb/common/modules/genunix/taskq.c (revision 7ab4e62e3b5c454f248a38bec0d489e8f5543324)
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 (the "License").
6  * You may not use this file except in compliance with the License.
7  *
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.
12  *
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]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <mdb/mdb_param.h>
27 #include <mdb/mdb_modapi.h>
28 #include <mdb/mdb_ks.h>
29 #include <sys/taskq.h>
30 #include <sys/taskq_impl.h>
31 
32 #include "taskq.h"
33 
34 typedef struct tqarray_ent {
35 	uintptr_t	tq_addr;
36 	char		tq_name[TASKQ_NAMELEN + 1];
37 	int		tq_instance;
38 	uint_t		tq_flags;
39 } tqarray_ent_t;
40 
41 typedef struct tq_info {
42 	tqarray_ent_t	*tqi_array;
43 	size_t		tqi_count;
44 	size_t		tqi_size;
45 } tq_info_t;
46 
47 /*
48  * We sort taskqs as follows:
49  *
50  *	DYNAMIC last
51  *	NOINSTANCE first
52  *	within NOINSTANCE, sort by order of creation (instance #)
53  *	within non-NOINSTANCE, sort by name (case-insensitive) then instance #
54  */
55 int
56 tqcmp(const void *lhs, const void *rhs)
57 {
58 	const tqarray_ent_t *l = lhs;
59 	const tqarray_ent_t *r = rhs;
60 	uint_t lflags = l->tq_flags;
61 	uint_t rflags = r->tq_flags;
62 	int ret;
63 
64 	if ((lflags & TASKQ_DYNAMIC) && !(rflags & TASKQ_DYNAMIC))
65 		return (1);
66 	if (!(lflags & TASKQ_DYNAMIC) && (rflags & TASKQ_DYNAMIC))
67 		return (-1);
68 
69 	if ((lflags & TASKQ_NOINSTANCE) && !(rflags & TASKQ_NOINSTANCE))
70 		return (-1);
71 	if (!(lflags & TASKQ_NOINSTANCE) && (rflags & TASKQ_NOINSTANCE))
72 		return (1);
73 
74 	if (!(lflags & TASKQ_NOINSTANCE) &&
75 	    (ret = strcasecmp(l->tq_name, r->tq_name)) != 0)
76 		return (ret);
77 
78 	if (l->tq_instance < r->tq_instance)
79 		return (-1);
80 	if (l->tq_instance > r->tq_instance)
81 		return (1);
82 	return (0);
83 }
84 
85 /*ARGSUSED*/
86 int
87 tq_count(uintptr_t addr, const void *ignored, void *arg)
88 {
89 	tq_info_t *ti = arg;
90 
91 	ti->tqi_size++;
92 	return (WALK_NEXT);
93 }
94 
95 /*ARGSUSED*/
96 int
97 tq_fill(uintptr_t addr, const void *ignored, tq_info_t *ti)
98 {
99 	int idx = ti->tqi_count;
100 	taskq_t tq;
101 	tqarray_ent_t *tqe = &ti->tqi_array[idx];
102 
103 	if (idx == ti->tqi_size) {
104 		mdb_warn("taskq: inadequate slop\n");
105 		return (WALK_ERR);
106 	}
107 	if (mdb_vread(&tq, sizeof (tq), addr) == -1) {
108 		mdb_warn("unable to read taskq_t at %p", addr);
109 		return (WALK_NEXT);
110 	}
111 
112 	ti->tqi_count++;
113 	tqe->tq_addr = addr;
114 	strncpy(tqe->tq_name, tq.tq_name, TASKQ_NAMELEN);
115 	tqe->tq_instance = tq.tq_instance;
116 	tqe->tq_flags = tq.tq_flags;
117 
118 	return (WALK_NEXT);
119 }
120 
121 void
122 taskq_help(void)
123 {
124 	mdb_printf("%s",
125 	    "  -a    Only show taskqs with active threads.\n"
126 	    "  -t    Display active thread stacks in each taskq.\n"
127 	    "  -T    Display all thread stacks in each taskq.\n"
128 	    "  -m min_maxq\n"
129 	    "        Only show Dynamic taskqs and taskqs with a MAXQ of at\n"
130 	    "        least min_maxq.\n"
131 	    "  -n name\n"
132 	    "        Only show taskqs which contain name somewhere in their\n"
133 	    "        name.\n");
134 }
135 
136 int
137 taskq(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
138 {
139 	taskq_t tq;
140 
141 	const char *name = NULL;
142 	uintptr_t minmaxq = 0;
143 	uint_t	active = FALSE;
144 	uint_t	print_threads = FALSE;
145 	uint_t	print_threads_all = FALSE;
146 
147 	size_t tact, tcount, queued, maxq;
148 
149 	if (mdb_getopts(argc, argv,
150 	    'a', MDB_OPT_SETBITS, TRUE, &active,
151 	    'm', MDB_OPT_UINTPTR, &minmaxq,
152 	    'n', MDB_OPT_STR, &name,
153 	    't', MDB_OPT_SETBITS, TRUE, &print_threads,
154 	    'T', MDB_OPT_SETBITS, TRUE, &print_threads_all,
155 	    NULL) != argc)
156 		return (DCMD_USAGE);
157 
158 	if (!(flags & DCMD_ADDRSPEC)) {
159 		size_t idx;
160 		tq_info_t tqi;
161 
162 		bzero(&tqi, sizeof (tqi));
163 
164 		if (mdb_walk("taskq_cache", tq_count, &tqi) == -1) {
165 			mdb_warn("unable to walk taskq_cache");
166 			return (DCMD_ERR);
167 		}
168 		tqi.tqi_size += 10; 	/* slop */
169 		tqi.tqi_array = mdb_zalloc(
170 		    sizeof (*tqi.tqi_array) * tqi.tqi_size, UM_SLEEP|UM_GC);
171 
172 		if (mdb_walk("taskq_cache", (mdb_walk_cb_t)tq_fill,
173 		    &tqi) == -1) {
174 			mdb_warn("unable to walk taskq_cache");
175 			return (DCMD_ERR);
176 		}
177 		qsort(tqi.tqi_array, tqi.tqi_count, sizeof (*tqi.tqi_array),
178 		    tqcmp);
179 
180 		flags &= ~DCMD_PIPE;
181 		flags |= DCMD_LOOP | DCMD_LOOPFIRST | DCMD_ADDRSPEC;
182 		for (idx = 0; idx < tqi.tqi_count; idx++) {
183 			int ret = taskq(tqi.tqi_array[idx].tq_addr, flags,
184 			    argc, argv);
185 			if (ret != DCMD_OK)
186 				return (ret);
187 			flags &= ~DCMD_LOOPFIRST;
188 		}
189 
190 		return (DCMD_OK);
191 	}
192 
193 	if (DCMD_HDRSPEC(flags) && !(flags & DCMD_PIPE_OUT)) {
194 		mdb_printf("%<u>%-?s %-31s %4s/%4s %4s %5s %4s%</u>\n",
195 		    "ADDR", "NAME", "ACT", "THDS",
196 		    "Q'ED", "MAXQ", "INST");
197 	}
198 
199 	if (mdb_vread(&tq, sizeof (tq), addr) == -1) {
200 		mdb_warn("failed to read taskq_t at %p", addr);
201 		return (DCMD_ERR);
202 	}
203 
204 	/* terminate the name, just in case */
205 	tq.tq_name[sizeof (tq.tq_name) - 1] = 0;
206 
207 	tact = tq.tq_active;
208 	tcount = tq.tq_nthreads;
209 	queued = tq.tq_tasks - tq.tq_executed;
210 	maxq = tq.tq_maxtasks;
211 
212 	if (tq.tq_flags & TASKQ_DYNAMIC) {
213 		size_t bsize = tq.tq_nbuckets * sizeof (*tq.tq_buckets);
214 		size_t idx;
215 		taskq_bucket_t *b = mdb_zalloc(bsize, UM_SLEEP | UM_GC);
216 
217 		if (mdb_vread(b, bsize, (uintptr_t)tq.tq_buckets) == -1) {
218 			mdb_warn("unable to read buckets for taskq %p", addr);
219 			return (DCMD_ERR);
220 		}
221 
222 		tcount += (tq.tq_tcreates - tq.tq_tdeaths);
223 
224 		for (idx = 0; idx < tq.tq_nbuckets; idx++) {
225 			tact += b[idx].tqbucket_nalloc;
226 		}
227 	}
228 
229 	/* filter out taskqs that aren't of interest. */
230 	if (name != NULL && strstr(tq.tq_name, name) == NULL)
231 		return (DCMD_OK);
232 	if (active && tact == 0 && queued == 0)
233 		return (DCMD_OK);
234 	if (!(tq.tq_flags & TASKQ_DYNAMIC) && maxq < minmaxq)
235 		return (DCMD_OK);
236 
237 	if (flags & DCMD_PIPE_OUT) {
238 		mdb_printf("%#lr\n", addr);
239 		return (DCMD_OK);
240 	}
241 
242 	mdb_printf("%?p %-31s %4d/%4d %4d ",
243 	    addr, tq.tq_name, tact, tcount, queued);
244 
245 	if (tq.tq_flags & TASKQ_DYNAMIC)
246 		mdb_printf("%5s ", "-");
247 	else
248 		mdb_printf("%5d ", maxq);
249 
250 	if (tq.tq_flags & TASKQ_NOINSTANCE)
251 		mdb_printf("%4s", "-");
252 	else
253 		mdb_printf("%4x", tq.tq_instance);
254 
255 	mdb_printf("\n");
256 
257 	if (print_threads || print_threads_all) {
258 		int ret;
259 		char strbuf[128];
260 		const char *arg =
261 		    print_threads_all ? "" : "-C \"taskq_thread_wait\"";
262 
263 		/*
264 		 * We can't use mdb_pwalk_dcmd() here, because ::stacks needs
265 		 * to get the full pipeline.
266 		 */
267 		mdb_snprintf(strbuf, sizeof (strbuf),
268 		    "%p::walk taskq_thread | ::stacks -a %s",
269 		    addr, arg);
270 
271 		(void) mdb_inc_indent(4);
272 		ret = mdb_eval(strbuf);
273 		(void) mdb_dec_indent(4);
274 
275 		/* abort, since they could have control-Ced the eval */
276 		if (ret == -1)
277 			return (DCMD_ABORT);
278 	}
279 
280 	return (DCMD_OK);
281 }
282 
283 /*
284  * Dump a taskq_ent_t given its address.
285  */
286 /*ARGSUSED*/
287 int
288 taskq_ent(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
289 {
290 	taskq_ent_t	taskq_ent;
291 
292 	if (!(flags & DCMD_ADDRSPEC)) {
293 		return (DCMD_USAGE);
294 	}
295 
296 	if (mdb_vread(&taskq_ent, sizeof (taskq_ent_t), addr) == -1) {
297 		mdb_warn("failed to read taskq_ent_t at %p", addr);
298 		return (DCMD_ERR);
299 	}
300 
301 	if (DCMD_HDRSPEC(flags)) {
302 		mdb_printf("%<u>%-?s %-?s %-s%</u>\n",
303 		"ENTRY", "ARG", "FUNCTION");
304 	}
305 
306 	mdb_printf("%-?p %-?p %a\n", addr, taskq_ent.tqent_arg,
307 	    taskq_ent.tqent_func);
308 
309 	return (DCMD_OK);
310 }
311 
312 
313 /*
314  * Given the address of the (taskq_t) task queue head, walk the queue listing
315  * the address of every taskq_ent_t.
316  */
317 int
318 taskq_ent_walk_init(mdb_walk_state_t *wsp)
319 {
320 	taskq_t	tq_head;
321 
322 
323 	if (wsp->walk_addr == NULL) {
324 		mdb_warn("start address required\n");
325 		return (WALK_ERR);
326 	}
327 
328 
329 	/*
330 	 * Save the address of the list head entry.  This terminates the list.
331 	 */
332 	wsp->walk_data = (void *)
333 	    ((size_t)wsp->walk_addr + OFFSETOF(taskq_t, tq_task));
334 
335 
336 	/*
337 	 * Read in taskq head, set walk_addr to point to first taskq_ent_t.
338 	 */
339 	if (mdb_vread((void *)&tq_head, sizeof (taskq_t), wsp->walk_addr) ==
340 	    -1) {
341 		mdb_warn("failed to read taskq list head at %p",
342 		    wsp->walk_addr);
343 	}
344 	wsp->walk_addr = (uintptr_t)tq_head.tq_task.tqent_next;
345 
346 
347 	/*
348 	 * Check for null list (next=head)
349 	 */
350 	if (wsp->walk_addr == (uintptr_t)wsp->walk_data) {
351 		return (WALK_DONE);
352 	}
353 
354 	return (WALK_NEXT);
355 }
356 
357 
358 int
359 taskq_ent_walk_step(mdb_walk_state_t *wsp)
360 {
361 	taskq_ent_t	tq_ent;
362 	int		status;
363 
364 
365 	if (mdb_vread((void *)&tq_ent, sizeof (taskq_ent_t), wsp->walk_addr) ==
366 	    -1) {
367 		mdb_warn("failed to read taskq_ent_t at %p", wsp->walk_addr);
368 		return (DCMD_ERR);
369 	}
370 
371 	status = wsp->walk_callback(wsp->walk_addr, (void *)&tq_ent,
372 	    wsp->walk_cbdata);
373 
374 	wsp->walk_addr = (uintptr_t)tq_ent.tqent_next;
375 
376 
377 	/* Check if we're at the last element (next=head) */
378 	if (wsp->walk_addr == (uintptr_t)wsp->walk_data) {
379 		return (WALK_DONE);
380 	}
381 
382 	return (status);
383 }
384 
385 typedef struct taskq_thread_info {
386 	uintptr_t	tti_addr;
387 	uintptr_t	*tti_tlist;
388 	size_t		tti_nthreads;
389 	size_t		tti_idx;
390 
391 	kthread_t	tti_thread;
392 } taskq_thread_info_t;
393 
394 int
395 taskq_thread_walk_init(mdb_walk_state_t *wsp)
396 {
397 	taskq_thread_info_t	*tti;
398 	taskq_t			tq;
399 	uintptr_t		*tlist;
400 	size_t			nthreads;
401 
402 	tti = wsp->walk_data = mdb_zalloc(sizeof (*tti), UM_SLEEP);
403 	tti->tti_addr = wsp->walk_addr;
404 
405 	if (wsp->walk_addr != NULL &&
406 	    mdb_vread(&tq, sizeof (tq), wsp->walk_addr) != -1 &&
407 	    !(tq.tq_flags & TASKQ_DYNAMIC)) {
408 
409 		nthreads = tq.tq_nthreads;
410 		tlist = mdb_alloc(nthreads * sizeof (*tlist), UM_SLEEP);
411 		if (tq.tq_nthreads_max == 1) {
412 			tlist[0] = (uintptr_t)tq.tq_thread;
413 
414 		} else if (mdb_vread(tlist, nthreads * sizeof (*tlist),
415 		    (uintptr_t)tq.tq_threadlist) == -1) {
416 			mdb_warn("unable to read threadlist for taskq_t %p",
417 			    wsp->walk_addr);
418 			mdb_free(tlist, nthreads * sizeof (*tlist));
419 			return (WALK_ERR);
420 		}
421 
422 		tti->tti_tlist = tlist;
423 		tti->tti_nthreads = nthreads;
424 		return (WALK_NEXT);
425 	}
426 
427 	wsp->walk_addr = 0;
428 	if (mdb_layered_walk("thread", wsp) == -1) {
429 		mdb_warn("can't walk \"thread\"");
430 		return (WALK_ERR);
431 	}
432 	return (0);
433 }
434 
435 int
436 taskq_thread_walk_step(mdb_walk_state_t *wsp)
437 {
438 	taskq_thread_info_t	*tti = wsp->walk_data;
439 
440 	const kthread_t *kt = wsp->walk_layer;
441 	taskq_t *tq = (taskq_t *)tti->tti_addr;
442 
443 	if (kt == NULL) {
444 		uintptr_t addr;
445 
446 		if (tti->tti_idx >= tti->tti_nthreads)
447 			return (WALK_DONE);
448 
449 		addr = tti->tti_tlist[tti->tti_idx];
450 		tti->tti_idx++;
451 
452 		if (addr == NULL)
453 			return (WALK_NEXT);
454 
455 		if (mdb_vread(&tti->tti_thread, sizeof (kthread_t),
456 		    addr) == -1) {
457 			mdb_warn("unable to read kthread_t at %p", addr);
458 			return (WALK_ERR);
459 		}
460 		return (wsp->walk_callback(addr, &tti->tti_thread,
461 		    wsp->walk_cbdata));
462 	}
463 
464 	if (kt->t_taskq == NULL)
465 		return (WALK_NEXT);
466 
467 	if (tq != NULL && kt->t_taskq != tq)
468 		return (WALK_NEXT);
469 
470 	return (wsp->walk_callback(wsp->walk_addr, kt, wsp->walk_cbdata));
471 }
472 
473 void
474 taskq_thread_walk_fini(mdb_walk_state_t *wsp)
475 {
476 	taskq_thread_info_t	*tti = wsp->walk_data;
477 
478 	if (tti->tti_nthreads > 0) {
479 		mdb_free(tti->tti_tlist,
480 		    tti->tti_nthreads * sizeof (*tti->tti_tlist));
481 	}
482 	mdb_free(tti, sizeof (*tti));
483 }
484