Разработка / Применение AOP для отладки чужой библиотеки

в 14:05, , рубрики: aop, aspectj, debug, отладка, трассировка, метки: , , , ,

На Хабре уже поднималась тема аспектно-ориентированного программирования (Aspect-oriented programming, AOP). Этой парадигме уже не так мало лет, но ее использование отнюдь не повсеместно. В комментариях регулярно обсуждается, а нужна ли она вообще или для каких целей всё же выгодно ее использовать.

В этой статье я хочу описать один из примеров применения — реальную ситуацию, где AOP меня выручило. Притом, я просто не знаю приемлемого способа решения без использования аспектов.

Приходилось ли вам когда-нибудь сталкиваться ситуацией, когда не работает связка вашего кода со сторонней библиотекой или фреймворком. И есть смутное подозрение, что ошибка где-то у вас, но без отладчика ее найти тяжело. А потом вдруг оказывается, что у библиотеки нет исходников, или по какой-то другой причине отладка не доступна…

Если вам интересно, что можно сделать в таких обстоятельствах, добро пожаловать под кат.

Проблема

Недавно при работе над своим open-source проектом, я столкнулся с похожей проблемой при отладке autocompletion в IDE на основе Eclipse DLTK (Dynamic Languages ToolKit).

Попробовал воспользоваться отладчиком(все исходники, благо, были), но к сожалению, ошибка, которую мне предстояло отловить спряталась в середине кода по работе с UI. И там такая особенность, что окошко автокомплита автоматически закрывается при потере фокуса, то есть любые брейкпоинты в том коде и пошаговая отладка были исключены.

Что можно в таких случаях сделать обычно? Можно воспользоваться трассировкой. Есть несколько общеизвестных способов, начиная от отладочного вывода, заканчивая полным профилированием. Перечень этих способов с кратким сравнением можно найти тут. В этой статье упомянуто и аспектно-ориентированное программирование (я бы даже сказал, что там ему незаслуженно приписали один минус).

Так вот, простой отладочный вывод требовал бы перекомпиляции не только DLTK, но и всего JFace; прокси не подходили из-за наличия приватных полей и методов; а полная профилировка такой тяжелой платформы как эклипс — нерадостная перспектива.

Вот так я и пришел к необходимости использовать AOP.

Решение

Тут всё оказалось просто — я создал один-единственный аспект, в котором описано, вызовы каких методов перехватывать, и какую информацию выводить в консоль.

Я не хочу описывать инструкцию по установке и основы синтаксиса. Для AspectJ и PostSharp это введение уже есть на Хабре. В моём же случае, использование AOP в OSGi среде — тема для отдельной статьи в более тематический блог.

Отмечу лишь, что подключить аспект удалось без какого либо вмешательства в исходный код основных модулей проект или библиотек платформы. Не потребовалась даже их пересборка(как утверждала статья по ссылке выше), так как использовалось динамическое вплетение (runtime weaving).

Напоследок, хочу еще привести полезный пример на AspectJ, который по сути был моим шаблоном для описания правил перехвата.

Трассировка всех вызовов внутри вызова метода Foo.bar:

public aspect TraceAspect {   before() : cflowbelow(execution(void Foo.bar(..))) && 		execution(* *.*(..)) && !within(TraceAspect){ 	 	try { 		System.out.println(thisJoinPoint.getSourceLocation()); 	} catch (Exception e) { 	}   } } 

Отсечь слишком низкоуровневые вызовы можно (и нужно) с помощью отрицания cflowbelow, но это уже должно делаться отдельно для каждого конкретного случая.

Возможности по фильтровке необходимых вызовов у AOP очень впечатляющие. Единственное, что меня смущает в описанном выше — это необходимость перезапускать приложение для введение в действие дополнительных хуков.

Выводы

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

Но сейчас я даже подумываю о том, как бы инкапсулировать всю эту мощь в небольшую библиотечку для неинтерактивной отладки.

Так или иначе, описанный в статье способ помог мне локализовать проблему и, я надеюсь, может в будущем пригодиться и вам.

Автор: ENargit

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js