Skip to content
  • David Johnson's avatar
    Refactor part two: convert xen-process target into generic os-process. · 39819b65
    David Johnson authored
    This commit contains the conversion of the target_xen_vm_process target
    into the target_os_process target.  What this basically means is that
    most of the linux-specific and Xen-specific code that was in the
    previous driver is now moved into the os_linux_generic personality; and
    the logic in the target_xen_vm_process driver is now part of the
    target_os_process driver.  That driver is completely generic; it can sit
    atop any other driver that is paired with an OS personality.  The
    os_process driver gets all its information from loading target_process
    objects from the underlying driver, and using that information to build
    its own model of the world.  Its active probing support is implemented
    by enabling OS_PROCESS active probing in the underlying target, and
    listening for the target-object-events that are generated -- and
    handling them.  There is better generic support for both the OS and
    process level.
    
    One core piece of this refactor is the use of the full weak reference
    support.  This basically means that many target objects assure the
    validity of backref pointers better -- and that deallocation can happen
    to refcnt'd objects when weak refs are dropped -- not just regular refs.
    I use weak refs as a cyclic dependency breaker, which is all I need.
    Kind of weird, but I don't need a completely generic weak refcnt system!
    Anyway, what this means is that we are now ready for the case where a
    language binding can hold onto C-level objects in crazy ways (i.e.,
    given the standard target object hierarchy of
    target->space->region->range, a Python binding could be able to hold
    onto range, drop the target object, and the whole backref chain will be
    intact, and get dropped when range is dropped).  (One thing the weak ref
    stuff does mean is that the <type>_free deallocators must be more
    careful!  Even if their refcnt has gone to nil, they must hold a temp
    ref if they might free things that hold a weak ref to them -- because if
    both the refcnt and refcntw counts go to 0, the deallocator will be
    called again on an RPUTW.  Hence the use of RWGUARD/RWUNGUARD.)
    
    Drivers can now generate target events to broadcast new, changed, or
    deleted target objects.  This replaces the state change stuff.  Events
    are now broadcast to relevant parties -- namely underlying and
    overlaying targets.  For instance, active probing support in the
    os_process target is built atop the assumption that the underlying
    target supports active OS_PROCESS probing, and generates OS_PROCESS
    events when things change.
    
    I also needed to make active probing mechanism a bit more useful.  So
    rather than just the the thread_entry, thread_exit, and memory flags for
    each target, I split them into personality-level-specific flags:
    os_thread_entry, process_memory, etc.  This allowed to add a set of
    os_process_* active probing flags, which the os_linux_generic
    personality understands, and uses to dynamically track processes and
    their address spaces.  This is the bit that was critical enable part of
    the conversion from target_xen_process to target_os_process, because it
    allowed me to have the linux personality track processes and their
    addrspaces actively, and to have the generic os_process driver toggle
    that via active probing.  I'm not sure how active probing will change in
    the future; hopefully this is enough for now...
    
    Also, refactor the memory code (addrspaces, memregions, memranges) to
    use GLists instead of the Linux linked lists.  We don't need the
    advantages of the Linux linked lists.  Also, employ weak refs and object
    liveness data and macros throughout, instead of the old custom "new" and
    "updated" (et al) members.  Much improved.  Technically, a user could
    now hold onto a memrange, and the object chain all the way up to the
    containing target or target_process should be intact until they drop the
    memrange; perfect.  This means they're easier to expose to users via a
    language binding, and more amenable to reuse throughout the target
    library (i.e., the target_process object owns a (disconnected --
    meaning, not bound to a target) addrspace object, which is populated
    with regions and ranges like a normal addrspace describing a valid
    target.  Thus, we avoid duplicating a lot of code and/or logic, by
    having silly duplicates like target_process and target_os_process, or
    having multiple kinds of memregions.  There is some waste; memregions
    and spaces that are not bound to targets will never be fully utilized;
    but lots of code reuse.
    
    A few more notes:
    
    This commit introduces target_finalize as the replacement for
    target_free.  Basically, the target library never RHOLDs target objects
    on behalf of users; it only caches them in its global hashtable (iff the
    user has called target_init()), and holds them there.  target_free thus
    becomes the refcnt-callable deallocator for target objects, and thus
    cannot be exposed to users.  It's a better pairing for
    target_instantiate() anyway.
    
    target_notify_overlay() now passes target_exception_flags_t to better
    encapsulate the nature of the underlying exception.  We'll see if this
    is a good compromise between abstraction and exception handling, or not.
    
    Get rid of all the personality wrapper functions and replace them with
    SAFE_* macros.  These are better!
    
    Make debugfile caching explicit, and expose functions to evict
    unnecessary debugfiles.  This also handles the cached debugfile RHOLDs
    appropriately in dwdebug_fini().  Much better.
    39819b65