getappmap / appmap-python

AppMap client agent for Python
https://appland.org
Other
99 stars 16 forks source link

uninstrumented functions may be called #365

Open apotterri opened 2 months ago

apotterri commented 2 months ago

If application code saves a reference to function when the code is imported, a call to that function won't generate any events in the recording.

For example, with this definition

def free_method():
    return "free method"

def Example:
    static_method = staticmethod(partial(free_method))

Example.static_method()

no events for the call to free_method will be recorded. This is because instrumentation of free_method happens after the module is loaded, and so after the call to partial(free_method)) has saved the reference to free_method.

github-actions[bot] commented 2 months ago

Title

Instrument Pre-loaded Function References to Ensure Event Recording

Problem

Uninstrumented functions referenced at the module import stage do not generate any events in the recording. When application code saves a reference to a function upon importing, calls to this function fail to trigger events. This issue arises because instrumentation occurs after module load, affecting reference semantics.

Analysis

When the program loads a module, any references to functions made before the instrumentation process will point to uninstrumented versions. Calls to these pre-loaded references will not generate events, as they bypass the instrumentation logic intended to track function calls.

To address this issue, it is necessary to ensure that function references saved at module import time are instrumented. One way to fix this is by wrapping references to functions at the point of assignment. This would involve modifying the function reference wrapping logic to check for already-referenced functions and instrument them as needed.

Proposed Changes

  1. _appmap/instrument.py:

    • Modify the function instrumentation logic to check existing references when a function is being instrumented.
    • Ensure that any pre-loaded function references are wrapped and instrumented correctly using the existing instrumentation mechanism.
  2. vendor/_appmap/wrapt/wrappers.py:

    • Enhance the WeakFunctionProxy and ObjectProxy classes to ensure that references created before the instrumentation can be detected and instrumented.
    • Integrate logic to handle the call to pre-instrumented references.
  3. _appmap/recorder.py:

    • Adjust the event recording functions to recognize calls made through pre-loaded references and register the associated events correctly.

Detailed Steps

  1. _appmap/instrument.py:

    • Update the instrument function to handle pre-existing references.
    • Implement a mechanism to identify and wrap pre-existing function references during module load.
  2. vendor/_appmap/wrapt/wrappers.py:

    • Modify the WeakFunctionProxy class to ensure it can instrument references to functions pre-loaded before wrapping logic is applied.
    • Adjust the nested function handling within ObjectProxy to accommodate instrumented pre-loaded references.
  3. _appmap/recorder.py:

    • Modify the _add_event method to include functionality for tracking events generated by calls through pre-loaded references.
    • Ensure that events are correctly recorded and appended, even for early-bound references.

By implementing these changes, we ensure that calls to functions referenced before the instrumentation will generate the necessary events, maintaining the integrity of the recording process even for pre-loaded function references.