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 } 1051