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