«Неуправляемая среда выполнения» - это немного оксюморон. Неуправляемый код не запускается на виртуальной машине. Кроме того, управляемая среда выполнения сама по себе является неуправляемым кодом. Я думаю, что вы заинтересованы в коде в целом, а не только в CLR и стандартных библиотеках, но, в конечном счете, все является нативным кодом, некоторые из них были созданы CLR JIT, а некоторые - нативным компилятором, но все это нативный код в конце. Существует даже общая модель обработки исключений (SEH) как для .NET, так и для собственных исключений (или, точнее, .NET использует предоставляемую ОС модель исключений).
После запуска JIT нет особых признаков управляемых-> неуправляемых переходов, поскольку они представляют собой простой вызов функции (отладчик может определить разницу, потому что неуправляемый код находится в адресном пространстве собственной библиотеки, и JIT-скомпилированный управляемый код находится в динамическом адресном пространстве, принадлежащем JIT). Обратное направление немного сложнее, поскольку инструкция вызова была создана перед кодом, который она вызывает, это должен быть косвенный вызов (через указатель на функцию). Но это по-прежнему указатель на JIT-скомпилированный код, а не на какой-то один неуправляемый -> управляемый thunk, где вы можете поставить точку останова. На самом деле указатель на функцию, вероятно, начинает указывать на сам JIT, который выясняет, какой метод вызывается, компилирует его, обновляет указатель на функцию и, наконец, вызывает конечный вызов, как если бы он был вызван в первую очередь. .
Это магия взаимодействия C ++. Это также означает, что управляемые -> неуправляемые переходы действительно даже не стоит беспокоиться. Профилируйте и узнайте, какие пути кода дороги, и если это окажется вызовом между управляемым и неуправляемым кодом, оптимизируйте его тогда. Но не ищите случайно управляемые / неуправляемые переходы.
Теперь есть и другие причины, по которым вы можете искать все управляемые / неуправляемые переходы. Например, сборщик мусора может прерывать любой поток, выполняющий управляемый код (опять же, код, который фактически выполняется, является нативным кодом, но GC может идентифицировать его, поскольку он находится внутри пространства памяти, используемого JIT, и таблицы использования объектов, созданные JIT, подробно описывают, что переменные стека являются достижимыми объектами при любом указателе инструкций). Поэтому, если вы хотите гарантировать, что определенный поток НИКОГДА не останавливается сборщиком мусора, вам нужно выследить и устранить все неуправляемые-> управляемые переходы в функциях, которые выполняются в этом потоке. Инъекция точки останова по-прежнему не будет правильным подходом, даже если бы было место для установки точки останова, так как она ловит только фактически выполненные переходы, а не условные.