diff --git a/libdrgn/examples/load_debug_info.c b/libdrgn/examples/load_debug_info.c index d5a812c7e..40047ce05 100644 --- a/libdrgn/examples/load_debug_info.c +++ b/libdrgn/examples/load_debug_info.c @@ -3,13 +3,50 @@ #include #include +#include #include #include #include +#include #include #include #include "drgn.h" +#include "util.h" + +extern char **environ; + +static struct drgn_error *run_command(const char *which, const char *command) +{ + if (!command) + return NULL; + + char pid_arg[max_decimal_length(long)]; + sprintf(pid_arg, "%ld", (long)getpid()); + const char * const argv[] = {"sh", "-c", command, "sh", pid_arg, NULL}; + + pid_t pid; + int errnum = + posix_spawnp(&pid, "sh", NULL, NULL, (char **)argv, environ); + if (errnum) + return drgn_error_create_os("posix_spawnp", errnum, "sh"); + + int wstatus; + if (waitpid(pid, &wstatus, 0) < 0) + return drgn_error_create_os("waitpid", errno, NULL); + + if (!WIFEXITED(wstatus)) { + return drgn_error_format(DRGN_ERROR_OTHER, + "%s-exec command exited abnormally: %d", + which, wstatus); + } + if (WEXITSTATUS(wstatus) != 0) { + return drgn_error_format(DRGN_ERROR_OTHER, + "%s-exec command exited with status %d", + which, WEXITSTATUS(wstatus)); + } + return NULL; +} static inline struct timespec timespec_sub(struct timespec a, struct timespec b) { @@ -29,7 +66,7 @@ static inline struct timespec timespec_sub(struct timespec a, struct timespec b) noreturn static void usage(bool error) { fprintf(error ? stderr : stdout, - "usage: load_debug_info [-k|-c CORE|-p PID] [PATH...]\n" + "usage: load_debug_info [OPTION...] [-k|-c CORE|-p PID] [PATH...]\n" "\n" "Example libdrgn program that loads default debug information\n" "\n" @@ -38,6 +75,10 @@ noreturn static void usage(bool error) " -c PATH, --core PATH debug the given core dump\n" " -p PID, --pid PID debug the running process with the given PID\n" " -T, --time print how long loading debug info took in seconds\n" + " --pre-exec CMD before loading debug info, execute shell command with\n" + " PID of this process as argument\n" + " --post-exec CMD after loading debug info, execute shell command with\n" + " PID of this process as argument\n" " -h, --help display this help message and exit\n"); exit(error ? EXIT_FAILURE : EXIT_SUCCESS); } @@ -49,6 +90,8 @@ int main(int argc, char **argv) {"core", required_argument, NULL, 'c'}, {"pid", required_argument, NULL, 'p'}, {"time", no_argument, NULL, 'T'}, + {"pre-exec", required_argument, NULL, 'x'}, + {"post-exec", required_argument, NULL, 'X'}, {"help", no_argument, NULL, 'h'}, {}, }; @@ -56,6 +99,8 @@ int main(int argc, char **argv) const char *core = NULL; const char *pid = NULL; bool print_time = false; + const char *pre_exec = NULL; + const char *post_exec = NULL; for (;;) { int c = getopt_long(argc, argv, "kc:p:Th", long_options, NULL); if (c == -1) @@ -73,6 +118,12 @@ int main(int argc, char **argv) case 'T': print_time = true; break; + case 'x': + pre_exec = optarg; + break; + case 'X': + post_exec = optarg; + break; case 'h': usage(false); default: @@ -98,6 +149,10 @@ int main(int argc, char **argv) if (err) goto out; + err = run_command("pre", pre_exec); + if (err) + goto out; + struct timespec start, end; if (print_time && clock_gettime(CLOCK_MONOTONIC, &start)) abort(); @@ -110,18 +165,20 @@ int main(int argc, char **argv) struct timespec diff = timespec_sub(end, start); printf("%lld.%09ld\n", (long long)diff.tv_sec, diff.tv_nsec); } + if (err && err->code == DRGN_ERROR_MISSING_DEBUG_INFO) { + drgn_error_fwrite(stderr, err); + drgn_error_destroy(err); + } + + err = run_command("post", post_exec); + if (err) + goto out; out:; - int status; + int status = err ? EXIT_FAILURE : EXIT_SUCCESS; if (err) { - if (err->code == DRGN_ERROR_MISSING_DEBUG_INFO) - status = EXIT_SUCCESS; - else - status = EXIT_FAILURE; drgn_error_fwrite(stderr, err); drgn_error_destroy(err); - } else { - status = EXIT_SUCCESS; } drgn_program_destroy(prog); return status;