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