/* Add an activity to the global list. */ struct activity * add_activity (const char *name, int flags) { struct activity *ret; size_t i; /* You shouldn't have two activities with the same name. */ assert (!activity_exists (name)); nr_activities++; activities = realloc (activities, sizeof (struct activity) * nr_activities); if (activities == NULL) error (EXIT_FAILURE, errno, "realloc"); ret = &activities[nr_activities-1]; ret->name = strdup (name); if (ret->name == NULL) error (EXIT_FAILURE, errno, "strdup"); ret->flags = flags; for (i = 0; i < NR_TEST_PASSES; ++i) ret->start_event[i] = ret->end_event[i] = 0; return ret; }
/* 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; } } } } } }