Skip to content

[dev] Parent call tracing

Infernio edited this page Oct 28, 2022 · 7 revisions

It can be really annoying to figure out where some mysterious calls found via profiling come from. Something like this helps:

+    _parent_cache = set()
+    _is_on = False
     def isfile(self):
+        if self._is_on:
+            parent_frame = sys._getframe(1)
+            code_obj = parent_frame.f_code
+            calling_loc = f'{code_obj.co_filename}:{parent_frame.f_lineno}' \
+                          f'({code_obj.co_name})'
+            if calling_loc not in self._parent_cache:
+                self._parent_cache.add(calling_loc)
+                deprint(f'Called from {calling_loc}')
         return os.path.isfile(self._s)

Change _is_on to True once you want to start monitoring, change it to False once you're done.

Output looks like this:

bolt.py  814 isfile: Called from E:\Infernio\Desktop\Programming\wrye-bash\Mopy\bash\bosh\__init__.py:476(__init__)
bolt.py  814 isfile: Called from E:\Infernio\Desktop\Programming\wrye-bash\Mopy\bash\bosh\__init__.py:640(setGhost)

Copy-pastable version:

_parent_cache = set()
_is_on = False

if self._is_on:
    parent_frame = sys._getframe(1)
    code_obj = parent_frame.f_code
    calling_loc = f'{code_obj.co_filename}:{parent_frame.f_lineno}' \
                  f'({code_obj.co_name})'
    if calling_loc not in self._parent_cache:
        self._parent_cache.add(calling_loc)
        deprint(f'Called from {calling_loc}')

Counter Version

When you want to know not just where the calls are coming from, but how many are coming from where, this minor variation is useful:

+    _parent_counter = collections.Counter()
+    _is_on = False
    def __hash__(self):
+        if self._is_on:
+            parent_frame = sys._getframe(1)
+            code_obj = parent_frame.f_code
+            calling_loc = f'{code_obj.co_filename}:{parent_frame.f_lineno}' \
+                          f'({code_obj.co_name})'
+            self._parent_counter[calling_loc] += 1
         return hash(self._lower)

Use as before, but once done, pprint the contents of _parent_counter:

Counter({'E:\\Infernio\\Desktop\\Programming\\wrye-bash\\Mopy\\bash\\load_order.py:104(__init__)': 144537,
         'E:\\Infernio\\Desktop\\Programming\\wrye-bash\\Mopy\\bash\\load_order.py:109(<dictcomp>)': 144537,
         'E:\\Infernio\\Desktop\\Programming\\wrye-bash\\Mopy\\bash\\bolt.py:633(forward_compat_path_to_fn_list)': 133558,
         'E:\\Infernio\\Desktop\\Programming\\wrye-bash\\Mopy\\bash\\load_order.py:112(__init__)': 133558,
         'E:\\Infernio\\Desktop\\Programming\\wrye-bash\\Mopy\\bash\\load_order.py:113(<dictcomp>)': 133558})

Copy-pastable version:

_parent_counter = collections.Counter()
_is_on = False

if self._is_on:
    parent_frame = sys._getframe(1)
    code_obj = parent_frame.f_code
    calling_loc = f'{code_obj.co_filename}:{parent_frame.f_lineno}' \
                  f'({code_obj.co_name})'
    self._parent_counter[calling_loc] += 1
Clone this wiki locally