From patchwork Mon Oct 3 04:22:19 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tom Tromey X-Patchwork-Id: 16211 Received: (qmail 75414 invoked by alias); 3 Oct 2016 04:22:48 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Delivered-To: mailing list gdb-patches@sourceware.org Received: (qmail 75047 invoked by uid 89); 3 Oct 2016 04:22:45 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=1.1 required=5.0 tests=AWL, BAYES_50, RCVD_IN_DNSWL_NONE, RCVD_IN_SORBS_SPAM, SPF_PASS autolearn=no version=3.3.2 spammy=co, notified, events, SHELL X-HELO: gproxy4-pub.mail.unifiedlayer.com Received: from gproxy4-pub.mail.unifiedlayer.com (HELO gproxy4-pub.mail.unifiedlayer.com) (69.89.23.142) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with SMTP; Mon, 03 Oct 2016 04:22:34 +0000 Received: (qmail 28170 invoked by uid 0); 3 Oct 2016 04:22:33 -0000 Received: from unknown (HELO cmgw4) (10.0.90.85) by gproxy4.mail.unifiedlayer.com with SMTP; 3 Oct 2016 04:22:33 -0000 Received: from box522.bluehost.com ([74.220.219.122]) by cmgw4 with id qsNV1t00F2f2jeq01sNYt2; Sun, 02 Oct 2016 22:22:32 -0600 X-Authority-Analysis: v=2.1 cv=WP/sABcR c=1 sm=1 tr=0 a=GsOEXm/OWkKvwdLVJsfwcA==:117 a=GsOEXm/OWkKvwdLVJsfwcA==:17 a=L9H7d07YOLsA:10 a=9cW_t1CCXrUA:10 a=s5jvgZ67dGcA:10 a=CH0kA5CcgfcA:10 a=zstS-IiYAAAA:8 a=20KFwNOVAAAA:8 a=mDV3o1hIAAAA:8 a=8BNTpv7G5QZZsJ53PbwA:9 a=JsjLO97BI7MvVoIl:21 a=s0WSEVcYyZ6nupPj:21 a=4G6NA9xxw8l3yy4pmD5M:22 a=e_O65bzb51kRm2y5VmPK:22 a=_FVE-zBwftR9WsbkzFJk:22 Received: from 71-218-192-86.hlrn.qwest.net ([71.218.192.86]:60644 helo=bapiya.Home) by box522.bluehost.com with esmtpsa (TLSv1.2:ECDHE-RSA-AES256-GCM-SHA384:256) (Exim 4.86_1) (envelope-from ) id 1bqulp-0001Vt-DX; Sun, 02 Oct 2016 22:22:29 -0600 From: Tom Tromey To: gdb-patches@sourceware.org Cc: Tom Tromey Subject: [RFA 1/4] Convert observers to C++ Date: Sun, 2 Oct 2016 22:22:19 -0600 Message-Id: <1475468542-11446-2-git-send-email-tom@tromey.com> In-Reply-To: <1475468542-11446-1-git-send-email-tom@tromey.com> References: <1475468542-11446-1-git-send-email-tom@tromey.com> X-BWhitelist: no X-Exim-ID: 1bqulp-0001Vt-DX X-Source-Sender: 71-218-192-86.hlrn.qwest.net (bapiya.Home) [71.218.192.86]:60644 X-Source-Auth: tom+tromey.com X-Email-Count: 2 X-Source-Cap: ZWx5bnJvYmk7ZWx5bnJvYmk7Ym94NTIyLmJsdWVob3N0LmNvbQ== This converts observers from using a special source-generating script to be plain C++. This translation is less ideal than I'd like in a couple of ways. Still, I think it's a mild improvement, on the basis that ordinary code is preferable to an ad hoc code generator. First, it introduces a gdb analogue to std::function, the real one not being available until C++11. Second, it works around the lack of variadic templates in a mildly ugly way. In C++11 it would be ~300 lines of code shorter. 2016-10-02 Tom Tromey * observer.sh: Remove. * observer.h: New file. * observer.c (namespace gdb_observers): Define new objects. (observer_debug): Move into gdb_observers namespace. (struct observer, struct observer_list, xalloc_observer_list_node) (xfree_observer_list_node, generic_observer_attach) (generic_observer_detach, generic_observer_notify): Remove. (_initialize_observer): Update. Don't include observer.inc. * gdb_function.h: New file. * Makefile.in (generated_files): Remove observer.h, observer.inc. (clean mostlyclean): Likewise. (observer.h, observer.inc): Remove targets. * .gitignore: Remove observer.h. 2016-10-02 Tom Tromey * observer.texi: Remove. --- gdb/.gitignore | 1 - gdb/ChangeLog | 17 +++ gdb/Makefile.in | 10 +- gdb/doc/ChangeLog | 4 + gdb/doc/observer.texi | 309 --------------------------------------- gdb/gdb_function.h | 149 +++++++++++++++++++ gdb/observer.c | 236 ++++++++++++------------------ gdb/observer.h | 394 ++++++++++++++++++++++++++++++++++++++++++++++++++ gdb/observer.sh | 200 ------------------------- 9 files changed, 658 insertions(+), 662 deletions(-) delete mode 100644 gdb/doc/observer.texi create mode 100644 gdb/gdb_function.h create mode 100644 gdb/observer.h delete mode 100755 gdb/observer.sh diff --git a/gdb/.gitignore b/gdb/.gitignore index e7e1087..b9b2c6f 100644 --- a/gdb/.gitignore +++ b/gdb/.gitignore @@ -21,7 +21,6 @@ /jv-exp.c /m2-exp.c /objc-exp.c -/observer.h /observer.inc /p-exp.c /rust-exp.c diff --git a/gdb/ChangeLog b/gdb/ChangeLog index ffbdc3d..69d8a82 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,20 @@ +2016-10-02 Tom Tromey + + * observer.sh: Remove. + * observer.h: New file. + * observer.c (namespace gdb_observers): Define new objects. + (observer_debug): Move into gdb_observers namespace. + (struct observer, struct observer_list, xalloc_observer_list_node) + (xfree_observer_list_node, generic_observer_attach) + (generic_observer_detach, generic_observer_notify): Remove. + (_initialize_observer): Update. + Don't include observer.inc. + * gdb_function.h: New file. + * Makefile.in (generated_files): Remove observer.h, observer.inc. + (clean mostlyclean): Likewise. + (observer.h, observer.inc): Remove targets. + * .gitignore: Remove observer.h. + 2016-09-29 Jan Kratochvil PR gdb/20609 - attach of JIT-debug-enabled inf 7.11.1 regression diff --git a/gdb/Makefile.in b/gdb/Makefile.in index d0a968a..cf242b3 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -1121,7 +1121,7 @@ DISTSTUFF = $(YYFILES) # All generated files which can be included by another file. -generated_files = config.h observer.h observer.inc ada-lex.c jit-reader.h \ +generated_files = config.h ada-lex.c jit-reader.h \ $(GNULIB_H) $(NAT_GENERATED_FILES) gcore .c.o: @@ -1450,7 +1450,7 @@ tags: TAGS clean mostlyclean: $(CONFIG_CLEAN) @$(MAKE) $(FLAGS_TO_PASS) DO=clean "DODIRS=$(CLEANDIRS)" subdir_do rm -f *.o *.a $(ADD_FILES) *~ init.c-tmp init.l-tmp version.c-tmp - rm -f init.c version.c observer.h observer.inc + rm -f init.c version.c rm -f gdb$(EXEEXT) core make.log rm -f gdb[0-9]$(EXEEXT) rm -f test-cp-name-parser$(EXEEXT) @@ -1631,12 +1631,6 @@ version.c: Makefile version.in $(srcdir)/../bfd/version.h $(srcdir)/common/creat $(SHELL) $(srcdir)/common/create-version.sh $(srcdir) \ $(host_alias) $(target_alias) version.c -observer.h: observer.sh doc/observer.texi - ${srcdir}/observer.sh h ${srcdir}/doc/observer.texi observer.h - -observer.inc: observer.sh doc/observer.texi - ${srcdir}/observer.sh inc ${srcdir}/doc/observer.texi observer.inc - lint: $(LINTFILES) $(LINT) $(INCLUDE_CFLAGS) $(LINTFLAGS) $(LINTFILES) \ `echo $(DEPFILES) $(CONFIG_OBS) | sed 's/\.o /\.c /g'` diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog index d0b071a..cf5a7af 100644 --- a/gdb/doc/ChangeLog +++ b/gdb/doc/ChangeLog @@ -1,3 +1,7 @@ +2016-10-02 Tom Tromey + + * observer.texi: Remove. + 2016-09-28 Tom Tromey * gdb.texinfo (Packets) : Use "software breakpoint" rather diff --git a/gdb/doc/observer.texi b/gdb/doc/observer.texi deleted file mode 100644 index fc7aac4..0000000 --- a/gdb/doc/observer.texi +++ /dev/null @@ -1,309 +0,0 @@ -@c -*-texinfo-*- - -@c This file is part of the GDB manual. -@c -@c Copyright (C) 2003-2016 Free Software Foundation, Inc. -@c -@c See the file gdbint.texinfo for copying conditions. -@c -@c Also, the @deftypefun lines from this file are processed into a -@c header file during the GDB build process. Permission is granted -@c to redistribute and/or modify those lines under the terms of the -@c GNU General Public License as published by the Free Software -@c Foundation; either version 3 of the License, or (at your option) -@c any later version. - -@node GDB Observers -@appendix @value{GDBN} Currently available observers - -@section Implementation rationale -@cindex observers implementation rationale - -An @dfn{observer} is an entity which is interested in being notified -when GDB reaches certain states, or certain events occur in GDB. -The entity being observed is called the @dfn{subject}. To receive -notifications, the observer attaches a callback to the subject. -One subject can have several observers. - -@file{observer.c} implements an internal generic low-level event -notification mechanism. This generic event notification mechanism is -then re-used to implement the exported high-level notification -management routines for all possible notifications. - -The current implementation of the generic observer provides support -for contextual data. This contextual data is given to the subject -when attaching the callback. In return, the subject will provide -this contextual data back to the observer as a parameter of the -callback. - -Note that the current support for the contextual data is only partial, -as it lacks a mechanism that would deallocate this data when the -callback is detached. This is not a problem so far, as this contextual -data is only used internally to hold a function pointer. Later on, if -a certain observer needs to provide support for user-level contextual -data, then the generic notification mechanism will need to be -enhanced to allow the observer to provide a routine to deallocate the -data when attaching the callback. - -The observer implementation is also currently not reentrant. -In particular, it is therefore not possible to call the attach -or detach routines during a notification. - -@section Debugging -Observer notifications can be traced using the command @samp{set debug -observer 1} (@pxref{Debugging Output, , Optional messages about -internal happenings, gdb, Debugging with @var{GDBN}}). - -@section @code{normal_stop} Notifications -@cindex @code{normal_stop} observer -@cindex notification about inferior execution stop - -@value{GDBN} notifies all @code{normal_stop} observers when the -inferior execution has just stopped, the associated messages and -annotations have been printed, and the control is about to be returned -to the user. - -Note that the @code{normal_stop} notification is not emitted when -the execution stops due to a breakpoint, and this breakpoint has -a condition that is not met. If the breakpoint has any associated -commands list, the commands are executed after the notification -is emitted. - -The following interfaces are available to manage observers: - -@deftypefun extern struct observer *observer_attach_@var{event} (observer_@var{event}_ftype *@var{f}) -Using the function @var{f}, create an observer that is notified when -ever @var{event} occurs, return the observer. -@end deftypefun - -@deftypefun extern void observer_detach_@var{event} (struct observer *@var{observer}); -Remove @var{observer} from the list of observers to be notified when -@var{event} occurs. -@end deftypefun - -@deftypefun extern void observer_notify_@var{event} (void); -Send a notification to all @var{event} observers. -@end deftypefun - -The following observable events are defined: - -@deftypefun void normal_stop (struct bpstats *@var{bs}, int @var{print_frame}) -The inferior has stopped for real. The @var{bs} argument describes -the breakpoints were are stopped at, if any. Second argument -@var{print_frame} non-zero means display the location where the -inferior has stopped. -@end deftypefun - -@deftypefun void signal_received (enum gdb_signal @var{siggnal}) -The inferior was stopped by a signal. -@end deftypefun - -@deftypefun void end_stepping_range (void) -We are done with a step/next/si/ni command. -@end deftypefun - -@deftypefun void signal_exited (enum gdb_signal @var{siggnal}) -The inferior was terminated by a signal. -@end deftypefun - -@deftypefun void exited (int @var{exitstatus}) -The inferior program is finished. -@end deftypefun - -@deftypefun void no_history (void) -Reverse execution: target ran out of history info. -@end deftypefun - -@deftypefun void sync_execution_done (void) -A synchronous command finished. -@end deftypefun - -@deftypefun void command_error (void) -An error was caught while executing a command. -@end deftypefun - -@deftypefun void target_changed (struct target_ops *@var{target}) -The target's register contents have changed. -@end deftypefun - -@deftypefun void executable_changed (void) -The executable being debugged by GDB has changed: The user decided -to debug a different program, or the program he was debugging has -been modified since being loaded by the debugger (by being recompiled, -for instance). -@end deftypefun - -@deftypefun void inferior_created (struct target_ops *@var{objfile}, int @var{from_tty}) -@value{GDBN} has just connected to an inferior. For @samp{run}, -@value{GDBN} calls this observer while the inferior is still stopped -at the entry-point instruction. For @samp{attach} and @samp{core}, -@value{GDBN} calls this observer immediately after connecting to the -inferior, and before any information on the inferior has been printed. -@end deftypefun - -@deftypefun void record_changed (struct inferior *@var{inferior}, int @var{started}, const char *@var{method}, const char *@var{format}) -The status of process record for inferior @var{inferior} in -@value{GDBN} has changed. The process record is started if -@var{started} is true, and the process record is stopped if -@var{started} is false. - -When @var{started} is true, @var{method} indicates the short name of the method -used for recording. If the method supports multiple formats, @var{format} -indicates which one is being used, otherwise it is NULL. When @var{started} is -false, they are both NULL. -@end deftypefun - -@deftypefun void solib_loaded (struct so_list *@var{solib}) -The shared library specified by @var{solib} has been loaded. Note that -when @value{GDBN} calls this observer, the library's symbols probably -haven't been loaded yet. -@end deftypefun - -@deftypefun void solib_unloaded (struct so_list *@var{solib}) -The shared library specified by @var{solib} has been unloaded. -Note that when @value{GDBN} calls this observer, the library's -symbols have not been unloaded yet, and thus are still available. -@end deftypefun - -@deftypefun void new_objfile (struct objfile *@var{objfile}) -The symbol file specified by @var{objfile} has been loaded. -Called with @var{objfile} equal to @code{NULL} to indicate -previously loaded symbol table data has now been invalidated. -@end deftypefun - -@deftypefun void free_objfile (struct objfile *@var{objfile}) -The object file specified by @var{objfile} is about to be freed. -@end deftypefun - -@deftypefun void new_thread (struct thread_info *@var{t}) -The thread specified by @var{t} has been created. -@end deftypefun - -@deftypefun void thread_exit (struct thread_info *@var{t}, int @var{silent}) -The thread specified by @var{t} has exited. The @var{silent} argument -indicates that @value{GDBN} is removing the thread from its tables -without wanting to notify the user about it. -@end deftypefun - -@deftypefun void thread_stop_requested (ptid_t @var{ptid}) -An explicit stop request was issued to @var{ptid}. If @var{ptid} -equals @var{minus_one_ptid}, the request applied to all threads. If -@code{ptid_is_pid(ptid)} returns true, the request applied to all -threads of the process pointed at by @var{ptid}. Otherwise, the -request applied to the single thread pointed at by @var{ptid}. -@end deftypefun - -@deftypefun void target_resumed (ptid_t @var{ptid}) -The target was resumed. The @var{ptid} parameter specifies which -thread was resume, and may be RESUME_ALL if all threads are resumed. -@end deftypefun - -@deftypefun void about_to_proceed (void) -The target is about to be proceeded. -@end deftypefun - -@deftypefun void breakpoint_created (struct breakpoint *@var{b}) -A new breakpoint @var{b} has been created. -@end deftypefun - -@deftypefun void breakpoint_deleted (struct breakpoint *@var{b}) -A breakpoint has been destroyed. The argument @var{b} is the -pointer to the destroyed breakpoint. -@end deftypefun - -@deftypefun void breakpoint_modified (struct breakpoint *@var{b}) -A breakpoint has been modified in some way. The argument @var{b} -is the modified breakpoint. -@end deftypefun - -@deftypefun void traceframe_changed (int @var{tfnum}, int @var{tpnum}) -The trace frame is changed to @var{tfnum} (e.g., by using the -@code{tfind} command). If @var{tfnum} is negative, it means -@value{GDBN} resumes live debugging. The number of the tracepoint -associated with this traceframe is @var{tpnum}. -@end deftypefun - -@deftypefun void architecture_changed (struct gdbarch *@var{newarch}) -The current architecture has changed. The argument @var{newarch} is -a pointer to the new architecture. -@end deftypefun - -@deftypefun void thread_ptid_changed (ptid_t @var{old_ptid}, ptid_t @var{new_ptid}) -The thread's ptid has changed. The @var{old_ptid} parameter specifies -the old value, and @var{new_ptid} specifies the new value. -@end deftypefun - -@deftypefun void inferior_added (struct inferior *@var{inf}) -The inferior @var{inf} has been added to the list of inferiors. At -this point, it might not be associated with any process. -@end deftypefun - -@deftypefun void inferior_appeared (struct inferior *@var{inf}) -The inferior identified by @var{inf} has been attached to a process. -@end deftypefun - -@deftypefun void inferior_exit (struct inferior *@var{inf}) -Either the inferior associated with @var{inf} has been detached from the -process, or the process has exited. -@end deftypefun - -@deftypefun void inferior_removed (struct inferior *@var{inf}) -The inferior @var{inf} has been removed from the list of inferiors. -This method is called immediately before freeing @var{inf}. -@end deftypefun - -@deftypefun void memory_changed (struct inferior *@var{inferior}, CORE_ADDR @var{addr}, ssize_t @var{len}, const bfd_byte *@var{data}) -Bytes from @var{data} to @var{data} + @var{len} have been written -to the @var{inferior} at @var{addr}. -@end deftypefun - -@deftypefun void before_prompt (const char *@var{current_prompt}) -Called before a top-level prompt is displayed. @var{current_prompt} is -the current top-level prompt. -@end deftypefun - -@deftypefun void gdb_datadir_changed (void) -Variable gdb_datadir has been set. The value may not necessarily change. -@end deftypefun - -@deftypefun void command_param_changed (const char *@var{param}, const char *@var{value}) -The parameter of some @code{set} commands in console are changed. This -method is called after a command @code{set @var{param} @var{value}}. -@var{param} is the parameter of @code{set} command, and @var{value} -is the value of changed parameter. -@end deftypefun - -@deftypefun void tsv_created (const struct trace_state_variable *@var{tsv}) -The new trace state variable @var{tsv} is created. -@end deftypefun - -@deftypefun void tsv_deleted (const struct trace_state_variable *@var{tsv}) -The trace state variable @var{tsv} is deleted. If @var{tsv} is -@code{NULL}, all trace state variables are deleted. -@end deftypefun - -@deftypefun void tsv_modified (const struct trace_state_variable *@var{tsv}) -The trace state value @var{tsv} is modified. -@end deftypefun - -@deftypefun void inferior_call_pre (ptid_t @var{thread}, CORE_ADDR @var{address}) -An inferior function at @var{address} is about to be called in thread -@var{thread}. -@end deftypefun - -@deftypefun void inferior_call_post (ptid_t @var{thread}, CORE_ADDR @var{address}) -The inferior function at @var{address} has just been called. This observer -is called even if the inferior exits during the call. @var{thread} is the -thread in which the function was called, which may be different from the -current thread. -@end deftypefun - -@deftypefun void register_changed (struct frame_info *@var{frame}, int @var{regnum}) -A register in the inferior has been modified by the @value{GDBN} user. -@end deftypefun - -@deftypefun void test_notification (int @var{somearg}) -This observer is used for internal testing. Do not use. -See testsuite/gdb.gdb/observer.exp. -@end deftypefun - diff --git a/gdb/gdb_function.h b/gdb/gdb_function.h new file mode 100644 index 0000000..2b8b5b1 --- /dev/null +++ b/gdb/gdb_function.h @@ -0,0 +1,149 @@ +/* Functions + + Copyright (C) 2016 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 . */ + +#if !defined (GDB_FUNCTION_H) +#define GDB_FUNCTION_H + +// Replacements for std::function. This only implements the subset of +// the API needed by gdb. Because we don't have variadic templates, +// there is a separate template for each required number of arguments. +// Also, this doesn't do perfect forwarding correctly, so should only +// be used with C types. + +template +class gdb_function0 +{ + typedef R self_func_type (); + + public: + + gdb_function0 () : m_func (NULL) + { + } + + gdb_function0 (self_func_type *func) : m_func (func) + { + } + + R operator() () const + { + return m_func (); + } + + bool operator== (const gdb_function0 &other) + { + return m_func == other.m_func; + } + + private: + + self_func_type *m_func; +}; + +template +class gdb_function1 +{ + typedef R self_func_type (A); + + public: + + gdb_function1 () : m_func (NULL) + { + } + + gdb_function1 (self_func_type *func) : m_func (func) + { + } + + R operator() (A a) const + { + return m_func (a); + } + + bool operator== (const gdb_function1 &other) + { + return m_func == other.m_func; + } + + private: + + self_func_type *m_func; +}; + +template +class gdb_function2 +{ + typedef R self_func_type (A1, A2); + + public: + + gdb_function2 () : m_func (NULL) + { + } + + gdb_function2 (self_func_type *func) : m_func (func) + { + } + + R operator() (A1 a1, A2 a2) const + { + return m_func (a1, a2); + } + + bool operator== (const gdb_function2 &other) + { + return m_func == other.m_func; + } + + private: + + self_func_type *m_func; +}; + +template +class gdb_function4 +{ + typedef R self_func_type (A1, A2, A3, A4); + + public: + + gdb_function4 () : m_func (NULL) + { + } + + gdb_function4 (self_func_type *func) : m_func (func) + { + } + + R operator() (A1 a1, A2 a2, A3 a3, A4 a4) const + { + return m_func (a1, a2, a3, a4); + } + + bool operator== (const gdb_function4 &other) + { + return m_func == other.m_func; + } + + private: + + self_func_type *m_func; +}; + +#endif /* GDB_FUNCTION_H */ diff --git a/gdb/observer.c b/gdb/observer.c index d678eea..ae7b725 100644 --- a/gdb/observer.c +++ b/gdb/observer.c @@ -17,159 +17,109 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ -/* An observer is an entity who is interested in being notified when GDB - reaches certain states, or certain events occur in GDB. The entity being - observed is called the Subject. To receive notifications, the observer - attaches a callback to the subject. One subject can have several - observers. - - This file implements an internal generic low-level event notification - mechanism based on the Observer paradigm described in the book "Design - Patterns". This generic event notification mechansim is then re-used - to implement the exported high-level notification management routines - for all possible notifications. - - The current implementation of the generic observer provides support - for contextual data. This contextual data is given to the subject - when attaching the callback. In return, the subject will provide - this contextual data back to the observer as a parameter of the - callback. - - FIXME: The current support for the contextual data is only partial, - as it lacks a mechanism that would deallocate this data when the - callback is detached. This is not a problem so far, as this contextual - data is only used internally to hold a function pointer. Later on, - if a certain observer needs to provide support for user-level - contextual data, then the generic notification mechanism will need - need to be enhanced to allow the observer to provide a routine to - deallocate the data when attaching the callback. - - This file is currently maintained by hand, but the long term plan - if the number of different notifications starts growing is to create - a new script (observer.sh) that would generate this file, and the - associated documentation. */ - #include "defs.h" #include "observer.h" #include "command.h" #include "gdbcmd.h" -static unsigned int observer_debug; -static void -show_observer_debug (struct ui_file *file, int from_tty, - struct cmd_list_element *c, const char *value) -{ - fprintf_filtered (file, _("Observer debugging is %s.\n"), value); -} - -/* The internal generic observer. */ - -typedef void (generic_observer_notification_ftype) (const void *data, - const void *args); - -struct observer -{ - generic_observer_notification_ftype *notify; - /* No memory management needed for the following field for now. */ - void *data; -}; - -/* A list of observers, maintained by the subject. A subject is - actually represented by its list of observers. */ - -struct observer_list -{ - struct observer_list *next; - struct observer *observer; -}; - -/* Allocate a struct observer_list, intended to be used as a node - in the list of observers maintained by a subject. */ - -static struct observer_list * -xalloc_observer_list_node (void) -{ - struct observer_list *node = XNEW (struct observer_list); - - node->observer = XNEW (struct observer); - return node; -} - -/* The opposite of xalloc_observer_list_node, frees the memory for - the given node. */ - -static void -xfree_observer_list_node (struct observer_list *node) -{ - xfree (node->observer); - xfree (node); -} - -/* Attach the callback NOTIFY to a SUBJECT. The DATA is also stored, - in order for the subject to provide it back to the observer during - a notification. */ - -static struct observer * -generic_observer_attach (struct observer_list **subject, - generic_observer_notification_ftype * notify, - void *data) -{ - struct observer_list *observer_list = xalloc_observer_list_node (); - - observer_list->next = *subject; - observer_list->observer->notify = notify; - observer_list->observer->data = data; - *subject = observer_list; - - return observer_list->observer; -} - -/* Remove the given OBSERVER from the SUBJECT. Once detached, OBSERVER - should no longer be used, as it is no longer valid. */ - -static void -generic_observer_detach (struct observer_list **subject, - const struct observer *observer) +namespace gdb_observers { - struct observer_list *previous_node = NULL; - struct observer_list *current_node = *subject; - - while (current_node != NULL) - { - if (current_node->observer == observer) - { - if (previous_node != NULL) - previous_node->next = current_node->next; - else - *subject = current_node->next; - xfree_observer_list_node (current_node); - return; - } - previous_node = current_node; - current_node = current_node->next; - } - - /* We should never reach this point. However, this should not be - a very serious error, so simply report a warning to the user. */ - warning (_("Failed to detach observer")); + unsigned int observer_debug; + + observer2 + normal_stop ("normal_stop"); + observer1 + signal_received ("signal_received"); + observer0 + end_stepping_range ("end_stepping_range"); + observer1 + signal_exited ("signal_exited"); + observer1 + exited ("exited"); + observer0 + no_history ("no_history"); + observer0 + sync_execution_done ("sync_execution_done"); + observer0 + command_error ("command_error"); + observer1 + target_changed ("target_changed"); + observer0 + executable_changed ("executable_changed"); + observer2 + inferior_created ("inferior_created"); + observer4 + record_changed ("record_changed"); + observer1 + solib_loaded ("solib_loaded"); + observer1 + solib_unloaded ("solib_unloaded"); + observer1 + new_objfile ("new_objfile"); + observer1 + free_objfile ("free_objfile"); + observer1 + new_thread ("new_thread"); + observer2 + thread_exit ("thread_exit"); + observer1 + thread_stop_requested ("thread_stop_requested"); + observer1 + target_resumed ("target_resumed"); + observer0 + about_to_proceed ("about_to_proceed"); + observer1 + breakpoint_created ("breakpoint_created"); + observer1 + breakpoint_deleted ("breakpoint_deleted"); + observer1 + breakpoint_modified ("breakpoint_modified"); + observer2 + traceframe_changed ("traceframe_changed"); + observer1 + architecture_changed ("architecture_changed"); + observer2 + thread_ptid_changed ("thread_ptid_changed"); + observer1 + inferior_added ("inferior_added"); + observer1 + inferior_appeared ("inferior_appeared"); + observer1 + inferior_exit ("inferior_exit"); + observer1 + inferior_removed ("inferior_removed"); + observer4 + memory_changed ("memory_changed"); + observer1 + before_prompt ("before_prompt"); + observer0 + gdb_datadir_changed ("gdb_datadir_changed"); + observer2 + command_param_changed ("command_param_changed"); + observer1 + tsv_created ("tsv_created"); + observer1 + tsv_deleted ("tsv_deleted"); + observer1 + tsv_modified ("tsv_modified"); + observer2 + inferior_call_pre ("inferior_call_pre"); + observer2 + inferior_call_post ("inferior_call_post"); + observer2 + register_changed ("register_changed"); + observer1 + test_notification ("test_notification"); } -/* Send a notification to all the observers of SUBJECT. ARGS is passed to - all observers as an argument to the notification callback. */ - static void -generic_observer_notify (struct observer_list *subject, const void *args) +show_observer_debug (struct ui_file *file, int from_tty, + struct cmd_list_element *c, const char *value) { - struct observer_list *current_node = subject; - - while (current_node != NULL) - { - (*current_node->observer->notify) (current_node->observer->data, args); - current_node = current_node->next; - } + fprintf_filtered (file, _("Observer debugging is %s.\n"), value); } - /* The following code is only used to unit-test the observers from our testsuite. DO NOT USE IT within observer.c (or anywhere else for that matter)! */ @@ -210,7 +160,7 @@ void _initialize_observer (void) { add_setshow_zuinteger_cmd ("observer", class_maintenance, - &observer_debug, _("\ + &gdb_observers::observer_debug, _("\ Set observer debugging."), _("\ Show observer debugging."), _("\ When non-zero, observer debugging is enabled."), @@ -218,5 +168,3 @@ When non-zero, observer debugging is enabled."), show_observer_debug, &setdebuglist, &showdebuglist); } - -#include "observer.inc" diff --git a/gdb/observer.h b/gdb/observer.h new file mode 100644 index 0000000..0fa5ab6 --- /dev/null +++ b/gdb/observer.h @@ -0,0 +1,394 @@ +/* Observers + + Copyright (C) 2016 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 . */ + +#if !defined (GDB_OBSERVER_H) +#define GDB_OBSERVER_H + +#include "gdb_function.h" +#include + +struct bpstats; +struct so_list; +struct objfile; +struct thread_info; +struct inferior; +struct trace_state_variable; + +namespace gdb_observers +{ + extern unsigned int observer_debug; + + // An observer is an entity which is interested in being notified when + // GDB reaches certain states, or certain events occur in GDB. The + // entity being observed is called the subject. To receive + // notifications, the observer attaches a callback to the subject. + // One subject can have several observers. + // + // The observer implementation is also currently not reentrant. + // In particular, it is therefore not possible to call the attach + // or detach routines during a notification. + // + // Because we don't have varied templates, we have one template for + // each needed number of arguments. + class observer0 + { + public: + + typedef gdb_function0 func_type; + + observer0 (const char *name) : m_name (name) + { + } + + func_type attach (const func_type &f) + { + m_observers.push_front (f); + return f; + } + + void detach (const func_type &f) + { + m_observers.remove (f); + } + + void notify () const + { + if (observer_debug) + fprintf_unfiltered (gdb_stdlog, "observer %s notify() called\n", + m_name); + for (iter_type iter = m_observers.begin (); + iter != m_observers.end (); + ++iter) + (*iter) (); + } + + private: + + typedef std::forward_list::const_iterator iter_type; + + std::forward_list m_observers; + const char *m_name; + }; + + template + class observer1 + { + public: + + typedef gdb_function1 func_type; + + observer1 (const char *name) : m_name (name) + { + } + + func_type attach (const func_type &f) + { + m_observers.push_front (f); + return f; + } + + void detach (const func_type &f) + { + m_observers.remove (f); + } + + void notify (A arg) const + { + if (observer_debug) + fprintf_unfiltered (gdb_stdlog, "observer %s notify() called\n", + m_name); + for (iter_type iter = m_observers.begin (); + iter != m_observers.end (); + ++iter) + (*iter) (arg); + } + + private: + + typedef typename std::forward_list::const_iterator iter_type; + + std::forward_list m_observers; + const char *m_name; + }; + + + template + class observer2 + { + public: + + typedef gdb_function2 func_type; + + observer2 (const char *name) : m_name (name) + { + } + + func_type attach (const func_type &f) + { + m_observers.push_front (f); + return f; + } + + void detach (const func_type &f) + { + m_observers.remove (f); + } + + void notify (A1 arg1, A2 arg2) const + { + if (observer_debug) + fprintf_unfiltered (gdb_stdlog, "observer %s notify() called\n", + m_name); + for (iter_type iter = m_observers.begin (); + iter != m_observers.end (); + ++iter) + (*iter) (arg1, arg2); + } + + private: + + typedef typename std::forward_list::const_iterator iter_type; + + std::forward_list m_observers; + const char *m_name; + }; + + template + class observer4 + { + public: + + typedef gdb_function4 func_type; + + observer4 (const char *name) : m_name (name) + { + } + + func_type attach (const func_type &f) + { + m_observers.push_front (f); + return f; + } + + void detach (const func_type &f) + { + m_observers.remove (f); + } + + void notify (A1 arg1, A2 arg2, A3 arg3, A4 arg4) const + { + if (observer_debug) + fprintf_unfiltered (gdb_stdlog, "observer %s notify() called\n", + m_name); + for (iter_type iter = m_observers.begin (); + iter != m_observers.end (); + ++iter) + (*iter) (arg1, arg2, arg3, arg4); + } + + private: + + typedef typename std::forward_list::const_iterator iter_type; + + std::forward_list m_observers; + const char *m_name; + }; + + // The inferior has stopped for real. The bs argument + // describes the breakpoints were are stopped at, if any. Second + // argument print_frame non-zero means display the location + // where the inferior has stopped. + extern observer2 normal_stop; + + // The inferior was stopped by a signal. + extern observer1 signal_received; + + // We are done with a step/next/si/ni command. + extern observer0 end_stepping_range; + + // The inferior was terminated by a signal. + extern observer1 signal_exited; + + // The inferior program is finished. + extern observer1 exited; + + // Reverse execution: target ran out of history info. + extern observer0 no_history; + + // A synchronous command finished. + extern observer0 sync_execution_done; + + // An error was caught while executing a command. + extern observer0 command_error; + + // The target's register contents have changed. + extern observer1 target_changed; + + // The executable being debugged by GDB has changed: The user decided + // to debug a different program, or the program he was debugging has + // been modified since being loaded by the debugger (by being recompiled, + // for instance). + extern observer0 executable_changed; + + // gdb has just connected to an inferior. For 'run', + // gdb calls this observer while the inferior is still stopped + // at the entry-point instruction. For 'attach' and 'core', + // gdb calls this observer immediately after connecting to the + // inferior, and before any information on the inferior has been printed. + extern observer2 inferior_created; + + // The status of process record for inferior inferior in + // gdb has changed. The process record is started if + // started is true, and the process record is stopped if + // started is false. + // + // When started is true, method indicates the short name + // of the method used for recording. If the method supports multiple + // formats, format indicates which one is being used, otherwise + // it is NULL. When started is false, they are both NULL. + extern observer4 + record_changed; + + // The shared library specified by solib has been loaded. Note that + // when gdb calls this observer, the library's symbols probably + // haven't been loaded yet. + extern observer1 solib_loaded; + + // The shared library specified by solib has been unloaded. + // Note that when gdb calls this observer, the library's + // symbols have not been unloaded yet, and thus are still available. + extern observer1 solib_unloaded; + + // The symbol file specified by objfile has been loaded. + // Called with objfile equal to NULL to indicate + // previously loaded symbol table data has now been invalidated. + extern observer1 new_objfile; + + // The object file specified by objfile is about to be freed. + extern observer1 free_objfile; + + // The thread specified by t has been created. + extern observer1 new_thread; + + // The thread specified by t has exited. The silent argument + // indicates that gdb is removing the thread from its tables + // without wanting to notify the user about it. + extern observer2 thread_exit; + + // An explicit stop request was issued to ptid. If ptid + // equals minus_one_ptid, the request applied to all threads. If + // ptid_is_pid(ptid) returns true, the request applied to all + // threads of the process pointed at by ptid. Otherwise, the + // request applied to the single thread pointed at by ptid. + extern observer1 thread_stop_requested; + + // The target was resumed. The ptid parameter specifies which + // thread was resume, and may be RESUME_ALL if all threads are resumed. + extern observer1 target_resumed; + + // The target is about to be proceeded. + extern observer0 about_to_proceed; + + // A new breakpoint b has been created. + extern observer1 breakpoint_created; + + // A breakpoint has been destroyed. The argument b is the + // pointer to the destroyed breakpoint. + extern observer1 breakpoint_deleted; + + // A breakpoint has been modified in some way. The argument b + // is the modified breakpoint. + extern observer1 breakpoint_modified; + + // The trace frame is changed to tfnum (e.g., by using the + // 'tfind' command). If tfnum is negative, it means + // gdb resumes live debugging. The number of the tracepoint + // associated with this traceframe is tpnum. + extern observer2 traceframe_changed; + + // The current architecture has changed. The argument newarch is + // a pointer to the new architecture. + extern observer1 architecture_changed; + + // The thread's ptid has changed. The old_ptid parameter specifies + // the old value, and new_ptid specifies the new value. + extern observer2 thread_ptid_changed; + + // The inferior inf has been added to the list of inferiors. At + // this point, it might not be associated with any process. + extern observer1 inferior_added; + + // The inferior identified by inf has been attached to a process. + extern observer1 inferior_appeared; + + // Either the inferior associated with inf has been detached from the + // process, or the process has exited. + extern observer1 inferior_exit; + + // The inferior inf has been removed from the list of inferiors. + // This method is called immediately before freeing inf. + extern observer1 inferior_removed; + + // Bytes from data to data + len have been written + // to the inferior at addr. + extern observer4 + memory_changed; + + // Called before a top-level prompt is displayed. current_prompt is + // the current top-level prompt. + extern observer1 before_prompt; + + // Variable gdb_datadir has been set. The value may not necessarily change. + extern observer0 gdb_datadir_changed; + + // The parameter of some 'set' commands in console are changed. This + // method is called after a command 'set param value'. + // param is the parameter of 'set' command, and value + // is the value of changed parameter. + extern observer2 command_param_changed; + + // The new trace state variable tsv is created. + extern observer1 tsv_created; + + // The trace state variable tsv is deleted. If tsv is + // NULL, all trace state variables are deleted. + extern observer1 tsv_deleted; + + // The trace state value tsv is modified. + extern observer1 tsv_modified; + + // An inferior function at address is about to be called in thread + // thread. + extern observer2 inferior_call_pre; + + // The inferior function at address has just been called. This observer + // is called even if the inferior exits during the call. thread is the + // thread in which the function was called, which may be different from the + // current thread. + extern observer2 inferior_call_post; + + // A register in the inferior has been modified by the gdb user. + extern observer2 register_changed; + + // This observer is used for internal testing. Do not use. + // See testsuite/gdb.gdb/observer.exp. + extern observer1 test_notification; +} + +#endif /* GDB_OBSERVER_H */ diff --git a/gdb/observer.sh b/gdb/observer.sh deleted file mode 100755 index cc613a7..0000000 --- a/gdb/observer.sh +++ /dev/null @@ -1,200 +0,0 @@ -#!/bin/sh -e - -# Make certain that the script is not running in an internationalized -# environment. -LANG=C ; export LANG -LC_ALL=C ; export LC_ALL - -if test $# -ne 3 -then - echo "Usage: $0 " 1>&2 - exit 0 -fi - -lang=$1 ; shift -texi=$1 ; shift -o=$1 -case $lang in - h) tmp=htmp ;; - inc) tmp=itmp ;; -esac -otmp="`echo $1 | sed -e 's,\.[^.]*$,,'`.$tmp"; shift -echo "Creating ${otmp}" 1>&2 -rm -f ${otmp} - -# Can use any of the following: cat cmp cp diff echo egrep expr false -# grep install-info ln ls mkdir mv pwd rm rmdir sed sleep sort tar -# test touch true - -cat <>${otmp} -/* GDB Notifications to Observers. - - Copyright (C) 2004-2016 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 . - - -- - - This file was generated using observer.sh and observer.texi. */ - -EOF - - -case $lang in - h) cat <>${otmp} -#ifndef OBSERVER_H -#define OBSERVER_H - -struct observer; -struct bpstats; -struct so_list; -struct objfile; -struct thread_info; -struct inferior; -struct trace_state_variable; -EOF - ;; -esac - -# We are about to set IFS=:, so DOS-style file names with a drive -# letter and a colon will be in trouble. - -if test -n "$DJGPP" -then - texi=`echo $texi | sed -e 's,^\([a-zA-Z]\):/,/dev/\1/,'` -fi - -# generate a list of events that can be observed - -IFS=: -sed -n ' -/@deftypefun void/{ -# Save original line for later processing into the actual parameter - h -# Convert from: @deftypefun void EVENT (TYPE @var{PARAM},...) -# to event and formals: EVENT:TYPE PARAM, ...: - s/^.* void \([a-z_][a-z_]*\) (\(.*\))$/\1:\2/ - s/@var{//g - s/}//g -# Switch to held - x -# Convert from: @deftypefun void FUNC (TYPE @var{PARAM},...) -# to actuals: PARAM, ... - s/^[^{]*[{]*// - s/[}]*[^}]*$// - s/}[^{]*{/, /g -# Combine held (EVENT:TYPE PARAM, ...:) and pattern (PARAM, ...) into -# FUNC:TYPE PARAM, ...:PARAM, ... - H - x - s/\n/:/g - p -} -' $texi | while read event formal actual -do - case $lang in - h) cat <>${otmp} - -/* ${event} notifications. */ - -typedef void (observer_${event}_ftype) (${formal}); - -extern struct observer *observer_attach_${event} (observer_${event}_ftype *f); -extern void observer_detach_${event} (struct observer *observer); -extern void observer_notify_${event} (${formal}); -EOF - ;; - - inc) - cat <>${otmp} - -/* ${event} notifications. */ - -static struct observer_list *${event}_subject = NULL; - -EOF - if test "$formal" != "void"; then - cat<>${otmp} -struct ${event}_args { `echo "${formal}" | sed -e 's/,/;/g'`; }; - -EOF - fi - cat <>${otmp} -static void -observer_${event}_notification_stub (const void *data, const void *args_data) -{ - observer_${event}_ftype *notify = (observer_${event}_ftype *) data; -EOF - - notify_args=`echo ${actual} | sed -e 's/\([a-z0-9_][a-z0-9_]*\)/args->\1/g'` - - if test ! -z "${notify_args}"; then - cat<>${otmp} - const struct ${event}_args *args = (const struct ${event}_args *) args_data; -EOF - fi - cat <>${otmp} - notify (${notify_args}); -} - -struct observer * -observer_attach_${event} (observer_${event}_ftype *f) -{ - return generic_observer_attach (&${event}_subject, - &observer_${event}_notification_stub, - (void *) f); -} - -void -observer_detach_${event} (struct observer *observer) -{ - generic_observer_detach (&${event}_subject, observer); -} - -void -observer_notify_${event} (${formal}) -{ -EOF - if test "$formal" != "void"; then - cat<>${otmp} - struct ${event}_args args; - `echo ${actual} | sed -e 's/\([a-z0-9_][a-z0-9_]*\)/args.\1 = \1/g'`; - -EOF - else - echo "char *args = NULL;" >> ${otmp} - fi - cat<>${otmp} - if (observer_debug) - fprintf_unfiltered (gdb_stdlog, "observer_notify_${event}() called\n"); - generic_observer_notify (${event}_subject, &args); -} -EOF - ;; - esac -done - - -case $lang in - h) cat <>${otmp} - -#endif /* OBSERVER_H */ -EOF -esac - - -echo Moving ${otmp} to ${o} -mv ${otmp} ${o}