## 3 Algorithms

gdb uses a number of debugging-specific algorithms. They are often not very complicated, but get lost in the thicket of special cases and real-world issues. This chapter describes the basic algorithms and mentions some of the specific target definitions that they use.

### 3.1 Frames

A frame is a construct that gdb uses to keep track of calling and called functions.

gdb's frame model, a fresh design, was implemented with the need to support dwarf's Call Frame Information in mind. In fact, the term “unwind” is taken directly from that specification. Developers wishing to learn more about unwinders, are encouraged to read the dwarf specification.

gdb's model is that you find a frame's registers by “unwinding” them from the next younger frame. That is, ``get_frame_register`' which returns the value of a register in frame #1 (the next-to-youngest frame), is implemented by calling frame #0's `frame_register_unwind` (the youngest frame). But then the obvious question is: how do you access the registers of the youngest frame itself?

To answer this question, GDB has the sentinel frame, the “-1st” frame. Unwinding registers from the sentinel frame gives you the current values of the youngest real frame's registers. If f is a sentinel frame, then `get_frame_type (`f`) == SENTINEL_FRAME`.

### 3.2 Prologue Analysis

To produce a backtrace and allow the user to manipulate older frames' variables and arguments, gdb needs to find the base addresses of older frames, and discover where those frames' registers have been saved. Since a frame's “callee-saves” registers get saved by younger frames if and when they're reused, a frame's registers may be scattered unpredictably across younger frames. This means that changing the value of a register-allocated variable in an older frame may actually entail writing to a save slot in some younger frame.

Modern versions of GCC emit Dwarf call frame information (“CFI”), which describes how to find frame base addresses and saved registers. But CFI is not always available, so as a fallback gdb uses a technique called prologue analysis to find frame sizes and saved registers. A prologue analyzer disassembles the function's machine code starting from its entry point, and looks for instructions that allocate frame space, save the stack pointer in a frame pointer register, save registers, and so on. Obviously, this can't be done accurately in general, but it's tractable to do well enough to be very helpful. Prologue analysis predates the GNU toolchain's support for CFI; at one time, prologue analysis was the only mechanism gdb used for stack unwinding at all, when the function calling conventions didn't specify a fixed frame layout.

In the olden days, function prologues were generated by hand-written, target-specific code in GCC, and treated as opaque and untouchable by optimizers. Looking at this code, it was usually straightforward to write a prologue analyzer for gdb that would accurately understand all the prologues GCC would generate. However, over time GCC became more aggressive about instruction scheduling, and began to understand more about the semantics of the prologue instructions themselves; in response, gdb's analyzers became more complex and fragile. Keeping the prologue analyzers working as GCC (and the instruction sets themselves) evolved became a substantial task.

To try to address this problem, the code in `prologue-value.h` and `prologue-value.c` provides a general framework for writing prologue analyzers that are simpler and more robust than ad-hoc analyzers. When we analyze a prologue using the prologue-value framework, we're really doing “abstract interpretation” or “pseudo-evaluation”: running the function's code in simulation, but using conservative approximations of the values registers and memory would hold when the code actually runs. For example, if our function starts with the instruction:

         addi r1, 42     # add 42 to r1

we don't know exactly what value will be in `r1` after executing this instruction, but we do know it'll be 42 greater than its original value.

If we then see an instruction like:

         addi r1, 22     # add 22 to r1

we still don't know what `r1's` value is, but again, we can say it is now 64 greater than its original value.

If the next instruction were:

         mov r2, r1      # set r2 to r1's value

then we can say that `r2's` value is now the original value of `r1` plus 64.

It's common for prologues to save registers on the stack, so we'll need to track the values of stack frame slots, as well as the registers. So after an instruction like this:

         mov (fp+4), r2

then we'd know that the stack slot four bytes above the frame pointer holds the original value of `r1` plus 64.

And so on.

Of course, this can only go so far before it gets unreasonable. If we wanted to be able to say anything about the value of `r1` after the instruction:

         xor r1, r3      # exclusive-or r1 and r3, place result in r1

then things would get pretty complex. But remember, we're just doing a conservative approximation; if exclusive-or instructions aren't relevant to prologues, we can just say `r1`'s value is now “unknown”. We can ignore things that are too complex, if that loss of information is acceptable for our application.

So when we say “conservative approximation” here, what we mean is an approximation that is either accurate, or marked “unknown”, but never inaccurate.

Using this framework, a prologue analyzer is simply an interpreter for machine code, but one that uses conservative approximations for the contents of registers and memory instead of actual values. Starting from the function's entry point, you simulate instructions up to the current PC, or an instruction that you don't know how to simulate. Now you can examine the state of the registers and stack slots you've kept track of.

- To see how large your stack frame is, just check the value of the stack pointer register; if it's the original value of the SP minus a constant, then that constant is the stack frame's size. If the SP's value has been marked as “unknown”, then that means the prologue has done something too complex for us to track, and we don't know the frame size.
- To see where we've saved the previous frame's registers, we just search the values we've tracked — stack slots, usually, but registers, too, if you want — for something equal to the register's original value. If the calling conventions suggest a standard place to save a given register, then we can check there first, but really, anything that will get us back the original value will probably work.

This does take some work. But prologue analyzers aren't quick-and-simple pattern patching to recognize a few fixed prologue forms any more; they're big, hairy functions. Along with inferior function calls, prologue analysis accounts for a substantial portion of the time needed to stabilize a gdb port. So it's worthwhile to look for an approach that will be easier to understand and maintain. In the approach described above:

- It's easier to see that the analyzer is correct: you just see whether the analyzer properly (albeit conservatively) simulates the effect of each instruction.
- It's easier to extend the analyzer: you can add support for new instructions, and know that you haven't broken anything that wasn't already broken before.
- It's orthogonal: to gather new information, you don't need to complicate the code for each instruction. As long as your domain of conservative values is already detailed enough to tell you what you need, then all the existing instruction simulations are already gathering the right data for you.

The file `prologue-value.h` contains detailed comments explaining the framework and how to use it.

### 3.3 Breakpoint Handling

In general, a breakpoint is a user-designated location in the program where the user wants to regain control if program execution ever reaches that location.

There are two main ways to implement breakpoints; either as “hardware” breakpoints or as “software” breakpoints.

Hardware breakpoints are sometimes available as a builtin debugging features with some chips. Typically these work by having dedicated register into which the breakpoint address may be stored. If the PC (shorthand for program counter) ever matches a value in a breakpoint registers, the CPU raises an exception and reports it to gdb.

Another possibility is when an emulator is in use; many emulators include circuitry that watches the address lines coming out from the processor, and force it to stop if the address matches a breakpoint's address.

A third possibility is that the target already has the ability to do breakpoints somehow; for instance, a ROM monitor may do its own software breakpoints. So although these are not literally “hardware breakpoints”, from gdb's point of view they work the same; gdb need not do anything more than set the breakpoint and wait for something to happen.

Since they depend on hardware resources, hardware breakpoints may be limited in number; when the user asks for more, gdb will start trying to set software breakpoints. (On some architectures, notably the 32-bit x86 platforms, gdb cannot always know whether there's enough hardware resources to insert all the hardware breakpoints and watchpoints. On those platforms, gdb prints an error message only when the program being debugged is continued.)

Software breakpoints require gdb to do somewhat more work. The basic theory is that gdb will replace a program instruction with a trap, illegal divide, or some other instruction that will cause an exception, and then when it's encountered, gdb will take the exception and stop the program. When the user says to continue, gdb will restore the original instruction, single-step, re-insert the trap, and continue on.

Since it literally overwrites the program being tested, the program area must be writable, so this technique won't work on programs in ROM. It can also distort the behavior of programs that examine themselves, although such a situation would be highly unusual.

Also, the software breakpoint instruction should be the smallest size of instruction, so it doesn't overwrite an instruction that might be a jump target, and cause disaster when the program jumps into the middle of the breakpoint instruction. (Strictly speaking, the breakpoint must be no larger than the smallest interval between instructions that may be jump targets; perhaps there is an architecture where only even-numbered instructions may jumped to.) Note that it's possible for an instruction set not to have any instructions usable for a software breakpoint, although in practice only the ARC has failed to define such an instruction.

The basic definition of the software breakpoint is the macro `BREAKPOINT`.

Basic breakpoint object handling is in `breakpoint.c`. However, much of the interesting breakpoint action is in `infrun.c`.

`target_remove_breakpoint (`bp_tgt`)`
`target_insert_breakpoint (`bp_tgt`)`
  Insert or remove a software breakpoint at address bp_tgt`->placed_address`. Returns zero for success, non-zero for failure. On input, bp_tgt contains the address of the breakpoint, and is otherwise initialized to zero. The fields of the `struct bp_target_info` pointed to by bp_tgt are updated to contain other information about the breakpoint on output. The field `placed_address` may be updated if the breakpoint was placed at a related address; the field `shadow_contents` contains the real contents of the bytes where the breakpoint has been inserted, if reading memory would return the breakpoint instead of the underlying memory; the field `shadow_len` is the length of memory cached in `shadow_contents`, if any; and the field `placed_size` is optionally set and used by the target, if it could differ from `shadow_len`.

  For example, the remote target ``Z0`' packet does not require shadowing memory, so `shadow_len` is left at zero. However, the length reported by `gdbarch_breakpoint_from_pc` is cached in `placed_size`, so that a matching ``z0`' packet can be used to remove the breakpoint.

`target_remove_hw_breakpoint (`bp_tgt`)`
`target_insert_hw_breakpoint (`bp_tgt`)`
  Insert or remove a hardware-assisted breakpoint at address bp_tgt`->placed_address`. Returns zero for success, non-zero for failure. See `target_insert_breakpoint` for a description of the `struct bp_target_info` pointed to by bp_tgt; the `shadow_contents` and `shadow_len` members are not used for hardware breakpoints, but `placed_size` may be.

### 3.4 Single Stepping

### 3.5 Signal Handling

### 3.6 Thread Handling

### 3.7 Inferior Function Calls

### 3.8 Longjmp Support

gdb has support for figuring out that the target is doing a `longjmp` and for stopping at the target of the jump, if we are stepping. This is done with a few specialized internal breakpoints, which are visible in the output of the ``maint info breakpoint`' command.

To make this work, you need to define a function called `gdbarch_get_longjmp_target`, which will examine the `jmp_buf` structure and extract the longjmp target address. Since `jmp_buf` is target specific, you will need to define it in the appropriate `tm-target.h` file. Look in `tm-sun4os4.h` and `sparc-tdep.c` for examples of how to do this.

### 3.9 Watchpoints

Watchpoints are a special kind of breakpoints (see breakpoints) which break when data is accessed rather than when some instruction is executed. When you have data which changes without your knowing what code does that, watchpoints are the silver bullet to hunt down and kill such bugs.

Watchpoints can be either hardware-assisted or not; the latter type is known as “software watchpoints.” gdb always uses hardware-assisted watchpoints if they are available, and falls back on software watchpoints otherwise. Typical situations where gdb will use software watchpoints are:

- The watched memory region is too large for the underlying hardware watchpoint support. For example, each x86 debug register can watch up to 4 bytes of memory, so trying to watch data structures whose size is more than 16 bytes will cause gdb to use software watchpoints.
- The value of the expression to be watched depends on data held in registers (as opposed to memory).
- Too many different watchpoints requested. (On some architectures, this situation is impossible to detect until the debugged program is resumed.) Note that x86 debug registers are used both for hardware breakpoints and for watchpoints, so setting too many hardware breakpoints might cause watchpoint insertion to fail.
- No hardware-assisted watchpoints provided by the target implementation.

Software watchpoints are very slow, since gdb needs to single-step the program being debugged and test the value of the watched expression(s) after each instruction. The rest of this section is mostly irrelevant for software watchpoints.

When the inferior stops, gdb tries to establish, among other possible reasons, whether it stopped due to a watchpoint being hit. It first uses `STOPPED_BY_WATCHPOINT` to see if any watchpoint was hit. If not, all watchpoint checking is skipped.

Then gdb calls `target_stopped_data_address` exactly once. This method returns the address of the watchpoint which triggered, if the target can determine it. If the triggered address is available, gdb compares the address returned by this method with each watched memory address in each active watchpoint. For data-read and data-access watchpoints, gdb announces every watchpoint that watches the triggered address as being hit. For this reason, data-read and data-access watchpoints require that the triggered address be available; if not, read and access watchpoints will never be considered hit. For data-write watchpoints, if the triggered address is available, gdb considers only those watchpoints which match that address; otherwise, gdb considers all data-write watchpoints. For each data-write watchpoint that gdb considers, it evaluates the expression whose value is being watched, and tests whether the watched value has changed. Watchpoints whose watched values have changed are announced as hit.

gdb uses several macros and primitives to support hardware watchpoints:

`TARGET_HAS_HARDWARE_WATCHPOINTS`
  If defined, the target supports hardware watchpoints.

`TARGET_CAN_USE_HARDWARE_WATCHPOINT (`type`,` count`,` other`)`
  Return the number of hardware watchpoints of type type that are possible to be set. The value is positive if count watchpoints of this type can be set, zero if setting watchpoints of this type is not supported, and negative if count is more than the maximum number of watchpoints of type type that can be set. other is non-zero if other types of watchpoints are currently enabled (there are architectures which cannot set watchpoints of different types at the same time).

`TARGET_REGION_OK_FOR_HW_WATCHPOINT (`addr`,` len`)`
  Return non-zero if hardware watchpoints can be used to watch a region whose address is addr and whose length in bytes is len.

`target_insert_watchpoint (`addr`,` len`,` type`)`
`target_remove_watchpoint (`addr`,` len`,` type`)`
  Insert or remove a hardware watchpoint starting at addr, for len bytes. type is the watchpoint type, one of the possible values of the enumerated data type `target_hw_bp_type`, defined by `breakpoint.h` as follows:

                 enum target_hw_bp_type
                   {
                     hw_write   = 0, /* Common (write) HW watchpoint */
                     hw_read    = 1, /* Read    HW watchpoint */
                     hw_access  = 2, /* Access (read or write) HW watchpoint */
                     hw_execute = 3  /* Execute HW breakpoint */
                   };

  These two macros should return 0 for success, non-zero for failure.

`target_stopped_data_address (`addr_p`)`
  If the inferior has some watchpoint that triggered, place the address associated with the watchpoint at the location pointed to by addr_p and return non-zero. Otherwise, return zero. This is required for data-read and data-access watchpoints. It is not required for data-write watchpoints, but gdb uses it to improve handling of those also.

  gdb will only call this method once per watchpoint stop, immediately after calling `STOPPED_BY_WATCHPOINT`. If the target's watchpoint indication is sticky, i.e., stays set after resuming, this method should clear it. For instance, the x86 debug control register has sticky triggered flags.

`HAVE_STEPPABLE_WATCHPOINT`
  If defined to a non-zero value, it is not necessary to disable a watchpoint to step over it. Like `gdbarch_have_nonsteppable_watchpoint`, this is usually set when watchpoints trigger at the instruction which will perform an interesting read or write. It should be set if there is a temporary disable bit which allows the processor to step over the interesting instruction without raising the watchpoint exception again.

`int gdbarch_have_nonsteppable_watchpoint (`gdbarch`)`
  If it returns a non-zero value, gdb should disable a watchpoint to step the inferior over it. This is usually set when watchpoints trigger at the instruction which will perform an interesting read or write.

`HAVE_CONTINUABLE_WATCHPOINT`
  If defined to a non-zero value, it is possible to continue the inferior after a watchpoint has been hit. This is usually set when watchpoints trigger at the instruction following an interesting read or write.

`CANNOT_STEP_HW_WATCHPOINTS`
  If this is defined to a non-zero value, gdb will remove all watchpoints before stepping the inferior.

`STOPPED_BY_WATCHPOINT (`wait_status`)`
  Return non-zero if stopped by a watchpoint. wait_status is of the type `struct target_waitstatus`, defined by `target.h`. Normally, this macro is defined to invoke the function pointed to by the `to_stopped_by_watchpoint` member of the structure (of the type `target_ops`, defined on `target.h`) that describes the target-specific operations; `to_stopped_by_watchpoint` ignores the wait_status argument.

  gdb does not require the non-zero value returned by `STOPPED_BY_WATCHPOINT` to be 100% correct, so if a target cannot determine for sure whether the inferior stopped due to a watchpoint, it could return non-zero “just in case”.

#### 3.9.1 Watchpoints and Threads

gdb only supports process-wide watchpoints, which trigger in all threads. gdb uses the thread ID to make watchpoints act as if they were thread-specific, but it cannot set hardware watchpoints that only trigger in a specific thread. Therefore, even if the target supports threads, per-thread debug registers, and watchpoints which only affect a single thread, it should set the per-thread debug registers for all threads to the same value. On gnu/Linux native targets, this is accomplished by using `ALL_LWPS` in `target_insert_watchpoint` and `target_remove_watchpoint` and by using `linux_set_new_thread` to register a handler for newly created threads.

gdb's gnu/Linux support only reports a single event at a time, although multiple events can trigger simultaneously for multi-threaded programs. When multiple events occur, `linux-nat.c` queues subsequent events and returns them the next time the program is resumed. This means that `STOPPED_BY_WATCHPOINT` and `target_stopped_data_address` only need to consult the current thread's state—the thread indicated by `inferior_ptid`. If two threads have hit watchpoints simultaneously, those routines will be called a second time for the second thread.

#### 3.9.2 x86 Watchpoints

The 32-bit Intel x86 (a.k.a. ia32) processors feature special debug registers designed to facilitate debugging. gdb provides a generic library of functions that x86-based ports can use to implement support for watchpoints and hardware-assisted breakpoints. This subsection documents the x86 watchpoint facilities in gdb.

To use the generic x86 watchpoint support, a port should do the following:

- Define the macro `I386_USE_GENERIC_WATCHPOINTS` somewhere in the target-dependent headers.
- Include the `config/i386/nm-i386.h` header file after defining `I386_USE_GENERIC_WATCHPOINTS`.
- Add `i386-nat.o` to the value of the Make variable `NATDEPFILES` (see NATDEPFILES) or `TDEPFILES` (see TDEPFILES).
- Provide implementations for the `I386_DR_LOW_*` macros described below. Typically, each macro should call a target-specific function which does the real work.

The x86 watchpoint support works by maintaining mirror images of the debug registers. Values are copied between the mirror images and the real debug registers via a set of macros which each target needs to provide:

`I386_DR_LOW_SET_CONTROL (`val`)`
  Set the Debug Control (DR7) register to the value val.

`I386_DR_LOW_SET_ADDR (`idx`,` addr`)`
  Put the address addr into the debug register number idx.

`I386_DR_LOW_RESET_ADDR (`idx`)`
  Reset (i.e. zero out) the address stored in the debug register number idx.

`I386_DR_LOW_GET_STATUS`
  Return the value of the Debug Status (DR6) register. This value is used immediately after it is returned by `I386_DR_LOW_GET_STATUS`, so as to support per-thread status register values.

For each one of the 4 debug registers (whose indices are from 0 to 3) that store addresses, a reference count is maintained by gdb, to allow sharing of debug registers by several watchpoints. This allows users to define several watchpoints that watch the same expression, but with different conditions and/or commands, without wasting debug registers which are in short supply. gdb maintains the reference counts internally, targets don't have to do anything to use this feature.

The x86 debug registers can each watch a region that is 1, 2, or 4 bytes long. The ia32 architecture requires that each watched region be appropriately aligned: 2-byte region on 2-byte boundary, 4-byte region on 4-byte boundary. However, the x86 watchpoint support in gdb can watch unaligned regions and regions larger than 4 bytes (up to 16 bytes) by allocating several debug registers to watch a single region. This allocation of several registers per a watched region is also done automatically without target code intervention.

The generic x86 watchpoint support provides the following API for the gdb's application code:

`i386_region_ok_for_watchpoint (`addr`,` len`)`
  The macro `TARGET_REGION_OK_FOR_HW_WATCHPOINT` is set to call this function. It counts the number of debug registers required to watch a given region, and returns a non-zero value if that number is less than 4, the number of debug registers available to x86 processors.

`i386_stopped_data_address (`addr_p`)`
  The target function `target_stopped_data_address` is set to call this function. This function examines the breakpoint condition bits in the DR6 Debug Status register, as returned by the `I386_DR_LOW_GET_STATUS` macro, and returns the address associated with the first bit that is set in DR6.

`i386_stopped_by_watchpoint (void)`
  The macro `STOPPED_BY_WATCHPOINT` is set to call this function. The argument passed to `STOPPED_BY_WATCHPOINT` is ignored. This function examines the breakpoint condition bits in the DR6 Debug Status register, as returned by the `I386_DR_LOW_GET_STATUS` macro, and returns true if any bit is set. Otherwise, false is returned.

`i386_insert_watchpoint (`addr`,` len`,` type`)`
`i386_remove_watchpoint (`addr`,` len`,` type`)`
  Insert or remove a watchpoint. The macros `target_insert_watchpoint` and `target_remove_watchpoint` are set to call these functions. `i386_insert_watchpoint` first looks for a debug register which is already set to watch the same region for the same access types; if found, it just increments the reference count of that debug register, thus implementing debug register sharing between watchpoints. If no such register is found, the function looks for a vacant debug register, sets its mirrored value to addr, sets the mirrored value of DR7 Debug Control register as appropriate for the len and type parameters, and then passes the new values of the debug register and DR7 to the inferior by calling `I386_DR_LOW_SET_ADDR` and `I386_DR_LOW_SET_CONTROL`. If more than one debug register is required to cover the given region, the above process is repeated for each debug register.

  `i386_remove_watchpoint` does the opposite: it resets the address in the mirrored value of the debug register and its read/write and length bits in the mirrored value of DR7, then passes these new values to the inferior via `I386_DR_LOW_RESET_ADDR` and `I386_DR_LOW_SET_CONTROL`. If a register is shared by several watchpoints, each time a `i386_remove_watchpoint` is called, it decrements the reference count, and only calls `I386_DR_LOW_RESET_ADDR` and `I386_DR_LOW_SET_CONTROL` when the count goes to zero.

`i386_insert_hw_breakpoint (`bp_tgt`)`
`i386_remove_hw_breakpoint (`bp_tgt`)`
  These functions insert and remove hardware-assisted breakpoints. The macros `target_insert_hw_breakpoint` and `target_remove_hw_breakpoint` are set to call these functions. The argument is a `struct bp_target_info *`, as described in the documentation for `target_insert_breakpoint`. These functions work like `i386_insert_watchpoint` and `i386_remove_watchpoint`, respectively, except that they set up the debug registers to watch instruction execution, and each hardware-assisted breakpoint always requires exactly one debug register.

`i386_stopped_by_hwbp (void)`
  This function returns non-zero if the inferior has some watchpoint or hardware breakpoint that triggered. It works like `i386_stopped_data_address`, except that it doesn't record the address whose watchpoint triggered.

`i386_cleanup_dregs (void)`
  This function clears all the reference counts, addresses, and control bits in the mirror images of the debug registers. It doesn't affect the actual debug registers in the inferior process.

Notes:

1. x86 processors support setting watchpoints on I/O reads or writes. However, since no target supports this (as of March 2001), and since `enum target_hw_bp_type` doesn't even have an enumeration for I/O watchpoints, this feature is not yet available to gdb running on x86.
2. x86 processors can enable watchpoints locally, for the current task only, or globally, for all the tasks. For each debug register, there's a bit in the DR7 Debug Control register that determines whether the associated address is watched locally or globally. The current implementation of x86 watchpoint support in gdb always sets watchpoints to be locally enabled, since global watchpoints might interfere with the underlying OS and are probably unavailable in many platforms.

### 3.10 Checkpoints

In the abstract, a checkpoint is a point in the execution history of the program, which the user may wish to return to at some later time.

Internally, a checkpoint is a saved copy of the program state, including whatever information is required in order to restore the program to that state at a later time. This can be expected to include the state of registers and memory, and may include external state such as the state of open files and devices.

There are a number of ways in which checkpoints may be implemented in gdb, e.g. as corefiles, as forked processes, and as some opaque method implemented on the target side.

A corefile can be used to save an image of target memory and register state, which can in principle be restored later — but corefiles do not typically include information about external entities such as open files. Currently this method is not implemented in gdb.

A forked process can save the state of user memory and registers, as well as some subset of external (kernel) state. This method is used to implement checkpoints on Linux, and in principle might be used on other systems.

Some targets, e.g. simulators, might have their own built-in method for saving checkpoints, and gdb might be able to take advantage of that capability without necessarily knowing any details of how it is done.

### 3.11 Observing changes in gdb internals

In order to function properly, several modules need to be notified when some changes occur in the gdb internals. Traditionally, these modules have relied on several paradigms, the most common ones being hooks and gdb-events. Unfortunately, none of these paradigms was versatile enough to become the standard notification mechanism in gdb. The fact that they only supported one “client” was also a strong limitation.

A new paradigm, based on the Observer pattern of the Design Patterns book, has therefore been implemented. The goal was to provide a new interface overcoming the issues with the notification mechanisms previously available. This new interface needed to be strongly typed, easy to extend, and versatile enough to be used as the standard interface when adding new notifications.

See GDB Observers for a brief description of the observers currently implemented in GDB. The rationale for the current implementation is also briefly discussed.