diff options
-rw-r--r-- | gdb/frame-id.h | 135 | ||||
-rw-r--r-- | gdb/frame-info.h | 31 | ||||
-rw-r--r-- | gdb/frame.h | 116 | ||||
-rw-r--r-- | gdb/infcmd.c | 2 | ||||
-rw-r--r-- | gdb/mi/mi-cmd-stack.c | 2 | ||||
-rw-r--r-- | gdb/stack.c | 11 | ||||
-rw-r--r-- | gdb/testsuite/gdb.python/pretty-print-call-by-hand.c | 53 | ||||
-rw-r--r-- | gdb/testsuite/gdb.python/pretty-print-call-by-hand.exp | 136 | ||||
-rw-r--r-- | gdb/testsuite/gdb.python/pretty-print-call-by-hand.py | 41 |
9 files changed, 409 insertions, 118 deletions
diff --git a/gdb/frame-id.h b/gdb/frame-id.h new file mode 100644 index 0000000..142488f --- /dev/null +++ b/gdb/frame-id.h @@ -0,0 +1,135 @@ +/* Definitions for dealing with stack frames, for GDB, the GNU debugger. + + Copyright (C) 1986-2022 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/>. */ + +#ifndef GDB_FRAME_ID_H +#define GDB_FRAME_ID_H 1 + +/* Status of a given frame's stack. */ + +enum frame_id_stack_status +{ + /* Stack address is invalid. */ + FID_STACK_INVALID = 0, + + /* Stack address is valid, and is found in the stack_addr field. */ + FID_STACK_VALID = 1, + + /* Sentinel frame. */ + FID_STACK_SENTINEL = 2, + + /* Outer frame. Since a frame's stack address is typically defined as the + value the stack pointer had prior to the activation of the frame, an outer + frame doesn't have a stack address. The frame ids of frames inlined in the + outer frame are also of this type. */ + FID_STACK_OUTER = 3, + + /* Stack address is unavailable. I.e., there's a valid stack, but + we don't know where it is (because memory or registers we'd + compute it from were not collected). */ + FID_STACK_UNAVAILABLE = -1 +}; + +/* The frame object's ID. This provides a per-frame unique identifier + that can be used to relocate a `struct frame_info' after a target + resume or a frame cache destruct. It of course assumes that the + inferior hasn't unwound the stack past that frame. */ + +struct frame_id +{ + /* The frame's stack address. This shall be constant through out + the lifetime of a frame. Note that this requirement applies to + not just the function body, but also the prologue and (in theory + at least) the epilogue. Since that value needs to fall either on + the boundary, or within the frame's address range, the frame's + outer-most address (the inner-most address of the previous frame) + is used. Watch out for all the legacy targets that still use the + function pointer register or stack pointer register. They are + wrong. + + This field is valid only if frame_id.stack_status is + FID_STACK_VALID. It will be 0 for other + FID_STACK_... statuses. */ + CORE_ADDR stack_addr; + + /* The frame's code address. This shall be constant through out the + lifetime of the frame. While the PC (a.k.a. resume address) + changes as the function is executed, this code address cannot. + Typically, it is set to the address of the entry point of the + frame's function (as returned by get_frame_func). + + For inlined functions (INLINE_DEPTH != 0), this is the address of + the first executed instruction in the block corresponding to the + inlined function. + + This field is valid only if code_addr_p is true. Otherwise, this + frame is considered to have a wildcard code address, i.e. one that + matches every address value in frame comparisons. */ + CORE_ADDR code_addr; + + /* The frame's special address. This shall be constant through out the + lifetime of the frame. This is used for architectures that may have + frames that do not change the stack but are still distinct and have + some form of distinct identifier (e.g. the ia64 which uses a 2nd + stack for registers). This field is treated as unordered - i.e. will + not be used in frame ordering comparisons. + + This field is valid only if special_addr_p is true. Otherwise, this + frame is considered to have a wildcard special address, i.e. one that + matches every address value in frame comparisons. */ + CORE_ADDR special_addr; + + /* Flags to indicate the above fields have valid contents. */ + ENUM_BITFIELD(frame_id_stack_status) stack_status : 3; + unsigned int code_addr_p : 1; + unsigned int special_addr_p : 1; + + /* It is non-zero for a frame made up by GDB without stack data + representation in inferior, such as INLINE_FRAME or TAILCALL_FRAME. + Caller of inlined function will have it zero, each more inner called frame + will have it increasingly one, two etc. Similarly for TAILCALL_FRAME. */ + int artificial_depth; + + /* Return a string representation of this frame id. */ + std::string to_string () const; + + /* Returns true when this frame_id and R identify the same + frame. */ + bool operator== (const frame_id &r) const; + + /* Inverse of ==. */ + bool operator!= (const frame_id &r) const + { + return !(*this == r); + } +}; + +/* Methods for constructing and comparing Frame IDs. */ + +/* For convenience. All fields are zero. This means "there is no frame". */ +extern const struct frame_id null_frame_id; + +/* Sentinel frame. */ +extern const struct frame_id sentinel_frame_id; + +/* This means "there is no frame ID, but there is a frame". It should be + replaced by best-effort frame IDs for the outermost frame, somehow. + The implementation is only special_addr_p set. */ +extern const struct frame_id outer_frame_id; + +#endif /* ifdef GDB_FRAME_ID_H */ diff --git a/gdb/frame-info.h b/gdb/frame-info.h index 195aa5c..665f4bd 100644 --- a/gdb/frame-info.h +++ b/gdb/frame-info.h @@ -21,10 +21,15 @@ #define GDB_FRAME_INFO_H #include "gdbsupport/intrusive_list.h" +#include "frame-id.h" struct frame_info; +/* Forward declarations of functions, needed for the frame_info_ptr + to work correctly. */ extern void reinit_frame_cache (); +extern struct frame_id get_frame_id (frame_info_ptr); +extern frame_info_ptr frame_find_by_id (struct frame_id id); /* A wrapper for "frame_info *". frame_info objects are invalidated whenever reinit_frame_cache is called. This class arranges to @@ -57,13 +62,13 @@ public: } frame_info_ptr (const frame_info_ptr &other) - : m_ptr (other.m_ptr) + : m_ptr (other.m_ptr), m_cached_id (other.m_cached_id) { frame_list.push_back (*this); } frame_info_ptr (frame_info_ptr &&other) - : m_ptr (other.m_ptr) + : m_ptr (other.m_ptr), m_cached_id (other.m_cached_id) { other.m_ptr = nullptr; frame_list.push_back (*this); @@ -77,19 +82,23 @@ public: frame_info_ptr &operator= (const frame_info_ptr &other) { m_ptr = other.m_ptr; + m_cached_id = other.m_cached_id; return *this; } frame_info_ptr &operator= (std::nullptr_t) { m_ptr = nullptr; + m_cached_id = null_frame_id; return *this; } frame_info_ptr &operator= (frame_info_ptr &&other) { m_ptr = other.m_ptr; + m_cached_id = other.m_cached_id; other.m_ptr = nullptr; + other.m_cached_id = null_frame_id; return *this; } @@ -125,11 +134,29 @@ public: m_ptr = nullptr; } + /* Cache the frame_id that the pointer will use to reinflate. */ + void prepare_reinflate () + { + m_cached_id = get_frame_id (*this); + } + + /* Use the cached frame_id to reinflate the pointer. */ + void reinflate () + { + gdb_assert (m_cached_id != null_frame_id); + + if (m_ptr == nullptr) + m_ptr = frame_find_by_id (m_cached_id).get (); + gdb_assert (m_ptr != nullptr); + } + private: /* The underlying pointer. */ frame_info *m_ptr = nullptr; + /* The frame_id of the underlying pointer. */ + frame_id m_cached_id = null_frame_id; /* All frame_info_ptr objects are kept on an intrusive list. This keeps their construction and destruction costs diff --git a/gdb/frame.h b/gdb/frame.h index 759cd32..5ee96ce 100644 --- a/gdb/frame.h +++ b/gdb/frame.h @@ -84,109 +84,10 @@ struct ui_file; struct ui_out; struct frame_print_options; -/* Status of a given frame's stack. */ - -enum frame_id_stack_status -{ - /* Stack address is invalid. */ - FID_STACK_INVALID = 0, - - /* Stack address is valid, and is found in the stack_addr field. */ - FID_STACK_VALID = 1, - - /* Sentinel frame. */ - FID_STACK_SENTINEL = 2, - - /* Outer frame. Since a frame's stack address is typically defined as the - value the stack pointer had prior to the activation of the frame, an outer - frame doesn't have a stack address. The frame ids of frames inlined in the - outer frame are also of this type. */ - FID_STACK_OUTER = 3, - - /* Stack address is unavailable. I.e., there's a valid stack, but - we don't know where it is (because memory or registers we'd - compute it from were not collected). */ - FID_STACK_UNAVAILABLE = -1 -}; - /* The frame object. */ class frame_info_ptr; -/* The frame object's ID. This provides a per-frame unique identifier - that can be used to relocate a `struct frame_info' after a target - resume or a frame cache destruct. It of course assumes that the - inferior hasn't unwound the stack past that frame. */ - -struct frame_id -{ - /* The frame's stack address. This shall be constant through out - the lifetime of a frame. Note that this requirement applies to - not just the function body, but also the prologue and (in theory - at least) the epilogue. Since that value needs to fall either on - the boundary, or within the frame's address range, the frame's - outer-most address (the inner-most address of the previous frame) - is used. Watch out for all the legacy targets that still use the - function pointer register or stack pointer register. They are - wrong. - - This field is valid only if frame_id.stack_status is - FID_STACK_VALID. It will be 0 for other - FID_STACK_... statuses. */ - CORE_ADDR stack_addr; - - /* The frame's code address. This shall be constant through out the - lifetime of the frame. While the PC (a.k.a. resume address) - changes as the function is executed, this code address cannot. - Typically, it is set to the address of the entry point of the - frame's function (as returned by get_frame_func). - - For inlined functions (INLINE_DEPTH != 0), this is the address of - the first executed instruction in the block corresponding to the - inlined function. - - This field is valid only if code_addr_p is true. Otherwise, this - frame is considered to have a wildcard code address, i.e. one that - matches every address value in frame comparisons. */ - CORE_ADDR code_addr; - - /* The frame's special address. This shall be constant through out the - lifetime of the frame. This is used for architectures that may have - frames that do not change the stack but are still distinct and have - some form of distinct identifier (e.g. the ia64 which uses a 2nd - stack for registers). This field is treated as unordered - i.e. will - not be used in frame ordering comparisons. - - This field is valid only if special_addr_p is true. Otherwise, this - frame is considered to have a wildcard special address, i.e. one that - matches every address value in frame comparisons. */ - CORE_ADDR special_addr; - - /* Flags to indicate the above fields have valid contents. */ - ENUM_BITFIELD(frame_id_stack_status) stack_status : 3; - unsigned int code_addr_p : 1; - unsigned int special_addr_p : 1; - - /* It is non-zero for a frame made up by GDB without stack data - representation in inferior, such as INLINE_FRAME or TAILCALL_FRAME. - Caller of inlined function will have it zero, each more inner called frame - will have it increasingly one, two etc. Similarly for TAILCALL_FRAME. */ - int artificial_depth; - - /* Return a string representation of this frame id. */ - std::string to_string () const; - - /* Returns true when this frame_id and R identify the same - frame. */ - bool operator== (const frame_id &r) const; - - /* Inverse of ==. */ - bool operator!= (const frame_id &r) const - { - return !(*this == r); - } -}; - /* Save and restore the currently selected frame. */ class scoped_restore_selected_frame @@ -212,19 +113,6 @@ private: enum language m_lang; }; -/* Methods for constructing and comparing Frame IDs. */ - -/* For convenience. All fields are zero. This means "there is no frame". */ -extern const struct frame_id null_frame_id; - -/* Sentinel frame. */ -extern const struct frame_id sentinel_frame_id; - -/* This means "there is no frame ID, but there is a frame". It should be - replaced by best-effort frame IDs for the outermost frame, somehow. - The implementation is only special_addr_p set. */ -extern const struct frame_id outer_frame_id; - /* Flag to control debugging. */ extern bool frame_debug; @@ -399,10 +287,6 @@ extern frame_info_ptr get_next_frame_sentinel_okay (frame_info_ptr ); frame. */ extern frame_info_ptr get_prev_frame_always (frame_info_ptr ); -/* Given a frame's ID, relocate the frame. Returns NULL if the frame - is not found. */ -extern frame_info_ptr frame_find_by_id (struct frame_id id); - /* Base attributes of a frame: */ /* The frame's `resume' address. Where the program will resume in diff --git a/gdb/infcmd.c b/gdb/infcmd.c index cbff0cd..d729732 100644 --- a/gdb/infcmd.c +++ b/gdb/infcmd.c @@ -1814,6 +1814,7 @@ finish_command (const char *arg, int from_tty) frame = get_prev_frame (get_selected_frame (_("No selected frame."))); if (frame == 0) error (_("\"finish\" not meaningful in the outermost frame.")); + frame.prepare_reinflate (); clear_proceed_status (0); @@ -1872,6 +1873,7 @@ finish_command (const char *arg, int from_tty) print_stack_frame (get_selected_frame (NULL), 1, LOCATION, 0); } + frame.reinflate (); if (execution_direction == EXEC_REVERSE) finish_backward (sm); diff --git a/gdb/mi/mi-cmd-stack.c b/gdb/mi/mi-cmd-stack.c index 7340b4c..78688d5 100644 --- a/gdb/mi/mi-cmd-stack.c +++ b/gdb/mi/mi-cmd-stack.c @@ -176,10 +176,12 @@ mi_cmd_stack_list_frames (const char *command, char **argv, int argc) i++, fi = get_prev_frame (fi)) { QUIT; + fi.prepare_reinflate (); /* Print the location and the address always, even for level 0. If args is 0, don't print the arguments. */ print_frame_info (user_frame_print_options, fi, 1, LOC_AND_ADDRESS, 0 /* args */, 0); + fi.reinflate (); } } } diff --git a/gdb/stack.c b/gdb/stack.c index abdd6f3..4867669 100644 --- a/gdb/stack.c +++ b/gdb/stack.c @@ -362,11 +362,13 @@ print_stack_frame (frame_info_ptr frame, int print_level, if (current_uiout->is_mi_like_p ()) print_what = LOC_AND_ADDRESS; + frame.prepare_reinflate (); try { print_frame_info (user_frame_print_options, frame, print_level, print_what, 1 /* print_args */, set_current_sal); + frame.reinflate (); if (set_current_sal) set_current_sal_from_frame (frame); } @@ -742,6 +744,11 @@ print_frame_args (const frame_print_options &fp_opts, = (print_names && fp_opts.print_frame_arguments != print_frame_arguments_none); + /* If one of the arguments has a pretty printer that calls a + function of the inferior to print it, the pointer must be + reinflatable. */ + frame.prepare_reinflate (); + /* Temporarily change the selected frame to the given FRAME. This allows routines that rely on the selected frame instead of being given a frame as parameter to use the correct frame. */ @@ -902,6 +909,7 @@ print_frame_args (const frame_print_options &fp_opts, } first = 0; + frame.reinflate (); } } @@ -1172,6 +1180,7 @@ print_frame_info (const frame_print_options &fp_opts, print_source_lines (sal.symtab, sal.line, sal.line + 1, 0); } + frame.reinflate (); /* If disassemble-next-line is set to on and there is line debug messages, output assembly codes for next line. */ @@ -2061,6 +2070,7 @@ backtrace_command_1 (const frame_print_options &fp_opts, for (fi = trailing; fi && count--; fi = get_prev_frame (fi)) { QUIT; + fi.prepare_reinflate (); /* Don't use print_stack_frame; if an error() occurs it probably means further attempts to backtrace would fail (on the other @@ -2085,6 +2095,7 @@ backtrace_command_1 (const frame_print_options &fp_opts, } /* Save the last frame to check for error conditions. */ + fi.reinflate (); trailing = fi; } diff --git a/gdb/testsuite/gdb.python/pretty-print-call-by-hand.c b/gdb/testsuite/gdb.python/pretty-print-call-by-hand.c new file mode 100644 index 0000000..3be5675 --- /dev/null +++ b/gdb/testsuite/gdb.python/pretty-print-call-by-hand.c @@ -0,0 +1,53 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2022 Free Software Foundation, Inc. + + 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/>. */ + +struct mytype +{ + char *x; +}; + +void +rec (int i) +{ + if (i <= 0) + return; + rec (i-1); +} + +int +f () +{ + rec (100); + return 2; +} + +void +g (struct mytype mt, int depth) +{ + if (depth <= 0) + return; /* TAG: final frame */ + g (mt, depth - 1); /* TAG: first frame */ +} + +int +main () +{ + struct mytype mt; + mt.x = "hello world"; + g (mt, 10); /* TAG: outside the frame */ + return 0; +} diff --git a/gdb/testsuite/gdb.python/pretty-print-call-by-hand.exp b/gdb/testsuite/gdb.python/pretty-print-call-by-hand.exp new file mode 100644 index 0000000..0aeb221 --- /dev/null +++ b/gdb/testsuite/gdb.python/pretty-print-call-by-hand.exp @@ -0,0 +1,136 @@ +# Copyright (C) 2022 Free Software Foundation, Inc. + +# 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/>. + +# This file is part of the GDB testsuite. It tests a pretty printer that +# calls an inferior function by hand, triggering a Use-after-Free bug +# (PR gdb/28856). + +load_lib gdb-python.exp + +standard_testfile + +# gdb needs to be started here for skip_python_tests to work. +# prepare_for_testing could be used instead, but it could compile the program +# unnecessarily, so starting GDB like this is preferable. +gdb_start + +# Skip all tests if Python scripting is not enabled. +if { [skip_python_tests] } { continue } + +if { [prepare_for_testing "failed to prepare" $testfile $srcfile debug] } { + untested "failed to compile" + return -1 +} + +# This proc restarts GDB, makes the inferior reach the desired spot - marked +# by a comment in the .c file - and turns on the pretty printer for testing. +# Starting with a new GDB is important because the test may crash GDB. The +# return values are here to avoid us trying to test the pretty printer if +# there was a problem getting to main. +proc start_test { breakpoint_comment } { + global srcdir subdir testfile binfile + + # Start with a fresh gdb. + # This is important because the test can crash GDB. + + clean_restart ${binfile} + + if { ![runto_main] } then { + untested "couldn't run to breakpoint" + return -1 + } + + # Let GDB get to the return line. + gdb_breakpoint [gdb_get_line_number ${breakpoint_comment} ${testfile}.c ] + gdb_continue_to_breakpoint ${breakpoint_comment} ".*" + + gdb_test_no_output "set print pretty on" "starting to pretty print" + + set remote_python_file [gdb_remote_download host ${srcdir}/${subdir}/${testfile}.py] + gdb_test_no_output "source ${remote_python_file}" "load python file" + + return 0 +} + +# Start by testing the "run" command, it can't leverage start_test +with_test_prefix "run to frame" { + if { ![runto_main] } then { + untested "couldn't run to main" + } + + gdb_test_no_output "set print pretty on" "starting to pretty print" + + set remote_python_file [gdb_remote_download host ${srcdir}/${subdir}/${testfile}.py] + gdb_test_no_output "source ${remote_python_file}" "load python file" + + gdb_breakpoint [gdb_get_line_number "TAG: final frame" ${testfile}.c] + gdb_continue_to_breakpoint "TAG: final frame" ".*" +} + +# Testing the backtrace command. +with_test_prefix "frame print" { + if { [start_test "TAG: final frame"] == 0 } { + gdb_test "backtrace -frame-arguments all" [multi_line \ + "#0 .*g \\(mt=mytype is .*\\, depth=0\\).*"\ + "#1 .*g \\(mt=mytype is .*\\, depth=1\\).*"\ + "#2 .*g \\(mt=mytype is .*\\, depth=2\\).*"\ + "#3 .*g \\(mt=mytype is .*\\, depth=3\\).*"\ + "#4 .*g \\(mt=mytype is .*\\, depth=4\\).*"\ + "#5 .*g \\(mt=mytype is .*\\, depth=5\\).*"\ + "#6 .*g \\(mt=mytype is .*\\, depth=6\\).*"\ + "#7 .*g \\(mt=mytype is .*\\, depth=7\\).*"\ + "#8 .*g \\(mt=mytype is .*\\, depth=8\\).*"\ + "#9 .*g \\(mt=mytype is .*\\, depth=9\\).*"\ + "#10 .*g \\(mt=mytype is .*\\, depth=10\\).*"\ + "#11 .*main \\(\\).*"] \ + "backtrace test" + } +} +# Testing the down command. +with_test_prefix "frame movement down" { + if { [start_test "TAG: first frame"] == 0 } { + gdb_test "up" [multi_line "#1 .*in main \\(\\) at .*" ".*outside the frame.*"] + gdb_test "down" [multi_line "#0\\s+g \\(mt=mytype is .*\\, depth=10\\).*" ".*first frame.*"] + } +} + +# Testing the up command. +with_test_prefix "frame movement up" { + if { [start_test "TAG: final frame"] == 0 } { + gdb_test "up" [multi_line "#1 .*in g \\(mt=mytype is .*\\, depth=1\\).*" ".*first frame.*"] + } +} + +# Testing the finish command. +with_test_prefix "frame exit through finish" { + if { [start_test "TAG: final frame"] == 0 } { + gdb_test "finish" [multi_line ".*.*g \\(mt=mytype is .*\\, depth=0\\).*" ".*g \\(mt=mytype is .*\\, depth=1\\).*" ".*"] + } +} + +# Testing the step command. +with_test_prefix "frame enter through step" { + if { [start_test "TAG: outside the frame"] == 0 } { + gdb_test "step" [multi_line "g \\(mt=mytype is .*\\, depth=10\\).*" "41.*if \\(depth \\<= 0\\)"] + } +} + +# Testing the continue command. +with_test_prefix "frame enter through continue" { + if { [start_test "TAG: outside the frame"] == 0 } { + gdb_breakpoint [gdb_get_line_number "TAG: first frame" ${testfile}.c ] + gdb_continue_to_breakpoint "TAG: first frame" ".*TAG: first frame.*" + } +} diff --git a/gdb/testsuite/gdb.python/pretty-print-call-by-hand.py b/gdb/testsuite/gdb.python/pretty-print-call-by-hand.py new file mode 100644 index 0000000..f8f5df6 --- /dev/null +++ b/gdb/testsuite/gdb.python/pretty-print-call-by-hand.py @@ -0,0 +1,41 @@ +# Copyright (C) 2022 Free Software Foundation, Inc. + +# 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/>. + + +class MytypePrinter: + """pretty print my type""" + + def __init__(self, val): + self.val = val + + def to_string(self): + calls = gdb.parse_and_eval('f()') + return "mytype is %s" % self.val['x'] + +def ec_lookup_function(val): + typ = val.type + if typ.code == gdb.TYPE_CODE_REF: + typ = typ.target() + if str(typ) == 'struct mytype': + return MytypePrinter(val) + return None + +def disable_lookup_function(): + ec_lookup_function.enabled = False + +def enable_lookup_function(): + ec_lookup_function.enabled = True + +gdb.pretty_printers.append(ec_lookup_function) |