Ну то есть, вот есть например sampling profiler, он периодически смотрит на текущую инструкцию и таким образом знает какая функция бинаря выполнена. Соответственно что если у тебя одноразовое событие то оно может вообще не попасть в профайл, т.е. для вышеобозначенной задачи не очень полезно. У тебя есть инструменты ядра типа strace и т.д. Когда отдается контроль ядру во время системного вызова, профилировщик встроенный в ядро может что-то сделать. Соответственно если ты не обращаешься к ядру, то этот механизм бесполезен. Есть еще дебаггер, который ставит hardware breakpoint и если процессор выполняет инструкцию по адресу breakpoint'a то контроль переходит профилировщику. Этот подход тоже мало полезен потому что емнип существуют аппаратные ограничения на количество breakpoint'ов и всюду ты их не расставишь. Да и муторно это в продакшне запускать бинарь под дебагерром.
Остается последний метод - code injection. Каждый раз выполняется функция, компилятор добавляет экстра код который записывает текущее состояние и его потом можно восстановить. Из минусов - просадка производительности (разна в зависимости от реализации) но бонусом идет 100% точность.