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 (c) 2008-2009, Intel Corporation.
  23  * All Rights Reserved.
  24  */
  25 
  26 #include <stdlib.h>
  27 #include <stdio.h>
  28 #include <memory.h>
  29 #include <string.h>
  30 #include <limits.h>
  31 #include <sys/stat.h>
  32 
  33 #include "latencytop.h"
  34 
  35 /* Statistics for each process/thread. */
  36 typedef struct _lt_stat_collection lt_stat_collection_t;
  37 typedef gboolean (*check_child_func_t) (gpointer key,
  38     lt_stat_collection_t *stat, void *user);
  39 
  40 typedef struct {
  41         lt_stat_entry_t lt_grp_summary;
  42         /* cause_id -> stat entry */
  43         GHashTable *lt_grp_cidlist;
  44 } lt_datagroup_t;
  45 
  46 #define NGROUPS                 2
  47 #define GROUP_CAUSE             0
  48 #define GROUP_SOBJ              1
  49 
  50 /*
  51  * A data collection hierarchy involving three entities - system, process
  52  * and thread. The hierarchic relationship is as follows :
  53  *
  54  *              1 system -> 1 or more processes -> 1 or more threads
  55  */
  56 struct _lt_stat_collection {
  57         lt_stat_level_t lt_sc_level;
  58         unsigned int lt_sc_id;
  59         char *lt_sc_name;
  60         lt_datagroup_t lt_sc_groups[NGROUPS];
  61         /*
  62          * The following fields: lt_sc_parent, lt_sc_children and
  63          * lt_sc_check_child_func maintain the tree structure.
  64          */
  65         lt_stat_collection_t *lt_sc_parent;             /* Parent node */
  66         GHashTable *lt_sc_children;     /* pid/tid -> lt_stat_collection_t */
  67         check_child_func_t lt_sc_check_child_func; /* Release dead children */
  68 };
  69 
  70 /* Internal data structure to back up a stat_list */
  71 typedef struct _lt_stat_list lt_stat_list_t;
  72 typedef void (*free_list_func_t)(lt_stat_list_t *);
  73 struct _lt_stat_list {
  74         int lt_sl_entry_count;
  75         lt_stat_entry_t **lt_sl_entries;
  76         uint64_t lt_sl_gtotal;
  77         free_list_func_t lt_sl_free_func;
  78 };
  79 
  80 /* Root of the collection hierarchy: system level statistics */
  81 static lt_stat_collection_t *stat_system = NULL;
  82 
  83 /*
  84  * Data structure to hold synchronization objects.
  85  * We don't use normal "cause table" because this needs to be cleared
  86  * every time we refresh in order to make sure that stale synchronization
  87  * objects don't consume memory.
  88  */
  89 typedef struct {
  90         int lt_soi_type;
  91         unsigned long long lt_soi_addr;
  92 } lt_sobj_id_t;
  93 
  94 typedef struct {
  95         lt_sobj_id_t lt_so_oid;
  96         int lt_so_cause_id;
  97         char lt_so_string[32];  /* Enough to hold "%s: 0x%llX" */
  98 } lt_sobj_t;
  99 
 100 static GHashTable *sobj_table = NULL;
 101 static int sobj_table_len = 0;
 102 
 103 /*
 104  * Lower 32-bit of the address of synchronization objects is used to hash
 105  * them.
 106  */
 107 static guint
 108 sobj_id_hash(lt_sobj_id_t *id)
 109 {
 110         g_assert(id != NULL);
 111         return (id->lt_soi_addr & 0xFFFFFFFF);
 112 }
 113 
 114 /*
 115  * Test if two synchronization objects are the same.
 116  */
 117 static gboolean
 118 sobj_id_equal(lt_sobj_id_t *a, lt_sobj_id_t *b)
 119 {
 120         g_assert(a != NULL && b != NULL);
 121         return (a->lt_soi_type == b->lt_soi_type &&
 122             a->lt_soi_addr == b->lt_soi_addr);
 123 }
 124 
 125 /*
 126  * Look up the cause_id of a synchronization object.
 127  * Note that this cause_id is only unique in GROUP_SOBJ, and changes after
 128  * a refresh.
 129  */
 130 static lt_sobj_t *
 131 lookup_sobj(lt_sobj_id_t *id)
 132 {
 133         const char *stype_str[] = {
 134                 "None",
 135                 "Mutex",
 136                 "RWLock",
 137                 "CV",
 138                 "Sema",
 139                 "User",
 140                 "User_PI",
 141                 "Shuttle"
 142         };
 143         const int stype_str_len =
 144             sizeof (stype_str) / sizeof (stype_str[0]);
 145         lt_sobj_t *ret = NULL;
 146         g_assert(id != NULL);
 147 
 148         if (id->lt_soi_type < 0 || id->lt_soi_type >= stype_str_len) {
 149                 return (NULL);
 150         }
 151 
 152         if (sobj_table != NULL) {
 153                 ret = (lt_sobj_t *)g_hash_table_lookup(sobj_table, id);
 154         } else {
 155                 sobj_table = g_hash_table_new_full(
 156                     (GHashFunc)sobj_id_hash, (GEqualFunc)sobj_id_equal,
 157                     NULL, (GDestroyNotify)free);
 158                 lt_check_null(sobj_table);
 159         }
 160 
 161         if (ret == NULL) {
 162                 ret = (lt_sobj_t *)lt_zalloc(sizeof (lt_sobj_t));
 163                 ret->lt_so_cause_id = ++sobj_table_len;
 164                 (void) snprintf(ret->lt_so_string, sizeof (ret->lt_so_string),
 165                     "%s: 0x%llX", stype_str[id->lt_soi_type], id->lt_soi_addr);
 166                 ret->lt_so_oid.lt_soi_type = id->lt_soi_type;
 167                 ret->lt_so_oid.lt_soi_addr = id->lt_soi_addr;
 168 
 169                 g_hash_table_insert(sobj_table, &ret->lt_so_oid, ret);
 170         }
 171 
 172         return (ret);
 173 }
 174 
 175 /*
 176  * Check if a process exists by using /proc/pid
 177  */
 178 /* ARGSUSED */
 179 static gboolean
 180 check_process(gpointer key, lt_stat_collection_t *stat, void *user)
 181 {
 182         char name[PATH_MAX];
 183 
 184         (void) snprintf(name, PATH_MAX, "/proc/%u", stat->lt_sc_id);
 185         return (lt_file_exist(name) ? FALSE : TRUE);
 186 }
 187 
 188 /*
 189  * Check if a thread exists by using /proc/pid/lwp/tid
 190  */
 191 /* ARGSUSED */
 192 static gboolean
 193 check_thread(gpointer key, lt_stat_collection_t *stat, void *user)
 194 {
 195         char name[PATH_MAX];
 196 
 197         g_assert(stat->lt_sc_parent != NULL);
 198         g_assert(stat->lt_sc_parent->lt_sc_level == LT_LEVEL_PROCESS);
 199 
 200         (void) snprintf(name, PATH_MAX, "/proc/%u/lwp/%u",
 201             stat->lt_sc_parent->lt_sc_id, stat->lt_sc_id);
 202         return (lt_file_exist(name) ? FALSE : TRUE);
 203 }
 204 
 205 /*
 206  * Helper function to free a stat node.
 207  */
 208 static void
 209 free_stat(lt_stat_collection_t *stat)
 210 {
 211         int i;
 212 
 213         if (stat == NULL) {
 214                 return;
 215         }
 216 
 217         for (i = 0; i < NGROUPS; ++i) {
 218                 if (stat->lt_sc_groups[i].lt_grp_cidlist != NULL) {
 219                         g_hash_table_destroy(stat->lt_sc_groups[i].
 220                             lt_grp_cidlist);
 221                 }
 222         }
 223 
 224         if (stat->lt_sc_children != NULL) {
 225                 g_hash_table_destroy(stat->lt_sc_children);
 226         }
 227 
 228         if (stat->lt_sc_name != NULL) {
 229                 free(stat->lt_sc_name);
 230         }
 231 
 232         free(stat);
 233 }
 234 
 235 /*
 236  * Helper function to initialize a stat node.
 237  */
 238 /* ARGSUSED */
 239 static void
 240 clear_stat(gpointer key, lt_stat_collection_t *stat, void *user)
 241 {
 242         int i;
 243 
 244         g_assert(stat != NULL);
 245 
 246         for (i = 0; i < NGROUPS; ++i) {
 247                 if (stat->lt_sc_groups[i].lt_grp_cidlist != NULL) {
 248                         g_hash_table_destroy(stat->lt_sc_groups[i].
 249                             lt_grp_cidlist);
 250                         stat->lt_sc_groups[i].lt_grp_cidlist = NULL;
 251                 }
 252 
 253                 stat->lt_sc_groups[i].lt_grp_summary.lt_se_data.lt_s_count = 0;
 254                 stat->lt_sc_groups[i].lt_grp_summary.lt_se_data.lt_s_total = 0;
 255                 stat->lt_sc_groups[i].lt_grp_summary.lt_se_data.lt_s_max = 0;
 256         }
 257 
 258         if (stat->lt_sc_children != NULL) {
 259                 g_hash_table_foreach_remove(stat->lt_sc_children,
 260                     (GHRFunc)stat->lt_sc_check_child_func, NULL);
 261                 g_hash_table_foreach(stat->lt_sc_children,
 262                     (GHFunc)clear_stat, NULL);
 263         }
 264 }
 265 
 266 /*
 267  * Update a collection with the given value.
 268  * Recursively update parents in the hierarchy  until the root is reached.
 269  */
 270 static void
 271 update_stat_entry(lt_stat_collection_t *stat, int cause_id,
 272                 lt_stat_type_t type, uint64_t value,
 273                 const char *string, int group_to_use)
 274 {
 275         lt_stat_entry_t *entry = NULL;
 276         lt_datagroup_t *group;
 277 
 278         if (group_to_use < 0 || group_to_use >= NGROUPS) {
 279                 return;
 280         }
 281 
 282         group = &(stat->lt_sc_groups[group_to_use]);
 283 
 284         if (group->lt_grp_cidlist != NULL) {
 285                 entry = (lt_stat_entry_t *)g_hash_table_lookup(
 286                     group->lt_grp_cidlist, LT_INT_TO_POINTER(cause_id));
 287         } else   {
 288                 group->lt_grp_cidlist = g_hash_table_new_full(
 289                     g_direct_hash, g_direct_equal,
 290                     NULL, (GDestroyNotify)free);
 291                 lt_check_null(group->lt_grp_cidlist);
 292         }
 293 
 294         if (entry == NULL) {
 295                 entry = (lt_stat_entry_t *)lt_zalloc(sizeof (lt_stat_entry_t));
 296                 entry->lt_se_string = string;
 297 
 298                 switch (group_to_use) {
 299                 case GROUP_CAUSE:
 300                         entry->lt_se_type = STAT_CAUSE;
 301                         entry->lt_se_tsdata.lt_se_t_cause.lt_se_c_id = cause_id;
 302                         entry->lt_se_tsdata.lt_se_t_cause.lt_se_c_flags =
 303                             lt_table_get_cause_flag(cause_id, CAUSE_ALL_FLAGS);
 304 
 305                         /* hide the first '#' */
 306                         if ((entry->lt_se_tsdata.lt_se_t_cause.lt_se_c_flags
 307                             & CAUSE_FLAG_HIDE_IN_SUMMARY) != 0) {
 308                                 ++entry->lt_se_string;
 309                         }
 310 
 311                         break;
 312                 case GROUP_SOBJ:
 313                         entry->lt_se_type = STAT_SOBJ;
 314                         entry->lt_se_tsdata.lt_se_t_sobj.lt_se_s_id = cause_id;
 315                         break;
 316                 }
 317 
 318                 g_hash_table_insert(group->lt_grp_cidlist,
 319                     LT_INT_TO_POINTER(cause_id), entry);
 320         }
 321 
 322         lt_update_stat_value(&entry->lt_se_data, type, value);
 323 
 324         if (group_to_use == GROUP_SOBJ ||
 325             (entry->lt_se_tsdata.lt_se_t_cause.lt_se_c_flags &
 326             CAUSE_FLAG_HIDE_IN_SUMMARY) == 0) {
 327                 lt_update_stat_value(&group->lt_grp_summary.lt_se_data, type,
 328                     value);
 329         }
 330 
 331         if (stat->lt_sc_parent != NULL) {
 332                 update_stat_entry(stat->lt_sc_parent, cause_id, type, value,
 333                     string, group_to_use);
 334         }
 335 }
 336 
 337 /*
 338  * Identify the cause of latency from the given stack trace.
 339  * Return cause_id.
 340  */
 341 static void
 342 find_cause(char *stack, int *cause_id, int *cause_priority)
 343 {
 344         int cause_temp;
 345         int prio_temp;
 346         int cause = INVALID_CAUSE;
 347         int priority = 0;
 348         int found = 0;
 349 
 350         g_assert(cause_id != NULL);
 351         g_assert(cause_priority != NULL);
 352 
 353         while (stack != NULL) {
 354                 char *sep;
 355                 sep = strchr(stack, ' ');
 356 
 357                 if (sep != NULL) {
 358                         *sep = '\0';
 359                 }
 360 
 361                 found = lt_table_cause_from_stack(stack, &cause_temp,
 362                     &prio_temp);
 363 
 364                 if (found && (cause == INVALID_CAUSE ||
 365                     HIGHER_PRIORITY(prio_temp, priority))) {
 366                         cause = cause_temp;
 367                         priority = prio_temp;
 368                 }
 369 
 370                 if (sep != NULL) {
 371                         *sep = ' ';
 372                         stack = sep + 1;
 373                 } else   {
 374                         stack = NULL;
 375                 }
 376         }
 377 
 378         *cause_id = cause;
 379         *cause_priority = priority;
 380 }
 381 
 382 /*
 383  * Create a new collection and hook it to the parent.
 384  */
 385 static lt_stat_collection_t *
 386 new_collection(lt_stat_level_t level, unsigned int id, char *name,
 387     lt_stat_collection_t *parent, check_child_func_t check_child_func)
 388 {
 389         int i;
 390         lt_stat_collection_t *ret;
 391 
 392         ret = (lt_stat_collection_t *)
 393             lt_zalloc(sizeof (lt_stat_collection_t));
 394 
 395         ret->lt_sc_level = level;
 396         ret->lt_sc_check_child_func = check_child_func;
 397         ret->lt_sc_id = id;
 398         ret->lt_sc_name = name;
 399 
 400         for (i = 0; i < NGROUPS; ++i) {
 401                 ret->lt_sc_groups[i].lt_grp_summary.lt_se_string =
 402                     (const char *)name;
 403         }
 404 
 405         if (parent != NULL) {
 406                 ret->lt_sc_parent = parent;
 407 
 408                 if (parent->lt_sc_children == NULL) {
 409                         parent->lt_sc_children = g_hash_table_new_full(
 410                             g_direct_hash, g_direct_equal,
 411                             NULL, (GDestroyNotify)free_stat);
 412                         lt_check_null(parent->lt_sc_children);
 413                 }
 414 
 415                 g_hash_table_insert(parent->lt_sc_children,
 416                     LT_INT_TO_POINTER((int)id), ret);
 417         }
 418 
 419         return (ret);
 420 }
 421 
 422 /*
 423  * Find the "leaf" in the collection hierarchy, using the given pid and tid.
 424  */
 425 static lt_stat_collection_t *
 426 get_stat_c(pid_t pid, id_t tid)
 427 {
 428         lt_stat_collection_t *stat_p = NULL;
 429         lt_stat_collection_t *stat_t = NULL;
 430 
 431         if (stat_system == NULL) {
 432                 stat_system = new_collection(LT_LEVEL_GLOBAL,
 433                     PID_SYS_GLOBAL, lt_strdup("SYSTEM"), NULL, check_process);
 434         } else if (stat_system->lt_sc_children != NULL) {
 435                 stat_p = (lt_stat_collection_t *)
 436                     g_hash_table_lookup(stat_system->lt_sc_children,
 437                     LT_INT_TO_POINTER(pid));
 438         }
 439 
 440         if (stat_p == NULL) {
 441                 char *fname;
 442                 fname = lt_get_proc_field(pid, LT_FIELD_FNAME);
 443 
 444                 if (fname == NULL) {
 445                         /*
 446                          * we could not get the executable name of the
 447                          * process; the process is probably already dead.
 448                          */
 449                         return (NULL);
 450                 }
 451 
 452                 stat_p = new_collection(LT_LEVEL_PROCESS,
 453                     (unsigned int)pid, fname, stat_system, check_thread);
 454         } else if (stat_p->lt_sc_children != NULL) {
 455                 stat_t = (lt_stat_collection_t *)
 456                     g_hash_table_lookup(stat_p->lt_sc_children,
 457                     LT_INT_TO_POINTER(tid));
 458         }
 459 
 460         if (stat_t == NULL) {
 461                 const int tname_size = 16; /* Enough for "Thread %d" */
 462                 char *tname;
 463 
 464                 tname = (char *)lt_zalloc(tname_size);
 465                 (void) snprintf(tname, tname_size, "Thread %d", tid);
 466 
 467                 stat_t = new_collection(LT_LEVEL_THREAD,
 468                     (unsigned int)tid, tname, stat_p, NULL);
 469         }
 470 
 471         return (stat_t);
 472 }
 473 
 474 /*
 475  * Update statistics with the given cause_id. Values will be added to
 476  * internal statistics.
 477  */
 478 void
 479 lt_stat_update_cause(pid_t pid, id_t tid, int cause_id, lt_stat_type_t type,
 480     uint64_t value)
 481 {
 482         const char *string;
 483         lt_stat_collection_t *stat_t = NULL;
 484 
 485         if (cause_id < 0 || value == 0) {
 486                 return;
 487         }
 488 
 489         if (lt_table_get_cause_flag(cause_id, CAUSE_FLAG_DISABLED)) {
 490                 /* Ignore this cause */
 491                 return;
 492         }
 493 
 494         stat_t = get_stat_c(pid, tid);
 495 
 496         if (stat_t == NULL) {
 497                 /* Process must be dead. */
 498                 return;
 499         }
 500 
 501         string = lt_table_get_cause_name(cause_id);
 502 
 503         update_stat_entry(stat_t, cause_id, type, value, string, GROUP_CAUSE);
 504 }
 505 
 506 /*
 507  * Update statistics with the given stack trace.
 508  * The stack trace is mapped to a cause and lt_stat_update_cause() is called
 509  * to update statistics.
 510  */
 511 void
 512 lt_stat_update(pid_t pid, id_t tid, char *stack, char *tag,
 513     unsigned int tag_priority, lt_stat_type_t type, uint64_t value)
 514 {
 515         int tag_cause_id = INVALID_CAUSE;
 516         int stack_cause_id = INVALID_CAUSE;
 517         int cause_id = INVALID_CAUSE;
 518         int stack_priority = 0;
 519 
 520         if (value == 0) {
 521                 return;
 522         }
 523 
 524         find_cause(stack, &stack_cause_id, &stack_priority);
 525 
 526         if (tag_priority != 0) {
 527                 tag_cause_id = lt_table_cause_from_name(tag, 0, 0);
 528 
 529                 if (tag_cause_id == INVALID_CAUSE) {
 530                         /* This must be a syscall tag */
 531                         char tmp[64];
 532                         (void) snprintf(tmp, sizeof (tmp), "Syscall: %s", tag);
 533                         tag_cause_id = lt_table_cause_from_name(tmp, 1, 0);
 534                 }
 535         }
 536 
 537         cause_id = (tag_priority > stack_priority) ? tag_cause_id :
 538             stack_cause_id;
 539 
 540         if (cause_id == INVALID_CAUSE) {
 541                 /*
 542                  * We got an unmapped stack. Set SPECIAL flag to display it
 543                  * in pane 2. This makes it easier to find the cause.
 544                  */
 545                 cause_id = lt_table_cause_from_name(stack, 1,
 546                     CAUSE_FLAG_SPECIAL);
 547                 lt_klog_log(LT_KLOG_LEVEL_UNMAPPED, pid, stack, type, value);
 548         } else   {
 549                 lt_klog_log(LT_KLOG_LEVEL_MAPPED, pid, stack, type, value);
 550         }
 551 
 552         lt_stat_update_cause(pid, tid, cause_id, type, value);
 553 }
 554 
 555 /*
 556  * Zero out all statistics, but keep the data structures in memory
 557  * to be used to hold new data immediately following.
 558  */
 559 void
 560 lt_stat_clear_all(void)
 561 {
 562         if (stat_system != NULL) {
 563                 clear_stat(NULL, stat_system, NULL);
 564         }
 565 
 566         if (sobj_table != NULL) {
 567                 g_hash_table_destroy(sobj_table);
 568                 sobj_table = NULL;
 569         }
 570 }
 571 
 572 /*
 573  * Clean up function that frees all memory used for statistics.
 574  */
 575 void
 576 lt_stat_free_all(void)
 577 {
 578         if (stat_system != NULL) {
 579                 free_stat(stat_system);
 580                 stat_system = NULL;
 581         }
 582 
 583         if (sobj_table != NULL) {
 584                 g_hash_table_destroy(sobj_table);
 585                 sobj_table = NULL;
 586         }
 587 }
 588 
 589 /*
 590  * Get top N causes of latency for a process. Return handle to a stat_list.
 591  * Use pid = PID_SYS_GLOBAL to get global top list.
 592  * Call lt_stat_list_free after use to clean up.
 593  */
 594 void *
 595 lt_stat_list_create(lt_list_type_t list_type, lt_stat_level_t level,
 596     pid_t pid, id_t tid, int count, lt_sort_t sort_by)
 597 {
 598         GCompareFunc func;
 599         GList *list, *walk;
 600         lt_stat_collection_t *stat_c = NULL;
 601         lt_stat_list_t *ret;
 602         lt_datagroup_t *group;
 603 
 604         if (level == LT_LEVEL_GLOBAL) {
 605                 /* Use global entry */
 606                 stat_c = stat_system;
 607         } else if (stat_system != NULL && stat_system->lt_sc_children != NULL) {
 608                 /* Find process entry first */
 609                 stat_c = (lt_stat_collection_t *)g_hash_table_lookup(
 610                     stat_system->lt_sc_children, LT_INT_TO_POINTER(pid));
 611 
 612                 if (level == LT_LEVEL_THREAD) {
 613                         /*
 614                          * If thread entry is requested, find it based on
 615                          * process entry.
 616                          */
 617                         if (stat_c != NULL && stat_c->lt_sc_children != NULL) {
 618                                 stat_c = (lt_stat_collection_t *)
 619                                     g_hash_table_lookup(stat_c->lt_sc_children,
 620                                     LT_INT_TO_POINTER(tid));
 621                         } else {
 622                                 /*
 623                                  * Thread entry was not found; set it to NULL,
 624                                  * so that we can return empty list later.
 625                                  */
 626                                 stat_c = NULL;
 627                         }
 628                 }
 629         }
 630 
 631         ret = (lt_stat_list_t *)lt_zalloc(sizeof (lt_stat_list_t));
 632         ret->lt_sl_entries = (lt_stat_entry_t **)
 633             lt_zalloc(count * sizeof (lt_stat_entry_t *));
 634 
 635         if (stat_c == NULL) {
 636                 /* Empty list */
 637                 return (ret);
 638         }
 639 
 640         if (list_type == LT_LIST_SOBJ) {
 641                 group = &(stat_c->lt_sc_groups[GROUP_SOBJ]);
 642         } else {
 643                 group = &(stat_c->lt_sc_groups[GROUP_CAUSE]);
 644         }
 645 
 646         if (group->lt_grp_cidlist == NULL) {
 647                 /* Empty list */
 648                 return (ret);
 649         }
 650 
 651         ret->lt_sl_gtotal = group->lt_grp_summary.lt_se_data.lt_s_total;
 652 
 653         list = g_hash_table_get_values(group->lt_grp_cidlist);
 654 
 655         switch (sort_by) {
 656         case LT_SORT_TOTAL:
 657                 func = (GCompareFunc)lt_sort_by_total_desc;
 658                 break;
 659         case LT_SORT_MAX:
 660                 func = (GCompareFunc)lt_sort_by_max_desc;
 661                 break;
 662         case LT_SORT_AVG:
 663                 func = (GCompareFunc)lt_sort_by_avg_desc;
 664                 break;
 665         case LT_SORT_COUNT:
 666                 func = (GCompareFunc)lt_sort_by_count_desc;
 667                 break;
 668         }
 669         list = g_list_sort(list, func);
 670 
 671         for (walk = list;
 672             walk != NULL && count > 0;
 673             walk = g_list_next(walk), --count) {
 674                 lt_stat_entry_t *data = (lt_stat_entry_t *)walk->data;
 675 
 676                 if (list_type == LT_LIST_CAUSE &&
 677                     data->lt_se_type == STAT_CAUSE &&
 678                     (data->lt_se_tsdata.lt_se_t_cause.lt_se_c_flags &
 679                     CAUSE_FLAG_HIDE_IN_SUMMARY) != 0) {
 680                         continue;
 681                 }
 682 
 683                 if (list_type == LT_LIST_SPECIALS &&
 684                     data->lt_se_type == STAT_CAUSE &&
 685                     (data->lt_se_tsdata.lt_se_t_cause.lt_se_c_flags &
 686                     CAUSE_FLAG_SPECIAL) == 0) {
 687                         continue;
 688                 }
 689 
 690                 if (data->lt_se_data.lt_s_count == 0) {
 691                         break;
 692                 }
 693 
 694                 ret->lt_sl_entries[ret->lt_sl_entry_count++] = data;
 695         }
 696 
 697         g_list_free(list);
 698 
 699         return (ret);
 700 }
 701 
 702 /*
 703  * Free memory allocated by lt_stat_list_create().
 704  */
 705 void
 706 lt_stat_list_free(void *ptr)
 707 {
 708         lt_stat_list_t *list = (lt_stat_list_t *)ptr;
 709 
 710         if (list == NULL) {
 711                 return;
 712         }
 713 
 714         if (list->lt_sl_free_func != NULL) {
 715                 list->lt_sl_free_func(list);
 716         }
 717 
 718         if (list->lt_sl_entries != NULL) {
 719                 free(list->lt_sl_entries);
 720         }
 721 
 722         free(list);
 723 }
 724 
 725 /*
 726  * Check if the given list contains the given item.
 727  */
 728 int
 729 lt_stat_list_has_item(void *ptr, int i)
 730 {
 731         lt_stat_list_t *list = (lt_stat_list_t *)ptr;
 732 
 733         if (list == NULL || i < 0 || i >= list->lt_sl_entry_count ||
 734             list->lt_sl_entries[i] == NULL) {
 735                 return (0);
 736         }
 737 
 738         return (1);
 739 }
 740 
 741 /*
 742  * Get display name of the given item i in the given list.
 743  */
 744 const char *
 745 lt_stat_list_get_reason(void *ptr, int i)
 746 {
 747         lt_stat_list_t *list = (lt_stat_list_t *)ptr;
 748 
 749         if (list == NULL || i < 0 || i >= list->lt_sl_entry_count ||
 750             list->lt_sl_entries[i] == NULL) {
 751                 return (NULL);
 752         }
 753 
 754         g_assert(list->lt_sl_entries[i]->lt_se_string != NULL);
 755 
 756         return (list->lt_sl_entries[i]->lt_se_string);
 757 }
 758 
 759 /*
 760  * Get maximum value of the given item i in the given list.
 761  */
 762 uint64_t
 763 lt_stat_list_get_max(void *ptr, int i)
 764 {
 765         lt_stat_list_t *list = (lt_stat_list_t *)ptr;
 766 
 767         if (list == NULL || i < 0 || i >= list->lt_sl_entry_count ||
 768             list->lt_sl_entries[i] == NULL) {
 769                 return (0);
 770         }
 771 
 772         return (list->lt_sl_entries[i]->lt_se_data.lt_s_max);
 773 }
 774 
 775 /*
 776  * Get total value of the given item i in the given list.
 777  */
 778 uint64_t
 779 lt_stat_list_get_sum(void *ptr, int i)
 780 {
 781         lt_stat_list_t *list = (lt_stat_list_t *)ptr;
 782 
 783         if (list == NULL || i < 0 || i >= list->lt_sl_entry_count ||
 784             list->lt_sl_entries[i] == NULL) {
 785                 return (0);
 786         }
 787 
 788         return (list->lt_sl_entries[i]->lt_se_data.lt_s_total);
 789 }
 790 
 791 /*
 792  * Get count value of the given item i in the given list.
 793  */
 794 uint64_t
 795 lt_stat_list_get_count(void *ptr, int i)
 796 {
 797         lt_stat_list_t *list = (lt_stat_list_t *)ptr;
 798 
 799         if (list == NULL || i < 0 || i >= list->lt_sl_entry_count ||
 800             list->lt_sl_entries[i] == NULL) {
 801                 return (0);
 802         }
 803 
 804         return (list->lt_sl_entries[i]->lt_se_data.lt_s_count);
 805 }
 806 
 807 /*
 808  * Get grand total of all latency in the list.
 809  */
 810 uint64_t
 811 lt_stat_list_get_gtotal(void *ptr)
 812 {
 813         lt_stat_list_t *list = (lt_stat_list_t *)ptr;
 814 
 815         if (list == NULL) {
 816                 return (0);
 817         }
 818 
 819         return (list->lt_sl_gtotal);
 820 }
 821 
 822 /*
 823  * ============================================================================
 824  * Process and thread list.
 825  * They share a lot of the static variables that are used for keeping
 826  * statistics, hence they are located in this file.
 827  */
 828 
 829 /*
 830  * Helper function, sort by PID/TID ascend.
 831  */
 832 static int
 833 sort_id(lt_stat_collection_t *a, lt_stat_collection_t *b)
 834 {
 835         return ((int)(a->lt_sc_id - b->lt_sc_id));
 836 }
 837 
 838 /*
 839  * Get the current list of processes. Call lt_stat_proc_list_free after use
 840  * to clean up.
 841  */
 842 static int
 843 plist_create(pid_t ** list)
 844 {
 845         GList *pid_list, *walk;
 846         int ret, count;
 847 
 848         ret = g_hash_table_size(stat_system->lt_sc_children);
 849         *list = (pid_t *)lt_malloc(sizeof (pid_t) * ret);
 850 
 851         pid_list = g_hash_table_get_values(stat_system->lt_sc_children);
 852         pid_list = g_list_sort(pid_list, (GCompareFunc)sort_id);
 853 
 854         for (walk = pid_list, count = 0;
 855             walk != NULL && count < ret;
 856             walk = g_list_next(walk), ++count) {
 857                 (*list)[count] = (int)
 858                     ((lt_stat_collection_t *)(walk->data))->lt_sc_id;
 859         }
 860 
 861         g_list_free(pid_list);
 862 
 863         return (ret);
 864 }
 865 
 866 /*
 867  * Count the no. of threads currently present in a process.
 868  * Only thread that have SSLEEP are counted.
 869  */
 870 /* ARGSUSED */
 871 static void
 872 count_threads(gpointer key, lt_stat_collection_t *stat_c, int *ret)
 873 {
 874         g_assert(ret != NULL);
 875 
 876         if (stat_c->lt_sc_children != NULL) {
 877                 *ret += g_hash_table_size(stat_c->lt_sc_children);
 878         }
 879 }
 880 
 881 /*
 882  * Get current list of processes and threads.
 883  * Call lt_stat_proc_list_free after use to clean up.
 884  */
 885 static int
 886 tlist_create(pid_t ** plist, id_t ** tlist)
 887 {
 888         GList *pid_list, *walk_p;
 889         GList *tid_list, *walk_t;
 890         int ret = 0;
 891         int count = 0;
 892 
 893         g_hash_table_foreach(stat_system->lt_sc_children,
 894             (GHFunc)count_threads, &ret);
 895 
 896         *plist = (pid_t *)lt_malloc(sizeof (pid_t) * ret);
 897         *tlist = (id_t *)lt_malloc(sizeof (id_t) * ret);
 898 
 899         pid_list = g_hash_table_get_values(stat_system->lt_sc_children);
 900         pid_list = g_list_sort(pid_list, (GCompareFunc)sort_id);
 901 
 902         for (walk_p = pid_list; walk_p != NULL;
 903             walk_p = g_list_next(walk_p)) {
 904                 lt_stat_collection_t *stat_p =
 905                     (lt_stat_collection_t *)walk_p->data;
 906 
 907                 if (stat_p->lt_sc_children == NULL) {
 908                         continue;
 909                 }
 910 
 911                 tid_list = g_hash_table_get_values(stat_p->lt_sc_children);
 912                 tid_list = g_list_sort(tid_list, (GCompareFunc)sort_id);
 913 
 914                 for (walk_t = tid_list; walk_t != NULL;
 915                     walk_t = g_list_next(walk_t)) {
 916                         lt_stat_collection_t *stat_t =
 917                             (lt_stat_collection_t *)walk_t->data;
 918 
 919                         (*plist)[count] = (int)stat_p->lt_sc_id;
 920                         (*tlist)[count] = (int)stat_t->lt_sc_id;
 921 
 922                         ++count;
 923                 }
 924                 g_list_free(tid_list);
 925         }
 926 
 927         g_list_free(pid_list);
 928         g_assert(count == ret);
 929 
 930         return (ret);
 931 }
 932 
 933 /*
 934  * List of processes that are tracked by LatencyTOP.
 935  */
 936 int
 937 lt_stat_proc_list_create(pid_t ** plist, id_t ** tlist)
 938 {
 939         if (plist == NULL) {
 940                 return (-1);
 941         }
 942 
 943         if (stat_system == NULL || stat_system->lt_sc_children == NULL) {
 944                 *plist = NULL;
 945 
 946                 if (tlist != NULL) {
 947                         *tlist = NULL;
 948                 }
 949 
 950                 return (0);
 951         }
 952 
 953         if (tlist == NULL) {
 954                 return (plist_create(plist));
 955         } else {
 956                 return (tlist_create(plist, tlist));
 957         }
 958 }
 959 
 960 /*
 961  * Free memory allocated by lt_stat_proc_list_create().
 962  */
 963 void
 964 lt_stat_proc_list_free(pid_t *plist, id_t *tlist)
 965 {
 966         if (plist != NULL) {
 967                 free(plist);
 968         }
 969 
 970         if (tlist != NULL) {
 971                 free(tlist);
 972         }
 973 }
 974 
 975 /*
 976  * Get executable name of the given process (ID).
 977  */
 978 const char *
 979 lt_stat_proc_get_name(pid_t pid)
 980 {
 981         lt_stat_collection_t *stat_p = NULL;
 982 
 983         if (stat_system == NULL || stat_system->lt_sc_children == NULL) {
 984                 return (NULL);
 985         }
 986 
 987         stat_p = (lt_stat_collection_t *)g_hash_table_lookup(
 988             stat_system->lt_sc_children, LT_INT_TO_POINTER(pid));
 989 
 990         if (stat_p != NULL) {
 991                 return (stat_p->lt_sc_name);
 992         } else   {
 993                 return (NULL);
 994         }
 995 }
 996 
 997 /*
 998  * Get number of threads.
 999  */
1000 int
1001 lt_stat_proc_get_nthreads(pid_t pid)
1002 {
1003         lt_stat_collection_t *stat_p = NULL;
1004 
1005         if (stat_system == NULL || stat_system->lt_sc_children == NULL) {
1006                 return (0);
1007         }
1008 
1009         stat_p = (lt_stat_collection_t *)g_hash_table_lookup(
1010             stat_system->lt_sc_children, LT_INT_TO_POINTER(pid));
1011 
1012         if (stat_p != NULL) {
1013                 return (g_hash_table_size(stat_p->lt_sc_children));
1014         } else   {
1015                 return (0);
1016         }
1017 }
1018 
1019 /*
1020  * Update statistics for synchronization objects.
1021  */
1022 void
1023 lt_stat_update_sobj(pid_t pid, id_t tid, int stype,
1024     unsigned long long wchan,
1025     lt_stat_type_t type, uint64_t value)
1026 {
1027         lt_sobj_id_t id;
1028         lt_sobj_t *sobj;
1029         int cause_id;
1030         lt_stat_collection_t *stat_t = NULL;
1031 
1032         stat_t = get_stat_c(pid, tid);
1033 
1034         if (stat_t == NULL) {
1035                 return;
1036         }
1037 
1038         id.lt_soi_type = stype;
1039         id.lt_soi_addr = wchan;
1040         sobj = lookup_sobj(&id);
1041 
1042         if (sobj == NULL) {
1043                 return;
1044         }
1045 
1046         cause_id = sobj->lt_so_cause_id;
1047 
1048         update_stat_entry(stat_t, cause_id, type, value,
1049             sobj->lt_so_string, GROUP_SOBJ);
1050 }