dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / cmd / latencytop / common / stat.c
blob330173c30247060e7a7bde136df8575a87d76943
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * Copyright (c) 2008-2009, Intel Corporation.
23 * All Rights Reserved.
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>
33 #include "latencytop.h"
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);
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;
46 #define NGROUPS 2
47 #define GROUP_CAUSE 0
48 #define GROUP_SOBJ 1
51 * A data collection hierarchy involving three entities - system, process
52 * and thread. The hierarchic relationship is as follows :
54 * 1 system -> 1 or more processes -> 1 or more threads
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];
62 * The following fields: lt_sc_parent, lt_sc_children and
63 * lt_sc_check_child_func maintain the tree structure.
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 */
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;
80 /* Root of the collection hierarchy: system level statistics */
81 static lt_stat_collection_t *stat_system = NULL;
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.
89 typedef struct {
90 int lt_soi_type;
91 unsigned long long lt_soi_addr;
92 } lt_sobj_id_t;
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;
100 static GHashTable *sobj_table = NULL;
101 static int sobj_table_len = 0;
104 * Lower 32-bit of the address of synchronization objects is used to hash
105 * them.
107 static guint
108 sobj_id_hash(lt_sobj_id_t *id)
110 g_assert(id != NULL);
111 return (id->lt_soi_addr & 0xFFFFFFFF);
115 * Test if two synchronization objects are the same.
117 static gboolean
118 sobj_id_equal(lt_sobj_id_t *a, lt_sobj_id_t *b)
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);
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.
130 static lt_sobj_t *
131 lookup_sobj(lt_sobj_id_t *id)
133 const char *stype_str[] = {
134 "None",
135 "Mutex",
136 "RWLock",
137 "CV",
138 "Sema",
139 "User",
140 "User_PI",
141 "Shuttle"
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);
148 if (id->lt_soi_type < 0 || id->lt_soi_type >= stype_str_len) {
149 return (NULL);
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);
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;
169 g_hash_table_insert(sobj_table, &ret->lt_so_oid, ret);
172 return (ret);
176 * Check if a process exists by using /proc/pid
178 /* ARGSUSED */
179 static gboolean
180 check_process(gpointer key, lt_stat_collection_t *stat, void *user)
182 char name[PATH_MAX];
184 (void) snprintf(name, PATH_MAX, "/proc/%u", stat->lt_sc_id);
185 return (lt_file_exist(name) ? FALSE : TRUE);
189 * Check if a thread exists by using /proc/pid/lwp/tid
191 /* ARGSUSED */
192 static gboolean
193 check_thread(gpointer key, lt_stat_collection_t *stat, void *user)
195 char name[PATH_MAX];
197 g_assert(stat->lt_sc_parent != NULL);
198 g_assert(stat->lt_sc_parent->lt_sc_level == LT_LEVEL_PROCESS);
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);
206 * Helper function to free a stat node.
208 static void
209 free_stat(lt_stat_collection_t *stat)
211 int i;
213 if (stat == NULL) {
214 return;
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);
224 if (stat->lt_sc_children != NULL) {
225 g_hash_table_destroy(stat->lt_sc_children);
228 if (stat->lt_sc_name != NULL) {
229 free(stat->lt_sc_name);
232 free(stat);
236 * Helper function to initialize a stat node.
238 /* ARGSUSED */
239 static void
240 clear_stat(gpointer key, lt_stat_collection_t *stat, void *user)
242 int i;
244 g_assert(stat != NULL);
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;
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;
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);
267 * Update a collection with the given value.
268 * Recursively update parents in the hierarchy until the root is reached.
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)
275 lt_stat_entry_t *entry = NULL;
276 lt_datagroup_t *group;
278 if (group_to_use < 0 || group_to_use >= NGROUPS) {
279 return;
282 group = &(stat->lt_sc_groups[group_to_use]);
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);
294 if (entry == NULL) {
295 entry = (lt_stat_entry_t *)lt_zalloc(sizeof (lt_stat_entry_t));
296 entry->lt_se_string = string;
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);
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;
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;
318 g_hash_table_insert(group->lt_grp_cidlist,
319 LT_INT_TO_POINTER(cause_id), entry);
322 lt_update_stat_value(&entry->lt_se_data, type, value);
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);
331 if (stat->lt_sc_parent != NULL) {
332 update_stat_entry(stat->lt_sc_parent, cause_id, type, value,
333 string, group_to_use);
338 * Identify the cause of latency from the given stack trace.
339 * Return cause_id.
341 static void
342 find_cause(char *stack, int *cause_id, int *cause_priority)
344 int cause_temp;
345 int prio_temp;
346 int cause = INVALID_CAUSE;
347 int priority = 0;
348 int found = 0;
350 g_assert(cause_id != NULL);
351 g_assert(cause_priority != NULL);
353 while (stack != NULL) {
354 char *sep;
355 sep = strchr(stack, ' ');
357 if (sep != NULL) {
358 *sep = '\0';
361 found = lt_table_cause_from_stack(stack, &cause_temp,
362 &prio_temp);
364 if (found && (cause == INVALID_CAUSE ||
365 HIGHER_PRIORITY(prio_temp, priority))) {
366 cause = cause_temp;
367 priority = prio_temp;
370 if (sep != NULL) {
371 *sep = ' ';
372 stack = sep + 1;
373 } else {
374 stack = NULL;
378 *cause_id = cause;
379 *cause_priority = priority;
383 * Create a new collection and hook it to the parent.
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)
389 int i;
390 lt_stat_collection_t *ret;
392 ret = (lt_stat_collection_t *)
393 lt_zalloc(sizeof (lt_stat_collection_t));
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;
400 for (i = 0; i < NGROUPS; ++i) {
401 ret->lt_sc_groups[i].lt_grp_summary.lt_se_string =
402 (const char *)name;
405 if (parent != NULL) {
406 ret->lt_sc_parent = parent;
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);
415 g_hash_table_insert(parent->lt_sc_children,
416 LT_INT_TO_POINTER((int)id), ret);
419 return (ret);
423 * Find the "leaf" in the collection hierarchy, using the given pid and tid.
425 static lt_stat_collection_t *
426 get_stat_c(pid_t pid, id_t tid)
428 lt_stat_collection_t *stat_p = NULL;
429 lt_stat_collection_t *stat_t = NULL;
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));
440 if (stat_p == NULL) {
441 char *fname;
442 fname = lt_get_proc_field(pid, LT_FIELD_FNAME);
444 if (fname == NULL) {
446 * we could not get the executable name of the
447 * process; the process is probably already dead.
449 return (NULL);
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));
460 if (stat_t == NULL) {
461 const int tname_size = 16; /* Enough for "Thread %d" */
462 char *tname;
464 tname = (char *)lt_zalloc(tname_size);
465 (void) snprintf(tname, tname_size, "Thread %d", tid);
467 stat_t = new_collection(LT_LEVEL_THREAD,
468 (unsigned int)tid, tname, stat_p, NULL);
471 return (stat_t);
475 * Update statistics with the given cause_id. Values will be added to
476 * internal statistics.
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)
482 const char *string;
483 lt_stat_collection_t *stat_t = NULL;
485 if (cause_id < 0 || value == 0) {
486 return;
489 if (lt_table_get_cause_flag(cause_id, CAUSE_FLAG_DISABLED)) {
490 /* Ignore this cause */
491 return;
494 stat_t = get_stat_c(pid, tid);
496 if (stat_t == NULL) {
497 /* Process must be dead. */
498 return;
501 string = lt_table_get_cause_name(cause_id);
503 update_stat_entry(stat_t, cause_id, type, value, string, GROUP_CAUSE);
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.
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)
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;
520 if (value == 0) {
521 return;
524 find_cause(stack, &stack_cause_id, &stack_priority);
526 if (tag_priority != 0) {
527 tag_cause_id = lt_table_cause_from_name(tag, 0, 0);
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);
537 cause_id = (tag_priority > stack_priority) ? tag_cause_id :
538 stack_cause_id;
540 if (cause_id == INVALID_CAUSE) {
542 * We got an unmapped stack. Set SPECIAL flag to display it
543 * in pane 2. This makes it easier to find the cause.
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);
552 lt_stat_update_cause(pid, tid, cause_id, type, value);
556 * Zero out all statistics, but keep the data structures in memory
557 * to be used to hold new data immediately following.
559 void
560 lt_stat_clear_all(void)
562 if (stat_system != NULL) {
563 clear_stat(NULL, stat_system, NULL);
566 if (sobj_table != NULL) {
567 g_hash_table_destroy(sobj_table);
568 sobj_table = NULL;
573 * Clean up function that frees all memory used for statistics.
575 void
576 lt_stat_free_all(void)
578 if (stat_system != NULL) {
579 free_stat(stat_system);
580 stat_system = NULL;
583 if (sobj_table != NULL) {
584 g_hash_table_destroy(sobj_table);
585 sobj_table = NULL;
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.
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)
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;
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));
612 if (level == LT_LEVEL_THREAD) {
614 * If thread entry is requested, find it based on
615 * process entry.
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 {
623 * Thread entry was not found; set it to NULL,
624 * so that we can return empty list later.
626 stat_c = NULL;
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 *));
635 if (stat_c == NULL) {
636 /* Empty list */
637 return (ret);
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]);
646 if (group->lt_grp_cidlist == NULL) {
647 /* Empty list */
648 return (ret);
651 ret->lt_sl_gtotal = group->lt_grp_summary.lt_se_data.lt_s_total;
653 list = g_hash_table_get_values(group->lt_grp_cidlist);
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;
669 list = g_list_sort(list, func);
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;
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;
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;
690 if (data->lt_se_data.lt_s_count == 0) {
691 break;
694 ret->lt_sl_entries[ret->lt_sl_entry_count++] = data;
697 g_list_free(list);
699 return (ret);
703 * Free memory allocated by lt_stat_list_create().
705 void
706 lt_stat_list_free(void *ptr)
708 lt_stat_list_t *list = (lt_stat_list_t *)ptr;
710 if (list == NULL) {
711 return;
714 if (list->lt_sl_free_func != NULL) {
715 list->lt_sl_free_func(list);
718 if (list->lt_sl_entries != NULL) {
719 free(list->lt_sl_entries);
722 free(list);
726 * Check if the given list contains the given item.
729 lt_stat_list_has_item(void *ptr, int i)
731 lt_stat_list_t *list = (lt_stat_list_t *)ptr;
733 if (list == NULL || i < 0 || i >= list->lt_sl_entry_count ||
734 list->lt_sl_entries[i] == NULL) {
735 return (0);
738 return (1);
742 * Get display name of the given item i in the given list.
744 const char *
745 lt_stat_list_get_reason(void *ptr, int i)
747 lt_stat_list_t *list = (lt_stat_list_t *)ptr;
749 if (list == NULL || i < 0 || i >= list->lt_sl_entry_count ||
750 list->lt_sl_entries[i] == NULL) {
751 return (NULL);
754 g_assert(list->lt_sl_entries[i]->lt_se_string != NULL);
756 return (list->lt_sl_entries[i]->lt_se_string);
760 * Get maximum value of the given item i in the given list.
762 uint64_t
763 lt_stat_list_get_max(void *ptr, int i)
765 lt_stat_list_t *list = (lt_stat_list_t *)ptr;
767 if (list == NULL || i < 0 || i >= list->lt_sl_entry_count ||
768 list->lt_sl_entries[i] == NULL) {
769 return (0);
772 return (list->lt_sl_entries[i]->lt_se_data.lt_s_max);
776 * Get total value of the given item i in the given list.
778 uint64_t
779 lt_stat_list_get_sum(void *ptr, int i)
781 lt_stat_list_t *list = (lt_stat_list_t *)ptr;
783 if (list == NULL || i < 0 || i >= list->lt_sl_entry_count ||
784 list->lt_sl_entries[i] == NULL) {
785 return (0);
788 return (list->lt_sl_entries[i]->lt_se_data.lt_s_total);
792 * Get count value of the given item i in the given list.
794 uint64_t
795 lt_stat_list_get_count(void *ptr, int i)
797 lt_stat_list_t *list = (lt_stat_list_t *)ptr;
799 if (list == NULL || i < 0 || i >= list->lt_sl_entry_count ||
800 list->lt_sl_entries[i] == NULL) {
801 return (0);
804 return (list->lt_sl_entries[i]->lt_se_data.lt_s_count);
808 * Get grand total of all latency in the list.
810 uint64_t
811 lt_stat_list_get_gtotal(void *ptr)
813 lt_stat_list_t *list = (lt_stat_list_t *)ptr;
815 if (list == NULL) {
816 return (0);
819 return (list->lt_sl_gtotal);
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.
830 * Helper function, sort by PID/TID ascend.
832 static int
833 sort_id(lt_stat_collection_t *a, lt_stat_collection_t *b)
835 return ((int)(a->lt_sc_id - b->lt_sc_id));
839 * Get the current list of processes. Call lt_stat_proc_list_free after use
840 * to clean up.
842 static int
843 plist_create(pid_t ** list)
845 GList *pid_list, *walk;
846 int ret, count;
848 ret = g_hash_table_size(stat_system->lt_sc_children);
849 *list = (pid_t *)lt_malloc(sizeof (pid_t) * ret);
851 pid_list = g_hash_table_get_values(stat_system->lt_sc_children);
852 pid_list = g_list_sort(pid_list, (GCompareFunc)sort_id);
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;
861 g_list_free(pid_list);
863 return (ret);
867 * Count the no. of threads currently present in a process.
868 * Only thread that have SSLEEP are counted.
870 /* ARGSUSED */
871 static void
872 count_threads(gpointer key, lt_stat_collection_t *stat_c, int *ret)
874 g_assert(ret != NULL);
876 if (stat_c->lt_sc_children != NULL) {
877 *ret += g_hash_table_size(stat_c->lt_sc_children);
882 * Get current list of processes and threads.
883 * Call lt_stat_proc_list_free after use to clean up.
885 static int
886 tlist_create(pid_t ** plist, id_t ** tlist)
888 GList *pid_list, *walk_p;
889 GList *tid_list, *walk_t;
890 int ret = 0;
891 int count = 0;
893 g_hash_table_foreach(stat_system->lt_sc_children,
894 (GHFunc)count_threads, &ret);
896 *plist = (pid_t *)lt_malloc(sizeof (pid_t) * ret);
897 *tlist = (id_t *)lt_malloc(sizeof (id_t) * ret);
899 pid_list = g_hash_table_get_values(stat_system->lt_sc_children);
900 pid_list = g_list_sort(pid_list, (GCompareFunc)sort_id);
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;
907 if (stat_p->lt_sc_children == NULL) {
908 continue;
911 tid_list = g_hash_table_get_values(stat_p->lt_sc_children);
912 tid_list = g_list_sort(tid_list, (GCompareFunc)sort_id);
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;
919 (*plist)[count] = (int)stat_p->lt_sc_id;
920 (*tlist)[count] = (int)stat_t->lt_sc_id;
922 ++count;
924 g_list_free(tid_list);
927 g_list_free(pid_list);
928 g_assert(count == ret);
930 return (ret);
934 * List of processes that are tracked by LatencyTOP.
937 lt_stat_proc_list_create(pid_t ** plist, id_t ** tlist)
939 if (plist == NULL) {
940 return (-1);
943 if (stat_system == NULL || stat_system->lt_sc_children == NULL) {
944 *plist = NULL;
946 if (tlist != NULL) {
947 *tlist = NULL;
950 return (0);
953 if (tlist == NULL) {
954 return (plist_create(plist));
955 } else {
956 return (tlist_create(plist, tlist));
961 * Free memory allocated by lt_stat_proc_list_create().
963 void
964 lt_stat_proc_list_free(pid_t *plist, id_t *tlist)
966 if (plist != NULL) {
967 free(plist);
970 if (tlist != NULL) {
971 free(tlist);
976 * Get executable name of the given process (ID).
978 const char *
979 lt_stat_proc_get_name(pid_t pid)
981 lt_stat_collection_t *stat_p = NULL;
983 if (stat_system == NULL || stat_system->lt_sc_children == NULL) {
984 return (NULL);
987 stat_p = (lt_stat_collection_t *)g_hash_table_lookup(
988 stat_system->lt_sc_children, LT_INT_TO_POINTER(pid));
990 if (stat_p != NULL) {
991 return (stat_p->lt_sc_name);
992 } else {
993 return (NULL);
998 * Get number of threads.
1001 lt_stat_proc_get_nthreads(pid_t pid)
1003 lt_stat_collection_t *stat_p = NULL;
1005 if (stat_system == NULL || stat_system->lt_sc_children == NULL) {
1006 return (0);
1009 stat_p = (lt_stat_collection_t *)g_hash_table_lookup(
1010 stat_system->lt_sc_children, LT_INT_TO_POINTER(pid));
1012 if (stat_p != NULL) {
1013 return (g_hash_table_size(stat_p->lt_sc_children));
1014 } else {
1015 return (0);
1020 * Update statistics for synchronization objects.
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)
1027 lt_sobj_id_t id;
1028 lt_sobj_t *sobj;
1029 int cause_id;
1030 lt_stat_collection_t *stat_t = NULL;
1032 stat_t = get_stat_c(pid, tid);
1034 if (stat_t == NULL) {
1035 return;
1038 id.lt_soi_type = stype;
1039 id.lt_soi_addr = wchan;
1040 sobj = lookup_sobj(&id);
1042 if (sobj == NULL) {
1043 return;
1046 cause_id = sobj->lt_so_cause_id;
1048 update_stat_entry(stat_t, cause_id, type, value,
1049 sobj->lt_so_string, GROUP_SOBJ);