Обзор и применение отладочного интерфейса Java (JDI) для модификации ПО

Введение

 

В практическом программировании периодически возникают задачи, связанные с необходимостью модификации программного обеспечения при условии отсутствия доступа к его исходным кодам (например, в случае утраты исходных кодов приложения, необходимости доработки стороннего программного обеспечения (добавление в сторонние продукты поддержки сертифицированных в России криптоалгоритмов, реализованных в сторонних криптопровайдерах, например «Крипто-Про JCP»).
Автором предлагается для модификации ПО использовать отладочный интерфейс (в данной статье рассмотрена платформа Java и Java Debug Interface (JDI)): устанавливать в некоторые места точки останова и ждать их активации (по активации точек останова анализировать состояние внутренних структур данных модифицируемого приложения и выполнять действия на основании полученных данных).
В статье описывается, как можно провести анализ модифицируемого приложения (реверс инжиниринг), дается небольшой обзор JDI и общие рекомендации по созданию модифицирующего приложения-отладчика. В конце стать есть пример.

Схема


Рисунок 1. Общая архитектура программного комплекса

На рис.1 представлена общая архитектура программного комплекса, состоящего из отлаживаемого модифицируемого приложения, которое необходимо запускать с ключом JVM, активирующим отладку, и разработанного приложения, которое подключается к целевому приложению в качестве отладчика, устанавливает необходимые точки останова и ожидает их активации.
В первую очередь для модификации ПО необходимо провести анализ модифицируемого приложения (реверс-инжиниринг) и выявить места, где необходима и возможна установка точек останова. Это описано в разделе «Анализ модифицируемого приложения». Далее необходимо создать приложение, которое через отладочный интерфейс: создает необходимые точки останова, следящее за их состоянием и анализирующее внутренние структуры данных модифицируемого приложения при их активации. Этот этап описан в разделе «Создание приложения-отладчика». Замечания по полученной системе, ее компонентах и их развертывании описаны в разделе «Развертывание модифицированного приложения».

 

Анализ модифицируемого приложения

Получение примерных исходных кодов модифицируемого приложения

Для начала анализа модифицируемого приложения необходимо получить его бинарные jar-файлы с основной логикой его работы (обычно находятся в папке, куда устанавливается модифицируемое приложение). Далее для простоты описания предполагается, что вся логика модифицируемого приложения содержится в одном jar-файле.
Первоначально необходимо получить примерные исходные коды приложения, для этого следует распаковать jar-архив как архив zip. В результате этого действия в целевой директории (куда происходила распаковка jar-архива) окажется откомпилированный байт-код Java, сгруппированный в class-файлы, находящиеся в каталогах, соответствующих пакету класса.


Для класса a.b.c.D – файл .\a\b\c\D.class

Для последующего анализа целесообразно получить примерные исходные коды приложения. При этом следует иметь в виду, что декомпиляторы для Java не могут полностью восстановить исходные коды реальных приложений. Полностью восстановить исходные коды возможно лишь для примитивных примеров. Для восстановления исходных кодов по откомпилированному байт-коду, находящемуся в class-файлах, автор использует декомпилятор JAD, распространяемый как свободный для некоммерческого использования. JAD запускается командой вида:

jad -o -r -sjava -dsrc tree/**/*.class


tree/**/*.class – путь к скомпилированным class-файлам, распакованным из jar-архива приложения.
По выполнению данной команды в каталоге src будут находиться декомпилированные исходные коды классов модифицируемого приложения.

Нумерация строк в восстановленных исходных кодах отличается от реальной. Для установки точки останова по линии необходимо обратиться к ассемблерному листингу class файла.

jad не совсем корректно определяет try…catch блоки обработки исключений.

Анализ исходных кодов

Следующие 2 пункта являются итерационными действиями. Т.е., если по их выполнении не достигнута цель, необходимо повторить их выполнение.

Затем необходимо провести анализ исходных кодов, чтобы найти возможные участки кода, обрабатывающие события, факт которых необходимо логировать (это можно сделать, анализируя названия классов/методов/логику методов/используемые константы/…). Для этого в среде программирования можно создать пустой проект и поместить туда полученный исходный код. Несмотря на то, что проект не скомпилируется и среда сообщит о множестве ошибок, можно будет пользоваться навигацией по коду.

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

Проверка положения точек останова

Для уточнения и проверки мест, при выполнении которых будут активироваться точки останова, необходимо воспользоваться отладочными функциями платформы Java. Необходимо запустить виртуальную машину Java (далее JVM – Java Virtual Machine), которая выполняет модифицируемое приложение в отладочном режиме. Для этого необходимо скорректировать (добавить) параметры, передающиеся приложению JVM при старте (параметры командной строки). Обычно java-приложение запускается строчкой вида Java –jar ПутьКJAR.jar (для более серьезных программных продуктов используются собственные «запускальщики», в конфигурационных файлах которых содержатся параметры запуска JVM), к которой необходимо добавить параметры

-Xdebug -Xnoagent -Xrunjdwp:transport=dt_socket,address=8787,server=y,suspend=y

Теперь можно открыть среду программирования на Java (например, NetBeans) и начать удаленную отладку приложения: среда подключится к модифицируемому приложению в качестве отладчика, что даст возможность уточнить места модифицируемой функциональности, в которые необходимо устанавливать точки останова. В первом приближении для более точного определения участка, обрабатывающего необходимое событие, автор рекомендует ставить точки останова (Breakpoint) для всех методов класса (флажок All Methods), полученного в ходе анализа декомпилированных исходных кодов. Также рекомендуется установить реакцию на точку останова – продолжение выполнения.

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

  • локальных переменных, то информация о локальных переменных будет недоступна

Стоит обратить внимание на поле ввода “Print Text” свойств точек останова – во время активации точки останова на панель Output будет выводиться сообщение из этого поля ввода. В это поле ввода можно добавить вывод значений полей или выполнения методов. Вычисляемые значения обрамляются символами “{=” и “}” в начале и конце соответственно. Пример: Break point active: {=SomeField}, {=toString()}, {=SomeMethod()}

Далее следует проанализировать результат, выведенный в панели Output. В нем необходимо найти классы и методы, вызываемые при возникновении ожидаемого события. В результате этого будет получен список методов классов, пригодных для обнаружения факта возникновения события.

Для проверки пригодности полученных методов классов для обнаружения события можно провести проверку: создать точки останова только для полученных методов классов и запустить приложение на выполнение. В логах точек останова (на панели Output) должны появляться сообщения об активации точек останова одновременно с возникающим событием. В результате выполнения данных итерационных действий будет получен один/несколько целевых методов классов, вызов которых происходит при обработке события. После прохождения первой итерации, возможно, полное понимание, изменение каких внутренних структур модифицируемого приложения необходимо отслеживать не появится. Необходимо вернуться к анализу примерного исходного кода модифицируемого приложения для уточнения.

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

Необходимо установить точки останова на методы классов, полученных во время предыдущей итерации. В качестве реакции на активацию точки останова необходимо установить остановку потока/всего приложения. По активации точки останова (возникновении события) необходимо перейти на панель Variables (переменные) и просмотреть значения локальных переменных. Также рекомендуется еще раз обратиться к примерным исходным кодам.

Можно рекомендовать изменить окно стека и проанализировать поля/локальные переменные других окон стека.

Также возможна реализация такого механизма модификации: выявляется некий относительно простой (по объему кода) класс (возможно, предок какой-то иерархии), чей код достаточно читаем после декомпиляции. Необходимо полностью восстановить его, чтобы класс компилировался и добавить необходимый функционал (например, записи в файл). Откомпилированный класс, представляющий собой class файл можно будет на лету заменить при помощи метода redefineClasses(…) объекта, реализующего интерфейс VirtualMachine (подробнее рассмотрено в разделе «Выполнение методов в отлаживаемой JVM»).

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

Создание приложения-отладчика

Установление связи отладчика и отлаживаемого приложения

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

Автор использовал реализацию JDI от Sun, распространяемую вместе с Java Development Kit (JDK) в виде jar-архива tools.jar.

Для установки соединения c удаленной JVM используются классы com.sun.jdi.Bootstrap com.sun.jdi.VirtualMachineManager. В результате подключения к удаленной JVM будет получен объект, реализующий интерфейс com.sun.jdi.VirtualMachine (см. Листинг 1).


    1. VirtualMachineManager VMManager = Bootstrap.virtualMachineManager();

    1. for (AttachingConnector Connector : VMManager.attachingConnectors())

    1. {

    1.   if (Connector.transport().name().equals(“dt_socket”))

    1.   {

    1.     AttConnector = Connector;

    1.     break;

    1.   }

    1. }

    1. Map<String, Argument> connectorParameters = AttConnector.defaultArguments();

    1. connectorParameters.get("port").setValue(Port);

    1. connectorParameters.get("hostname").setValue(Host);

    1. VirtualMachine result = AttConnector.attach(connectorParameters);


Листинг 1. Подключение к отлаживаемой JVM


В документации JavaDoc описаны несколько методов подключения отладчика. Выше показано использование самого интересного варианта – подключение на лету (Just-In-Time Debugging). Стоит отметить, что подключение к отлаживаемой JVM возможно с помощью нескольких коннекторов:



    • dt_socket – подключение через сетевые сокеты;

    • dt_shmem – подключение через общий участок памяти



Выбор коннектора из списка возможных, возвращаемых методом attachingConnectors() экземпляра класса VirtualMachineManager, осуществляется по названию. Различные реализации JVM могут предоставлять разные коннекторы.

Описание параметров подключения для каждого коннектора представлены в Таблице 1.


Таблица 1. Коннекторы и их параметры













Коннектор dt_socket dt_shmem
Параметры

    • host – адрес (символьный или IP) компьютера, на котором запущено отлаживаемое приложение

    • port – порт, который слушает отлаживаемое приложение

    • timeout – время ожидания ответа




    • name – имя общего участка памяти

    • timeout – таймаут ожидания ответа


Пример, приведенный в Листинге 1, иллюстрирует подключение через коннектор dt_socket. Данный коннектор позволяет отлаживать приложения не только в рамках локального компьютера, но и удаленно (по сети).

Типы точек останова, поддерживаемые JVM

Интерфейс JDI поддерживает типы точек останова, представленные в Таблице 2. Все точки останова унаследованы от интерфейса EventRequest.


Таблица 2. Типы точек останова













































































Тип точки останова Активируется по
AccessWatchpointRequest доступу к полю класса
BreakpointRequest выполнению кода
ClassPrepareRequest загрузке нового класса
ClassUnloadRequest выгрузке класса
ExceptionRequest возникновению исключения в отлаживаемой JVM
MethodEntryRequest началу выполнения метода
MethodExitRequest окончанию выполнения метода
ModificationWatchpointRequest модификации значения поля
StepRequest пошаговому выполнению
ThreadDeathRequest остановке потока
ThreadStartRequest созданию потока
VMDeathRequest остановке отлаживаемой JVM

Установку точки останова, активирующейся по началу выполнения определенного кода (в данном случае – в начале метода), можно провести с помощью следующего кода:


    1. EventRequestManager Manager = JVM.eventRequestManager();

    1. ReferenceType RefType = JVM.classesByName("Целевой.Класс").get(0);

    1. Method RefMethod = RefType.methodsByName("ЦелевойМетод").get(0);

    1. BreakpointRequest Request = Manager.createBreakpointRequest(RefMethod.location());

    1. Request.setSuspendPolicy(BreakpointRequest.SUSPEND_EVENT_THREAD);

    1. Request.enable();


Листинг 2. Создание и «включение» точки останова

Пояснения к коду: первоначально создается менеджер точек останова (объект Manager). Далее происходит получение объектов с интерфейсами ReferenceType и Method, описывающих, соответственно, целевой класс и метод, находящиеся на удаленной JVM. Интерфейс Method унаследован от интерфейса Locatable, в результате чего можно получить точное местоположение метода в выполняемом байт-коде и создать в этом месте точку останова. Далее создается точка останова (объект Request типа BreakpointRequest), активирующаяся при выполнении каким-либо потоком приложения целевого метода (точнее, его начала, на которое указывает метод location()).

При создании точки останова можно изменить политику остановки потоков при активации точки останова с помощью метода setSuspendPolicy(int policy). На данный момент в JDI возможно 3 значения:



    • SUSPEND_ALL – останавливать все потоки при активации точки останова

    • SUSPEND_EVENT_THREAD – останавливать только поток, активировавший точку останова

    • SUSPEND_NONE – не останавливать потоки



Главный цикл обработки событий активации точек останова

После создания всех необходимых точек останова и их «включения» (методом enable()) необходимо перейти к ожиданию активации точек останова. Если необходимые классы не были загружены на момент старта JVM и создания точек останова, можно создать дополнительную точку останова, активирующуюся по загрузке класса, и следить за моментом загрузки этого класса (см. полный текст программы в приложении).

После создания всех точек останова можно возобновить выполнение JVM, прерванное подключением отладчика, с помощью метода JVM.resume().

Для обработки активации точек останова необходимо получить объект типа EventQueue с помощью метода JVM.eventQueue(). Этот объект позволяет получить итератор событий активации точек останова (объектов с интерфейсом Event). Реализацию данных действий иллюстрирует листинг, приведенный ниже:


    1. EventQueue  Events = JVM.eventQueue();

    1. while(true)

    1. {

    1.   EventSet ProcessedEvents = Events.remove();

    1.   try

    1.   {

    1.     EventIterator ItEvents = ProcessedEvents.eventIterator();

    1.     while (ItEvents.hasNext())

    1.     {

    1.       Event NextEvent = ItEvents.nextEvent();

    1.       ...

    1.     }

    1.   } finally {

    1.     ProcessedEvents.resume();

    1.   }

    1. }


Листинг 3. Обработка очереди активированных точек останова

После обработки всех событий следует вызвать метод resume(), возвращающий управление в отлаживаемую JVM.

Обработка событий активации точек останова

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


    1. if (NextEvent instanceof BreakpointEvent)

    1. {

    1.   BreakpointEvent Event = (BreakpoinEvent)NextEvent;

    1.   StackFrame CurrentMethodContext = Event.thread().frame(0);

    1.   ObjectReference SomeObject = CurrentMethodContext.thisObject();

    1.   Field SomeFldRef = SomeObject.referenceType().fieldByName(FieldName);

    1.   StringReference FieldValue =

    1.     (StringReference)SomeObject.getValue(SomeFldRef);

    1.   System.out.println(FieldValue.value());

    1. }

    1. else if (NextEvent instanceof ClassPrepareEvent)

    1. {

    1.   //обработка загрузки класса

    1. }


Листинг 4. Обработка события активации точки останова

В данном листинге стоит обратить внимание на получение объекта this из верхнего окна стека и получение полей объекта this. У событий активации точек останова, реализующих интерфейс LocatableEvent (события о доступе/модификации поля, пошаговом выполнении, выполнении определенного кода и т.д.), есть возможность получить объект потока отлаживаемой JVM, в котором произошла активация точки останова. Данный объект предоставляет крайне широкие возможности: обход окон стека (получение объекта this, локальных переменных и полей объекта в выбранном окне стека), выполнение методов в отлаживаемой JVM.

Выполнение методов в отлаживаемой JVM

Для выполнения метода объекта необходимо первоначально получить объект, описывающий необходимый метод (как Method из reflection)(в данном примере получение метода упрощено). Для передачи параметров выполняемому методу передается массив объектов типа Value. Интерфейс Value представляет собой ссылку на конкретное значение в отлаживаемой JVM. Маршалинг из JVM отладчика в отлаживаемую JVM значений примитивных типов осуществляется с помощью перегруженных методов mirrorOf() интерфейса VirtualMachine, показанных в [1]. Таким же образом можно провести маршалинг строк (точнее, объектов типа java.lang.String). Таблица 3. Методы для маршалинга примитивных типов в отлаживаемую JVM













































Возвращаемое значение Сигнатура метода
BooleanValue mirrorOf(boolean value)
ByteValue mirrorOf(byte value)
CharValue mirrorOf(char value)
DoubleValue mirrorOf(double value)
FloatValue mirrorOf(float value)
IntegerValue mirrorOf(int value)
LongValue mirrorOf(long value)
ShortValue mirrorOf(short value)
StringReference mirrorOf(String value)


    1. static public Value InvokeMethod(ThreadReference Context,

    1.   ObjectReference Object, String MethodName, Value[] Params)

    1. {

    1.   Method CallMethod = Object.referenceType().methodsByName(MethodName).get(0);

    1.   List<Value> ParamsList = new ArrayList<Value>();

    1.   result = Object.invokeMethod(

    1.                        Context,

    1.                        CallMethod,

    1.                        Arrays.asList(Params),

    1.                        ObjectReference.INVOKE_SINGLE_THREADED

    1.                    );

    1.  

    1.   return result;

    1. }


Листинг 5. Выполнение метода в отлаживаемой JVM

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

Еще одной чрезвычайно интересной возможностью JDI является переопределение байт-кода класса на лету, во время выполнения. Доступность данной возможности можно определить с помощью метода canRedefineClasses() интерфейса VirtualMachine. Переопределение байт-кода класса производится с помощью метода redefineClasses(Map<? extends ReferenceType,byte[]> classToBytes) интерфейса VirtualMachine. Данный метод будет успешно выполнен и модификация классов осуществится только если метод canRedefineClasses() вернул истину, т.е. отлаживаемая JVM поддерживает динамическое переопределение классов.

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

Развертывание модифицированного приложения

Для развертывания модифицированного приложения рекомендуется создать bat-файл, запускающий JVM в отладочном режиме (или дописать необходимые параметры в конфиг «стартера» приложения), вида:

Java -Xdebug -Xnoagent -Xrunjdwp:transport=dt_socket,address=8787,server=y,suspend=y –jar ПутьКJARПриложения.jar

Особо стоит обратить внимание на флаг suspend (ожидание подключение отладчика), установленный в “y” (сокр. от “yes”). Это сделано сознательно, с целью предотвратить запуск приложения отдельно от приложения-отладчика, реализованного в виде стороннего приложения.

После выполнения данной команды ОС загрузит JVM, которая будет ожидать подключения отладчика на порт 8787. После подключениия отладчика выполнение jar-файла будет продолжено.

Пример работы с JDI

Для запуска примера необходимо:



    1. Скомпилировать отлаживаемое приложение.

    1. Запустить отлаживаемое приложение командой вида:
      Java -Xdebug -Xnoagent -Xrunjdwp:transport=dt_socket,address=8787,server=y,suspend=n –jar ПутьКJARПриложения.jar

    1. Скомпилировать приложение-отладчик.

    1. Запустить приложение-отладчик командой вида:
      Java –jar ПутьКJARПриложения.jar



Пример приложения-отладчик


    1. package Jdi;

    1.  

    1. import com.sun.jdi.*;

    1. import com.sun.jdi.event.*;

    1. import com.sun.jdi.connect.AttachingConnector;

    1. import com.sun.jdi.connect.Connector.Argument;

    1. import com.sun.jdi.connect.IllegalConnectorArgumentsException;

    1. import com.sun.jdi.request.BreakpointRequest;

    1. import com.sun.jdi.request.EventRequestManager;

    1. import java.util.Map;

    1. import java.io.IOException;

    1.  

    1. /**

    1.  *

    1.  * @author Sergey Melnikov

    1.  */

    1. public class HelloJDI

    1.   private static final String TRANSPORT = "dt_socket";

    1.   private static final String ClassName = "Jdi.Main";

    1.   private static final String MethodName = "PrintFieldValue";

    1.   private static final String FieldName = "_someField";

    1.  

    1.  

    1.   private VirtualMachine JVM;

    1.  

    1.   // создаем точку останова для класса ClassName, метода MethodName

    1.   private BreakpointRequest CreateBreakpointRequest()

    1.   {

    1.       // получаем менеджер точек останова

    1.       EventRequestManager Manager = JVM.eventRequestManager();

    1.  

    1.       // получаем типы

    1.       ReferenceType RefType = JVM.classesByName(ClassName).get(0);

    1.       Method RefMethod = RefType.methodsByName(MethodName).get(0);

    1.  

    1.       //создаем точку останова

    1.       BreakpointRequest Request =

    1.           Manager.createBreakpointRequest(RefMethod.location());

    1.       Request.setSuspendPolicy(BreakpointRequest.SUSPEND_EVENT_THREAD);

    1.  

    1.       return Request;

    1.   }

    1.  

    1.   // подключаемся к удаленной JVM, ожидающей подключения отладчика

    1.   private void ConnectToJVM(String Host, String Port)

    1.     throws IOException, IllegalConnectorArgumentsException

    1.   {

    1.       VirtualMachineManager JVMManager = Bootstrap.virtualMachineManager();

    1.  

    1.       AttachingConnector AttConnector = null;

    1.       for (AttachingConnector Connector : JVMManager.attachingConnectors())

    1.       {

    1.         if (Connector.transport() !=

    1.           null && Connector.transport().name().equals(TRANSPORT))

    1.         {

    1.           AttConnector = Connector;

    1.           break;

    1.         }

    1.       }

    1.  

    1.       // заполняем параметры

    1.       Map<String, Argument> ConnectionParams =

    1.         AttConnector.defaultArguments();

    1.       if (ConnectionParams.containsKey("port"))

    1.           ConnectionParams.get("port").setValue(Port);

    1.       if (ConnectionParams.containsKey("hostname"))

    1.           ConnectionParams.get("hostname").setValue(Host);

    1.       if (ConnectionParams.containsKey("timeout"))

    1.           ConnectionParams.get("timeout").setValue("10000");

    1.  

    1.       JVM = AttConnector.attach(ConnectionParams);

    1.   }

    1.  

    1.   public HelloJDI()

    1.   {

    1.   }

    1.  

    1.   public void Process () throws Exception

    1.   {

    1.     // подключаемся к JVM

    1.     ConnectToJVM("127.0.0.1", "8787");

    1.     //создаем и активируем точку останова

    1.     CreateBreakpointRequest().enable();

    1.  

    1.     // возобновляем выполнение в JVM

    1.     JVM.resume();

    1.  

    1.     int i = 0;

    1.     // получаем очередь

    1.     EventQueue  Events = JVM.eventQueue();

    1.     while (true)

    1.     {

    1.       EventSet ProcessedEvents = Events.remove();

    1.       try

    1.       {

    1.         // разбираем очередь

    1.         EventIterator ItEvents = ProcessedEvents.eventIterator();

    1.         while (ItEvents.hasNext())

    1.         {

    1.           Event NextEvent = ItEvents.nextEvent();

    1.           BreakpointEvent MyEvent = (BreakpointEvent) NextEvent;

    1.  

    1.           // получаем окно стека, объект this и поле FieldName

    1.           StackFrame CurrentMethodContext = MyEvent.thread().frame(0);

    1.           ObjectReference SomeObject = CurrentMethodContext.thisObject();

    1.           Field SomeFldRef =

    1.             SomeObject.referenceType().fieldByName(FieldName);

    1.           //получаем и выводим значение поля

    1.           StringReference FieldValue =

    1.             (StringReference)SomeObject.getValue(SomeFldRef);

    1.           System.out.println(FieldValue.value());

    1.  

    1.           SomeObject.setValue(SomeFldRef, JVM.mirrorOf(String.valueOf(i)));

    1.  

    1.           ++i;

    1.         }

    1.       }

    1.       finally

    1.       {

    1.         ProcessedEvents.resume();

    1.       }

    1.     }

    1.  

    1.   }

    1.  

    1.   public static void main(String[] Args) throws Exception

    1.   {

    1.     HelloJDI Hello = new HelloJDI();

    1.     Hello.Process();

    1.   }

    1. }

    1.  

Тестовое модифицируемое приложение


    1. package Jdi;

    1.  

    1. /**

    1.  *

    1.  * @author Sergey Melnikov

    1.  */

    1. public class Main

    1. {

    1.  

    1.     private String _someField = "text";

    1.  

    1.     public void PrintFieldValue()

    1.     {

    1.         System.out.println(_someField);

    1.     }

    1.  

    1.     /**

    1.      * @param args the command line arguments

    1.      */

    1.     public static void main(String[] args) throws InterruptedException

    1.     {

    1.         // TODO code application logic here

    1.  

    1.         Main obj = new Main();

    1.         while(true)

    1.         {

    1.             obj.PrintFieldValue();

    1.             Thread.currentThread().sleep(1000);

    1.         }

    1.     }

    1.  

    1. }

Список литературы



    1. JavaTM Debug Interface [Электронный ресурс] : Java SE Technical Documentation. URL: http://download.oracle.com/javase/1.5.0/docs/guide/jpda/jdi/

    1. JPDA Enhancements [Электронный ресурс] : Java SE Technical Documentation. URL: http://download.oracle.com/javase/1.5.0/docs/guide/jpda/enhancements.html

    1. JavaTM Native Interface (JNI) [Электронный ресурс] : Java SE Technical Documentation. URL: http://download.oracle.com/javase/1.5.0/docs/guide/jpda/jdi/

    1. Java Art Chapter 3. Tracing with JPDA [Электронный ресурс] URL: http://fivedots.coe.psu.ac.th/~ad/jg/javaArt3/traceJPDA.pdf

    1. Java Virtual Machine Specification, The class File Format [Электронный ресурс] URL: http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html
For more complete information about compiler optimizations, see our Optimization Notice.