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]
22 * Copyright (c) 2008-2009, Intel Corporation.
23 * All Rights Reserved.
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
);
41 lt_stat_entry_t lt_grp_summary
;
42 /* cause_id -> stat entry */
43 GHashTable
*lt_grp_cidlist
;
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
;
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.
91 unsigned long long lt_soi_addr
;
95 lt_sobj_id_t lt_so_oid
;
97 char lt_so_string
[32]; /* Enough to hold "%s: 0x%llX" */
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
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.
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
131 lookup_sobj(lt_sobj_id_t
*id
)
133 const char *stype_str
[] = {
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
) {
152 if (sobj_table
!= NULL
) {
153 ret
= (lt_sobj_t
*)g_hash_table_lookup(sobj_table
, id
);
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
);
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
);
176 * Check if a process exists by using /proc/pid
180 check_process(gpointer key
, lt_stat_collection_t
*stat
, void *user
)
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
193 check_thread(gpointer key
, lt_stat_collection_t
*stat
, void *user
)
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.
209 free_stat(lt_stat_collection_t
*stat
)
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
].
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
);
236 * Helper function to initialize a stat node.
240 clear_stat(gpointer key
, lt_stat_collection_t
*stat
, void *user
)
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
].
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.
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
) {
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
));
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
);
295 entry
= (lt_stat_entry_t
*)lt_zalloc(sizeof (lt_stat_entry_t
));
296 entry
->lt_se_string
= string
;
298 switch (group_to_use
) {
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
;
313 entry
->lt_se_type
= STAT_SOBJ
;
314 entry
->lt_se_tsdata
.lt_se_t_sobj
.lt_se_s_id
= cause_id
;
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
,
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.
342 find_cause(char *stack
, int *cause_id
, int *cause_priority
)
346 int cause
= INVALID_CAUSE
;
350 g_assert(cause_id
!= NULL
);
351 g_assert(cause_priority
!= NULL
);
353 while (stack
!= NULL
) {
355 sep
= strchr(stack
, ' ');
361 found
= lt_table_cause_from_stack(stack
, &cause_temp
,
364 if (found
&& (cause
== INVALID_CAUSE
||
365 HIGHER_PRIORITY(prio_temp
, priority
))) {
367 priority
= prio_temp
;
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
)
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
;
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
=
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
);
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
) {
442 fname
= lt_get_proc_field(pid
, LT_FIELD_FNAME
);
446 * we could not get the executable name of the
447 * process; the process is probably already dead.
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" */
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
);
475 * Update statistics with the given cause_id. Values will be added to
476 * internal statistics.
479 lt_stat_update_cause(pid_t pid
, id_t tid
, int cause_id
, lt_stat_type_t type
,
483 lt_stat_collection_t
*stat_t
= NULL
;
485 if (cause_id
< 0 || value
== 0) {
489 if (lt_table_get_cause_flag(cause_id
, CAUSE_FLAG_DISABLED
)) {
490 /* Ignore this cause */
494 stat_t
= get_stat_c(pid
, tid
);
496 if (stat_t
== NULL
) {
497 /* Process must be dead. */
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.
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;
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 */
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
:
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,
547 lt_klog_log(LT_KLOG_LEVEL_UNMAPPED
, pid
, stack
, type
, value
);
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.
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
);
573 * Clean up function that frees all memory used for statistics.
576 lt_stat_free_all(void)
578 if (stat_system
!= NULL
) {
579 free_stat(stat_system
);
583 if (sobj_table
!= NULL
) {
584 g_hash_table_destroy(sobj_table
);
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.
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
)
600 lt_stat_collection_t
*stat_c
= NULL
;
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
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
));
623 * Thread entry was not found; set it to NULL,
624 * so that we can return empty list later.
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
) {
640 if (list_type
== LT_LIST_SOBJ
) {
641 group
= &(stat_c
->lt_sc_groups
[GROUP_SOBJ
]);
643 group
= &(stat_c
->lt_sc_groups
[GROUP_CAUSE
]);
646 if (group
->lt_grp_cidlist
== NULL
) {
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
);
657 func
= (GCompareFunc
)lt_sort_by_total_desc
;
660 func
= (GCompareFunc
)lt_sort_by_max_desc
;
663 func
= (GCompareFunc
)lt_sort_by_avg_desc
;
666 func
= (GCompareFunc
)lt_sort_by_count_desc
;
669 list
= g_list_sort(list
, func
);
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) {
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) {
690 if (data
->lt_se_data
.lt_s_count
== 0) {
694 ret
->lt_sl_entries
[ret
->lt_sl_entry_count
++] = data
;
703 * Free memory allocated by lt_stat_list_create().
706 lt_stat_list_free(void *ptr
)
708 lt_stat_list_t
*list
= (lt_stat_list_t
*)ptr
;
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
);
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
) {
742 * Get display name of the given item i in the given list.
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
) {
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.
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
) {
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.
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
) {
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.
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
) {
804 return (list
->lt_sl_entries
[i
]->lt_se_data
.lt_s_count
);
808 * Get grand total of all latency in the list.
811 lt_stat_list_get_gtotal(void *ptr
)
813 lt_stat_list_t
*list
= (lt_stat_list_t
*)ptr
;
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.
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
843 plist_create(pid_t
** list
)
845 GList
*pid_list
, *walk
;
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
);
867 * Count the no. of threads currently present in a process.
868 * Only thread that have SSLEEP are counted.
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.
886 tlist_create(pid_t
** plist
, id_t
** tlist
)
888 GList
*pid_list
, *walk_p
;
889 GList
*tid_list
, *walk_t
;
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
) {
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
;
924 g_list_free(tid_list
);
927 g_list_free(pid_list
);
928 g_assert(count
== ret
);
934 * List of processes that are tracked by LatencyTOP.
937 lt_stat_proc_list_create(pid_t
** plist
, id_t
** tlist
)
943 if (stat_system
== NULL
|| stat_system
->lt_sc_children
== NULL
) {
954 return (plist_create(plist
));
956 return (tlist_create(plist
, tlist
));
961 * Free memory allocated by lt_stat_proc_list_create().
964 lt_stat_proc_list_free(pid_t
*plist
, id_t
*tlist
)
976 * Get executable name of the given process (ID).
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
) {
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
);
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
) {
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
));
1020 * Update statistics for synchronization objects.
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
)
1030 lt_stat_collection_t
*stat_t
= NULL
;
1032 stat_t
= get_stat_c(pid
, tid
);
1034 if (stat_t
== NULL
) {
1038 id
.lt_soi_type
= stype
;
1039 id
.lt_soi_addr
= wchan
;
1040 sobj
= lookup_sobj(&id
);
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
);