1253 lines
38 KiB
C
1253 lines
38 KiB
C
|
/* Python frame filters
|
||
|
|
||
|
Copyright (C) 2013-2020 Free Software Foundation, Inc.
|
||
|
|
||
|
This file is part of GDB.
|
||
|
|
||
|
This program is free software; you can redistribute it and/or modify
|
||
|
it under the terms of the GNU General Public License as published by
|
||
|
the Free Software Foundation; either version 3 of the License, or
|
||
|
(at your option) any later version.
|
||
|
|
||
|
This program is distributed in the hope that it will be useful,
|
||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
GNU General Public License for more details.
|
||
|
|
||
|
You should have received a copy of the GNU General Public License
|
||
|
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||
|
|
||
|
#include "defs.h"
|
||
|
#include "objfiles.h"
|
||
|
#include "symtab.h"
|
||
|
#include "language.h"
|
||
|
#include "arch-utils.h"
|
||
|
#include "python.h"
|
||
|
#include "ui-out.h"
|
||
|
#include "valprint.h"
|
||
|
#include "stack.h"
|
||
|
#include "source.h"
|
||
|
#include "annotate.h"
|
||
|
#include "hashtab.h"
|
||
|
#include "demangle.h"
|
||
|
#include "mi/mi-cmds.h"
|
||
|
#include "python-internal.h"
|
||
|
#include "gdbsupport/gdb_optional.h"
|
||
|
#include "cli/cli-style.h"
|
||
|
|
||
|
enum mi_print_types
|
||
|
{
|
||
|
MI_PRINT_ARGS,
|
||
|
MI_PRINT_LOCALS
|
||
|
};
|
||
|
|
||
|
/* Helper function to extract a symbol, a name and a language
|
||
|
definition from a Python object that conforms to the "Symbol Value"
|
||
|
interface. OBJ is the Python object to extract the values from.
|
||
|
NAME is a pass-through argument where the name of the symbol will
|
||
|
be written. NAME is allocated in this function, but the caller is
|
||
|
responsible for clean up. SYM is a pass-through argument where the
|
||
|
symbol will be written and SYM_BLOCK is a pass-through argument to
|
||
|
write the block where the symbol lies in. In the case of the API
|
||
|
returning a string, this will be set to NULL. LANGUAGE is also a
|
||
|
pass-through argument denoting the language attributed to the
|
||
|
Symbol. In the case of SYM being NULL, this will be set to the
|
||
|
current language. Returns EXT_LANG_BT_ERROR on error with the
|
||
|
appropriate Python exception set, and EXT_LANG_BT_OK on success. */
|
||
|
|
||
|
static enum ext_lang_bt_status
|
||
|
extract_sym (PyObject *obj, gdb::unique_xmalloc_ptr<char> *name,
|
||
|
struct symbol **sym, const struct block **sym_block,
|
||
|
const struct language_defn **language)
|
||
|
{
|
||
|
gdbpy_ref<> result (PyObject_CallMethod (obj, "symbol", NULL));
|
||
|
|
||
|
if (result == NULL)
|
||
|
return EXT_LANG_BT_ERROR;
|
||
|
|
||
|
/* For 'symbol' callback, the function can return a symbol or a
|
||
|
string. */
|
||
|
if (gdbpy_is_string (result.get ()))
|
||
|
{
|
||
|
*name = python_string_to_host_string (result.get ());
|
||
|
|
||
|
if (*name == NULL)
|
||
|
return EXT_LANG_BT_ERROR;
|
||
|
/* If the API returns a string (and not a symbol), then there is
|
||
|
no symbol derived language available and the frame filter has
|
||
|
either overridden the symbol with a string, or supplied a
|
||
|
entirely synthetic symbol/value pairing. In that case, use
|
||
|
python_language. */
|
||
|
*language = python_language;
|
||
|
*sym = NULL;
|
||
|
*sym_block = NULL;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/* This type checks 'result' during the conversion so we
|
||
|
just call it unconditionally and check the return. */
|
||
|
*sym = symbol_object_to_symbol (result.get ());
|
||
|
/* TODO: currently, we have no way to recover the block in which SYMBOL
|
||
|
was found, so we have no block to return. Trying to evaluate SYMBOL
|
||
|
will yield an incorrect value when it's located in a FRAME and
|
||
|
evaluated from another frame (as permitted in nested functions). */
|
||
|
*sym_block = NULL;
|
||
|
|
||
|
if (*sym == NULL)
|
||
|
{
|
||
|
PyErr_SetString (PyExc_RuntimeError,
|
||
|
_("Unexpected value. Expecting a "
|
||
|
"gdb.Symbol or a Python string."));
|
||
|
return EXT_LANG_BT_ERROR;
|
||
|
}
|
||
|
|
||
|
/* Duplicate the symbol name, so the caller has consistency
|
||
|
in garbage collection. */
|
||
|
name->reset (xstrdup ((*sym)->print_name ()));
|
||
|
|
||
|
/* If a symbol is specified attempt to determine the language
|
||
|
from the symbol. If mode is not "auto", then the language
|
||
|
has been explicitly set, use that. */
|
||
|
if (language_mode == language_mode_auto)
|
||
|
*language = language_def (SYMBOL_LANGUAGE (*sym));
|
||
|
else
|
||
|
*language = current_language;
|
||
|
}
|
||
|
|
||
|
return EXT_LANG_BT_OK;
|
||
|
}
|
||
|
|
||
|
/* Helper function to extract a value from an object that conforms to
|
||
|
the "Symbol Value" interface. OBJ is the Python object to extract
|
||
|
the value from. VALUE is a pass-through argument where the value
|
||
|
will be written. If the object does not have the value attribute,
|
||
|
or provides the Python None for a value, VALUE will be set to NULL
|
||
|
and this function will return as successful. Returns EXT_LANG_BT_ERROR
|
||
|
on error with the appropriate Python exception set, and EXT_LANG_BT_OK on
|
||
|
success. */
|
||
|
|
||
|
static enum ext_lang_bt_status
|
||
|
extract_value (PyObject *obj, struct value **value)
|
||
|
{
|
||
|
if (PyObject_HasAttrString (obj, "value"))
|
||
|
{
|
||
|
gdbpy_ref<> vresult (PyObject_CallMethod (obj, "value", NULL));
|
||
|
|
||
|
if (vresult == NULL)
|
||
|
return EXT_LANG_BT_ERROR;
|
||
|
|
||
|
/* The Python code has returned 'None' for a value, so we set
|
||
|
value to NULL. This flags that GDB should read the
|
||
|
value. */
|
||
|
if (vresult == Py_None)
|
||
|
{
|
||
|
*value = NULL;
|
||
|
return EXT_LANG_BT_OK;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
*value = convert_value_from_python (vresult.get ());
|
||
|
|
||
|
if (*value == NULL)
|
||
|
return EXT_LANG_BT_ERROR;
|
||
|
|
||
|
return EXT_LANG_BT_OK;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
*value = NULL;
|
||
|
|
||
|
return EXT_LANG_BT_OK;
|
||
|
}
|
||
|
|
||
|
/* MI prints only certain values according to the type of symbol and
|
||
|
also what the user has specified. SYM is the symbol to check, and
|
||
|
MI_PRINT_TYPES is an enum specifying what the user wants emitted
|
||
|
for the MI command in question. */
|
||
|
static int
|
||
|
mi_should_print (struct symbol *sym, enum mi_print_types type)
|
||
|
{
|
||
|
int print_me = 0;
|
||
|
|
||
|
switch (SYMBOL_CLASS (sym))
|
||
|
{
|
||
|
default:
|
||
|
case LOC_UNDEF: /* catches errors */
|
||
|
case LOC_CONST: /* constant */
|
||
|
case LOC_TYPEDEF: /* local typedef */
|
||
|
case LOC_LABEL: /* local label */
|
||
|
case LOC_BLOCK: /* local function */
|
||
|
case LOC_CONST_BYTES: /* loc. byte seq. */
|
||
|
case LOC_UNRESOLVED: /* unresolved static */
|
||
|
case LOC_OPTIMIZED_OUT: /* optimized out */
|
||
|
print_me = 0;
|
||
|
break;
|
||
|
|
||
|
case LOC_ARG: /* argument */
|
||
|
case LOC_REF_ARG: /* reference arg */
|
||
|
case LOC_REGPARM_ADDR: /* indirect register arg */
|
||
|
case LOC_LOCAL: /* stack local */
|
||
|
case LOC_STATIC: /* static */
|
||
|
case LOC_REGISTER: /* register */
|
||
|
case LOC_COMPUTED: /* computed location */
|
||
|
if (type == MI_PRINT_LOCALS)
|
||
|
print_me = ! SYMBOL_IS_ARGUMENT (sym);
|
||
|
else
|
||
|
print_me = SYMBOL_IS_ARGUMENT (sym);
|
||
|
}
|
||
|
return print_me;
|
||
|
}
|
||
|
|
||
|
/* Helper function which outputs a type name extracted from VAL to a
|
||
|
"type" field in the output stream OUT. OUT is the ui-out structure
|
||
|
the type name will be output too, and VAL is the value that the
|
||
|
type will be extracted from. */
|
||
|
|
||
|
static void
|
||
|
py_print_type (struct ui_out *out, struct value *val)
|
||
|
{
|
||
|
check_typedef (value_type (val));
|
||
|
|
||
|
string_file stb;
|
||
|
type_print (value_type (val), "", &stb, -1);
|
||
|
out->field_stream ("type", stb);
|
||
|
}
|
||
|
|
||
|
/* Helper function which outputs a value to an output field in a
|
||
|
stream. OUT is the ui-out structure the value will be output to,
|
||
|
VAL is the value that will be printed, OPTS contains the value
|
||
|
printing options, ARGS_TYPE is an enumerator describing the
|
||
|
argument format, and LANGUAGE is the language_defn that the value
|
||
|
will be printed with. */
|
||
|
|
||
|
static void
|
||
|
py_print_value (struct ui_out *out, struct value *val,
|
||
|
const struct value_print_options *opts,
|
||
|
int indent,
|
||
|
enum ext_lang_frame_args args_type,
|
||
|
const struct language_defn *language)
|
||
|
{
|
||
|
int should_print = 0;
|
||
|
|
||
|
/* MI does not print certain values, differentiated by type,
|
||
|
depending on what ARGS_TYPE indicates. Test type against option.
|
||
|
For CLI print all values. */
|
||
|
if (args_type == MI_PRINT_SIMPLE_VALUES
|
||
|
|| args_type == MI_PRINT_ALL_VALUES)
|
||
|
{
|
||
|
struct type *type = check_typedef (value_type (val));
|
||
|
|
||
|
if (args_type == MI_PRINT_ALL_VALUES)
|
||
|
should_print = 1;
|
||
|
else if (args_type == MI_PRINT_SIMPLE_VALUES
|
||
|
&& TYPE_CODE (type) != TYPE_CODE_ARRAY
|
||
|
&& TYPE_CODE (type) != TYPE_CODE_STRUCT
|
||
|
&& TYPE_CODE (type) != TYPE_CODE_UNION)
|
||
|
should_print = 1;
|
||
|
}
|
||
|
else if (args_type != NO_VALUES)
|
||
|
should_print = 1;
|
||
|
|
||
|
if (should_print)
|
||
|
{
|
||
|
string_file stb;
|
||
|
|
||
|
common_val_print (val, &stb, indent, opts, language);
|
||
|
out->field_stream ("value", stb);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Helper function to call a Python method and extract an iterator
|
||
|
from the result. If the function returns anything but an iterator
|
||
|
the exception is preserved and NULL is returned. FILTER is the
|
||
|
Python object to call, and FUNC is the name of the method. Returns
|
||
|
a PyObject, or NULL on error with the appropriate exception set.
|
||
|
This function can return an iterator, or NULL. */
|
||
|
|
||
|
static PyObject *
|
||
|
get_py_iter_from_func (PyObject *filter, const char *func)
|
||
|
{
|
||
|
if (PyObject_HasAttrString (filter, func))
|
||
|
{
|
||
|
gdbpy_ref<> result (PyObject_CallMethod (filter, func, NULL));
|
||
|
|
||
|
if (result != NULL)
|
||
|
{
|
||
|
if (result == Py_None)
|
||
|
{
|
||
|
return result.release ();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return PyObject_GetIter (result.get ());
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
Py_RETURN_NONE;
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
/* Helper function to output a single frame argument and value to an
|
||
|
output stream. This function will account for entry values if the
|
||
|
FV parameter is populated, the frame argument has entry values
|
||
|
associated with them, and the appropriate "set entry-value"
|
||
|
options are set. Will output in CLI or MI like format depending
|
||
|
on the type of output stream detected. OUT is the output stream,
|
||
|
SYM_NAME is the name of the symbol. If SYM_NAME is populated then
|
||
|
it must have an accompanying value in the parameter FV. FA is a
|
||
|
frame argument structure. If FA is populated, both SYM_NAME and
|
||
|
FV are ignored. OPTS contains the value printing options,
|
||
|
ARGS_TYPE is an enumerator describing the argument format,
|
||
|
PRINT_ARGS_FIELD is a flag which indicates if we output "ARGS=1"
|
||
|
in MI output in commands where both arguments and locals are
|
||
|
printed. */
|
||
|
|
||
|
static void
|
||
|
py_print_single_arg (struct ui_out *out,
|
||
|
const char *sym_name,
|
||
|
struct frame_arg *fa,
|
||
|
struct value *fv,
|
||
|
const struct value_print_options *opts,
|
||
|
enum ext_lang_frame_args args_type,
|
||
|
int print_args_field,
|
||
|
const struct language_defn *language)
|
||
|
{
|
||
|
struct value *val;
|
||
|
|
||
|
if (fa != NULL)
|
||
|
{
|
||
|
if (fa->val == NULL && fa->error == NULL)
|
||
|
return;
|
||
|
language = language_def (SYMBOL_LANGUAGE (fa->sym));
|
||
|
val = fa->val;
|
||
|
}
|
||
|
else
|
||
|
val = fv;
|
||
|
|
||
|
gdb::optional<ui_out_emit_tuple> maybe_tuple;
|
||
|
|
||
|
/* MI has varying rules for tuples, but generally if there is only
|
||
|
one element in each item in the list, do not start a tuple. The
|
||
|
exception is -stack-list-variables which emits an ARGS="1" field
|
||
|
if the value is a frame argument. This is denoted in this
|
||
|
function with PRINT_ARGS_FIELD which is flag from the caller to
|
||
|
emit the ARGS field. */
|
||
|
if (out->is_mi_like_p ())
|
||
|
{
|
||
|
if (print_args_field || args_type != NO_VALUES)
|
||
|
maybe_tuple.emplace (out, nullptr);
|
||
|
}
|
||
|
|
||
|
annotate_arg_begin ();
|
||
|
|
||
|
/* If frame argument is populated, check for entry-values and the
|
||
|
entry value options. */
|
||
|
if (fa != NULL)
|
||
|
{
|
||
|
string_file stb;
|
||
|
|
||
|
fprintf_symbol_filtered (&stb, fa->sym->print_name (),
|
||
|
SYMBOL_LANGUAGE (fa->sym),
|
||
|
DMGL_PARAMS | DMGL_ANSI);
|
||
|
if (fa->entry_kind == print_entry_values_compact)
|
||
|
{
|
||
|
stb.puts ("=");
|
||
|
|
||
|
fprintf_symbol_filtered (&stb, fa->sym->print_name (),
|
||
|
SYMBOL_LANGUAGE (fa->sym),
|
||
|
DMGL_PARAMS | DMGL_ANSI);
|
||
|
}
|
||
|
if (fa->entry_kind == print_entry_values_only
|
||
|
|| fa->entry_kind == print_entry_values_compact)
|
||
|
stb.puts ("@entry");
|
||
|
out->field_stream ("name", stb);
|
||
|
}
|
||
|
else
|
||
|
/* Otherwise, just output the name. */
|
||
|
out->field_string ("name", sym_name);
|
||
|
|
||
|
annotate_arg_name_end ();
|
||
|
|
||
|
out->text ("=");
|
||
|
|
||
|
if (print_args_field)
|
||
|
out->field_signed ("arg", 1);
|
||
|
|
||
|
/* For MI print the type, but only for simple values. This seems
|
||
|
weird, but this is how MI choose to format the various output
|
||
|
types. */
|
||
|
if (args_type == MI_PRINT_SIMPLE_VALUES && val != NULL)
|
||
|
py_print_type (out, val);
|
||
|
|
||
|
if (val != NULL)
|
||
|
annotate_arg_value (value_type (val));
|
||
|
|
||
|
/* If the output is to the CLI, and the user option "set print
|
||
|
frame-arguments" is set to none, just output "...". */
|
||
|
if (! out->is_mi_like_p () && args_type == NO_VALUES)
|
||
|
out->field_string ("value", "...");
|
||
|
else
|
||
|
{
|
||
|
/* Otherwise, print the value for both MI and the CLI, except
|
||
|
for the case of MI_PRINT_NO_VALUES. */
|
||
|
if (args_type != NO_VALUES)
|
||
|
{
|
||
|
if (val == NULL)
|
||
|
{
|
||
|
gdb_assert (fa != NULL && fa->error != NULL);
|
||
|
out->field_fmt ("value", metadata_style.style (),
|
||
|
_("<error reading variable: %s>"),
|
||
|
fa->error.get ());
|
||
|
}
|
||
|
else
|
||
|
py_print_value (out, val, opts, 0, args_type, language);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Helper function to loop over frame arguments provided by the
|
||
|
"frame_arguments" Python API. Elements in the iterator must
|
||
|
conform to the "Symbol Value" interface. ITER is the Python
|
||
|
iterable object, OUT is the output stream, ARGS_TYPE is an
|
||
|
enumerator describing the argument format, PRINT_ARGS_FIELD is a
|
||
|
flag which indicates if we output "ARGS=1" in MI output in commands
|
||
|
where both arguments and locals are printed, and FRAME is the
|
||
|
backing frame. Returns EXT_LANG_BT_ERROR on error, with any GDB
|
||
|
exceptions converted to a Python exception, or EXT_LANG_BT_OK on
|
||
|
success. */
|
||
|
|
||
|
static enum ext_lang_bt_status
|
||
|
enumerate_args (PyObject *iter,
|
||
|
struct ui_out *out,
|
||
|
enum ext_lang_frame_args args_type,
|
||
|
int print_args_field,
|
||
|
struct frame_info *frame)
|
||
|
{
|
||
|
struct value_print_options opts;
|
||
|
|
||
|
get_user_print_options (&opts);
|
||
|
|
||
|
if (args_type == CLI_SCALAR_VALUES)
|
||
|
{
|
||
|
/* True in "summary" mode, false otherwise. */
|
||
|
opts.summary = 1;
|
||
|
}
|
||
|
|
||
|
opts.deref_ref = 1;
|
||
|
|
||
|
annotate_frame_args ();
|
||
|
|
||
|
/* Collect the first argument outside of the loop, so output of
|
||
|
commas in the argument output is correct. At the end of the
|
||
|
loop block collect another item from the iterator, and, if it is
|
||
|
not null emit a comma. */
|
||
|
gdbpy_ref<> item (PyIter_Next (iter));
|
||
|
if (item == NULL && PyErr_Occurred ())
|
||
|
return EXT_LANG_BT_ERROR;
|
||
|
|
||
|
while (item != NULL)
|
||
|
{
|
||
|
const struct language_defn *language;
|
||
|
gdb::unique_xmalloc_ptr<char> sym_name;
|
||
|
struct symbol *sym;
|
||
|
const struct block *sym_block;
|
||
|
struct value *val;
|
||
|
enum ext_lang_bt_status success = EXT_LANG_BT_ERROR;
|
||
|
|
||
|
success = extract_sym (item.get (), &sym_name, &sym, &sym_block,
|
||
|
&language);
|
||
|
if (success == EXT_LANG_BT_ERROR)
|
||
|
return EXT_LANG_BT_ERROR;
|
||
|
|
||
|
success = extract_value (item.get (), &val);
|
||
|
if (success == EXT_LANG_BT_ERROR)
|
||
|
return EXT_LANG_BT_ERROR;
|
||
|
|
||
|
if (sym && out->is_mi_like_p ()
|
||
|
&& ! mi_should_print (sym, MI_PRINT_ARGS))
|
||
|
continue;
|
||
|
|
||
|
/* If the object did not provide a value, read it using
|
||
|
read_frame_args and account for entry values, if any. */
|
||
|
if (val == NULL)
|
||
|
{
|
||
|
struct frame_arg arg, entryarg;
|
||
|
|
||
|
/* If there is no value, and also no symbol, set error and
|
||
|
exit. */
|
||
|
if (sym == NULL)
|
||
|
{
|
||
|
PyErr_SetString (PyExc_RuntimeError,
|
||
|
_("No symbol or value provided."));
|
||
|
return EXT_LANG_BT_ERROR;
|
||
|
}
|
||
|
|
||
|
read_frame_arg (user_frame_print_options,
|
||
|
sym, frame, &arg, &entryarg);
|
||
|
|
||
|
/* The object has not provided a value, so this is a frame
|
||
|
argument to be read by GDB. In this case we have to
|
||
|
account for entry-values. */
|
||
|
|
||
|
if (arg.entry_kind != print_entry_values_only)
|
||
|
{
|
||
|
py_print_single_arg (out, NULL, &arg,
|
||
|
NULL, &opts,
|
||
|
args_type,
|
||
|
print_args_field,
|
||
|
NULL);
|
||
|
}
|
||
|
|
||
|
if (entryarg.entry_kind != print_entry_values_no)
|
||
|
{
|
||
|
if (arg.entry_kind != print_entry_values_only)
|
||
|
{
|
||
|
out->text (", ");
|
||
|
out->wrap_hint (" ");
|
||
|
}
|
||
|
|
||
|
py_print_single_arg (out, NULL, &entryarg, NULL, &opts,
|
||
|
args_type, print_args_field, NULL);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/* If the object has provided a value, we just print that. */
|
||
|
if (val != NULL)
|
||
|
py_print_single_arg (out, sym_name.get (), NULL, val, &opts,
|
||
|
args_type, print_args_field,
|
||
|
language);
|
||
|
}
|
||
|
|
||
|
/* Collect the next item from the iterator. If
|
||
|
this is the last item, do not print the
|
||
|
comma. */
|
||
|
item.reset (PyIter_Next (iter));
|
||
|
if (item != NULL)
|
||
|
out->text (", ");
|
||
|
else if (PyErr_Occurred ())
|
||
|
return EXT_LANG_BT_ERROR;
|
||
|
|
||
|
annotate_arg_end ();
|
||
|
}
|
||
|
|
||
|
return EXT_LANG_BT_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Helper function to loop over variables provided by the
|
||
|
"frame_locals" Python API. Elements in the iterable must conform
|
||
|
to the "Symbol Value" interface. ITER is the Python iterable
|
||
|
object, OUT is the output stream, INDENT is whether we should
|
||
|
indent the output (for CLI), ARGS_TYPE is an enumerator describing
|
||
|
the argument format, PRINT_ARGS_FIELD is flag which indicates
|
||
|
whether to output the ARGS field in the case of
|
||
|
-stack-list-variables and FRAME is the backing frame. Returns
|
||
|
EXT_LANG_BT_ERROR on error, with any GDB exceptions converted to a Python
|
||
|
exception, or EXT_LANG_BT_OK on success. */
|
||
|
|
||
|
static enum ext_lang_bt_status
|
||
|
enumerate_locals (PyObject *iter,
|
||
|
struct ui_out *out,
|
||
|
int indent,
|
||
|
enum ext_lang_frame_args args_type,
|
||
|
int print_args_field,
|
||
|
struct frame_info *frame)
|
||
|
{
|
||
|
struct value_print_options opts;
|
||
|
|
||
|
get_user_print_options (&opts);
|
||
|
opts.deref_ref = 1;
|
||
|
|
||
|
while (true)
|
||
|
{
|
||
|
const struct language_defn *language;
|
||
|
gdb::unique_xmalloc_ptr<char> sym_name;
|
||
|
struct value *val;
|
||
|
enum ext_lang_bt_status success = EXT_LANG_BT_ERROR;
|
||
|
struct symbol *sym;
|
||
|
const struct block *sym_block;
|
||
|
int local_indent = 8 + (8 * indent);
|
||
|
gdb::optional<ui_out_emit_tuple> tuple;
|
||
|
|
||
|
gdbpy_ref<> item (PyIter_Next (iter));
|
||
|
if (item == NULL)
|
||
|
break;
|
||
|
|
||
|
success = extract_sym (item.get (), &sym_name, &sym, &sym_block,
|
||
|
&language);
|
||
|
if (success == EXT_LANG_BT_ERROR)
|
||
|
return EXT_LANG_BT_ERROR;
|
||
|
|
||
|
success = extract_value (item.get (), &val);
|
||
|
if (success == EXT_LANG_BT_ERROR)
|
||
|
return EXT_LANG_BT_ERROR;
|
||
|
|
||
|
if (sym != NULL && out->is_mi_like_p ()
|
||
|
&& ! mi_should_print (sym, MI_PRINT_LOCALS))
|
||
|
continue;
|
||
|
|
||
|
/* If the object did not provide a value, read it. */
|
||
|
if (val == NULL)
|
||
|
val = read_var_value (sym, sym_block, frame);
|
||
|
|
||
|
/* With PRINT_NO_VALUES, MI does not emit a tuple normally as
|
||
|
each output contains only one field. The exception is
|
||
|
-stack-list-variables, which always provides a tuple. */
|
||
|
if (out->is_mi_like_p ())
|
||
|
{
|
||
|
if (print_args_field || args_type != NO_VALUES)
|
||
|
tuple.emplace (out, nullptr);
|
||
|
}
|
||
|
|
||
|
/* If the output is not MI we indent locals. */
|
||
|
out->spaces (local_indent);
|
||
|
out->field_string ("name", sym_name.get ());
|
||
|
out->text (" = ");
|
||
|
|
||
|
if (args_type == MI_PRINT_SIMPLE_VALUES)
|
||
|
py_print_type (out, val);
|
||
|
|
||
|
/* CLI always prints values for locals. MI uses the
|
||
|
simple/no/all system. */
|
||
|
if (! out->is_mi_like_p ())
|
||
|
{
|
||
|
int val_indent = (indent + 1) * 4;
|
||
|
|
||
|
py_print_value (out, val, &opts, val_indent, args_type,
|
||
|
language);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (args_type != NO_VALUES)
|
||
|
py_print_value (out, val, &opts, 0, args_type,
|
||
|
language);
|
||
|
}
|
||
|
|
||
|
out->text ("\n");
|
||
|
}
|
||
|
|
||
|
if (!PyErr_Occurred ())
|
||
|
return EXT_LANG_BT_OK;
|
||
|
|
||
|
return EXT_LANG_BT_ERROR;
|
||
|
}
|
||
|
|
||
|
/* Helper function for -stack-list-variables. Returns EXT_LANG_BT_ERROR on
|
||
|
error, or EXT_LANG_BT_OK on success. */
|
||
|
|
||
|
static enum ext_lang_bt_status
|
||
|
py_mi_print_variables (PyObject *filter, struct ui_out *out,
|
||
|
struct value_print_options *opts,
|
||
|
enum ext_lang_frame_args args_type,
|
||
|
struct frame_info *frame)
|
||
|
{
|
||
|
gdbpy_ref<> args_iter (get_py_iter_from_func (filter, "frame_args"));
|
||
|
if (args_iter == NULL)
|
||
|
return EXT_LANG_BT_ERROR;
|
||
|
|
||
|
gdbpy_ref<> locals_iter (get_py_iter_from_func (filter, "frame_locals"));
|
||
|
if (locals_iter == NULL)
|
||
|
return EXT_LANG_BT_ERROR;
|
||
|
|
||
|
ui_out_emit_list list_emitter (out, "variables");
|
||
|
|
||
|
if (args_iter != Py_None
|
||
|
&& (enumerate_args (args_iter.get (), out, args_type, 1, frame)
|
||
|
== EXT_LANG_BT_ERROR))
|
||
|
return EXT_LANG_BT_ERROR;
|
||
|
|
||
|
if (locals_iter != Py_None
|
||
|
&& (enumerate_locals (locals_iter.get (), out, 1, args_type, 1, frame)
|
||
|
== EXT_LANG_BT_ERROR))
|
||
|
return EXT_LANG_BT_ERROR;
|
||
|
|
||
|
return EXT_LANG_BT_OK;
|
||
|
}
|
||
|
|
||
|
/* Helper function for printing locals. This function largely just
|
||
|
creates the wrapping tuple, and calls enumerate_locals. Returns
|
||
|
EXT_LANG_BT_ERROR on error, or EXT_LANG_BT_OK on success. */
|
||
|
|
||
|
static enum ext_lang_bt_status
|
||
|
py_print_locals (PyObject *filter,
|
||
|
struct ui_out *out,
|
||
|
enum ext_lang_frame_args args_type,
|
||
|
int indent,
|
||
|
struct frame_info *frame)
|
||
|
{
|
||
|
gdbpy_ref<> locals_iter (get_py_iter_from_func (filter, "frame_locals"));
|
||
|
if (locals_iter == NULL)
|
||
|
return EXT_LANG_BT_ERROR;
|
||
|
|
||
|
ui_out_emit_list list_emitter (out, "locals");
|
||
|
|
||
|
if (locals_iter != Py_None
|
||
|
&& (enumerate_locals (locals_iter.get (), out, indent, args_type,
|
||
|
0, frame) == EXT_LANG_BT_ERROR))
|
||
|
return EXT_LANG_BT_ERROR;
|
||
|
|
||
|
return EXT_LANG_BT_OK;
|
||
|
}
|
||
|
|
||
|
/* Helper function for printing frame arguments. This function
|
||
|
largely just creates the wrapping tuple, and calls enumerate_args.
|
||
|
Returns EXT_LANG_BT_ERROR on error, with any GDB exceptions converted to
|
||
|
a Python exception, or EXT_LANG_BT_OK on success. */
|
||
|
|
||
|
static enum ext_lang_bt_status
|
||
|
py_print_args (PyObject *filter,
|
||
|
struct ui_out *out,
|
||
|
enum ext_lang_frame_args args_type,
|
||
|
struct frame_info *frame)
|
||
|
{
|
||
|
gdbpy_ref<> args_iter (get_py_iter_from_func (filter, "frame_args"));
|
||
|
if (args_iter == NULL)
|
||
|
return EXT_LANG_BT_ERROR;
|
||
|
|
||
|
ui_out_emit_list list_emitter (out, "args");
|
||
|
|
||
|
out->wrap_hint (" ");
|
||
|
annotate_frame_args ();
|
||
|
out->text (" (");
|
||
|
|
||
|
if (args_type == CLI_PRESENCE)
|
||
|
{
|
||
|
if (args_iter != Py_None)
|
||
|
{
|
||
|
gdbpy_ref<> item (PyIter_Next (args_iter.get ()));
|
||
|
|
||
|
if (item != NULL)
|
||
|
out->text ("...");
|
||
|
else if (PyErr_Occurred ())
|
||
|
return EXT_LANG_BT_ERROR;
|
||
|
}
|
||
|
}
|
||
|
else if (args_iter != Py_None
|
||
|
&& (enumerate_args (args_iter.get (), out, args_type, 0, frame)
|
||
|
== EXT_LANG_BT_ERROR))
|
||
|
return EXT_LANG_BT_ERROR;
|
||
|
|
||
|
out->text (")");
|
||
|
|
||
|
return EXT_LANG_BT_OK;
|
||
|
}
|
||
|
|
||
|
/* Print a single frame to the designated output stream, detecting
|
||
|
whether the output is MI or console, and formatting the output
|
||
|
according to the conventions of that protocol. FILTER is the
|
||
|
frame-filter associated with this frame. FLAGS is an integer
|
||
|
describing the various print options. The FLAGS variables is
|
||
|
described in "apply_frame_filter" function. ARGS_TYPE is an
|
||
|
enumerator describing the argument format. OUT is the output
|
||
|
stream to print, INDENT is the level of indention for this frame
|
||
|
(in the case of elided frames), and LEVELS_PRINTED is a hash-table
|
||
|
containing all the frames level that have already been printed.
|
||
|
If a frame level has been printed, do not print it again (in the
|
||
|
case of elided frames). Returns EXT_LANG_BT_ERROR on error, with any
|
||
|
GDB exceptions converted to a Python exception, or EXT_LANG_BT_OK
|
||
|
on success. It can also throw an exception RETURN_QUIT. */
|
||
|
|
||
|
static enum ext_lang_bt_status
|
||
|
py_print_frame (PyObject *filter, frame_filter_flags flags,
|
||
|
enum ext_lang_frame_args args_type,
|
||
|
struct ui_out *out, int indent, htab_t levels_printed)
|
||
|
{
|
||
|
int has_addr = 0;
|
||
|
CORE_ADDR address = 0;
|
||
|
struct gdbarch *gdbarch = NULL;
|
||
|
struct frame_info *frame = NULL;
|
||
|
struct value_print_options opts;
|
||
|
|
||
|
int print_level, print_frame_info, print_args, print_locals;
|
||
|
/* Note that the below default in non-mi mode is the same as the
|
||
|
default value for the backtrace command (see the call to print_frame_info
|
||
|
in backtrace_command_1).
|
||
|
Having the same default ensures that 'bt' and 'bt no-filters'
|
||
|
have the same behaviour when some filters exist but do not apply
|
||
|
to a frame. */
|
||
|
enum print_what print_what
|
||
|
= out->is_mi_like_p () ? LOC_AND_ADDRESS : LOCATION;
|
||
|
gdb::unique_xmalloc_ptr<char> function_to_free;
|
||
|
|
||
|
/* Extract print settings from FLAGS. */
|
||
|
print_level = (flags & PRINT_LEVEL) ? 1 : 0;
|
||
|
print_frame_info = (flags & PRINT_FRAME_INFO) ? 1 : 0;
|
||
|
print_args = (flags & PRINT_ARGS) ? 1 : 0;
|
||
|
print_locals = (flags & PRINT_LOCALS) ? 1 : 0;
|
||
|
|
||
|
get_user_print_options (&opts);
|
||
|
if (print_frame_info)
|
||
|
{
|
||
|
gdb::optional<enum print_what> user_frame_info_print_what;
|
||
|
|
||
|
get_user_print_what_frame_info (&user_frame_info_print_what);
|
||
|
if (!out->is_mi_like_p () && user_frame_info_print_what.has_value ())
|
||
|
{
|
||
|
/* Use the specific frame information desired by the user. */
|
||
|
print_what = *user_frame_info_print_what;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Get the underlying frame. This is needed to determine GDB
|
||
|
architecture, and also, in the cases of frame variables/arguments to
|
||
|
read them if they returned filter object requires us to do so. */
|
||
|
gdbpy_ref<> py_inf_frame (PyObject_CallMethod (filter, "inferior_frame",
|
||
|
NULL));
|
||
|
if (py_inf_frame == NULL)
|
||
|
return EXT_LANG_BT_ERROR;
|
||
|
|
||
|
frame = frame_object_to_frame_info (py_inf_frame.get ());
|
||
|
if (frame == NULL)
|
||
|
return EXT_LANG_BT_ERROR;
|
||
|
|
||
|
symtab_and_line sal = find_frame_sal (frame);
|
||
|
|
||
|
gdbarch = get_frame_arch (frame);
|
||
|
|
||
|
/* stack-list-variables. */
|
||
|
if (print_locals && print_args && ! print_frame_info)
|
||
|
{
|
||
|
if (py_mi_print_variables (filter, out, &opts,
|
||
|
args_type, frame) == EXT_LANG_BT_ERROR)
|
||
|
return EXT_LANG_BT_ERROR;
|
||
|
return EXT_LANG_BT_OK;
|
||
|
}
|
||
|
|
||
|
gdb::optional<ui_out_emit_tuple> tuple;
|
||
|
|
||
|
/* -stack-list-locals does not require a
|
||
|
wrapping frame attribute. */
|
||
|
if (print_frame_info || (print_args && ! print_locals))
|
||
|
tuple.emplace (out, "frame");
|
||
|
|
||
|
if (print_frame_info)
|
||
|
{
|
||
|
/* Elided frames are also printed with this function (recursively)
|
||
|
and are printed with indention. */
|
||
|
if (indent > 0)
|
||
|
out->spaces (indent * 4);
|
||
|
|
||
|
/* The address is required for frame annotations, and also for
|
||
|
address printing. */
|
||
|
if (PyObject_HasAttrString (filter, "address"))
|
||
|
{
|
||
|
gdbpy_ref<> paddr (PyObject_CallMethod (filter, "address", NULL));
|
||
|
|
||
|
if (paddr == NULL)
|
||
|
return EXT_LANG_BT_ERROR;
|
||
|
|
||
|
if (paddr != Py_None)
|
||
|
{
|
||
|
if (get_addr_from_python (paddr.get (), &address) < 0)
|
||
|
return EXT_LANG_BT_ERROR;
|
||
|
|
||
|
has_addr = 1;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* For MI, each piece is controlled individually. */
|
||
|
bool location_print = (print_frame_info
|
||
|
&& !out->is_mi_like_p ()
|
||
|
&& (print_what == LOCATION
|
||
|
|| print_what == SRC_AND_LOC
|
||
|
|| print_what == LOC_AND_ADDRESS
|
||
|
|| print_what == SHORT_LOCATION));
|
||
|
|
||
|
/* Print frame level. MI does not require the level if
|
||
|
locals/variables only are being printed. */
|
||
|
if (print_level
|
||
|
&& (location_print
|
||
|
|| (out->is_mi_like_p () && (print_frame_info || print_args))))
|
||
|
{
|
||
|
struct frame_info **slot;
|
||
|
int level;
|
||
|
|
||
|
slot = (struct frame_info **) htab_find_slot (levels_printed,
|
||
|
frame, INSERT);
|
||
|
|
||
|
level = frame_relative_level (frame);
|
||
|
|
||
|
/* Check if this frame has already been printed (there are cases
|
||
|
where elided synthetic dummy-frames have to 'borrow' the frame
|
||
|
architecture from the eliding frame. If that is the case, do
|
||
|
not print 'level', but print spaces. */
|
||
|
if (*slot == frame)
|
||
|
out->field_skip ("level");
|
||
|
else
|
||
|
{
|
||
|
*slot = frame;
|
||
|
annotate_frame_begin (print_level ? level : 0,
|
||
|
gdbarch, address);
|
||
|
out->text ("#");
|
||
|
out->field_fmt_signed (2, ui_left, "level", level);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (location_print || (out->is_mi_like_p () && print_frame_info))
|
||
|
{
|
||
|
/* Print address to the address field. If an address is not provided,
|
||
|
print nothing. */
|
||
|
if (opts.addressprint && has_addr)
|
||
|
{
|
||
|
if (!sal.symtab
|
||
|
|| frame_show_address (frame, sal)
|
||
|
|| print_what == LOC_AND_ADDRESS)
|
||
|
{
|
||
|
annotate_frame_address ();
|
||
|
out->field_core_addr ("addr", gdbarch, address);
|
||
|
if (get_frame_pc_masked (frame))
|
||
|
out->field_string ("pac", " [PAC]");
|
||
|
annotate_frame_address_end ();
|
||
|
out->text (" in ");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Print frame function name. */
|
||
|
if (PyObject_HasAttrString (filter, "function"))
|
||
|
{
|
||
|
gdbpy_ref<> py_func (PyObject_CallMethod (filter, "function", NULL));
|
||
|
const char *function = NULL;
|
||
|
|
||
|
if (py_func == NULL)
|
||
|
return EXT_LANG_BT_ERROR;
|
||
|
|
||
|
if (gdbpy_is_string (py_func.get ()))
|
||
|
{
|
||
|
function_to_free = python_string_to_host_string (py_func.get ());
|
||
|
|
||
|
if (function_to_free == NULL)
|
||
|
return EXT_LANG_BT_ERROR;
|
||
|
|
||
|
function = function_to_free.get ();
|
||
|
}
|
||
|
else if (PyLong_Check (py_func.get ()))
|
||
|
{
|
||
|
CORE_ADDR addr;
|
||
|
struct bound_minimal_symbol msymbol;
|
||
|
|
||
|
if (get_addr_from_python (py_func.get (), &addr) < 0)
|
||
|
return EXT_LANG_BT_ERROR;
|
||
|
|
||
|
msymbol = lookup_minimal_symbol_by_pc (addr);
|
||
|
if (msymbol.minsym != NULL)
|
||
|
function = msymbol.minsym->print_name ();
|
||
|
}
|
||
|
else if (py_func != Py_None)
|
||
|
{
|
||
|
PyErr_SetString (PyExc_RuntimeError,
|
||
|
_("FrameDecorator.function: expecting a " \
|
||
|
"String, integer or None."));
|
||
|
return EXT_LANG_BT_ERROR;
|
||
|
}
|
||
|
|
||
|
annotate_frame_function_name ();
|
||
|
if (function == NULL)
|
||
|
out->field_skip ("func");
|
||
|
else
|
||
|
out->field_string ("func", function, function_name_style.style ());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Frame arguments. Check the result, and error if something went
|
||
|
wrong. */
|
||
|
if (print_args && (location_print || out->is_mi_like_p ()))
|
||
|
{
|
||
|
if (py_print_args (filter, out, args_type, frame) == EXT_LANG_BT_ERROR)
|
||
|
return EXT_LANG_BT_ERROR;
|
||
|
}
|
||
|
|
||
|
/* File name/source/line number information. */
|
||
|
bool print_location_source
|
||
|
= ((location_print && print_what != SHORT_LOCATION)
|
||
|
|| (out->is_mi_like_p () && print_frame_info));
|
||
|
if (print_location_source)
|
||
|
{
|
||
|
annotate_frame_source_begin ();
|
||
|
|
||
|
if (PyObject_HasAttrString (filter, "filename"))
|
||
|
{
|
||
|
gdbpy_ref<> py_fn (PyObject_CallMethod (filter, "filename", NULL));
|
||
|
|
||
|
if (py_fn == NULL)
|
||
|
return EXT_LANG_BT_ERROR;
|
||
|
|
||
|
if (py_fn != Py_None)
|
||
|
{
|
||
|
gdb::unique_xmalloc_ptr<char>
|
||
|
filename (python_string_to_host_string (py_fn.get ()));
|
||
|
|
||
|
if (filename == NULL)
|
||
|
return EXT_LANG_BT_ERROR;
|
||
|
|
||
|
out->wrap_hint (" ");
|
||
|
out->text (" at ");
|
||
|
annotate_frame_source_file ();
|
||
|
out->field_string ("file", filename.get (),
|
||
|
file_name_style.style ());
|
||
|
annotate_frame_source_file_end ();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (PyObject_HasAttrString (filter, "line"))
|
||
|
{
|
||
|
gdbpy_ref<> py_line (PyObject_CallMethod (filter, "line", NULL));
|
||
|
int line;
|
||
|
|
||
|
if (py_line == NULL)
|
||
|
return EXT_LANG_BT_ERROR;
|
||
|
|
||
|
if (py_line != Py_None)
|
||
|
{
|
||
|
line = PyLong_AsLong (py_line.get ());
|
||
|
if (PyErr_Occurred ())
|
||
|
return EXT_LANG_BT_ERROR;
|
||
|
|
||
|
out->text (":");
|
||
|
annotate_frame_source_line ();
|
||
|
out->field_signed ("line", line);
|
||
|
}
|
||
|
}
|
||
|
if (out->is_mi_like_p ())
|
||
|
out->field_string ("arch",
|
||
|
(gdbarch_bfd_arch_info (gdbarch))->printable_name);
|
||
|
}
|
||
|
|
||
|
bool source_print
|
||
|
= (! out->is_mi_like_p ()
|
||
|
&& (print_what == SRC_LINE || print_what == SRC_AND_LOC));
|
||
|
if (source_print)
|
||
|
{
|
||
|
if (print_location_source)
|
||
|
out->text ("\n"); /* Newline after the location source. */
|
||
|
print_source_lines (sal.symtab, sal.line, sal.line + 1, 0);
|
||
|
}
|
||
|
|
||
|
/* For MI we need to deal with the "children" list population of
|
||
|
elided frames, so if MI output detected do not send newline. */
|
||
|
if (! out->is_mi_like_p ())
|
||
|
{
|
||
|
annotate_frame_end ();
|
||
|
/* print_source_lines has already printed a newline. */
|
||
|
if (!source_print)
|
||
|
out->text ("\n");
|
||
|
}
|
||
|
|
||
|
if (print_locals)
|
||
|
{
|
||
|
if (py_print_locals (filter, out, args_type, indent,
|
||
|
frame) == EXT_LANG_BT_ERROR)
|
||
|
return EXT_LANG_BT_ERROR;
|
||
|
}
|
||
|
|
||
|
if ((flags & PRINT_HIDE) == 0)
|
||
|
{
|
||
|
/* Finally recursively print elided frames, if any. */
|
||
|
gdbpy_ref<> elided (get_py_iter_from_func (filter, "elided"));
|
||
|
if (elided == NULL)
|
||
|
return EXT_LANG_BT_ERROR;
|
||
|
|
||
|
if (elided != Py_None)
|
||
|
{
|
||
|
PyObject *item;
|
||
|
|
||
|
ui_out_emit_list inner_list_emiter (out, "children");
|
||
|
|
||
|
indent++;
|
||
|
|
||
|
while ((item = PyIter_Next (elided.get ())))
|
||
|
{
|
||
|
gdbpy_ref<> item_ref (item);
|
||
|
|
||
|
enum ext_lang_bt_status success
|
||
|
= py_print_frame (item, flags, args_type, out, indent,
|
||
|
levels_printed);
|
||
|
|
||
|
if (success == EXT_LANG_BT_ERROR)
|
||
|
return EXT_LANG_BT_ERROR;
|
||
|
}
|
||
|
if (item == NULL && PyErr_Occurred ())
|
||
|
return EXT_LANG_BT_ERROR;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return EXT_LANG_BT_OK;
|
||
|
}
|
||
|
|
||
|
/* Helper function to initiate frame filter invocation at starting
|
||
|
frame FRAME. */
|
||
|
|
||
|
static PyObject *
|
||
|
bootstrap_python_frame_filters (struct frame_info *frame,
|
||
|
int frame_low, int frame_high)
|
||
|
{
|
||
|
gdbpy_ref<> frame_obj (frame_info_to_frame_object (frame));
|
||
|
if (frame_obj == NULL)
|
||
|
return NULL;
|
||
|
|
||
|
gdbpy_ref<> module (PyImport_ImportModule ("gdb.frames"));
|
||
|
if (module == NULL)
|
||
|
return NULL;
|
||
|
|
||
|
gdbpy_ref<> sort_func (PyObject_GetAttrString (module.get (),
|
||
|
"execute_frame_filters"));
|
||
|
if (sort_func == NULL)
|
||
|
return NULL;
|
||
|
|
||
|
gdbpy_ref<> py_frame_low (PyInt_FromLong (frame_low));
|
||
|
if (py_frame_low == NULL)
|
||
|
return NULL;
|
||
|
|
||
|
gdbpy_ref<> py_frame_high (PyInt_FromLong (frame_high));
|
||
|
if (py_frame_high == NULL)
|
||
|
return NULL;
|
||
|
|
||
|
gdbpy_ref<> iterable (PyObject_CallFunctionObjArgs (sort_func.get (),
|
||
|
frame_obj.get (),
|
||
|
py_frame_low.get (),
|
||
|
py_frame_high.get (),
|
||
|
NULL));
|
||
|
if (iterable == NULL)
|
||
|
return NULL;
|
||
|
|
||
|
if (iterable != Py_None)
|
||
|
return PyObject_GetIter (iterable.get ());
|
||
|
else
|
||
|
return iterable.release ();
|
||
|
}
|
||
|
|
||
|
/* This is the only publicly exported function in this file. FRAME
|
||
|
is the source frame to start frame-filter invocation. FLAGS is an
|
||
|
integer holding the flags for printing. The following elements of
|
||
|
the FRAME_FILTER_FLAGS enum denotes the make-up of FLAGS:
|
||
|
PRINT_LEVEL is a flag indicating whether to print the frame's
|
||
|
relative level in the output. PRINT_FRAME_INFO is a flag that
|
||
|
indicates whether this function should print the frame
|
||
|
information, PRINT_ARGS is a flag that indicates whether to print
|
||
|
frame arguments, and PRINT_LOCALS, likewise, with frame local
|
||
|
variables. ARGS_TYPE is an enumerator describing the argument
|
||
|
format, OUT is the output stream to print. FRAME_LOW is the
|
||
|
beginning of the slice of frames to print, and FRAME_HIGH is the
|
||
|
upper limit of the frames to count. Returns EXT_LANG_BT_ERROR on error,
|
||
|
or EXT_LANG_BT_OK on success. */
|
||
|
|
||
|
enum ext_lang_bt_status
|
||
|
gdbpy_apply_frame_filter (const struct extension_language_defn *extlang,
|
||
|
struct frame_info *frame, frame_filter_flags flags,
|
||
|
enum ext_lang_frame_args args_type,
|
||
|
struct ui_out *out, int frame_low, int frame_high)
|
||
|
{
|
||
|
struct gdbarch *gdbarch = NULL;
|
||
|
enum ext_lang_bt_status success = EXT_LANG_BT_ERROR;
|
||
|
|
||
|
if (!gdb_python_initialized)
|
||
|
return EXT_LANG_BT_NO_FILTERS;
|
||
|
|
||
|
try
|
||
|
{
|
||
|
gdbarch = get_frame_arch (frame);
|
||
|
}
|
||
|
catch (const gdb_exception_error &except)
|
||
|
{
|
||
|
/* Let gdb try to print the stack trace. */
|
||
|
return EXT_LANG_BT_NO_FILTERS;
|
||
|
}
|
||
|
|
||
|
gdbpy_enter enter_py (gdbarch, current_language);
|
||
|
|
||
|
/* When we're limiting the number of frames, be careful to request
|
||
|
one extra frame, so that we can print a message if there are more
|
||
|
frames. */
|
||
|
int frame_countdown = -1;
|
||
|
if ((flags & PRINT_MORE_FRAMES) != 0 && frame_low >= 0 && frame_high >= 0)
|
||
|
{
|
||
|
++frame_high;
|
||
|
/* This has an extra +1 because it is checked before a frame is
|
||
|
printed. */
|
||
|
frame_countdown = frame_high - frame_low + 1;
|
||
|
}
|
||
|
|
||
|
gdbpy_ref<> iterable (bootstrap_python_frame_filters (frame, frame_low,
|
||
|
frame_high));
|
||
|
|
||
|
if (iterable == NULL)
|
||
|
{
|
||
|
/* Normally if there is an error GDB prints the exception,
|
||
|
abandons the backtrace and exits. The user can then call "bt
|
||
|
no-filters", and get a default backtrace (it would be
|
||
|
confusing to automatically start a standard backtrace halfway
|
||
|
through a Python filtered backtrace). However in the case
|
||
|
where GDB cannot initialize the frame filters (most likely
|
||
|
due to incorrect auto-load paths), GDB has printed nothing.
|
||
|
In this case it is OK to print the default backtrace after
|
||
|
printing the error message. GDB returns EXT_LANG_BT_NO_FILTERS
|
||
|
here to signify there are no filters after printing the
|
||
|
initialization error. This return code will trigger a
|
||
|
default backtrace. */
|
||
|
|
||
|
gdbpy_print_stack_or_quit ();
|
||
|
return EXT_LANG_BT_NO_FILTERS;
|
||
|
}
|
||
|
|
||
|
/* If iterable is None, then there are no frame filters registered.
|
||
|
If this is the case, defer to default GDB printing routines in MI
|
||
|
and CLI. */
|
||
|
if (iterable == Py_None)
|
||
|
return EXT_LANG_BT_NO_FILTERS;
|
||
|
|
||
|
htab_up levels_printed (htab_create (20,
|
||
|
htab_hash_pointer,
|
||
|
htab_eq_pointer,
|
||
|
NULL));
|
||
|
|
||
|
while (true)
|
||
|
{
|
||
|
gdbpy_ref<> item (PyIter_Next (iterable.get ()));
|
||
|
|
||
|
if (item == NULL)
|
||
|
{
|
||
|
if (PyErr_Occurred ())
|
||
|
{
|
||
|
gdbpy_print_stack_or_quit ();
|
||
|
return EXT_LANG_BT_ERROR;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (frame_countdown != -1)
|
||
|
{
|
||
|
gdb_assert ((flags & PRINT_MORE_FRAMES) != 0);
|
||
|
--frame_countdown;
|
||
|
if (frame_countdown == 0)
|
||
|
{
|
||
|
/* We've printed all the frames we were asked to
|
||
|
print, but more frames existed. */
|
||
|
printf_filtered (_("(More stack frames follow...)\n"));
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
try
|
||
|
{
|
||
|
success = py_print_frame (item.get (), flags, args_type, out, 0,
|
||
|
levels_printed.get ());
|
||
|
}
|
||
|
catch (const gdb_exception_error &except)
|
||
|
{
|
||
|
gdbpy_convert_exception (except);
|
||
|
success = EXT_LANG_BT_ERROR;
|
||
|
}
|
||
|
|
||
|
/* Do not exit on error printing a single frame. Print the
|
||
|
error and continue with other frames. */
|
||
|
if (success == EXT_LANG_BT_ERROR)
|
||
|
gdbpy_print_stack_or_quit ();
|
||
|
}
|
||
|
|
||
|
return success;
|
||
|
}
|