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 <mdb/mdb_ctf.h>
  31 
  32 #include <configd.h>
  33 
  34 mdb_ctf_id_t request_enum;
  35 mdb_ctf_id_t response_enum;
  36 mdb_ctf_id_t ptr_type_enum;
  37 mdb_ctf_id_t thread_state_enum;
  38 
  39 hrtime_t max_time_seen;
  40 
  41 static void
  42 enum_lookup(char *out, size_t size, mdb_ctf_id_t id, int val,
  43     const char *prefix, const char *prefix2)
  44 {
  45         const char *cp;
  46         size_t len = strlen(prefix);
  47         size_t len2 = strlen(prefix2);
  48 
  49         if ((cp = mdb_ctf_enum_name(id, val)) != NULL) {
  50                 if (strncmp(cp, prefix, len) == 0)
  51                         cp += len;
  52                 if (strncmp(cp, prefix2, len2) == 0)
  53                         cp += len2;
  54                 (void) strlcpy(out, cp, size);
  55         } else {
  56                 mdb_snprintf(out, size, "? (%d)", val);
  57         }
  58 }
  59 
  60 static void
  61 make_lower(char *out, size_t sz)
  62 {
  63         while (*out != 0 && sz > 0) {
  64                 if (*out >= 'A' && *out <= 'Z')
  65                         *out += 'a' - 'A';
  66                 out++;
  67                 sz--;
  68         }
  69 }
  70 
  71 /*ARGSUSED*/
  72 static int
  73 configd_status(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
  74 {
  75         int num_servers;
  76         int num_started;
  77 
  78         if (argc != 0)
  79                 return (DCMD_USAGE);
  80 
  81         if (mdb_readvar(&num_servers, "num_servers") == -1) {
  82                 mdb_warn("unable to read num_servers");
  83                 return (DCMD_ERR);
  84         }
  85         if (mdb_readvar(&num_started, "num_started") == -1) {
  86                 mdb_warn("unable to read num_started");
  87                 return (DCMD_ERR);
  88         }
  89         mdb_printf(
  90             "\nserver threads:\t%d running, %d starting\n\n", num_servers,
  91             num_started - num_servers);
  92 
  93         if (mdb_walk_dcmd("configd_threads", "configd_thread", argc,
  94             argv) == -1) {
  95                 mdb_warn("can't walk 'configd_threads'");
  96                 return (DCMD_ERR);
  97         }
  98         return (DCMD_OK);
  99 }
 100 
 101 /*ARGSUSED*/
 102 static int
 103 configd_thread(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
 104 {
 105         thread_info_t t;
 106         char state[20];
 107         char oldstate[20];
 108 
 109         if (!(flags & DCMD_ADDRSPEC)) {
 110                 if (mdb_walk_dcmd("configd_threads", "configd_thread", argc,
 111                     argv) == -1) {
 112                         mdb_warn("can't walk 'configd_threads'");
 113                         return (DCMD_ERR);
 114                 }
 115                 return (DCMD_OK);
 116         }
 117 
 118         if (argc != 0)
 119                 return (DCMD_USAGE);
 120 
 121         if (DCMD_HDRSPEC(flags)) {
 122                 mdb_printf("%<u>%-?s %5s %-12s %-12s %-?s %-?s %-?s%</u>\n",
 123                     "ADDR", "TID", "STATE", "PREV_STATE", "CLIENT", "CLIENTRQ",
 124                     "MAINREQ");
 125         }
 126 
 127         if (mdb_vread(&t, sizeof (t), addr) == -1) {
 128                 mdb_warn("failed to read thread_info_t at %p", addr);
 129                 return (DCMD_ERR);
 130         }
 131 
 132         enum_lookup(state, sizeof (state), thread_state_enum,
 133             t.ti_state, "TI_", "");
 134         make_lower(state, sizeof (state));
 135 
 136         enum_lookup(oldstate, sizeof (oldstate), thread_state_enum,
 137             t.ti_prev_state, "TI_", "");
 138         make_lower(oldstate, sizeof (oldstate));
 139 
 140         mdb_printf("%0?p %5d %-12s %-12s %?p %?p %?p\n",
 141             (void *)addr, t.ti_thread, state, oldstate,
 142             t.ti_active_client, t.ti_client_request, t.ti_main_door_request);
 143 
 144         return (DCMD_OK);
 145 }
 146 
 147 static int
 148 walk_thread_info_init(mdb_walk_state_t *wsp)
 149 {
 150         if (mdb_readvar(&wsp->walk_addr, "thread_list") == -1) {
 151                 mdb_warn("unable to read thread_list");
 152                 return (WALK_ERR);
 153         }
 154 
 155         if (mdb_layered_walk("uu_list_node", wsp) == -1) {
 156                 mdb_warn("couldn't walk 'uu_list_node'");
 157                 return (WALK_ERR);
 158         }
 159 
 160         return (WALK_NEXT);
 161 }
 162 
 163 static int
 164 walk_thread_info_step(mdb_walk_state_t *wsp)
 165 {
 166         uintptr_t addr = wsp->walk_addr;
 167         thread_info_t ti;
 168 
 169         if (mdb_vread(&ti, sizeof (ti), addr) == -1) {
 170                 mdb_warn("unable to read thread_info_t at %p", addr);
 171                 return (WALK_ERR);
 172         }
 173 
 174         return (wsp->walk_callback(addr, &ti, wsp->walk_cbdata));
 175 }
 176 
 177 static int
 178 request_log(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
 179 {
 180         request_log_entry_t cur;
 181         hrtime_t dur;
 182         hrtime_t dursec;
 183         hrtime_t durnsec;
 184         char durstr[20];
 185         char stampstr[20];
 186         char requstr[30];
 187         char respstr[30];
 188         char typestr[30];
 189         uintptr_t node = 0;
 190         uintptr_t client = 0;
 191         uint64_t clientid = 0;
 192 
 193         int idx;
 194         int opt_v = FALSE;                      /* verbose */
 195 
 196         if (!(flags & DCMD_ADDRSPEC)) {
 197                 if (mdb_walk_dcmd("configd_log", "configd_log", argc,
 198                     argv) == -1) {
 199                         mdb_warn("can't walk 'configd_log'");
 200                         return (DCMD_ERR);
 201                 }
 202                 return (DCMD_OK);
 203         }
 204 
 205         if (mdb_getopts(argc, argv,
 206             'c', MDB_OPT_UINTPTR, &client,
 207             'i', MDB_OPT_UINT64, &clientid,
 208             'n', MDB_OPT_UINTPTR, &node,
 209             'v', MDB_OPT_SETBITS, TRUE, &opt_v, NULL) != argc)
 210                 return (DCMD_USAGE);
 211 
 212         if (DCMD_HDRSPEC(flags)) {
 213                 mdb_printf("%<u>%-?s %-4s %-14s %9s %-22s %-17s\n%</u>",
 214                     "ADDR", "THRD", "START", "DURATION", "REQUEST",
 215                     "RESPONSE");
 216         }
 217 
 218         if (mdb_vread(&cur, sizeof (cur), addr) == -1) {
 219                 mdb_warn("couldn't read log entry at %p", addr);
 220                 return (DCMD_ERR);
 221         }
 222 
 223         /*
 224          * apply filters, if any.
 225          */
 226         if (clientid != 0 && clientid != cur.rl_clientid)
 227                 return (DCMD_OK);
 228 
 229         if (client != 0 && client != (uintptr_t)cur.rl_client)
 230                 return (DCMD_OK);
 231 
 232         if (node != 0) {
 233                 for (idx = 0; idx < MIN(MAX_PTRS, cur.rl_num_ptrs); idx++) {
 234                         if ((uintptr_t)cur.rl_ptrs[idx].rlp_data == node) {
 235                                 node = 0;               /* found it */
 236                                 break;
 237                         }
 238                 }
 239                 if (node != 0)
 240                         return (DCMD_OK);
 241         }
 242 
 243         enum_lookup(requstr, sizeof (requstr), request_enum, cur.rl_request,
 244             "REP_PROTOCOL_", "");
 245 
 246         if (cur.rl_end != 0) {
 247                 enum_lookup(respstr, sizeof (respstr), response_enum,
 248                     cur.rl_response, "REP_PROTOCOL_", "FAIL_");
 249 
 250                 dur = cur.rl_end - cur.rl_start;
 251                 dursec = dur / NANOSEC;
 252                 durnsec = dur % NANOSEC;
 253 
 254                 if (dursec <= 9)
 255                         mdb_snprintf(durstr, sizeof (durstr),
 256                             "%lld.%06lld",
 257                             dursec, durnsec / (NANOSEC / MICROSEC));
 258                 else if (dursec <= 9999)
 259                         mdb_snprintf(durstr, sizeof (durstr),
 260                             "%lld.%03lld",
 261                             dursec, durnsec / (NANOSEC / MILLISEC));
 262                 else
 263                         mdb_snprintf(durstr, sizeof (durstr),
 264                             "%lld", dursec);
 265         } else {
 266                 (void) strcpy(durstr, "-");
 267                 (void) strcpy(respstr, "-");
 268         }
 269 
 270         if (max_time_seen != 0 && max_time_seen >= cur.rl_start) {
 271                 dur = max_time_seen - cur.rl_start;
 272                 dursec = dur / NANOSEC;
 273                 durnsec = dur % NANOSEC;
 274 
 275                 if (dursec <= 99ULL)
 276                         mdb_snprintf(stampstr, sizeof (stampstr),
 277                             "-%lld.%09lld", dursec, durnsec);
 278                 else if (dursec <= 99999ULL)
 279                         mdb_snprintf(stampstr, sizeof (stampstr),
 280                             "-%lld.%06lld",
 281                             dursec, durnsec / (NANOSEC / MICROSEC));
 282                 else if (dursec <= 99999999ULL)
 283                         mdb_snprintf(stampstr, sizeof (stampstr),
 284                             "-%lld.%03lld",
 285                             dursec, durnsec / (NANOSEC / MILLISEC));
 286                 else
 287                         mdb_snprintf(stampstr, sizeof (stampstr),
 288                             "-%lld", dursec);
 289         } else {
 290                 (void) strcpy(stampstr, "-");
 291         }
 292 
 293         mdb_printf("%0?x %4d T%13s %9s %-22s %-17s\n",
 294             addr, cur.rl_tid, stampstr, durstr, requstr, respstr);
 295 
 296         if (opt_v) {
 297                 mdb_printf("\tclient: %?p (%d)\tptrs: %d\tstamp: %llx\n",
 298                     cur.rl_client, cur.rl_clientid, cur.rl_num_ptrs,
 299                     cur.rl_start);
 300                 for (idx = 0; idx < MIN(MAX_PTRS, cur.rl_num_ptrs); idx++) {
 301                         enum_lookup(typestr, sizeof (typestr), ptr_type_enum,
 302                             cur.rl_ptrs[idx].rlp_type, "RC_PTR_TYPE_", "");
 303                         mdb_printf("\t\t%-7s %5d %?p %?p\n", typestr,
 304                             cur.rl_ptrs[idx].rlp_id, cur.rl_ptrs[idx].rlp_ptr,
 305                             cur.rl_ptrs[idx].rlp_data);
 306                 }
 307                 mdb_printf("\n");
 308         }
 309         return (DCMD_OK);
 310 }
 311 
 312 struct request_log_walk {
 313         size_t          rlw_max;
 314         size_t          rlw_count;
 315         size_t          rlw_cur;
 316         struct request_entry {
 317                 hrtime_t  timestamp;
 318                 uintptr_t addr;
 319         }               *rlw_list;
 320 };
 321 
 322 /*
 323  * we want newer items at the top
 324  */
 325 static int
 326 request_entry_compare(const void *l, const void *r)
 327 {
 328         const struct request_entry *lp = l;
 329         const struct request_entry *rp = r;
 330 
 331         if (rp->timestamp == lp->timestamp)
 332                 return (0);
 333 
 334         /*
 335          * 0 timestamps go first.
 336          */
 337         if (rp->timestamp == 0)
 338                 return (1);
 339         if (lp->timestamp == 0)
 340                 return (-1);
 341 
 342         if (lp->timestamp < rp->timestamp)
 343                 return (1);
 344         return (-1);
 345 }
 346 
 347 /*ARGSUSED*/
 348 static int
 349 request_log_count_thread(uintptr_t addr, thread_info_t *tip, uint_t *arg)
 350 {
 351         (*arg)++;
 352 
 353         return (WALK_NEXT);
 354 }
 355 
 356 static int
 357 request_log_add_thread(uintptr_t addr, thread_info_t *tip,
 358     struct request_entry **arg)
 359 {
 360         if (max_time_seen < tip->ti_log.rl_start)
 361                 max_time_seen = tip->ti_log.rl_start;
 362 
 363         if (max_time_seen < tip->ti_log.rl_end)
 364                 max_time_seen = tip->ti_log.rl_end;
 365 
 366         if (tip->ti_log.rl_start != 0) {
 367                 if (tip->ti_log.rl_end)
 368                         (*arg)->timestamp = tip->ti_log.rl_start;
 369                 else
 370                         (*arg)->timestamp = 0;               /* sort to the top */
 371 
 372                 (*arg)->addr = addr + offsetof(thread_info_t, ti_log);
 373                 ++*arg;
 374         }
 375         return (WALK_NEXT);
 376 }
 377 
 378 static int
 379 request_log_walk_init(mdb_walk_state_t *wsp)
 380 {
 381         struct request_log_walk *rlw;
 382         struct request_entry *list, *listp;
 383 
 384         uint_t log_size;
 385         uint_t size;
 386         uint_t idx;
 387         uint_t pos;
 388         request_log_entry_t *base;
 389         request_log_entry_t cur;
 390 
 391         if (mdb_readvar(&base, "request_log") == -1) {
 392                 mdb_warn("couldn't read 'request_log'");
 393                 return (WALK_ERR);
 394         }
 395         if (mdb_readvar(&log_size, "request_log_size") == -1) {
 396                 mdb_warn("couldn't read 'request_log_size'");
 397                 return (WALK_ERR);
 398         }
 399         size = log_size;
 400 
 401         if (mdb_walk("configd_threads", (mdb_walk_cb_t)request_log_count_thread,
 402             &size) == -1) {
 403                 mdb_warn("couldn't walk 'configd_threads'");
 404                 return (WALK_ERR);
 405         }
 406 
 407         list = mdb_zalloc(sizeof (*list) * size, UM_SLEEP);
 408         listp = list;
 409 
 410         if (mdb_walk("configd_threads", (mdb_walk_cb_t)request_log_add_thread,
 411             &listp) == -1) {
 412                 mdb_warn("couldn't walk 'configd_threads'");
 413                 mdb_free(list, sizeof (*list) * size);
 414                 return (WALK_ERR);
 415         }
 416 
 417         pos = listp - list;
 418         for (idx = 0; idx < log_size; idx++) {
 419                 uintptr_t addr = (uintptr_t)&base[idx];
 420                 if (mdb_vread(&cur, sizeof (cur), addr) == -1) {
 421                         mdb_warn("couldn't read log entry at %p", addr);
 422                         mdb_free(list, sizeof (*list) * size);
 423                         return (WALK_ERR);
 424                 }
 425 
 426                 if (max_time_seen < cur.rl_start)
 427                         max_time_seen = cur.rl_start;
 428 
 429                 if (max_time_seen < cur.rl_end)
 430                         max_time_seen = cur.rl_end;
 431 
 432                 if (cur.rl_start != 0) {
 433                         list[pos].timestamp = cur.rl_start;
 434                         list[pos].addr = addr;
 435                         pos++;
 436                 }
 437         }
 438         qsort(list, pos, sizeof (*list), &request_entry_compare);
 439 
 440         rlw = mdb_zalloc(sizeof (*rlw), UM_SLEEP);
 441         rlw->rlw_max = size;
 442         rlw->rlw_count = pos;
 443         rlw->rlw_cur = 0;
 444         rlw->rlw_list = list;
 445 
 446         wsp->walk_data = rlw;
 447 
 448         return (WALK_NEXT);
 449 }
 450 
 451 static int
 452 request_log_walk_step(mdb_walk_state_t *wsp)
 453 {
 454         struct request_log_walk *rlw = wsp->walk_data;
 455         uintptr_t addr;
 456         request_log_entry_t cur;
 457 
 458         if (rlw->rlw_cur >= rlw->rlw_count)
 459                 return (WALK_DONE);
 460 
 461         addr = rlw->rlw_list[rlw->rlw_cur++].addr;
 462 
 463         if (mdb_vread(&cur, sizeof (cur), addr) == -1) {
 464                 mdb_warn("couldn't read log entry at %p", addr);
 465                 return (WALK_ERR);
 466         }
 467         return (wsp->walk_callback(addr, &cur, wsp->walk_cbdata));
 468 }
 469 
 470 static void
 471 request_log_walk_fini(mdb_walk_state_t *wsp)
 472 {
 473         struct request_log_walk *rlw = wsp->walk_data;
 474 
 475         mdb_free(rlw->rlw_list, sizeof (*rlw->rlw_list) * rlw->rlw_max);
 476         mdb_free(rlw, sizeof (*rlw));
 477 }
 478 
 479 static const mdb_dcmd_t dcmds[] = {
 480         { "configd_status", NULL, "svc.configd status summary",
 481             configd_status },
 482         { "configd_thread", "?", "Print a thread_info_t tabularly",
 483             configd_thread },
 484         { "configd_log", "?[-v] [-c clientptr] [-i clientid]",
 485             "Print the request log, or a single entry", request_log },
 486         { NULL }
 487 };
 488 
 489 static const mdb_walker_t walkers[] = {
 490         { "configd_threads", "walks the thread_info_ts for all "
 491             "threads", walk_thread_info_init, walk_thread_info_step },
 492         { "configd_log", "walks the request_log_entry_ts",
 493             request_log_walk_init, request_log_walk_step,
 494             request_log_walk_fini},
 495         { NULL }
 496 };
 497 
 498 static const mdb_modinfo_t modinfo = {
 499         MDB_API_VERSION, dcmds, walkers
 500 };
 501 
 502 const mdb_modinfo_t *
 503 _mdb_init(void)
 504 {
 505         if (mdb_ctf_lookup_by_name("enum rep_protocol_requestid",
 506             &request_enum) == -1) {
 507                 mdb_warn("enum rep_protocol_requestid not found");
 508         }
 509         if (mdb_ctf_lookup_by_name("enum rep_protocol_responseid",
 510             &response_enum) == -1) {
 511                 mdb_warn("enum rep_protocol_responseid not found");
 512         }
 513         if (mdb_ctf_lookup_by_name("enum rc_ptr_type",
 514             &ptr_type_enum) == -1) {
 515                 mdb_warn("enum rc_ptr_type not found");
 516         }
 517         if (mdb_ctf_lookup_by_name("enum thread_state",
 518             &thread_state_enum) == -1) {
 519                 mdb_warn("enum thread_state not found");
 520         }
 521         return (&modinfo);
 522 }