xref: /titanic_51/usr/src/cmd/latencytop/common/stat.c (revision de3d2ce46fc25c7b67ccbae4afe5f15e5357568f)
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