/* Go through the activities, computing the start and elapsed time. */ static void analyze_timeline (void) { struct activity *activity; size_t i, j; int64_t delta_ns; for (j = 0; j < nr_activities; ++j) { activity = &activities[j]; activity->t = 0; activity->mean = 0; for (i = 0; i < NR_TEST_PASSES; ++i) { delta_ns = timespec_diff (&pass_data[i].events[0].t, &pass_data[i].events[activity->start_event[i]].t); activity->t += delta_ns; delta_ns = timespec_diff (&pass_data[i].events[activity->start_event[i]].t, &pass_data[i].events[activity->end_event[i]].t); activity->mean += delta_ns; } /* Divide through to get real start time and mean of each activity. */ activity->t /= NR_TEST_PASSES; activity->mean /= NR_TEST_PASSES; /* Calculate the end time of this activity. It's convenient when * drawing the timeline for one activity to finish just before the * next activity starts, rather than having them end and start at * the same time, hence ``- 1'' here. */ activity->end_t = activity->t + activity->mean - 1; /* The above only calculated mean. Now we are able to * calculate from the mean the variance and the standard * deviation. */ activity->variance = 0; for (i = 0; i < NR_TEST_PASSES; ++i) { delta_ns = timespec_diff (&pass_data[i].events[activity->start_event[i]].t, &pass_data[i].events[activity->end_event[i]].t); activity->variance += pow (delta_ns - activity->mean, 2); } activity->variance /= NR_TEST_PASSES; activity->sd = sqrt (activity->variance); } /* Get the total mean elapsed time from the special "run" activity. */ activity = find_activity ("run"); for (j = 0; j < nr_activities; ++j) { activities[j].percent = 100.0 * activities[j].mean / activity->mean; activities[j].warning = !(activities[j].flags & LONG_ACTIVITY) && activities[j].percent >= WARNING_THRESHOLD; } /* Sort the activities by start time. */ qsort (activities, nr_activities, sizeof (struct activity), compare_activities_by_t); }
/* Handling of initcall is so peculiar that we hide it in a separate * function from the rest. */ static void construct_initcall_timeline (void) { size_t i, j, k; struct pass_data *data; struct activity *activity; for (i = 0; i < NR_TEST_PASSES; ++i) { data = &pass_data[i]; /* Each kernel initcall is bracketed by: * * calling ehci_hcd_init+0x0/0xc1 @ 1" * initcall ehci_hcd_init+0x0/0xc1 returned 0 after 420 usecs" * * For initcall functions in modules: * * calling virtio_mmio_init+0x0/0x1000 [virtio_mmio] @ 1" * initcall virtio_mmio_init+0x0/0x1000 [virtio_mmio] returned 0 after 14 usecs" * * Initcall functions can be nested, and do not have unique names. */ for (j = 0; j < data->nr_events; ++j) { int vec[30], r; const char *message = data->events[j].message; if (data->events[j].source == GUESTFS_EVENT_APPLIANCE && ((r = pcre_exec (re_initcall_calling_module, NULL, message, strlen (message), 0, 0, vec, sizeof vec / sizeof vec[0])) >= 1 || (r = pcre_exec (re_initcall_calling, NULL, message, strlen (message), 0, 0, vec, sizeof vec / sizeof vec[0])) >= 1)) { CLEANUP_FREE char *fn_name = NULL, *module_name = NULL; if (r >= 2) /* because pcre_exec returns 1 + number of captures */ fn_name = strndup (message + vec[2], vec[3]-vec[2]); if (r >= 3) module_name = strndup (message + vec[4], vec[5]-vec[4]); CLEANUP_FREE char *fullname; if (asprintf (&fullname, "%s.%s", module_name ? module_name : "kernel", fn_name) == -1) error (EXIT_FAILURE, errno, "asprintf"); CLEANUP_FREE char *initcall_match; if (asprintf (&initcall_match, "initcall %s", fn_name) == -1) error (EXIT_FAILURE, errno, "asprintf"); /* Get a unique name for this activity. Unfortunately * kernel initcall function names are not unique! */ CLEANUP_FREE char *activity_name; if (asprintf (&activity_name, "initcall %s", fullname) == -1) error (EXIT_FAILURE, errno, "asprintf"); if (i == 0) { int n = 1; while (activity_exists (activity_name)) { free (activity_name); if (asprintf (&activity_name, "initcall %s:%d", fullname, n) == -1) error (EXIT_FAILURE, errno, "asprintf"); n++; } } else { int n = 1; while (!activity_exists_with_no_data (activity_name, i)) { free (activity_name); if (asprintf (&activity_name, "initcall %s:%d", fullname, n) == -1) error (EXIT_FAILURE, errno, "asprintf"); n++; } } /* Find the matching end event. It might be some time later, * since it appears initcalls can be nested. */ for (k = j+1; k < data->nr_events; ++k) { if (data->events[k].source == GUESTFS_EVENT_APPLIANCE && strstr (data->events[k].message, initcall_match)) { if (i == 0) activity = add_activity (activity_name, 0); else activity = find_activity (activity_name); activity->start_event[i] = j; activity->end_event[i] = k; break; } } } } } }