что такое профайлер в программировании
Профилирование уже запущенных программ
Все мы пользуемся профайлерами. Традиционная схема работы с ними такова, что приходится изначально запускать программу «под профайлером» а затем, после окончания ее работы, анализировать сырой дамп с помощью дополнительных утилит.
А что делать если мы не имея root’а хотим запрофилировать уже работающую программу, которая долго работала «как надо», а сейчас что-то пошло не так. И хотим это сделать быстро. Знакомая ситуация?
Тогда рассмотрим наиболее популярные профайлеры и принципы их работы. А затем профайлер, который решает именно указанную задачу.
Популярные профайлеры
Если вы знаете принципиально другой — напишите о нем в комментах. А пока рассмотрим эти 4:
I. gprof
Старый-добрый UNIX профайлер который, по словам Кирка МакКузика, был написан Биллом Джоем для анализа производительности подсистем BSD. Собственно, профайлер «предоставляется» компилятором — он должен расставить контрольные точки в начале и в конце каждой функции. Разница между двумя этими точками и будет временем ее исполнения.
Стоит отметить, что gprof в данном случе точно «знает» и то, сколько раз была вызвана каждая функция. И хотя это может быть необходимым в некоторых ситуациях, это также имеет отрицательный эффект — overhead от замеров может быть сравним или даже больше чем само тело функции. Поэтому, например, для при компиляции C++-кода используют оптимизации приводящие к inline.
Так или иначе, но gprof не работает с уже запущеными программами.
II. Callgrind
Callgrind является частью Valgrind’а — отличного фреймворка для построения средств динамического анализа кода. Valgrind запускает программу «в песочнице», фактически используя виртуализации. Callgrind производит профилирование основываясь на брейкпоинтах на инструкциях типа call и ret. Он значительно замедляет анализируемый код, как правило, от 5 до 20 раз. Таким образом, для анализа на больших данных в runtime он, как правило, не годен.
Однако инструмент очень популярен, и простой формат графа вызовов поддерживается отличными средствами визуализации, например, kcachegrind.
III. OProfile
OProfile is a system-wide profiler for Linux systems, capable of profiling all running code at low overhead.
IV. Google perftools
Этот профайлер является частью набора Google perftools. Я не нашел на хабре его обзора, поэтому очень кратко опишу.
Набор включает серию библиотек нацеленых на ускорение и анализ C/C++ — приложений. Центральной частью является аллокатор tcmalloc, который помимо ускорения распределения памяти несет средства для анализа классических проблем — memory leaks и heap profile.
Второй частью является libprofiler, который позволяет собирать статистику использования CPU. Важно остановиться на том, как он это делает. Несколько раз в секунду (по-умолчанию 100) программа прерывается на сигнал таймера. В обработчике этого сигнала раскручивается стек и запоминаются все указатели инструкций. По-окончанию сырые данные сбрасываются в файл, по которому уже можно строить статистику и граф вызовов.
1. По-умолчанию сигналом таймера выбирается таймер ITIMER_PROF, который тикает лишь при использовании программой CPU. Ведь, как-правило, нам не очень интересно где была программа ожидая ввод с клавиатуры или поступления данных по сокету. А если все же интересно, используйте env CPUPROFILE_REALTIME=1
Интересен принцип действия — программа прерывается лишь N раз в секунду, где N достаточно мало. Это т.н. сэмплирующий профайлер. Его преимущество в том, что он не оказывает существенного влияния на анализируемую программу, сколько бы мелких функций там не вызывалось. Ввиду особенностей работы, он, однако, не позволяют ответить на вопрос «сколько раз вызывалась данная функция».
В случае с google profiler есть еще несколько неприятностей:
Crxprof
Как и обещал, теперь про другой профайлер, который написан именно для решения обозначенной выше проблемы — легкое профилирование уже запущенных процессов.
Он собирает стек вызовов и выводит наиболее «горячие» части в консоль по нажатию ENTER. Также он умеет сохранять граф вызова в упомянутом ранее формате callgrind. Работает быстро, и как любой другой сэмплирующий профайлер не зависит от сложности вызовов в профилируемой программе.
В основном, crxprof работает также как perftools, но использует внешнее профилирование через ptrace(2). Подобно perftools он использует libunwind для раскрутки стека, а вместо тяжелой работы по преобразованию в имена функций, вместо addr2line(1) используется libbfd.
Несколько раз в секунду программа останавливается (SIGSTOP) и с помощью libunwind «снимается» стек вызова. Загрузив при старте crxprof карту функций профилируемой программы и связанных с ней библиотек, мы можем быстро найти какой функции пренадлежит каждый отделый IP (instruction pointer).
Параллельно выстраивается граф вызова, полагая что есть некая центральная функция — точка входа. Обычно это __libc_start_main из библиотеки libc.
Исходный код доступен на github. Т.к. утилита создавалась для меня и моих коллег, я вполне допускаю что она может не соответствовать Вашему use-case’у. Так или иначе, спрашивайте.
Соберем crxprof и посмотрим на пример его использования.
Сборка
Что необходимо: Linux (2.6+), autoconf+automake, binutils-dev (включает libbfd), libunwind-dev (у меня он называется libunwind8-dev).
Для сборки выполняем:
Если libunwind установлен в нестандартное место, используйте:
Профилирование
Для этого просто запустите
И все! Теперь используйте ENTER для вывода профайла в консоль, и ^C для завершения. Crxprof также выведет профайл и по выходу программы.
Реальный пример
Для того чтобы привести реальный, но не сложный пример я использую этот код на C. Скомпилируем, запустим его и попросим crxprof сохранить граф вызова функций (4054 — pid профилируемой программы):
Визуализация делается по схеме «наибольшие поддеревья — первыми». Таким образом, даже для больших реальных программ можно использовать простую визуализацию в консоли, что должно быть удобно на серверах.
Для визуализации сложных графов вызова удобно использовать KCachegrind:
Картинка, которая получилась у меня, представлена справа.
Вместо заключения, напомню что профайлером пока пользуются лишь несколько моих коллег и я сам. Надеюсь, он будет также полезен и Вам.
Профилирование сайтов на PHP
Что такое профилировщик?
тратятся ресурсы при выполнении скриптов. Эта информация полезна разработчикам, которые с ее помощью могут оптимизировать работу своих программ, уменьшить использование аппаратных ресурсов и сократить время работы скриптов.
Данные, которые собирает профилировщик, могут быть разными: количество использованной во время выполнения скрипта памяти, частота и продолжительность вызовов функций, время ответа на конкретный запрос и так далее.
Как установить профилировщик на сайт?
Установить профилировщик на сайт можно из раздела «Сайты» Панели управления: напротив доменного имени, прилинкованного к нужному сайту, кликните по иконке «Управление профилировщиком».
В открывшемся окне будет доступна справочная информация о том, какие действия необходимо будет предпринять после включения профилировщика для генерации отчета, а также кнопка «Включить».
По нажатию кнопки «Включить» запустится установка профилировщика и графического интерфейса для просмотра результатов профилирования. Профилировщик будет включен для сайта практически мгновенно, для установки интерфейса понадобится некоторое время: текущий статус установки будет отображаться в окне состояния профилировщика.
Как использовать профилировщик?
При включении профилировщик генерирует отчеты о работе вашего сайта, когда вы открываете его страницы. Каждую страницу сайта, для которой необходим отчет, вам нужно открыть самостоятельно: профилировщик запускается только для вашего IP-адреса. Посещения сайта другими пользователями не запускают профилирование, отчеты в таком случае создаваться не будут.
Главное окно
В главном окне интерфейса можно выбрать количество последних запросов, для которых собрана статистика, а также включить фильтр результатов по нескольким критериям: посещаемости страницы, нагрузки на CPU, используемой памяти и времени выполнения.
Для каждого отчета в таблице выводится информация о времени выполнения скрипта (в микросекундах), процессорному времени и пиковом потреблении памяти (в байтах). По клику по дате отчета откроется подробная информация о результатах профилирования.
Отчет о результатах
Каждый отчет профилировщика состоит из нескольких частей:
Общая информация об отчете
Интерфейс отображает следующие данные статистики:
Также предоставляются следующие данные (при их наличии):
В общую информацию включена и диаграмма распределения функций, вызываемых в скрипте, по времени их выполнения: при наведении на сектор диаграммы отобразится название функции и потраченное на ее выполнение время.
Подробная информация об отчете (таблица)
Таблица отображает следующие данные статистики:
По клику на имя функции откроется расширенная статистика ее профилирования.
В расширенной статистике доступна информация как о самой функции, так и о родительских (тех функциях, которые в ходе своей работы вызывали исследуемую нами) и дочерних (тех функциях, которые вызывала исследуемая нами функция). Статистика собирается по следующим данным:
Граф вызовов
Заключение
Профилирование кода — Как найти слабое звено?
Профилирование кода это попытка найти узкие места в вашем коде. Профилирование может найти самые долго выполняющиеся части вашего кода. Найдя их, вы можете оптимизировать эти части удобным вам способом. Python содержит три встроенных профайлера: cProfile, profile и hotshot. В соответствии с документацией Python, hotshot «не поддерживается, и может быть удален из Python». Модуль profile это в корне своем модуль Python, но добавляет много чего сверху в профилированные программы. Поэтому мы сфокусируемся на cProfile, который содержит интерфейс, который имитирует модуль profile.
Профилирование кода при помощи cProfile
Профилирование кода с cProfile это достаточно просто. Все что вам нужно сделать, это импортировать модуль и вызвать его функцию run. Давайте посмотрим на простой пример:
Здесь мы импортировали модуль hashlib и использовали cProfile для профилирования того, что создал хеш MD5. Первая строка показывает, что в ней 4 вызова функций. Следующая строка говорит нам, в каком порядке результаты выдачи. Здесь есть несколько столбцов.
Примитивный вызов – это вызов, который не был совершен при помощи рекурсии. Это очень интересный пример, так как здесь нет очевидных узких мест. Давайте создадим часть кода с узкими местами, и посмотрим, обнаружит ли их профайлер.
Как работают профайлеры в Ruby и Python?
Перевод статьи подготовлен в преддверии старта продвинутого курса «Разработчик Python».
Всем привет! В качестве аперитива к профайлеру на Ruby я хотела рассказать о том, как работают уже существующие профайлеры на Ruby и Python. Также это поможет дать ответ на вопрос, который мне задает множество людей: «Как написать профайлер?»
В этой статье мы сфокусируемся на профайлерах процессора (а не, допустим, профайлерах памяти/кучи). Я расскажу о некоторых базовых подходах к написанию профайлера, приведу примеры кода и покажу много примеров популярных профайлеров на Ruby и Python, а также расскажу, как они работают под капотом.
Вероятно, в статье могут быть ошибки (при подготовке к ее написанию, я частично просмотрела код 14 библиотек для профайлинга, и со многими из них я была не знакома до этого момента), так что, пожалуйста, дайте мне знать, если вы их обнаружите.
2 вида профайлеров
Есть два основных вида профайлеров процессора – sampling- и tracing-профайлеры.
Tracing-профайлеры записывают каждый вызов функции в вашей программе, в конечном итоге предоставляя отчет. Sampling-профайлеры придерживаются статистического подхода, они записывают стек каждые несколько миллисекунд, создавая отчет на основе этих данных.
Основная причина использовать sampling-профайлер вместо tracing-профайлера – это его легковесность. Делаете вы 20 или 200 снимков в секунду — это не занимает много времени. Такие профайлеры будут очень эффективны, если у вас есть серьезная проблема с производительностью (80% времени тратится на вызов одной медленной функции), поскольку 200 снимков в секунду будет вполне достаточно для определения проблемной функции!
Профайлеры
Дальше я приведу общую сводку профайлеров, рассматриваемых в данной статье(отсюда). Я объясню термины, используемые статье (setitimer, rb_add_event_hook, ptrace) немного позже. Интересно, что все профайлеры реализованы с использованием небольшого набора базовых возможностей.
Профайлеры Python
«gdb hacks» не совсем профайлер Python — он ссылается на веб-сайт, рассказывающий о том, как реализовать хакерский профайлер в качестве обертки shell-скрипта вокруг gdb. Речь идет именно о Python, так как новые версии gbd фактически развернут для вас стек Python. Что-то вроде pyflame для бедных.
Профайлеры Ruby
Почти все эти профайлеры живут внутри вашего процесса
Прежде чем мы начнем разбираться в деталях этих профайлеров, есть одна очень важная вещь – все эти профайлеры, кроме pyflame, запускаются внутри вашего процесса Python/Ruby. Если вы находитесь внутри программы на Python/Ruby, у вас, как правило, имеется простой доступ к стеку. Например, вот простая программа на Python, которая печатает содержимое стека каждого работающего потока:
Вот вывод консоли. Вы видите, что в нем есть имена функций из стека, номера строк, имена файлов – все, что вам может понадобиться, если вы выполняете профилирование.
В Ruby даже проще: вы можете использовать puts caller, чтобы получить стек.
Большинство этих профайлеров являются расширениями С в плане производительности, поэтому они немного отличаются, но такие расширения для программ на Ruby/Python также имеют легкий доступ к стеку вызовов.
Как работают tracing-профайлеры
Я перечислила все tracing-профайлеры Ruby и Python в таблицах выше: rblineprof, ruby-prof, line_profiler и cProfile. Все они работают схожим образом. Они записывают каждый вызов функции и являются расширениями С для уменьшения оверхеда.
Как они работают? И в Ruby, и в Python вы можете указать коллбэк, который запускается, когда происходят различные события интерпретатора (например, «вызов функции» или «выполнение строки кода»). Когда вызывается коллбэк, он записывает стек для последующего анализа.
Полезно видеть, где именно в коде находятся эти коллбэки, поэтому я сошлюсь на соответствующие строки кода на github.
Чем-то похоже на PyEval_SetTrace в Python, но в более гибкой форме – вы можете выбрать, о каких событиях вы хотите получать уведомления (например, «только вызовы функций»).
Недостатки tracing-профайлеров
Основным недостатком реализованных таким образом tracing-профайлеров является то, что они добавляют фиксированное количество кода для каждого выполняемого вызова функции/строки. Это может заставить вас принимать неправильные решения! Например, если у вас есть две реализации чего-либо — одна с большим количеством вызовов функций, а другая без, которые обычно выполняются за одинаковое время, то первая, с большим количеством вызовов функций, при профилировании будет казаться медленнее.
В документации cProfile сказано:
интерпретируемая природа Python добавляет столько накладных расходов во время выполнения, что детерминированное профилирование имеет тенденцию добавлять небольшую нагрузку на обработку в обычных приложениях
Большинство программ будет работать примерно вдвое медленнее, в то время как высокорекурсивные программы (такие как тест ряда Фибоначчи) будут работать в три раза медленнее.
Как в основном работают sampling-профайлеры: setitimer
Допустим, вы хотите получать снимок стека программы 50 раз в секунду. Сделать это можно следующим образом:
Причина, по которой stacksampler.py занимает всего 100 строк в Python, заключается в том, что при регистрации функции Python в качестве обработчика сигнала функция передается в текущий стек вашей программы. Поэтому обработчик сигналов stacksampler.py регистрируется очень просто:
Он просто извлекает стек из фрейма и увеличивает количество раз, которое конкретный стек просматривался. Так просто! Так круто!
Один ИНТЕРЕСНЫЙ недостаток профайлеров на основе setitimer – то, что таймеры вызывают сигналы! Сигналы иногда прерывают системные вызовы! Системные вызовы иногда занимают несколько миллисекунд! Если вы делаете снимки слишком часто, то можете заставить программу выполнять системные вызовы бесконечно долго!
Sampling-профайлеры, которые не используют setitimer
Существует несколько sampling-профайлеров, которые не используют setitimer :
Посты в блоге pyflame
Конечно же, все немного сложнее, чем я описала. Я не буду вдаваться в подробности, но Эван Клицке написал много хороших статей об этом в своем блоге:
На этом все на сегодня!
Есть много важных тонкостей, в которые я не вдавалась в этом посте – например, я в основном сказала, что vmprof и stacksampler – схожи (это не так — vmprof поддерживает профилирование строк и профилирование функций Python, написанных на C, что, я считаю, делает профайлер более сложным). Но у них есть некоторые одинаковые основные принципы, и поэтому я думаю, что сегодняшний обзор будет хорошей отправной точкой.
Общие сведения о профилировании
Профилировщик — это инструмент, который наблюдает за выполнением другого приложения. Профилировщик среды CLR — это библиотека DLL, содержащая функции, которые получают сообщения из среды CLR и отправляют сообщения в среду CLR с помощью API профилирования. Библиотека DLL профилировщика загружается средой CLR во время выполнения.
Традиционные средства профилирования основное внимание уделяют измерению выполнения приложения. То есть они измеряют время, затраченное на каждую функцию, или использование памяти приложением за период времени. API профилирования предназначен для более широкого класса диагностических средств, таких как служебные программы с покрытием кода и расширенные средства отладки. Сфера их применения — вся диагностика в природе. API профилирования не только измеряет, но также наблюдает за выполнением приложения. По этой причине API профилирования никогда не должен использоваться самим приложением, и выполнение приложения не должно ни зависеть от профилировщика, ни подвергаться его влиянию.
Для профилирования приложения среды CLR требуется дополнительная поддержка по сравнению с профилированием стандартно скомпилированного машинного кода. Это объясняется тем, что в среде CLR вводятся такие понятия, как домены приложений, сборка мусора, обработка управляемых исключений, JIT-компиляция кода (преобразование кода MSIL в машинный код) и другие аналогичные возможности. Механизмы традиционного профилирования не могут обнаруживать эти возможности или предоставлять полезные сведения о них. API профилирования эффективно предоставляет эти отсутствующие сведения с минимальным влиянием на производительность среды CLR и профилируемого приложения.
JIT-компиляция во время выполнения обеспечивает прекрасные возможности для профилирования. API профилирования позволяет профилировщику вносить изменения потока кода MSIL в памяти для подпрограммы перед ее JIT-компиляцией. Таким образом, профилировщик может динамически добавлять код инструментирования в определенные подпрограммы, требующие более глубокого анализа. Хотя такой подход возможен в обычных сценариях, его гораздо проще реализовать для среды CLR с помощью API профилирования.
API профилирования
Как правило, API профилирования используется для написания профилировщика кода, который является программой, отслеживающей выполнение управляемого приложения.
В одном процессе с профилируемым приложением должна запускаться только часть решения профилировщика, отвечающая за сбор данных. Весь анализ пользовательского интерфейса и данных должен выполняться в отдельном процессе.
На следующем рисунке показано, как библиотека DLL профилировщика взаимодействует с профилируемым приложением и средой CLR.
Интерфейсы уведомлений
ICorProfilerCallback и ICorProfilerCallback2 могут считаться интерфейсами уведомления. Эти интерфейсы состоят из таких методов, как класслоадстартед, класслоадфинишеди JITCompilationStarted. Каждый раз, когда среда CLR загружает или выгружает класс, компилирует функцию и т. д., она вызывает соответствующий метод в интерфейсе ICorProfilerCallback или ICorProfilerCallback2 профилировщика.
Например, профилировщик может измерять производительность кода с помощью двух функций уведомления: FunctionEnter2 и FunctionLeave2. Он просто устанавливает метки времени для каждого уведомления, собирает результаты и выводит список, в котором указывается, на какие функции было затрачено больше ресурсов ЦП или физического времени во время выполнения приложения.
Интерфейсы для извлечения сведений
Поддерживаемые функции
API профилирования извлекает сведения о следующих действиях и событиях, происходящих в среде CLR.
События запуска и завершения работы среды CLR.
События создания и завершения работы домена приложения.
События загрузки и выгрузки сборки.
События загрузки и выгрузки модуля.
События создания и удаления таблицы VTable COM.
События JIT-компиляции и пошагового выполнения кода.
События загрузки и выгрузки класса.
События создания и удаления потока.
События входа и выхода функции.
Переходы между выполнением управляемого и неуправляемого кода.
Переходы между различными контекстами среды выполнения.
Сведения о приостановках среды выполнения.
Сведения о действиях сборки мусора и кучи в памяти времени выполнения.
API профилирования можно вызывать из любого (неуправляемого) языка, совместимого с COM.
Этот API является эффективным с точки зрения потребления ресурсов ЦП и памяти. Профилирование не влечет за собой изменения профилируемого приложения, которые могут привести к недостоверным результатам.
API профилирования полезен для профилировщиков как с выборкой, так и без выборки. Профилировщик с выборкой проверяет профиль по регулярным тактовым импульсам, скажем, через 5 миллисекунд. Профилировщик без выборки уведомляется о событии синхронно с потоком, вызвавшим событие.
Неподдерживаемые функциональные возможности
API профилирования не поддерживает следующие функциональные возможности.
Неуправляемый код, который необходимо профилировать с помощью стандартных методов Win32. Однако профилировщик среды CLR включает события переходов для определения границ между управляемым и неуправляемым кодом.
Самоизменяющиеся приложения, которые изменяют собственный код приложения, которые изменяют собственный код, например в целях аспектно-ориентированного программирования.
Проверка привязок, поскольку API профилирования не предоставляет эти сведения. Среда CLR предоставляет существенную поддержку для проверки границ всего управляемого кода.
Удаленное профилирование, которое не поддерживается по следующим причинам.
Удаленное профилирование увеличивает время выполнения. При использовании интерфейсов профилирования необходимо минимизировать время выполнения, чтобы оно не слишком сильно сказывалось на результатах профилирования. Это особенно важно при мониторинге производительности. Тем не менее удаленное профилирование не является ограничением при использовании интерфейсов профилирования для мониторинга использования памяти или для получения сведений времени выполнения о кадрах стека, объектах и т. п.
Профилировщик кода среды CLR должен зарегистрировать один или несколько интерфейсов обратного вызова в среде выполнения на локальном компьютере, на котором выполняется профилируемое приложение. Это ограничивает возможность создания удаленного профилировщика кода.
Потоки уведомлений
Безопасность
Библиотека DLL профилировщика — это неуправляемая библиотека DLL, которая выполняется в рамках подсистемы выполнения среды CLR. В результате на код в библиотеке DLL профилировщика DLL не налагаются ограничения управления доступом для управляемого кода. Для библиотеки DLL профилировщика действуют только ограничения, накладываемые операционной системой на пользователя, запускающего профилируемое приложение.
Разработчики профилировщика должны принять соответствующие меры предосторожности, чтобы избежать проблем, связанных с безопасностью. Например, во время установки библиотека DLL профилировщика должна добавляться в список управления доступом (ACL), чтобы злоумышленник не мог изменить ее.
Объединение управляемого и неуправляемого кода в коде профилировщика
Неправильно написанный профилировщик может вызвать циклические ссылки на себя, что приводит к непредсказуемому поведению.
Обзор API профилирования среды CLR может создать впечатление, что можно написать профилировщик, содержащий управляемые и неуправляемые компоненты, которые вызывают друг друга посредством COM-взаимодействия или непрямых вызовов.
Хотя это возможно с точки зрения проектирования, API профилирования не поддерживает управляемые компоненты. Профилировщик среды CLR должен быть полностью неуправляемым. Попытки объединить управляемый и неуправляемый код в профилировщике среды CLR могут привести к нарушениям прав доступа, сбоям программы или взаимоблокировкам. Управляемые компоненты профилировщика будут возвращать события обратно их неуправляемым компонентам, что будет затем вызывать управляемые компоненты снова, и таким образом будут создаваться циклические ссылки.
Кроме того, для изменения MSIL можно использовать старые методы инструментирования. Перед завершением JIT-компиляции функции профилировщик может вставить управляемые вызовы в текст MSIL метода, а затем выполнить JIT-компиляцию (см. метод ICorProfilerInfo:: GetILFunctionBody ). Этот способ можно успешно использовать для выборочного инструментирования управляемого кода или для сбора статистики и данных производительности касательно JIT.
Кроме того, профилировщик кода может вставлять собственные обработчики в текст MSIL любой управляемой функции, которая вызывает неуправляемый код. Этот способ можно использовать для инструментирования и покрытия. Например, профилировщик кода может вставить обработчики инструментирования после каждого блока MSIL для обеспечения выполнения блока. Изменение текста MSIL метода следует выполнять очень аккуратно и принимать во внимание множество факторов.
Профилирование неуправляемого кода
API профилирования среды CLR предоставляет минимальную поддержку профилирования неуправляемого кода. Предоставляются следующие функциональные возможности.
Перечисление цепочек стека. Эта возможность позволяет профилировщику кода определить границу между управляемым и неуправляемым кодом.
Определение, соответствует ли цепочка стека управляемому коду или машинному коду.
Использование модели COM
Стеки вызовов
API профилирования предоставляет два способа получения стеков вызова: метод моментальных снимков стека, который позволяет реже выполнять сбор стеков вызовов, и метод теневого стека, который отслеживает стек вызовов в каждый момент времени.
Моментальный снимок стека
Моментальный снимок стека — это трассировка стека потока в момент времени. API профилирования поддерживает трассировку управляемых функций в стеке, но оставляет трассировку неуправляемых функций собственному обходчику стека профилировщика.
Теневой стек
Теневой стек может получать аргументы функций, возвращать значения и сведения об универсальных экземплярах. Эти сведения доступны только посредством теневого стека и могут быть получены, когда управление передается в функцию. Однако эти сведения могут оказаться недоступны позднее, во время выполнения функции.
Обратные вызовы и глубина стека вызовов
Обратные вызовы профилировщика могут осуществляться в условиях очень ограниченного стека, и переполнение стека в обратном вызове профилировщика приведет к немедленному завершению выполнения процесса. В ответ на обратные вызовы профилировщик должен гарантированно использовать минимально возможный стек. Если профилировщик предназначен для использования в процессах, устойчивых к переполнению стека, сам профилировщик должен также избегать активации переполнения стека.