Print this page
16446 dtrace consumers should not be isaexec'd
Change-Id: Ibf80c7283c421cba98e80dce272c6dd51d24bb87
| Split |
Close |
| Expand all |
| Collapse all |
--- old/usr/src/cmd/latencytop/common/stat.c
+++ new/usr/src/cmd/latencytop/stat.c
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
11 11 * and limitations under the License.
12 12 *
13 13 * When distributing Covered Code, include this CDDL HEADER in each
14 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 15 * If applicable, add the following below this CDDL HEADER, with the
16 16 * fields enclosed by brackets "[]" replaced with your own identifying
17 17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 18 *
19 19 * CDDL HEADER END
20 20 */
21 21 /*
22 22 * Copyright (c) 2008-2009, Intel Corporation.
23 23 * All Rights Reserved.
24 24 */
25 25
26 26 #include <stdlib.h>
27 27 #include <stdio.h>
28 28 #include <memory.h>
29 29 #include <string.h>
30 30 #include <limits.h>
31 31 #include <sys/stat.h>
32 32
33 33 #include "latencytop.h"
34 34
35 35 /* Statistics for each process/thread. */
36 36 typedef struct _lt_stat_collection lt_stat_collection_t;
37 37 typedef gboolean (*check_child_func_t) (gpointer key,
38 38 lt_stat_collection_t *stat, void *user);
39 39
40 40 typedef struct {
41 41 lt_stat_entry_t lt_grp_summary;
42 42 /* cause_id -> stat entry */
43 43 GHashTable *lt_grp_cidlist;
44 44 } lt_datagroup_t;
45 45
46 46 #define NGROUPS 2
47 47 #define GROUP_CAUSE 0
48 48 #define GROUP_SOBJ 1
49 49
50 50 /*
51 51 * A data collection hierarchy involving three entities - system, process
52 52 * and thread. The hierarchic relationship is as follows :
53 53 *
54 54 * 1 system -> 1 or more processes -> 1 or more threads
55 55 */
56 56 struct _lt_stat_collection {
57 57 lt_stat_level_t lt_sc_level;
58 58 unsigned int lt_sc_id;
59 59 char *lt_sc_name;
60 60 lt_datagroup_t lt_sc_groups[NGROUPS];
61 61 /*
62 62 * The following fields: lt_sc_parent, lt_sc_children and
63 63 * lt_sc_check_child_func maintain the tree structure.
64 64 */
65 65 lt_stat_collection_t *lt_sc_parent; /* Parent node */
66 66 GHashTable *lt_sc_children; /* pid/tid -> lt_stat_collection_t */
67 67 check_child_func_t lt_sc_check_child_func; /* Release dead children */
68 68 };
69 69
70 70 /* Internal data structure to back up a stat_list */
71 71 typedef struct _lt_stat_list lt_stat_list_t;
72 72 typedef void (*free_list_func_t)(lt_stat_list_t *);
73 73 struct _lt_stat_list {
74 74 int lt_sl_entry_count;
75 75 lt_stat_entry_t **lt_sl_entries;
76 76 uint64_t lt_sl_gtotal;
77 77 free_list_func_t lt_sl_free_func;
78 78 };
79 79
80 80 /* Root of the collection hierarchy: system level statistics */
81 81 static lt_stat_collection_t *stat_system = NULL;
82 82
83 83 /*
84 84 * Data structure to hold synchronization objects.
85 85 * We don't use normal "cause table" because this needs to be cleared
86 86 * every time we refresh in order to make sure that stale synchronization
87 87 * objects don't consume memory.
88 88 */
89 89 typedef struct {
90 90 int lt_soi_type;
91 91 unsigned long long lt_soi_addr;
92 92 } lt_sobj_id_t;
93 93
94 94 typedef struct {
95 95 lt_sobj_id_t lt_so_oid;
96 96 int lt_so_cause_id;
97 97 char lt_so_string[32]; /* Enough to hold "%s: 0x%llX" */
98 98 } lt_sobj_t;
99 99
100 100 static GHashTable *sobj_table = NULL;
101 101 static int sobj_table_len = 0;
102 102
103 103 /*
104 104 * Lower 32-bit of the address of synchronization objects is used to hash
105 105 * them.
106 106 */
107 107 static guint
108 108 sobj_id_hash(lt_sobj_id_t *id)
109 109 {
110 110 g_assert(id != NULL);
111 111 return (id->lt_soi_addr & 0xFFFFFFFF);
112 112 }
113 113
114 114 /*
115 115 * Test if two synchronization objects are the same.
116 116 */
117 117 static gboolean
118 118 sobj_id_equal(lt_sobj_id_t *a, lt_sobj_id_t *b)
119 119 {
120 120 g_assert(a != NULL && b != NULL);
121 121 return (a->lt_soi_type == b->lt_soi_type &&
122 122 a->lt_soi_addr == b->lt_soi_addr);
123 123 }
124 124
125 125 /*
126 126 * Look up the cause_id of a synchronization object.
127 127 * Note that this cause_id is only unique in GROUP_SOBJ, and changes after
128 128 * a refresh.
129 129 */
130 130 static lt_sobj_t *
131 131 lookup_sobj(lt_sobj_id_t *id)
132 132 {
133 133 const char *stype_str[] = {
134 134 "None",
135 135 "Mutex",
136 136 "RWLock",
137 137 "CV",
138 138 "Sema",
139 139 "User",
140 140 "User_PI",
141 141 "Shuttle"
142 142 };
143 143 const int stype_str_len =
144 144 sizeof (stype_str) / sizeof (stype_str[0]);
145 145 lt_sobj_t *ret = NULL;
146 146 g_assert(id != NULL);
147 147
148 148 if (id->lt_soi_type < 0 || id->lt_soi_type >= stype_str_len) {
149 149 return (NULL);
150 150 }
151 151
152 152 if (sobj_table != NULL) {
153 153 ret = (lt_sobj_t *)g_hash_table_lookup(sobj_table, id);
154 154 } else {
155 155 sobj_table = g_hash_table_new_full(
156 156 (GHashFunc)sobj_id_hash, (GEqualFunc)sobj_id_equal,
157 157 NULL, (GDestroyNotify)free);
158 158 lt_check_null(sobj_table);
159 159 }
160 160
161 161 if (ret == NULL) {
162 162 ret = (lt_sobj_t *)lt_zalloc(sizeof (lt_sobj_t));
163 163 ret->lt_so_cause_id = ++sobj_table_len;
164 164 (void) snprintf(ret->lt_so_string, sizeof (ret->lt_so_string),
165 165 "%s: 0x%llX", stype_str[id->lt_soi_type], id->lt_soi_addr);
166 166 ret->lt_so_oid.lt_soi_type = id->lt_soi_type;
167 167 ret->lt_so_oid.lt_soi_addr = id->lt_soi_addr;
168 168
169 169 g_hash_table_insert(sobj_table, &ret->lt_so_oid, ret);
170 170 }
171 171
172 172 return (ret);
173 173 }
174 174
175 175 /*
176 176 * Check if a process exists by using /proc/pid
177 177 */
178 178 /* ARGSUSED */
179 179 static gboolean
180 180 check_process(gpointer key, lt_stat_collection_t *stat, void *user)
181 181 {
182 182 char name[PATH_MAX];
183 183
184 184 (void) snprintf(name, PATH_MAX, "/proc/%u", stat->lt_sc_id);
185 185 return (lt_file_exist(name) ? FALSE : TRUE);
186 186 }
187 187
188 188 /*
189 189 * Check if a thread exists by using /proc/pid/lwp/tid
190 190 */
191 191 /* ARGSUSED */
192 192 static gboolean
193 193 check_thread(gpointer key, lt_stat_collection_t *stat, void *user)
194 194 {
195 195 char name[PATH_MAX];
196 196
197 197 g_assert(stat->lt_sc_parent != NULL);
198 198 g_assert(stat->lt_sc_parent->lt_sc_level == LT_LEVEL_PROCESS);
199 199
200 200 (void) snprintf(name, PATH_MAX, "/proc/%u/lwp/%u",
201 201 stat->lt_sc_parent->lt_sc_id, stat->lt_sc_id);
202 202 return (lt_file_exist(name) ? FALSE : TRUE);
203 203 }
204 204
205 205 /*
206 206 * Helper function to free a stat node.
207 207 */
208 208 static void
209 209 free_stat(lt_stat_collection_t *stat)
210 210 {
211 211 int i;
212 212
213 213 if (stat == NULL) {
214 214 return;
215 215 }
216 216
217 217 for (i = 0; i < NGROUPS; ++i) {
218 218 if (stat->lt_sc_groups[i].lt_grp_cidlist != NULL) {
219 219 g_hash_table_destroy(stat->lt_sc_groups[i].
220 220 lt_grp_cidlist);
221 221 }
222 222 }
223 223
224 224 if (stat->lt_sc_children != NULL) {
225 225 g_hash_table_destroy(stat->lt_sc_children);
226 226 }
227 227
228 228 if (stat->lt_sc_name != NULL) {
229 229 free(stat->lt_sc_name);
230 230 }
231 231
232 232 free(stat);
233 233 }
234 234
235 235 /*
236 236 * Helper function to initialize a stat node.
237 237 */
238 238 /* ARGSUSED */
239 239 static void
240 240 clear_stat(gpointer key, lt_stat_collection_t *stat, void *user)
241 241 {
242 242 int i;
243 243
244 244 g_assert(stat != NULL);
245 245
246 246 for (i = 0; i < NGROUPS; ++i) {
247 247 if (stat->lt_sc_groups[i].lt_grp_cidlist != NULL) {
248 248 g_hash_table_destroy(stat->lt_sc_groups[i].
249 249 lt_grp_cidlist);
250 250 stat->lt_sc_groups[i].lt_grp_cidlist = NULL;
251 251 }
252 252
253 253 stat->lt_sc_groups[i].lt_grp_summary.lt_se_data.lt_s_count = 0;
254 254 stat->lt_sc_groups[i].lt_grp_summary.lt_se_data.lt_s_total = 0;
255 255 stat->lt_sc_groups[i].lt_grp_summary.lt_se_data.lt_s_max = 0;
256 256 }
257 257
258 258 if (stat->lt_sc_children != NULL) {
259 259 g_hash_table_foreach_remove(stat->lt_sc_children,
260 260 (GHRFunc)stat->lt_sc_check_child_func, NULL);
261 261 g_hash_table_foreach(stat->lt_sc_children,
|
↓ open down ↓ |
261 lines elided |
↑ open up ↑ |
262 262 (GHFunc)clear_stat, NULL);
263 263 }
264 264 }
265 265
266 266 /*
267 267 * Update a collection with the given value.
268 268 * Recursively update parents in the hierarchy until the root is reached.
269 269 */
270 270 static void
271 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)
272 + lt_stat_type_t type, uint64_t value,
273 + const char *string, int group_to_use)
274 274 {
275 275 lt_stat_entry_t *entry = NULL;
276 276 lt_datagroup_t *group;
277 277
278 278 if (group_to_use < 0 || group_to_use >= NGROUPS) {
279 279 return;
280 280 }
281 281
282 282 group = &(stat->lt_sc_groups[group_to_use]);
283 283
284 284 if (group->lt_grp_cidlist != NULL) {
285 285 entry = (lt_stat_entry_t *)g_hash_table_lookup(
286 286 group->lt_grp_cidlist, LT_INT_TO_POINTER(cause_id));
287 287 } else {
288 288 group->lt_grp_cidlist = g_hash_table_new_full(
289 289 g_direct_hash, g_direct_equal,
290 290 NULL, (GDestroyNotify)free);
291 291 lt_check_null(group->lt_grp_cidlist);
292 292 }
293 293
294 294 if (entry == NULL) {
295 295 entry = (lt_stat_entry_t *)lt_zalloc(sizeof (lt_stat_entry_t));
296 296 entry->lt_se_string = string;
297 297
298 298 switch (group_to_use) {
299 299 case GROUP_CAUSE:
300 300 entry->lt_se_type = STAT_CAUSE;
301 301 entry->lt_se_tsdata.lt_se_t_cause.lt_se_c_id = cause_id;
302 302 entry->lt_se_tsdata.lt_se_t_cause.lt_se_c_flags =
303 303 lt_table_get_cause_flag(cause_id, CAUSE_ALL_FLAGS);
304 304
305 305 /* hide the first '#' */
306 306 if ((entry->lt_se_tsdata.lt_se_t_cause.lt_se_c_flags
307 307 & CAUSE_FLAG_HIDE_IN_SUMMARY) != 0) {
308 308 ++entry->lt_se_string;
309 309 }
310 310
311 311 break;
312 312 case GROUP_SOBJ:
313 313 entry->lt_se_type = STAT_SOBJ;
314 314 entry->lt_se_tsdata.lt_se_t_sobj.lt_se_s_id = cause_id;
315 315 break;
316 316 }
317 317
318 318 g_hash_table_insert(group->lt_grp_cidlist,
319 319 LT_INT_TO_POINTER(cause_id), entry);
320 320 }
321 321
322 322 lt_update_stat_value(&entry->lt_se_data, type, value);
323 323
324 324 if (group_to_use == GROUP_SOBJ ||
325 325 (entry->lt_se_tsdata.lt_se_t_cause.lt_se_c_flags &
326 326 CAUSE_FLAG_HIDE_IN_SUMMARY) == 0) {
327 327 lt_update_stat_value(&group->lt_grp_summary.lt_se_data, type,
328 328 value);
329 329 }
330 330
331 331 if (stat->lt_sc_parent != NULL) {
332 332 update_stat_entry(stat->lt_sc_parent, cause_id, type, value,
333 333 string, group_to_use);
334 334 }
335 335 }
336 336
337 337 /*
338 338 * Identify the cause of latency from the given stack trace.
339 339 * Return cause_id.
340 340 */
341 341 static void
342 342 find_cause(char *stack, int *cause_id, int *cause_priority)
343 343 {
344 344 int cause_temp;
345 345 int prio_temp;
346 346 int cause = INVALID_CAUSE;
347 347 int priority = 0;
348 348 int found = 0;
349 349
350 350 g_assert(cause_id != NULL);
351 351 g_assert(cause_priority != NULL);
352 352
353 353 while (stack != NULL) {
354 354 char *sep;
355 355 sep = strchr(stack, ' ');
356 356
357 357 if (sep != NULL) {
358 358 *sep = '\0';
359 359 }
360 360
361 361 found = lt_table_cause_from_stack(stack, &cause_temp,
362 362 &prio_temp);
363 363
364 364 if (found && (cause == INVALID_CAUSE ||
365 365 HIGHER_PRIORITY(prio_temp, priority))) {
366 366 cause = cause_temp;
367 367 priority = prio_temp;
368 368 }
369 369
370 370 if (sep != NULL) {
371 371 *sep = ' ';
372 372 stack = sep + 1;
373 373 } else {
374 374 stack = NULL;
375 375 }
376 376 }
377 377
378 378 *cause_id = cause;
379 379 *cause_priority = priority;
380 380 }
381 381
382 382 /*
383 383 * Create a new collection and hook it to the parent.
384 384 */
385 385 static lt_stat_collection_t *
386 386 new_collection(lt_stat_level_t level, unsigned int id, char *name,
387 387 lt_stat_collection_t *parent, check_child_func_t check_child_func)
388 388 {
389 389 int i;
390 390 lt_stat_collection_t *ret;
391 391
392 392 ret = (lt_stat_collection_t *)
393 393 lt_zalloc(sizeof (lt_stat_collection_t));
394 394
395 395 ret->lt_sc_level = level;
396 396 ret->lt_sc_check_child_func = check_child_func;
397 397 ret->lt_sc_id = id;
398 398 ret->lt_sc_name = name;
399 399
400 400 for (i = 0; i < NGROUPS; ++i) {
401 401 ret->lt_sc_groups[i].lt_grp_summary.lt_se_string =
402 402 (const char *)name;
403 403 }
404 404
405 405 if (parent != NULL) {
406 406 ret->lt_sc_parent = parent;
407 407
408 408 if (parent->lt_sc_children == NULL) {
409 409 parent->lt_sc_children = g_hash_table_new_full(
410 410 g_direct_hash, g_direct_equal,
411 411 NULL, (GDestroyNotify)free_stat);
412 412 lt_check_null(parent->lt_sc_children);
413 413 }
414 414
415 415 g_hash_table_insert(parent->lt_sc_children,
416 416 LT_INT_TO_POINTER((int)id), ret);
417 417 }
418 418
419 419 return (ret);
420 420 }
421 421
422 422 /*
423 423 * Find the "leaf" in the collection hierarchy, using the given pid and tid.
424 424 */
425 425 static lt_stat_collection_t *
426 426 get_stat_c(pid_t pid, id_t tid)
427 427 {
428 428 lt_stat_collection_t *stat_p = NULL;
429 429 lt_stat_collection_t *stat_t = NULL;
430 430
431 431 if (stat_system == NULL) {
432 432 stat_system = new_collection(LT_LEVEL_GLOBAL,
433 433 PID_SYS_GLOBAL, lt_strdup("SYSTEM"), NULL, check_process);
434 434 } else if (stat_system->lt_sc_children != NULL) {
435 435 stat_p = (lt_stat_collection_t *)
436 436 g_hash_table_lookup(stat_system->lt_sc_children,
437 437 LT_INT_TO_POINTER(pid));
438 438 }
439 439
440 440 if (stat_p == NULL) {
441 441 char *fname;
442 442 fname = lt_get_proc_field(pid, LT_FIELD_FNAME);
443 443
444 444 if (fname == NULL) {
445 445 /*
446 446 * we could not get the executable name of the
447 447 * process; the process is probably already dead.
448 448 */
449 449 return (NULL);
450 450 }
451 451
452 452 stat_p = new_collection(LT_LEVEL_PROCESS,
453 453 (unsigned int)pid, fname, stat_system, check_thread);
454 454 } else if (stat_p->lt_sc_children != NULL) {
455 455 stat_t = (lt_stat_collection_t *)
456 456 g_hash_table_lookup(stat_p->lt_sc_children,
457 457 LT_INT_TO_POINTER(tid));
458 458 }
459 459
460 460 if (stat_t == NULL) {
461 461 const int tname_size = 16; /* Enough for "Thread %d" */
462 462 char *tname;
463 463
464 464 tname = (char *)lt_zalloc(tname_size);
465 465 (void) snprintf(tname, tname_size, "Thread %d", tid);
466 466
467 467 stat_t = new_collection(LT_LEVEL_THREAD,
468 468 (unsigned int)tid, tname, stat_p, NULL);
469 469 }
470 470
471 471 return (stat_t);
472 472 }
473 473
474 474 /*
475 475 * Update statistics with the given cause_id. Values will be added to
476 476 * internal statistics.
477 477 */
478 478 void
479 479 lt_stat_update_cause(pid_t pid, id_t tid, int cause_id, lt_stat_type_t type,
480 480 uint64_t value)
481 481 {
482 482 const char *string;
483 483 lt_stat_collection_t *stat_t = NULL;
484 484
485 485 if (cause_id < 0 || value == 0) {
486 486 return;
487 487 }
488 488
489 489 if (lt_table_get_cause_flag(cause_id, CAUSE_FLAG_DISABLED)) {
490 490 /* Ignore this cause */
491 491 return;
492 492 }
493 493
494 494 stat_t = get_stat_c(pid, tid);
495 495
496 496 if (stat_t == NULL) {
497 497 /* Process must be dead. */
498 498 return;
499 499 }
500 500
501 501 string = lt_table_get_cause_name(cause_id);
502 502
503 503 update_stat_entry(stat_t, cause_id, type, value, string, GROUP_CAUSE);
504 504 }
505 505
506 506 /*
507 507 * Update statistics with the given stack trace.
508 508 * The stack trace is mapped to a cause and lt_stat_update_cause() is called
509 509 * to update statistics.
510 510 */
511 511 void
512 512 lt_stat_update(pid_t pid, id_t tid, char *stack, char *tag,
513 513 unsigned int tag_priority, lt_stat_type_t type, uint64_t value)
514 514 {
515 515 int tag_cause_id = INVALID_CAUSE;
516 516 int stack_cause_id = INVALID_CAUSE;
517 517 int cause_id = INVALID_CAUSE;
518 518 int stack_priority = 0;
519 519
520 520 if (value == 0) {
521 521 return;
522 522 }
523 523
524 524 find_cause(stack, &stack_cause_id, &stack_priority);
525 525
526 526 if (tag_priority != 0) {
527 527 tag_cause_id = lt_table_cause_from_name(tag, 0, 0);
528 528
529 529 if (tag_cause_id == INVALID_CAUSE) {
530 530 /* This must be a syscall tag */
531 531 char tmp[64];
532 532 (void) snprintf(tmp, sizeof (tmp), "Syscall: %s", tag);
533 533 tag_cause_id = lt_table_cause_from_name(tmp, 1, 0);
534 534 }
535 535 }
536 536
537 537 cause_id = (tag_priority > stack_priority) ? tag_cause_id :
538 538 stack_cause_id;
539 539
540 540 if (cause_id == INVALID_CAUSE) {
541 541 /*
542 542 * We got an unmapped stack. Set SPECIAL flag to display it
543 543 * in pane 2. This makes it easier to find the cause.
544 544 */
545 545 cause_id = lt_table_cause_from_name(stack, 1,
546 546 CAUSE_FLAG_SPECIAL);
547 547 lt_klog_log(LT_KLOG_LEVEL_UNMAPPED, pid, stack, type, value);
548 548 } else {
549 549 lt_klog_log(LT_KLOG_LEVEL_MAPPED, pid, stack, type, value);
550 550 }
551 551
552 552 lt_stat_update_cause(pid, tid, cause_id, type, value);
553 553 }
554 554
555 555 /*
556 556 * Zero out all statistics, but keep the data structures in memory
557 557 * to be used to hold new data immediately following.
558 558 */
559 559 void
560 560 lt_stat_clear_all(void)
561 561 {
562 562 if (stat_system != NULL) {
563 563 clear_stat(NULL, stat_system, NULL);
564 564 }
565 565
566 566 if (sobj_table != NULL) {
567 567 g_hash_table_destroy(sobj_table);
568 568 sobj_table = NULL;
569 569 }
570 570 }
571 571
572 572 /*
573 573 * Clean up function that frees all memory used for statistics.
574 574 */
575 575 void
576 576 lt_stat_free_all(void)
577 577 {
578 578 if (stat_system != NULL) {
579 579 free_stat(stat_system);
580 580 stat_system = NULL;
581 581 }
582 582
583 583 if (sobj_table != NULL) {
584 584 g_hash_table_destroy(sobj_table);
585 585 sobj_table = NULL;
586 586 }
587 587 }
588 588
589 589 /*
590 590 * Get top N causes of latency for a process. Return handle to a stat_list.
591 591 * Use pid = PID_SYS_GLOBAL to get global top list.
592 592 * Call lt_stat_list_free after use to clean up.
593 593 */
594 594 void *
595 595 lt_stat_list_create(lt_list_type_t list_type, lt_stat_level_t level,
596 596 pid_t pid, id_t tid, int count, lt_sort_t sort_by)
597 597 {
598 598 GCompareFunc func;
599 599 GList *list, *walk;
600 600 lt_stat_collection_t *stat_c = NULL;
601 601 lt_stat_list_t *ret;
602 602 lt_datagroup_t *group;
603 603
604 604 if (level == LT_LEVEL_GLOBAL) {
605 605 /* Use global entry */
606 606 stat_c = stat_system;
607 607 } else if (stat_system != NULL && stat_system->lt_sc_children != NULL) {
608 608 /* Find process entry first */
609 609 stat_c = (lt_stat_collection_t *)g_hash_table_lookup(
610 610 stat_system->lt_sc_children, LT_INT_TO_POINTER(pid));
611 611
612 612 if (level == LT_LEVEL_THREAD) {
613 613 /*
614 614 * If thread entry is requested, find it based on
615 615 * process entry.
616 616 */
617 617 if (stat_c != NULL && stat_c->lt_sc_children != NULL) {
618 618 stat_c = (lt_stat_collection_t *)
619 619 g_hash_table_lookup(stat_c->lt_sc_children,
620 620 LT_INT_TO_POINTER(tid));
621 621 } else {
622 622 /*
623 623 * Thread entry was not found; set it to NULL,
624 624 * so that we can return empty list later.
625 625 */
626 626 stat_c = NULL;
627 627 }
628 628 }
629 629 }
630 630
631 631 ret = (lt_stat_list_t *)lt_zalloc(sizeof (lt_stat_list_t));
632 632 ret->lt_sl_entries = (lt_stat_entry_t **)
633 633 lt_zalloc(count * sizeof (lt_stat_entry_t *));
634 634
635 635 if (stat_c == NULL) {
636 636 /* Empty list */
637 637 return (ret);
638 638 }
639 639
640 640 if (list_type == LT_LIST_SOBJ) {
641 641 group = &(stat_c->lt_sc_groups[GROUP_SOBJ]);
642 642 } else {
643 643 group = &(stat_c->lt_sc_groups[GROUP_CAUSE]);
644 644 }
645 645
646 646 if (group->lt_grp_cidlist == NULL) {
647 647 /* Empty list */
648 648 return (ret);
649 649 }
650 650
651 651 ret->lt_sl_gtotal = group->lt_grp_summary.lt_se_data.lt_s_total;
652 652
653 653 list = g_hash_table_get_values(group->lt_grp_cidlist);
654 654
655 655 switch (sort_by) {
656 656 case LT_SORT_TOTAL:
657 657 func = (GCompareFunc)lt_sort_by_total_desc;
658 658 break;
659 659 case LT_SORT_MAX:
660 660 func = (GCompareFunc)lt_sort_by_max_desc;
661 661 break;
662 662 case LT_SORT_AVG:
663 663 func = (GCompareFunc)lt_sort_by_avg_desc;
664 664 break;
665 665 case LT_SORT_COUNT:
666 666 func = (GCompareFunc)lt_sort_by_count_desc;
667 667 break;
668 668 }
669 669 list = g_list_sort(list, func);
670 670
671 671 for (walk = list;
672 672 walk != NULL && count > 0;
673 673 walk = g_list_next(walk), --count) {
674 674 lt_stat_entry_t *data = (lt_stat_entry_t *)walk->data;
675 675
676 676 if (list_type == LT_LIST_CAUSE &&
677 677 data->lt_se_type == STAT_CAUSE &&
678 678 (data->lt_se_tsdata.lt_se_t_cause.lt_se_c_flags &
679 679 CAUSE_FLAG_HIDE_IN_SUMMARY) != 0) {
680 680 continue;
681 681 }
682 682
683 683 if (list_type == LT_LIST_SPECIALS &&
684 684 data->lt_se_type == STAT_CAUSE &&
685 685 (data->lt_se_tsdata.lt_se_t_cause.lt_se_c_flags &
686 686 CAUSE_FLAG_SPECIAL) == 0) {
687 687 continue;
688 688 }
689 689
690 690 if (data->lt_se_data.lt_s_count == 0) {
691 691 break;
692 692 }
693 693
694 694 ret->lt_sl_entries[ret->lt_sl_entry_count++] = data;
695 695 }
696 696
697 697 g_list_free(list);
698 698
699 699 return (ret);
700 700 }
701 701
702 702 /*
703 703 * Free memory allocated by lt_stat_list_create().
704 704 */
705 705 void
706 706 lt_stat_list_free(void *ptr)
707 707 {
708 708 lt_stat_list_t *list = (lt_stat_list_t *)ptr;
709 709
710 710 if (list == NULL) {
711 711 return;
712 712 }
713 713
714 714 if (list->lt_sl_free_func != NULL) {
715 715 list->lt_sl_free_func(list);
716 716 }
717 717
718 718 if (list->lt_sl_entries != NULL) {
719 719 free(list->lt_sl_entries);
720 720 }
721 721
722 722 free(list);
723 723 }
724 724
725 725 /*
726 726 * Check if the given list contains the given item.
727 727 */
728 728 int
729 729 lt_stat_list_has_item(void *ptr, int i)
730 730 {
731 731 lt_stat_list_t *list = (lt_stat_list_t *)ptr;
732 732
733 733 if (list == NULL || i < 0 || i >= list->lt_sl_entry_count ||
734 734 list->lt_sl_entries[i] == NULL) {
735 735 return (0);
736 736 }
737 737
738 738 return (1);
739 739 }
740 740
741 741 /*
742 742 * Get display name of the given item i in the given list.
743 743 */
744 744 const char *
745 745 lt_stat_list_get_reason(void *ptr, int i)
746 746 {
747 747 lt_stat_list_t *list = (lt_stat_list_t *)ptr;
748 748
749 749 if (list == NULL || i < 0 || i >= list->lt_sl_entry_count ||
750 750 list->lt_sl_entries[i] == NULL) {
751 751 return (NULL);
752 752 }
753 753
754 754 g_assert(list->lt_sl_entries[i]->lt_se_string != NULL);
755 755
756 756 return (list->lt_sl_entries[i]->lt_se_string);
757 757 }
758 758
759 759 /*
760 760 * Get maximum value of the given item i in the given list.
761 761 */
762 762 uint64_t
763 763 lt_stat_list_get_max(void *ptr, int i)
764 764 {
765 765 lt_stat_list_t *list = (lt_stat_list_t *)ptr;
766 766
767 767 if (list == NULL || i < 0 || i >= list->lt_sl_entry_count ||
768 768 list->lt_sl_entries[i] == NULL) {
769 769 return (0);
770 770 }
771 771
772 772 return (list->lt_sl_entries[i]->lt_se_data.lt_s_max);
773 773 }
774 774
775 775 /*
776 776 * Get total value of the given item i in the given list.
777 777 */
778 778 uint64_t
779 779 lt_stat_list_get_sum(void *ptr, int i)
780 780 {
781 781 lt_stat_list_t *list = (lt_stat_list_t *)ptr;
782 782
783 783 if (list == NULL || i < 0 || i >= list->lt_sl_entry_count ||
784 784 list->lt_sl_entries[i] == NULL) {
785 785 return (0);
786 786 }
787 787
788 788 return (list->lt_sl_entries[i]->lt_se_data.lt_s_total);
789 789 }
790 790
791 791 /*
792 792 * Get count value of the given item i in the given list.
793 793 */
794 794 uint64_t
795 795 lt_stat_list_get_count(void *ptr, int i)
796 796 {
797 797 lt_stat_list_t *list = (lt_stat_list_t *)ptr;
798 798
799 799 if (list == NULL || i < 0 || i >= list->lt_sl_entry_count ||
800 800 list->lt_sl_entries[i] == NULL) {
801 801 return (0);
802 802 }
803 803
804 804 return (list->lt_sl_entries[i]->lt_se_data.lt_s_count);
805 805 }
806 806
807 807 /*
808 808 * Get grand total of all latency in the list.
809 809 */
810 810 uint64_t
811 811 lt_stat_list_get_gtotal(void *ptr)
812 812 {
813 813 lt_stat_list_t *list = (lt_stat_list_t *)ptr;
814 814
815 815 if (list == NULL) {
816 816 return (0);
817 817 }
818 818
819 819 return (list->lt_sl_gtotal);
820 820 }
821 821
822 822 /*
823 823 * ============================================================================
824 824 * Process and thread list.
825 825 * They share a lot of the static variables that are used for keeping
826 826 * statistics, hence they are located in this file.
827 827 */
828 828
829 829 /*
830 830 * Helper function, sort by PID/TID ascend.
831 831 */
832 832 static int
833 833 sort_id(lt_stat_collection_t *a, lt_stat_collection_t *b)
834 834 {
835 835 return ((int)(a->lt_sc_id - b->lt_sc_id));
836 836 }
837 837
838 838 /*
839 839 * Get the current list of processes. Call lt_stat_proc_list_free after use
840 840 * to clean up.
841 841 */
842 842 static int
843 843 plist_create(pid_t ** list)
844 844 {
845 845 GList *pid_list, *walk;
846 846 int ret, count;
847 847
848 848 ret = g_hash_table_size(stat_system->lt_sc_children);
849 849 *list = (pid_t *)lt_malloc(sizeof (pid_t) * ret);
850 850
851 851 pid_list = g_hash_table_get_values(stat_system->lt_sc_children);
852 852 pid_list = g_list_sort(pid_list, (GCompareFunc)sort_id);
853 853
854 854 for (walk = pid_list, count = 0;
855 855 walk != NULL && count < ret;
856 856 walk = g_list_next(walk), ++count) {
857 857 (*list)[count] = (int)
858 858 ((lt_stat_collection_t *)(walk->data))->lt_sc_id;
859 859 }
860 860
861 861 g_list_free(pid_list);
862 862
863 863 return (ret);
864 864 }
865 865
866 866 /*
867 867 * Count the no. of threads currently present in a process.
868 868 * Only thread that have SSLEEP are counted.
869 869 */
870 870 /* ARGSUSED */
871 871 static void
872 872 count_threads(gpointer key, lt_stat_collection_t *stat_c, int *ret)
873 873 {
874 874 g_assert(ret != NULL);
875 875
876 876 if (stat_c->lt_sc_children != NULL) {
877 877 *ret += g_hash_table_size(stat_c->lt_sc_children);
878 878 }
879 879 }
880 880
881 881 /*
882 882 * Get current list of processes and threads.
883 883 * Call lt_stat_proc_list_free after use to clean up.
884 884 */
885 885 static int
886 886 tlist_create(pid_t ** plist, id_t ** tlist)
887 887 {
888 888 GList *pid_list, *walk_p;
889 889 GList *tid_list, *walk_t;
890 890 int ret = 0;
891 891 int count = 0;
892 892
893 893 g_hash_table_foreach(stat_system->lt_sc_children,
894 894 (GHFunc)count_threads, &ret);
895 895
896 896 *plist = (pid_t *)lt_malloc(sizeof (pid_t) * ret);
897 897 *tlist = (id_t *)lt_malloc(sizeof (id_t) * ret);
898 898
899 899 pid_list = g_hash_table_get_values(stat_system->lt_sc_children);
900 900 pid_list = g_list_sort(pid_list, (GCompareFunc)sort_id);
901 901
902 902 for (walk_p = pid_list; walk_p != NULL;
903 903 walk_p = g_list_next(walk_p)) {
904 904 lt_stat_collection_t *stat_p =
905 905 (lt_stat_collection_t *)walk_p->data;
906 906
907 907 if (stat_p->lt_sc_children == NULL) {
908 908 continue;
909 909 }
910 910
911 911 tid_list = g_hash_table_get_values(stat_p->lt_sc_children);
912 912 tid_list = g_list_sort(tid_list, (GCompareFunc)sort_id);
913 913
914 914 for (walk_t = tid_list; walk_t != NULL;
915 915 walk_t = g_list_next(walk_t)) {
916 916 lt_stat_collection_t *stat_t =
917 917 (lt_stat_collection_t *)walk_t->data;
918 918
919 919 (*plist)[count] = (int)stat_p->lt_sc_id;
920 920 (*tlist)[count] = (int)stat_t->lt_sc_id;
921 921
922 922 ++count;
923 923 }
924 924 g_list_free(tid_list);
925 925 }
926 926
927 927 g_list_free(pid_list);
928 928 g_assert(count == ret);
929 929
930 930 return (ret);
931 931 }
932 932
933 933 /*
934 934 * List of processes that are tracked by LatencyTOP.
935 935 */
936 936 int
937 937 lt_stat_proc_list_create(pid_t ** plist, id_t ** tlist)
938 938 {
939 939 if (plist == NULL) {
940 940 return (-1);
941 941 }
942 942
943 943 if (stat_system == NULL || stat_system->lt_sc_children == NULL) {
944 944 *plist = NULL;
945 945
946 946 if (tlist != NULL) {
947 947 *tlist = NULL;
948 948 }
949 949
950 950 return (0);
951 951 }
952 952
953 953 if (tlist == NULL) {
954 954 return (plist_create(plist));
955 955 } else {
956 956 return (tlist_create(plist, tlist));
957 957 }
958 958 }
959 959
960 960 /*
961 961 * Free memory allocated by lt_stat_proc_list_create().
962 962 */
963 963 void
964 964 lt_stat_proc_list_free(pid_t *plist, id_t *tlist)
965 965 {
966 966 if (plist != NULL) {
967 967 free(plist);
968 968 }
969 969
970 970 if (tlist != NULL) {
971 971 free(tlist);
972 972 }
973 973 }
974 974
975 975 /*
976 976 * Get executable name of the given process (ID).
977 977 */
978 978 const char *
979 979 lt_stat_proc_get_name(pid_t pid)
980 980 {
981 981 lt_stat_collection_t *stat_p = NULL;
982 982
983 983 if (stat_system == NULL || stat_system->lt_sc_children == NULL) {
984 984 return (NULL);
985 985 }
986 986
987 987 stat_p = (lt_stat_collection_t *)g_hash_table_lookup(
988 988 stat_system->lt_sc_children, LT_INT_TO_POINTER(pid));
989 989
990 990 if (stat_p != NULL) {
991 991 return (stat_p->lt_sc_name);
992 992 } else {
993 993 return (NULL);
994 994 }
995 995 }
996 996
997 997 /*
998 998 * Get number of threads.
999 999 */
1000 1000 int
1001 1001 lt_stat_proc_get_nthreads(pid_t pid)
1002 1002 {
1003 1003 lt_stat_collection_t *stat_p = NULL;
1004 1004
1005 1005 if (stat_system == NULL || stat_system->lt_sc_children == NULL) {
1006 1006 return (0);
1007 1007 }
1008 1008
1009 1009 stat_p = (lt_stat_collection_t *)g_hash_table_lookup(
1010 1010 stat_system->lt_sc_children, LT_INT_TO_POINTER(pid));
1011 1011
1012 1012 if (stat_p != NULL) {
1013 1013 return (g_hash_table_size(stat_p->lt_sc_children));
1014 1014 } else {
1015 1015 return (0);
1016 1016 }
1017 1017 }
1018 1018
1019 1019 /*
1020 1020 * Update statistics for synchronization objects.
1021 1021 */
1022 1022 void
1023 1023 lt_stat_update_sobj(pid_t pid, id_t tid, int stype,
1024 1024 unsigned long long wchan,
1025 1025 lt_stat_type_t type, uint64_t value)
1026 1026 {
1027 1027 lt_sobj_id_t id;
1028 1028 lt_sobj_t *sobj;
1029 1029 int cause_id;
1030 1030 lt_stat_collection_t *stat_t = NULL;
1031 1031
1032 1032 stat_t = get_stat_c(pid, tid);
1033 1033
1034 1034 if (stat_t == NULL) {
1035 1035 return;
1036 1036 }
1037 1037
1038 1038 id.lt_soi_type = stype;
1039 1039 id.lt_soi_addr = wchan;
1040 1040 sobj = lookup_sobj(&id);
1041 1041
1042 1042 if (sobj == NULL) {
1043 1043 return;
1044 1044 }
1045 1045
1046 1046 cause_id = sobj->lt_so_cause_id;
1047 1047
1048 1048 update_stat_entry(stat_t, cause_id, type, value,
1049 1049 sobj->lt_so_string, GROUP_SOBJ);
1050 1050 }
|
↓ open down ↓ |
767 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX