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 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 #pragma ident "%Z%%M% %I% %E% SMI"
28
29 #include <mdb/mdb_modapi.h>
30 #include <sys/rctl.h>
31 #include <sys/proc.h>
32 #include <sys/task.h>
33 #include <sys/project.h>
34 #include <sys/zone.h>
35
36 static int
print_val(uintptr_t addr,rctl_val_t * val,uintptr_t * enforced)37 print_val(uintptr_t addr, rctl_val_t *val, uintptr_t *enforced)
38 {
39 char *priv;
40 static const mdb_bitmask_t val_localflag_bits[] = {
41 { "SIGNAL", RCTL_LOCAL_SIGNAL, RCTL_LOCAL_SIGNAL },
42 { "DENY", RCTL_LOCAL_DENY, RCTL_LOCAL_DENY },
43 { "MAX", RCTL_LOCAL_MAXIMAL, RCTL_LOCAL_MAXIMAL },
44 { NULL, 0, 0 }
45 };
46
47 switch (val->rcv_privilege) {
48 case (RCPRIV_BASIC):
49 priv = "basic";
50 break;
51 case (RCPRIV_PRIVILEGED):
52 priv = "privileged";
53 break;
54 case (RCPRIV_SYSTEM):
55 priv = "system";
56 break;
57 default:
58 priv = "???";
59 break;
60 };
61
62 mdb_printf("\t%s ", addr == *enforced ? "(cur)": " ");
63
64 mdb_printf("%-#18llx %11s\tflags=<%b>\n",
65 val->rcv_value, priv, val->rcv_flagaction, val_localflag_bits);
66
67 return (WALK_NEXT);
68 }
69
70 int
rctl(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)71 rctl(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
72 {
73 rctl_t rctl;
74 rctl_dict_entry_t dict;
75 char name[256];
76 rctl_hndl_t hndl;
77
78 if (!(flags & DCMD_ADDRSPEC))
79 return (DCMD_USAGE);
80
81 if (mdb_vread(&rctl, sizeof (rctl_t), addr) == -1) {
82 mdb_warn("failed to read rctl_t structure at %p", addr);
83 return (DCMD_ERR);
84 }
85
86 if (argc != 0) {
87 const mdb_arg_t *argp = &argv[0];
88
89 if (argp->a_type == MDB_TYPE_IMMEDIATE)
90 hndl = (rctl_hndl_t)argp->a_un.a_val;
91 else
92 hndl = (rctl_hndl_t)mdb_strtoull(argp->a_un.a_str);
93
94 if (rctl.rc_id != hndl)
95 return (DCMD_OK);
96 }
97
98 if (mdb_vread(&dict, sizeof (rctl_dict_entry_t),
99 (uintptr_t)rctl.rc_dict_entry) == -1) {
100 mdb_warn("failed to read dict entry for rctl_t %p at %p",
101 addr, rctl.rc_dict_entry);
102 return (DCMD_ERR);
103 }
104
105 if (mdb_readstr(name, 256, (uintptr_t)(dict.rcd_name)) == -1) {
106 mdb_warn("failed to read name for rctl_t %p", addr);
107 return (DCMD_ERR);
108 }
109
110 mdb_printf("%0?p\t%3d : %s\n", addr, rctl.rc_id, name);
111
112 if (mdb_pwalk("rctl_val", (mdb_walk_cb_t)print_val, &(rctl.rc_cursor),
113 addr) == -1) {
114 mdb_warn("failed to walk all values for rctl_t %p", addr);
115 return (DCMD_ERR);
116 }
117
118 return (DCMD_OK);
119 }
120
121 int
rctl_dict(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)122 rctl_dict(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
123 {
124 rctl_dict_entry_t dict;
125 char name[256], *type = NULL;
126
127 if (!(flags & DCMD_ADDRSPEC)) {
128 if (mdb_walk_dcmd("rctl_dict_list", "rctl_dict", argc,
129 argv) == -1) {
130 mdb_warn("failed to walk 'rctl_dict_list'");
131 return (DCMD_ERR);
132 }
133 return (DCMD_OK);
134 }
135
136 if (DCMD_HDRSPEC(flags))
137 mdb_printf("%<u>%2s %-27s %?s %7s %s%</u>\n",
138 "ID", "NAME", "ADDR", "TYPE", "GLOBAL_FLAGS");
139
140 if (mdb_vread(&dict, sizeof (dict), addr) == -1) {
141 mdb_warn("failed to read rctl_dict at %p", addr);
142 return (DCMD_ERR);
143 }
144 if (mdb_readstr(name, 256, (uintptr_t)(dict.rcd_name)) == -1) {
145 mdb_warn("failed to read rctl_dict name for %p", addr);
146 return (DCMD_ERR);
147 }
148
149 switch (dict.rcd_entity) {
150 case RCENTITY_PROCESS:
151 type = "process";
152 break;
153 case RCENTITY_TASK:
154 type = "task";
155 break;
156 case RCENTITY_PROJECT:
157 type = "project";
158 break;
159 case RCENTITY_ZONE:
160 type = "zone";
161 break;
162 default:
163 type = "unknown";
164 break;
165 }
166
167 mdb_printf("%2d %-27s %0?p %7s 0x%08x", dict.rcd_id, name, addr,
168 type, dict.rcd_flagaction);
169
170 return (DCMD_OK);
171 }
172
173 typedef struct dict_data {
174 rctl_hndl_t hndl;
175 uintptr_t dict_addr;
176 rctl_entity_t type;
177 } dict_data_t;
178
179 static int
hndl2dict(uintptr_t addr,rctl_dict_entry_t * entry,dict_data_t * data)180 hndl2dict(uintptr_t addr, rctl_dict_entry_t *entry, dict_data_t *data)
181 {
182 if (data->hndl == entry->rcd_id) {
183 data->dict_addr = addr;
184 data->type = entry->rcd_entity;
185 return (WALK_DONE);
186 }
187
188 return (WALK_NEXT);
189 }
190
191 /*
192 * Print out all project, task, and process rctls for a given process.
193 * If a handle is specified, print only the rctl matching that handle
194 * for the process.
195 */
196 int
rctl_list(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)197 rctl_list(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
198 {
199 proc_t proc;
200 uintptr_t set;
201 task_t task;
202 kproject_t proj;
203 zone_t zone;
204 dict_data_t rdict;
205 int i;
206
207 rdict.dict_addr = NULL;
208
209 if (!(flags & DCMD_ADDRSPEC))
210 return (DCMD_USAGE);
211
212 if (argc == 0)
213 rdict.hndl = 0;
214 else if (argc == 1) {
215 /*
216 * User specified a handle. Go find the rctl_dict_entity_t
217 * structure so we know what type of rctl to look for.
218 */
219 const mdb_arg_t *argp = &argv[0];
220
221 if (argp->a_type == MDB_TYPE_IMMEDIATE)
222 rdict.hndl = (rctl_hndl_t)argp->a_un.a_val;
223 else
224 rdict.hndl =
225 (rctl_hndl_t)mdb_strtoull(argp->a_un.a_str);
226
227 if (mdb_walk("rctl_dict_list", (mdb_walk_cb_t)hndl2dict,
228 &rdict) == -1) {
229 mdb_warn("failed to walk rctl_dict_list");
230 return (DCMD_ERR);
231 }
232 /* Couldn't find a rctl_dict_entry_t for this handle */
233 if (rdict.dict_addr == NULL)
234 return (DCMD_ERR);
235 } else
236 return (DCMD_USAGE);
237
238
239 if (mdb_vread(&proc, sizeof (proc_t), addr) == -1) {
240 mdb_warn("failed to read proc at %p", addr);
241 return (DCMD_ERR);
242 }
243 if (mdb_vread(&zone, sizeof (zone_t), (uintptr_t)proc.p_zone) == -1) {
244 mdb_warn("failed to read zone at %p", proc.p_zone);
245 return (DCMD_ERR);
246 }
247 if (mdb_vread(&task, sizeof (task_t), (uintptr_t)proc.p_task) == -1) {
248 mdb_warn("failed to read task at %p", proc.p_task);
249 return (DCMD_ERR);
250 }
251 if (mdb_vread(&proj, sizeof (kproject_t),
252 (uintptr_t)task.tk_proj) == -1) {
253 mdb_warn("failed to read proj at %p", task.tk_proj);
254 return (DCMD_ERR);
255 }
256
257 for (i = 0; i <= RC_MAX_ENTITY; i++) {
258 /*
259 * If user didn't specify a handle, print rctls for all
260 * types. Otherwise, we can walk the rctl_set for only the
261 * entity specified by the handle.
262 */
263 if (rdict.hndl != 0 && rdict.type != i)
264 continue;
265
266 switch (i) {
267 case (RCENTITY_PROCESS):
268 set = (uintptr_t)proc.p_rctls;
269 break;
270 case (RCENTITY_TASK):
271 set = (uintptr_t)task.tk_rctls;
272 break;
273 case (RCENTITY_PROJECT):
274 set = (uintptr_t)proj.kpj_rctls;
275 break;
276 case (RCENTITY_ZONE):
277 set = (uintptr_t)zone.zone_rctls;
278 break;
279 default:
280 mdb_warn("Unknown rctl type %d", i);
281 return (DCMD_ERR);
282 }
283
284 if (mdb_pwalk_dcmd("rctl_set", "rctl", argc, argv, set) == -1) {
285 mdb_warn("failed to walk rctls in set %p", set);
286 return (DCMD_ERR);
287 }
288 }
289
290 return (DCMD_OK);
291 }
292
293 typedef struct dict_walk_data {
294 int num_dicts;
295 int num_cur;
296 rctl_dict_entry_t **curdict;
297 } dict_walk_data_t;
298
299 int
rctl_dict_walk_init(mdb_walk_state_t * wsp)300 rctl_dict_walk_init(mdb_walk_state_t *wsp)
301 {
302 uintptr_t ptr;
303 int nlists;
304 GElf_Sym sym;
305 rctl_dict_entry_t **dicts;
306 dict_walk_data_t *dwd;
307
308 if (mdb_lookup_by_name("rctl_lists", &sym) == -1) {
309 mdb_warn("failed to find 'rctl_lists'\n");
310 return (WALK_ERR);
311 }
312
313 nlists = sym.st_size / sizeof (rctl_dict_entry_t *);
314 ptr = (uintptr_t)sym.st_value;
315
316 dicts = mdb_alloc(nlists * sizeof (rctl_dict_entry_t *), UM_SLEEP);
317 mdb_vread(dicts, sym.st_size, ptr);
318
319 dwd = mdb_alloc(sizeof (dict_walk_data_t), UM_SLEEP);
320 dwd->num_dicts = nlists;
321 dwd->num_cur = 0;
322 dwd->curdict = dicts;
323
324 wsp->walk_addr = 0;
325 wsp->walk_data = dwd;
326
327 return (WALK_NEXT);
328 }
329
330 int
rctl_dict_walk_step(mdb_walk_state_t * wsp)331 rctl_dict_walk_step(mdb_walk_state_t *wsp)
332 {
333 dict_walk_data_t *dwd = wsp->walk_data;
334 uintptr_t dp;
335 rctl_dict_entry_t entry;
336 int status;
337
338 dp = (uintptr_t)((dwd->curdict)[dwd->num_cur]);
339
340 while (dp != NULL) {
341 if (mdb_vread(&entry, sizeof (rctl_dict_entry_t), dp) == -1) {
342 mdb_warn("failed to read rctl_dict_entry_t structure "
343 "at %p", dp);
344 return (WALK_ERR);
345 }
346
347 status = wsp->walk_callback(dp, &entry, wsp->walk_cbdata);
348 if (status != WALK_NEXT)
349 return (status);
350
351 dp = (uintptr_t)entry.rcd_next;
352 }
353
354 dwd->num_cur++;
355
356 if (dwd->num_cur == dwd->num_dicts)
357 return (WALK_DONE);
358
359 return (WALK_NEXT);
360 }
361
362 void
rctl_dict_walk_fini(mdb_walk_state_t * wsp)363 rctl_dict_walk_fini(mdb_walk_state_t *wsp)
364 {
365 dict_walk_data_t *wd = wsp->walk_data;
366 mdb_free(wd->curdict, wd->num_dicts * sizeof (rctl_dict_entry_t *));
367 mdb_free(wd, sizeof (dict_walk_data_t));
368 }
369
370 typedef struct set_walk_data {
371 uint_t hashsize;
372 int hashcur;
373 void **hashloc;
374 } set_walk_data_t;
375
376 int
rctl_set_walk_init(mdb_walk_state_t * wsp)377 rctl_set_walk_init(mdb_walk_state_t *wsp)
378 {
379 rctl_set_t rset;
380 uint_t hashsz;
381 set_walk_data_t *swd;
382 rctl_t **rctls;
383
384 if (mdb_vread(&rset, sizeof (rctl_set_t), wsp->walk_addr) == -1) {
385 mdb_warn("failed to read rset at %p", wsp->walk_addr);
386 return (WALK_ERR);
387 }
388
389 if (mdb_readvar(&hashsz, "rctl_set_size") == -1 || hashsz == 0) {
390 mdb_warn("rctl_set_size not found or invalid");
391 return (WALK_ERR);
392 }
393
394 rctls = mdb_alloc(hashsz * sizeof (rctl_t *), UM_SLEEP);
395 if (mdb_vread(rctls, hashsz * sizeof (rctl_t *),
396 (uintptr_t)rset.rcs_ctls) == -1) {
397 mdb_warn("cannot read rctl hash at %p", rset.rcs_ctls);
398 mdb_free(rctls, hashsz * sizeof (rctl_t *));
399 return (WALK_ERR);
400 }
401
402 swd = mdb_alloc(sizeof (set_walk_data_t), UM_SLEEP);
403 swd->hashsize = hashsz;
404 swd->hashcur = 0;
405 swd->hashloc = (void **)rctls;
406
407 wsp->walk_addr = 0;
408 wsp->walk_data = swd;
409
410 return (WALK_NEXT);
411 }
412
413
414 int
rctl_set_walk_step(mdb_walk_state_t * wsp)415 rctl_set_walk_step(mdb_walk_state_t *wsp)
416 {
417 set_walk_data_t *swd = wsp->walk_data;
418 rctl_t rctl;
419 void **rhash = swd->hashloc;
420 int status;
421
422 if (swd->hashcur >= swd->hashsize)
423 return (WALK_DONE);
424
425 if (wsp->walk_addr == NULL) {
426 while (swd->hashcur < swd->hashsize) {
427 if (rhash[swd->hashcur] != NULL) {
428 break;
429 }
430 swd->hashcur++;
431 }
432
433 if (rhash[swd->hashcur] == NULL ||
434 swd->hashcur >= swd->hashsize)
435 return (WALK_DONE);
436
437 wsp->walk_addr = (uintptr_t)rhash[swd->hashcur];
438 swd->hashcur++;
439 }
440
441 if (mdb_vread(&rctl, sizeof (rctl_t), wsp->walk_addr) == -1) {
442 wsp->walk_addr = NULL;
443 mdb_warn("unable to read from %#p", wsp->walk_addr);
444 return (WALK_ERR);
445 }
446
447 status = wsp->walk_callback(wsp->walk_addr, &rctl, wsp->walk_cbdata);
448
449 wsp->walk_addr = (uintptr_t)rctl.rc_next;
450
451 return (status);
452 }
453
454 void
rctl_set_walk_fini(mdb_walk_state_t * wsp)455 rctl_set_walk_fini(mdb_walk_state_t *wsp)
456 {
457 set_walk_data_t *sd = wsp->walk_data;
458
459 mdb_free(sd->hashloc, sd->hashsize * sizeof (rctl_t *));
460 mdb_free(sd, sizeof (set_walk_data_t));
461 }
462
463 int
rctl_val_walk_init(mdb_walk_state_t * wsp)464 rctl_val_walk_init(mdb_walk_state_t *wsp)
465 {
466 rctl_t rctl;
467
468 if (mdb_vread(&rctl, sizeof (rctl_t), wsp->walk_addr) == -1) {
469 mdb_warn("failed to read rctl at %p", wsp->walk_addr);
470 return (WALK_ERR);
471 }
472 wsp->walk_addr = (uintptr_t)rctl.rc_values;
473 wsp->walk_data = rctl.rc_values;
474 return (WALK_NEXT);
475 }
476
477 int
rctl_val_walk_step(mdb_walk_state_t * wsp)478 rctl_val_walk_step(mdb_walk_state_t *wsp)
479 {
480 rctl_val_t val;
481 int status;
482
483 if (mdb_vread(&val, sizeof (rctl_val_t), wsp->walk_addr) == -1) {
484 mdb_warn("failed to read rctl_val at %p", wsp->walk_addr);
485 return (WALK_DONE);
486 }
487
488 status = wsp->walk_callback(wsp->walk_addr, &val, wsp->walk_cbdata);
489
490 if ((wsp->walk_addr = (uintptr_t)val.rcv_next) == NULL)
491 return (WALK_DONE);
492
493 return (status);
494 }
495
496 typedef struct rctl_val_seen {
497 uintptr_t s_ptr;
498 rctl_qty_t s_val;
499 } rctl_val_seen_t;
500
501 typedef struct rctl_validate_data {
502 uintptr_t v_rctl_addr;
503 rctl_val_t *v_cursor;
504 uint_t v_flags;
505 int v_bad_rctl;
506 int v_cursor_valid;
507 int v_circularity_detected;
508 uint_t v_seen_size;
509 uint_t v_seen_cnt;
510 rctl_val_seen_t *v_seen;
511 } rctl_validate_data_t;
512
513 #define RCV_VERBOSE 0x1
514
515 /*
516 * rctl_val_validate()
517 * Do validation on an individual rctl_val_t. This function is called
518 * as part of the rctl_val walker, and helps perform the checks described
519 * in the ::rctl_validate dcmd.
520 */
521 static int
rctl_val_validate(uintptr_t addr,rctl_val_t * val,rctl_validate_data_t * data)522 rctl_val_validate(uintptr_t addr, rctl_val_t *val, rctl_validate_data_t *data)
523 {
524 int i;
525
526 data->v_seen[data->v_seen_cnt].s_ptr = addr;
527
528 if (addr == (uintptr_t)data->v_cursor)
529 data->v_cursor_valid++;
530
531 data->v_seen[data->v_seen_cnt].s_val = val->rcv_value;
532
533 if (val->rcv_prev == (void *)0xbaddcafe ||
534 val->rcv_next == (void *)0xbaddcafe ||
535 val->rcv_prev == (void *)0xdeadbeef ||
536 val->rcv_next == (void *)0xdeadbeef) {
537 if (data->v_bad_rctl++ == 0)
538 mdb_printf("%p ", data->v_rctl_addr);
539 if (data->v_flags & RCV_VERBOSE)
540 mdb_printf("/ uninitialized or previously "
541 "freed link at %p ", addr);
542 }
543
544 if (data->v_seen_cnt == 0) {
545 if (val->rcv_prev != NULL) {
546 if (data->v_bad_rctl++ == 0)
547 mdb_printf("%p ", data->v_rctl_addr);
548 if (data->v_flags & RCV_VERBOSE)
549 mdb_printf("/ bad prev pointer at "
550 "head ");
551 }
552 } else {
553 if ((uintptr_t)val->rcv_prev !=
554 data->v_seen[data->v_seen_cnt - 1].s_ptr) {
555 if (data->v_bad_rctl++ == 0)
556 mdb_printf("%p ", data->v_rctl_addr);
557 if (data->v_flags & RCV_VERBOSE)
558 mdb_printf("/ bad prev pointer at %p ",
559 addr);
560 }
561
562 if (data->v_seen[data->v_seen_cnt].s_val <
563 data->v_seen[data->v_seen_cnt - 1].s_val) {
564 if (data->v_bad_rctl++ == 0)
565 mdb_printf("%p ", data->v_rctl_addr);
566 if (data->v_flags & RCV_VERBOSE)
567 mdb_printf("/ ordering error at %p ",
568 addr);
569 }
570 }
571
572 for (i = data->v_seen_cnt; i >= 0; i--) {
573 if (data->v_seen[i].s_ptr == (uintptr_t)val->rcv_next) {
574 if (data->v_bad_rctl++ == 0)
575 mdb_printf("%p ", data->v_rctl_addr);
576 if (data->v_flags & RCV_VERBOSE)
577 mdb_printf("/ circular next pointer "
578 "at %p ", addr);
579 data->v_circularity_detected++;
580 break;
581 }
582 }
583
584 if (data->v_circularity_detected)
585 return (WALK_DONE);
586
587 data->v_seen_cnt++;
588 if (data->v_seen_cnt >= data->v_seen_size) {
589 uint_t new_seen_size = data->v_seen_size * 2;
590 rctl_val_seen_t *tseen = mdb_zalloc(new_seen_size *
591 sizeof (rctl_val_seen_t), UM_SLEEP | UM_GC);
592
593 bcopy(data->v_seen, tseen, data->v_seen_size *
594 sizeof (rctl_val_seen_t));
595
596 data->v_seen = tseen;
597 data->v_seen_size = new_seen_size;
598 }
599
600 return (WALK_NEXT);
601 }
602
603 /*
604 * Validate a rctl pointer by checking:
605 * - rctl_val_t's for that rctl form an ordered, non-circular list
606 * - the cursor points to a rctl_val_t within that list
607 * - there are no more than UINT64_MAX (or # specified by -n)
608 * rctl_val_t's in the list
609 */
610 int
rctl_validate(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)611 rctl_validate(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
612 {
613 rctl_validate_data_t data;
614
615 rctl_t r;
616
617 uint64_t long_threshold = UINT64_MAX;
618
619 /* Initialize validate data structure */
620 data.v_rctl_addr = addr;
621 data.v_flags = 0;
622 data.v_bad_rctl = 0;
623 data.v_seen_cnt = 0;
624 data.v_cursor_valid = 0;
625 data.v_circularity_detected = 0;
626 data.v_seen_size = 1;
627 data.v_seen = mdb_zalloc(data.v_seen_size * sizeof (rctl_val_seen_t),
628 UM_SLEEP | UM_GC);
629
630 if (!(flags & DCMD_ADDRSPEC))
631 return (DCMD_USAGE);
632
633 if (mdb_getopts(argc, argv,
634 'v', MDB_OPT_SETBITS, RCV_VERBOSE, &data.v_flags,
635 'n', MDB_OPT_UINT64, &long_threshold,
636 NULL) != argc)
637 return (DCMD_USAGE);
638
639 if (mdb_vread(&r, sizeof (rctl_t), addr) != sizeof (rctl_t)) {
640 mdb_warn("failed to read rctl structure at %p", addr);
641 return (DCMD_ERR);
642 }
643
644 data.v_cursor = r.rc_cursor;
645
646 if (data.v_cursor == NULL) {
647 if (data.v_bad_rctl++ == 0)
648 mdb_printf("%p ", addr);
649 if (data.v_flags & RCV_VERBOSE)
650 mdb_printf("/ NULL cursor seen ");
651 } else if (data.v_cursor == (rctl_val_t *)0xbaddcafe) {
652 if (data.v_bad_rctl++ == 0)
653 mdb_printf("%p ", addr);
654 if (data.v_flags & RCV_VERBOSE)
655 mdb_printf("/ uninitialized cursor seen ");
656 }
657
658 /* Walk through each val in this rctl for individual validation. */
659 if (mdb_pwalk("rctl_val", (mdb_walk_cb_t)rctl_val_validate, &data,
660 addr) == -1) {
661 mdb_warn("failed to walk all values for rctl_t %p", addr);
662 return (DCMD_ERR);
663 }
664
665 if (data.v_seen_cnt >= long_threshold) {
666 if (data.v_bad_rctl++ == 0)
667 mdb_printf("%p ", addr);
668 if (data.v_flags & RCV_VERBOSE)
669 mdb_printf("/ sequence length = %d ",
670 data.v_seen_cnt);
671 }
672
673 if (!data.v_cursor_valid) {
674 if (data.v_bad_rctl++ == 0)
675 mdb_printf("%p ", addr);
676 if (data.v_flags & RCV_VERBOSE)
677 mdb_printf("/ cursor outside sequence");
678 }
679
680 if (data.v_bad_rctl)
681 mdb_printf("\n");
682
683 if (data.v_circularity_detected)
684 mdb_warn("circular list implies possible memory leak; "
685 "recommend invoking ::findleaks");
686
687 return (DCMD_OK);
688 }
689