coredumpctl: Add --debugger-args= option
This commit adds the possibility to pass command line options to the debugger invoked with coredumpctl debug. Resolves: #9905
This commit is contained in:
parent
a7c52e1ee1
commit
a2be8be2cf
|
@ -137,7 +137,8 @@
|
||||||
<citerefentry project='man-pages'><refentrytitle>gdb</refentrytitle><manvolnum>1</manvolnum></citerefentry>
|
<citerefentry project='man-pages'><refentrytitle>gdb</refentrytitle><manvolnum>1</manvolnum></citerefentry>
|
||||||
will be used. This may be changed using the <option>--debugger=</option>
|
will be used. This may be changed using the <option>--debugger=</option>
|
||||||
option or the <varname>$SYSTEMD_DEBUGGER</varname> environment
|
option or the <varname>$SYSTEMD_DEBUGGER</varname> environment
|
||||||
variable.</para></listitem>
|
variable. Use the <option>--debugger-arguments=</option> option to pass extra
|
||||||
|
command line arguments to the debugger.</para></listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
</variablelist>
|
</variablelist>
|
||||||
|
@ -217,6 +218,15 @@
|
||||||
will be used. </para></listitem>
|
will be used. </para></listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>-A</option> <replaceable>ARGS</replaceable></term>
|
||||||
|
<term><option>--debugger-arguments=</option><replaceable>ARGS</replaceable></term>
|
||||||
|
|
||||||
|
<listitem><para>Pass the given <replaceable>ARGS</replaceable> as extra command line arguments
|
||||||
|
to the debugger. Quote as appropriate when <replaceable>ARGS</replaceable> contain whitespace.
|
||||||
|
(See Examples.)</para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
<term><option>--file=<replaceable>GLOB</replaceable></option></term>
|
<term><option>--file=<replaceable>GLOB</replaceable></option></term>
|
||||||
|
|
||||||
|
@ -323,6 +333,12 @@
|
||||||
<programlisting># coredumpctl debug</programlisting>
|
<programlisting># coredumpctl debug</programlisting>
|
||||||
</example>
|
</example>
|
||||||
|
|
||||||
|
<example>
|
||||||
|
<title>Use gdb to display full register info from the last core dump</title>
|
||||||
|
|
||||||
|
<programlisting># coredumpctl debug --debugger-arguments="-batch -ex 'info all-registers'"</programlisting>
|
||||||
|
</example>
|
||||||
|
|
||||||
<example>
|
<example>
|
||||||
<title>Show information about a process that dumped core,
|
<title>Show information about a process that dumped core,
|
||||||
matching by its PID 6654</title>
|
matching by its PID 6654</title>
|
||||||
|
|
|
@ -39,7 +39,8 @@ _coredumpctl() {
|
||||||
local i verb comps
|
local i verb comps
|
||||||
local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]}
|
local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]}
|
||||||
local OPTS='-h --help --version --no-pager --no-legend -o --output -F --field -1
|
local OPTS='-h --help --version --no-pager --no-legend -o --output -F --field -1
|
||||||
-r --reverse -S --since -U --until -D --directory -q --quiet --debugger'
|
-r --reverse -S --since -U --until -D --directory -q --quiet --debugger
|
||||||
|
-A --debugger-arguments'
|
||||||
|
|
||||||
local -A VERBS=(
|
local -A VERBS=(
|
||||||
[LIST]='list info'
|
[LIST]='list info'
|
||||||
|
|
|
@ -44,6 +44,7 @@
|
||||||
static usec_t arg_since = USEC_INFINITY, arg_until = USEC_INFINITY;
|
static usec_t arg_since = USEC_INFINITY, arg_until = USEC_INFINITY;
|
||||||
static const char* arg_field = NULL;
|
static const char* arg_field = NULL;
|
||||||
static const char *arg_debugger = NULL;
|
static const char *arg_debugger = NULL;
|
||||||
|
static char **arg_debugger_args = NULL;
|
||||||
static const char *arg_directory = NULL;
|
static const char *arg_directory = NULL;
|
||||||
static char **arg_file = NULL;
|
static char **arg_file = NULL;
|
||||||
static PagerFlags arg_pager_flags = 0;
|
static PagerFlags arg_pager_flags = 0;
|
||||||
|
@ -53,6 +54,7 @@ static const char* arg_output = NULL;
|
||||||
static bool arg_reverse = false;
|
static bool arg_reverse = false;
|
||||||
static bool arg_quiet = false;
|
static bool arg_quiet = false;
|
||||||
|
|
||||||
|
STATIC_DESTRUCTOR_REGISTER(arg_debugger_args, strv_freep);
|
||||||
STATIC_DESTRUCTOR_REGISTER(arg_file, strv_freep);
|
STATIC_DESTRUCTOR_REGISTER(arg_file, strv_freep);
|
||||||
|
|
||||||
static int add_match(sd_journal *j, const char *match) {
|
static int add_match(sd_journal *j, const char *match) {
|
||||||
|
@ -161,20 +163,21 @@ static int help(void) {
|
||||||
" dump [MATCHES...] Print first matching coredump to stdout\n"
|
" dump [MATCHES...] Print first matching coredump to stdout\n"
|
||||||
" debug [MATCHES...] Start a debugger for the first matching coredump\n"
|
" debug [MATCHES...] Start a debugger for the first matching coredump\n"
|
||||||
"\nOptions:\n"
|
"\nOptions:\n"
|
||||||
" -h --help Show this help\n"
|
" -h --help Show this help\n"
|
||||||
" --version Print version string\n"
|
" --version Print version string\n"
|
||||||
" --no-pager Do not pipe output into a pager\n"
|
" --no-pager Do not pipe output into a pager\n"
|
||||||
" --no-legend Do not print the column headers\n"
|
" --no-legend Do not print the column headers\n"
|
||||||
" --debugger=DEBUGGER Use the given debugger\n"
|
" --debugger=DEBUGGER Use the given debugger\n"
|
||||||
" -1 Show information about most recent entry only\n"
|
" -A --debugger-arguments=ARGS Pass the given arguments to the debugger\n"
|
||||||
" -S --since=DATE Only print coredumps since the date\n"
|
" -1 Show information about most recent entry only\n"
|
||||||
" -U --until=DATE Only print coredumps until the date\n"
|
" -S --since=DATE Only print coredumps since the date\n"
|
||||||
" -r --reverse Show the newest entries first\n"
|
" -U --until=DATE Only print coredumps until the date\n"
|
||||||
" -F --field=FIELD List all values a certain field takes\n"
|
" -r --reverse Show the newest entries first\n"
|
||||||
" -o --output=FILE Write output to FILE\n"
|
" -F --field=FIELD List all values a certain field takes\n"
|
||||||
" --file=PATH Use journal file\n"
|
" -o --output=FILE Write output to FILE\n"
|
||||||
" -D --directory=DIR Use journal files from directory\n\n"
|
" --file=PATH Use journal file\n"
|
||||||
" -q --quiet Do not show info messages and privilege warning\n"
|
" -D --directory=DIR Use journal files from directory\n\n"
|
||||||
|
" -q --quiet Do not show info messages and privilege warning\n"
|
||||||
"\nSee the %s for details.\n"
|
"\nSee the %s for details.\n"
|
||||||
, program_invocation_short_name
|
, program_invocation_short_name
|
||||||
, ansi_highlight()
|
, ansi_highlight()
|
||||||
|
@ -197,26 +200,27 @@ static int parse_argv(int argc, char *argv[]) {
|
||||||
int c, r;
|
int c, r;
|
||||||
|
|
||||||
static const struct option options[] = {
|
static const struct option options[] = {
|
||||||
{ "help", no_argument, NULL, 'h' },
|
{ "help", no_argument, NULL, 'h' },
|
||||||
{ "version" , no_argument, NULL, ARG_VERSION },
|
{ "version" , no_argument, NULL, ARG_VERSION },
|
||||||
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
|
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
|
||||||
{ "no-legend", no_argument, NULL, ARG_NO_LEGEND },
|
{ "no-legend", no_argument, NULL, ARG_NO_LEGEND },
|
||||||
{ "debugger", required_argument, NULL, ARG_DEBUGGER },
|
{ "debugger", required_argument, NULL, ARG_DEBUGGER },
|
||||||
{ "output", required_argument, NULL, 'o' },
|
{ "debugger-arguments", required_argument, NULL, 'A' },
|
||||||
{ "field", required_argument, NULL, 'F' },
|
{ "output", required_argument, NULL, 'o' },
|
||||||
{ "file", required_argument, NULL, ARG_FILE },
|
{ "field", required_argument, NULL, 'F' },
|
||||||
{ "directory", required_argument, NULL, 'D' },
|
{ "file", required_argument, NULL, ARG_FILE },
|
||||||
{ "reverse", no_argument, NULL, 'r' },
|
{ "directory", required_argument, NULL, 'D' },
|
||||||
{ "since", required_argument, NULL, 'S' },
|
{ "reverse", no_argument, NULL, 'r' },
|
||||||
{ "until", required_argument, NULL, 'U' },
|
{ "since", required_argument, NULL, 'S' },
|
||||||
{ "quiet", no_argument, NULL, 'q' },
|
{ "until", required_argument, NULL, 'U' },
|
||||||
|
{ "quiet", no_argument, NULL, 'q' },
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
assert(argc >= 0);
|
assert(argc >= 0);
|
||||||
assert(argv);
|
assert(argv);
|
||||||
|
|
||||||
while ((c = getopt_long(argc, argv, "ho:F:1D:rS:U:q", options, NULL)) >= 0)
|
while ((c = getopt_long(argc, argv, "hA:o:F:1D:rS:U:q", options, NULL)) >= 0)
|
||||||
switch(c) {
|
switch(c) {
|
||||||
case 'h':
|
case 'h':
|
||||||
return help();
|
return help();
|
||||||
|
@ -236,6 +240,15 @@ static int parse_argv(int argc, char *argv[]) {
|
||||||
arg_debugger = optarg;
|
arg_debugger = optarg;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'A': {
|
||||||
|
_cleanup_strv_free_ char **l = NULL;
|
||||||
|
r = strv_split_full(&l, optarg, WHITESPACE, EXTRACT_UNQUOTE);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to parse debugger arguments '%s': %m", optarg);
|
||||||
|
strv_free_and_replace(arg_debugger_args, l);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case ARG_FILE:
|
case ARG_FILE:
|
||||||
r = glob_extend(&arg_file, optarg, GLOB_NOCHECK);
|
r = glob_extend(&arg_file, optarg, GLOB_NOCHECK);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
|
@ -921,7 +934,8 @@ static int dump_core(int argc, char **argv, void *userdata) {
|
||||||
|
|
||||||
static int run_debug(int argc, char **argv, void *userdata) {
|
static int run_debug(int argc, char **argv, void *userdata) {
|
||||||
_cleanup_(sd_journal_closep) sd_journal *j = NULL;
|
_cleanup_(sd_journal_closep) sd_journal *j = NULL;
|
||||||
_cleanup_free_ char *exe = NULL, *path = NULL, *debugger = NULL;
|
_cleanup_free_ char *exe = NULL, *path = NULL;
|
||||||
|
_cleanup_strv_free_ char **debugger_call = NULL;
|
||||||
bool unlink_path = false;
|
bool unlink_path = false;
|
||||||
const char *data, *fork_name;
|
const char *data, *fork_name;
|
||||||
size_t len;
|
size_t len;
|
||||||
|
@ -938,9 +952,13 @@ static int run_debug(int argc, char **argv, void *userdata) {
|
||||||
arg_debugger = "gdb";
|
arg_debugger = "gdb";
|
||||||
}
|
}
|
||||||
|
|
||||||
debugger = strdup(arg_debugger);
|
r = strv_extend(&debugger_call, arg_debugger);
|
||||||
if (!debugger)
|
if (r < 0)
|
||||||
return -ENOMEM;
|
return log_oom();
|
||||||
|
|
||||||
|
r = strv_extend_strv(&debugger_call, arg_debugger_args, false);
|
||||||
|
if (r < 0)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
if (arg_field)
|
if (arg_field)
|
||||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||||
|
@ -981,22 +999,26 @@ static int run_debug(int argc, char **argv, void *userdata) {
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
|
r = strv_extend_strv(&debugger_call, STRV_MAKE(exe, "-c", path), false);
|
||||||
|
if (r < 0)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
/* Don't interfere with gdb and its handling of SIGINT. */
|
/* Don't interfere with gdb and its handling of SIGINT. */
|
||||||
(void) ignore_signals(SIGINT, -1);
|
(void) ignore_signals(SIGINT, -1);
|
||||||
|
|
||||||
fork_name = strjoina("(", debugger, ")");
|
fork_name = strjoina("(", debugger_call[0], ")");
|
||||||
|
|
||||||
r = safe_fork(fork_name, FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_CLOSE_ALL_FDS|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &pid);
|
r = safe_fork(fork_name, FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_CLOSE_ALL_FDS|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &pid);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
goto finish;
|
goto finish;
|
||||||
if (r == 0) {
|
if (r == 0) {
|
||||||
execlp(debugger, debugger, exe, "-c", path, NULL);
|
execvp(debugger_call[0], debugger_call);
|
||||||
log_open();
|
log_open();
|
||||||
log_error_errno(errno, "Failed to invoke %s: %m", debugger);
|
log_error_errno(errno, "Failed to invoke %s: %m", debugger_call[0]);
|
||||||
_exit(EXIT_FAILURE);
|
_exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
r = wait_for_terminate_and_check(debugger, pid, WAIT_LOG_ABNORMAL);
|
r = wait_for_terminate_and_check(debugger_call[0], pid, WAIT_LOG_ABNORMAL);
|
||||||
|
|
||||||
finish:
|
finish:
|
||||||
(void) default_signals(SIGINT, -1);
|
(void) default_signals(SIGINT, -1);
|
||||||
|
|
Loading…
Reference in a new issue