Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

Инструментирующий профайлер своими руками

Anna | 5.06.2014 | нет комментариев

Давно хотел написать примитивную утилиту, которая дозволила бы показать в каких способах код проводит основное время, какие объекты и в каком числе создает и оперативно включать трассировку посещения способа не модифицируя исходник.

Основные требования — простота, вероятность запуска в текстовом режиме и автономность от архитектуры.


В большинстве java машин существует Java Virtual Machine Tool Interface (JVM TI).
Но данный интерфейс полагает написание native модуля, следственно не рассматривался.

Остается вероятность инструментирования (intstrumentation) байткода.
Типовой пакет java.lang.instrument разрешает встрять в процесс загрузки байткода с вероятностью его метаморфозы на лету.

Выглядит это дальнейшим образом:
При старте java машины указывается ключ -javaagent:jar-file-name=parameters,
где jar-file-name — это полный путь к jar файлу агента, parameters — параметры передаваемые агенту.

В манифесте jar файла указывается класс содержащий способ

public static void premain(String args, Instrumentation instrumentation)

тот, что jvm и вызовет позже загрузки агента.
Детально про данный механизм дозволено прочитать тут.

Задача нашего агента состоит в изменении способов загружаемых классов таким образом,
что бы при входе и выходе из способа вызывались способы нашего класса TracerAgent:

    @SuppressWarnings("unused")
    public static void methodEnter(MethodInfo methodInfo) {
        methodCallProcessor.methodEnter(methodInfo);
    }
    @SuppressWarnings("unused")
    public static void methodExit() {
        methodCallProcessor.methodExit();
    }

Экземпляр класса MethodInfo содержит информацию о способе.
Необходимо где то беречь MethodInfo для всякого инструментированного способа.
Для этого для всякого класса создается shadow класс дальнейшего вида:

public class ShadowClassXXX {
	public static  MethodInfo m1 = agent.TracerAgent.getMethodInfo(1L); // MethodInfo первого способа
	/* .. */
	public static  MethodInfo m99 = agent.TracerAgent.getMethodInfo(99L); // MethodInfo последнего способа
}

От того что поля shadow класса статические, они инициализируются механически при загрузке. В предисловие всякого изменяемого способа добавляется код:

TracerAgent.methodEnter(ShadowClass.mXXX);

в конец

TracerAgent.methodExit();

Для модификации и создания байткода использована библиотека Javassist. Разрешает не сосредотачиваться на байткоде, а писать код модификаций на java.

Сейчас при входе и выходе из способа будет перехватываться управление и
передаваться соответствующим способам класса MethodCallProcessor.

MethodCallProcessor хранит стэк вызовов для всякого потока применяя ThreadLocal.
methodEnter сберегает в стэк MethodCallMarker со временем вызова и увеличивает счетчик вызовов.
Если вызов способа необходимо трассировать, то выводится стэк вызовов.
methodExit выбирает из стэка MethodCallMarker, и добавляет время проведенное в способе к его MethodInfo.

В конструкторе TracerAgent создается Thread и регистрируется в shutdownhook. Данный Thread будет запущен при shutdown sequence jvm и распечатает собранную статистику. Там же создается и запускается Thread периодического итога статистики.

Параметры:
-javaagent:<полный путь к jar-файлу агента>=p:regexp[#t:regexp][#d:number][#i:number]
p - инструментируемые классы (полное совпадение - match)
t - трассируемые способы (частичное совпадение - find)
d - ярус отладки 
i - период итога статистики в секундах (0 отключает периодический итог)

Пример запуска для кода из статьи:

java -version
java version "1.7.0_21"
Java(TM) SE Runtime Environment (build 1.7.0_21-b11)
Java HotSpot(TM) 64-Bit Server VM (build 23.21-b01, mixed mode)

java -XX:-UseSplitVerifier -javaagent:D:traceragenttargettraceragent-1.0-SNAPSHOT-complete.jar=p:examples.*#d:1#i:5

javassist еще не неизменно справляется с новым форматом байткода классфайлов версии 51.
ключ -XX:-UseSplitVerifier отключает новейший верификатор байткода, введенный в JVM7, подробности.

итог

[AGENT ] *** performance report ***

Top 5 time consuming methods (microseconds):
          965050  examples.data.CashAccountRow$masks.access$100(examples.data.CashAccountRow$masks)
          779944  examples.data.CashAccountRow.setAge(int)
          766023  examples.data.CashAccountRow.setGender(int)
          764292  examples.data.CashAccountRow.setHeight(int)
          763387  examples.data.CashAccountRow.setAmount(int)

Top 5 called methods:
        22805261 examples.data.CashAccountRow$masks.access$100(examples.data.CashAccountRow$masks)
         9122242 examples.data.CashAccountRow$shifts.access$000(examples.data.CashAccountRow$shifts)
         2280592 examples.data.CashAccountRow.setAge(int)
         2280623 examples.data.CashAccountRow.setBitStorage(long)
         2280653 examples.data.CashAccountRow.setGender(int)

Top 5 objects constructed:
               1 examples.data.CashAccountStore
               1 examples.data.CashAccountRow

[AGENT-] *** end of performance report ***

--- skip ---

Number of records matched:38
Elapsed time:7707ms
Used Memory:91MB

[AGENT ] *** performance report ***

Top 10 time consuming methods (microseconds):
        36081913  examples.App.main(java.lang.String[])
        20606259  examples.data.CashAccountStore()
        15428502  examples.data.CashAccountStore.find2(examples.data.CashAccountStore$CashAccountFinder)
        12439241  examples.data.GenMatcherAMOUNTHEIGHTGENDER.c(examples.data.CashAccountRow)
         9469502  examples.data.CashAccountRow.getAmount()
         5928018  examples.data.CashAccountRow$masks.access$100(examples.data.CashAccountRow$masks)
         3392146  examples.data.CashAccountRow$shifts.access$000(examples.data.CashAccountRow$shifts)
         3279163  examples.data.CashAccountRow.setAge(int)
         3271959  examples.data.CashAccountRow.setHeight(int)
         3267890  examples.data.CashAccountRow.setAmount(int)

Top 10 called methods:
       140079808 examples.data.CashAccountRow$masks.access$100(examples.data.CashAccountRow$masks)
        80079812 examples.data.CashAccountRow$shifts.access$000(examples.data.CashAccountRow$shifts)
        40000000 examples.data.CashAccountRow.getAmount()
        30000000 examples.data.CashAccountRow.setBitStorage(long)
        20000000 examples.data.GenMatcherAMOUNTHEIGHTGENDER.c(examples.data.CashAccountRow)
        10000000 examples.data.CashAccountRow.setGender(int)
        10000000 examples.data.CashAccountRow.setAge(int)
        10000000 examples.data.CashAccountRow.setAmount(int)
        10000000 examples.data.CashAccountRow.setHeight(int)
        10000000 examples.data.CashAccountRow.getBitStorage()

Top 10 objects constructed:
               3 examples.data.CashAccountStore$CashAccountFinder$FieldGetter
               3 examples.data.CashAccountStore$CashAccountFinder$PredicateHolder
               3 examples.data.CashAccountRow
               1 examples.data.GenMatcherAMOUNTHEIGHTGENDER
               1 examples.data.GenMatcherBase
               1 examples.data.CashAccountStore$CashAccountFinder$HeightFieldGetter
               1 examples.data.CashAccountStore$CashAccountFinder
               1 examples.data.CashAccountStore
               1 examples.data.CashAccountStore$CashAccountFinder$AmountFieldGetter
               1 examples.data.CashAccountStore$CashAccountFinder$GenderFieldGetter

[AGENT-] *** end of performance report ***

Безусловно вызов способов агента не мог не сказаться на быстродействии.
Как видно время работы увеличилось примерно в 500 раз.
Hо это экстремальный случай, где вызываются способы с фактически пустым телом, в обыкновенной жизни с оверхедом дозволено мириться.

Целью является не столько точный профайлинг по времени, сколько точное кол-во вызовов,
сделанных объектов и вероятность трассировки. Код доступен на GitHub.

PS. Не стоит рассматривать получившийся код как готовый профайлер торгового яруса. Это не больше чем эксперимент, раскрывающий еще один механизм работы JVM.

Источник: programmingmaster.ru

Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB