gdb/amd-dbgapi: add basic watchpoint support

Add basic watchpoint support for the amd-dbgapi target.  This means
placing write watchpoints on globally addressable memory.  More
complexity will come eventually to allow placing watchpoints on the
various other address spaces, but that will require adding proper
support for non-default address spaces first.

Implementation
--------------

I think the implementation is not too surprising, just adding the
required target methods.  But there are some things worthy of mention:

 - amd-dbgapi does not support read watchpoints.  If the core attempts
   to insert a read (or access, which means read/write) watchpoint,
   amd_dbgapi_target::insert_watchpoint returns an error.

   If we silently let the beneath target (linux-nat) install the read
   watchpoint, it would be potentially confusing.  Everything would look
   fine to the user, but a read from the GPU would not be caught, so it
   would look like the watchpoint doesn't work.

   There is a loophole though: read watchpoints created before the
   runtime is loaded (and therefore the amd-dbgapi target is pushed)
   will still be inserted.  Only when execution stops, and the user
   tries to resume again, will the check in
   amd_dbgapi_target::insert_watchpoint be hit.

   Another option would be to allow the host read watchpoint to go
   through, but warn that the reads from the AMD GPU device will not be
   watched.  We would need to be smart to avoid flooding the user with
   warnings.  But I decided to upstream the current ROCgdb behavior
   first, we can always change it later.

 - When the amd-dbgapi target gets pushed, we create amd-dbgapi
   watchpoints for any existing hardware write watchpoint location.

 - When the core asks the target to insert a watchpoint, we ask the
   target beneath to insert it first.  If the beneath target fails, we
   return immediately with an error.

 - When the core asks to remove a watchpoint, we ask the target beneath
   to to remove it first.  Even if it fails, we still try to remove the
   amd-dbgapi watchpoint.

 - When stopping after a watchpoint hit while the "precise-memory"
   setting is not enabled, it is possible for the wave to stop a few
   instructions later than the instruction that made the write that
   triggered the watchpoint.  We print a warning in that case, similar
   to what we do when a memory violation happens while "precis-memory"
   is disabled.

Testing
-------

 - Tests precise-memory-warning-watchpoint.exp and
   watchpoint-at-end-of-shader.exp are more or less brought as-is from
   downstream ROCgdb.  I modified precise-memory-warning-watchpoint.exp
   to watch a hipMalloc'ed region instead of a `__device__` global
   variable.  The latter doesn't work upstream, because we don't yet
   support the DWARF constructs that describe the variable location.

 - I added test watchpoint-basic.exp with various simple cases to
   exercises different code paths added by this patch.

Differences from downstream ROCgdb
----------------------------------

While extracting this code from ROCgdb, I made a few minor but possibly
significant (read: erroneous) changes.  Those should be reviewed
carefully.  I think that some code in ROCgdb was written at a time where
the amd-dbgapi target was always pushed at the very start of the
inferior execution, so assumptions were different.

 - The value type for the `amd_dbgapi_inferior_info::watchpoint_map` map
   is now a structure, instead of an std::pair, just because it makes
   the code more readable.

 - The insert_watchpoint and remove_watchpoint methods (and perhaps
   others) now assume that if they are called, the runtime is in the
   "enabled" state.

 - insert_initial_watchpoints has one more check (loc->owner->type !=
   bp_hardware_watchpoint), to filter out non-write watchpoints.
   Otherwise, I think that we could mistakenly insert some write
   watchpoints for some pre-existing read watchpoints.

 - Because it is possible for read watchpoints to be created before the
   target is pushed, remove_watchpoint returns early if it sees that the
   code asks for the removal of a read watchpoint, instead of asserting
   "type == hw_write" (this was caught by the new test).

 - In ROCgdb, remove_watchpoint does:

     if (addr < it->first || (addr + len) > it->second.first)
       return 1;

   I replaced it with some assertions.

   The first half of this condition should always be true, due to how
   std::upper_bound works.

   For the second part: if the watchpoint was created successfully, it
   is because it did fully cover the requested region (see
   insert_one_watchpoint).  I don't see why the core would ask us to
   remove a watchpoint that wasn't successfully inserted.  I am not 100%
   sure about that one, there might be some edge cases where this is not
   true.

 - I changed a manual free in stopped_by_watchpoint to a
   gdb::unique_xmalloc_ptr, even though it changes nothing functionally.

 - I merged some conditions in amd_dbgapi_target_normal_stop.

Change-Id: Ia15fb7434dc0c142a5a32997ada2e3a163c89f98
Approved-by: Lancelot Six <lancelot.six@amd.com>
Co-Authored-By: Laurent Morichetti <laurent.morichetti@amd.com>
10 files changed