Навеяно постом http://elizarov.livejournal.com/29052.html – там описывается, как включить опцию дизассемблирования кода скомпилированных методов. Для этого нужно а) найти плагин hsdis и засунуть его в bin-директорию jdk б) запустить приложение со специальными опциями. От себя ещё добавлю пункт в) добиться того, чтобы метод, код которого хочется получить, был скомпилирован JIT-компилятором, а не просто интерпретирован.
Разбираемся с плагином
В посте лежит ссылка на DLL с hsdis плагином, но только для 32-битных систем. У меня 64-битная Windows 8, поэтому пришлось собирать самому. Делал по инструкции с сайта http://dropzone.nfshost.com/hsdis.htm, всё получилось (правда в первые разы почему-то сборка не прошла до конца по причине отсутствия команды gcc, но я доставил в cygwin пакет с gcc-core и всё собралось). Готовые файлы прилагаю: hsdis-amd64.dll и hsdis-i386.dll
Разбираемся с опциями
Вот такую портянку я использовал
-XX:CompileThreshold=1 -XX:+UnlockDiagnosticVMOptions -XX:CompileCommand=print,*Program.testMethod -XX:PrintAssemblyOptions=intel -XX:+PrintCompilation -XX:+LogCompilation
-XX:CompileThreshold=1 - чтобы JIT-компилятор компилировал методы после одного вызова
-XX:+UnlockDiagnosticVMOptions - нужная опция, чтобы остальные -XX-опции распознавались
-XX:CompileCommand=print,*Program.testMethod - указываем, что нам нужен дизассемблированный листинг метода testMethod класса Program. Зачем тут звездочка, я не понял, может быть потом разберусь
-XX:PrintAssemblyOptions=intel - устанавливает синтаксис, в котором будет выведен дизассемблированный код. Интеловский синтаксис более удобочитаем, поскольку вокруг регистров отсутствуют проценты %eax%
-XX:+PrintCompilation - выводить список методов, которые скомпилированы в нативный код - для отладки того, что наш искомый метод будет скомпилирован
-XX:+LogCompilation - вывести лог компиляции в файл hotspot.log (по умолчанию)
Разбираемся с компиляцией методов
Наблюдая за тем, как работает тестовая программа, я вывел следующие эмпирические заключения. За их корректность не ручаюсь, но судя по экспериментам, дело обстоит именно так. Методы компилируются после выхода из них и компилируются асинхронно, то есть кладутся в некую очередь для JIT-компилятора, и он потом их компилирует. Таким образом, если вы хотите посмотреть листинг метода main(), то навряд ли это у вас получится, потому что сразу же после выхода из main() программа завершится, и JIT-компилятор не успеет её скомпилировать. Я ставил задержки Thread.sleep(1000) после вызова нужного метода, и рантайм успевал скомпилировать его и вывести листинг в лог. Если же вас интересует именно main, то придётся вынести его тело в другой метод, и вызвать его из основного main, добавив после вызова задержку по времени.
Предварительно необходимо
Рутованный телефон с установленным BusyBox и один из SSH серверов (я использую SSH/SFTP Daemon).
Алгоритм
Скачиваем архив с собранным под ARM бинарником, запускаем SSH сервер на телефоне, подключаемся к нему через Putty, разблокируем файловую систему /system для записи:
su -
busybox mount -o remount,rw /system
Подключаемся через WinSCP, раскидываем файлы так, как они размещены в архиве.
Добавляем +x на файлы /data/xbin/* :
chmod +x /data/xbin/*
Также устанавливаем права тут (хотя вроде и без этого работает):
busybox chmod -R 0755 /system/etc/terminfo
busybox chmod -R 0644 /system/etc/terminfo/?/*
После этого проверяем, что /data/xbin/mc стартует в сеансе Putty, и возвращаем read-only на /system:
busybox mount -o remount,ro /system
Результат
Вполне рабочий mc с работающим вьювером (редактор глючит, видимо надо что-то пофиксить в конфигах или добавить). Остальные плагины пока не смотрел.
Потокобезопасный вариант остановки потока по требованию. Паттерн, который мы повсеместно применяли в .net. В java-мире аналогичное поведение можно более просто реализовать с помощью механизма interruptions, но этот способ более гибок, поскольку во-первых не зависит от тонкостей обработки InterruptException (а это довольно хитрая штука), а во-вторых, более расширяем – например, по сигналу в condition object можно не только завершать выполнение потока, но и передавать какие-то события внутрь потока – например, некоторое действие нужно выполнять либо по таймеру, либо по сигналу. И мы внутри функции потока всегда будем делать нужные операции сразу же по мере необходимости. В отличие от традиционного механизма wait-notify (которое не дает инфы о причине завершения ожидания – таймауте или внешнем сигнале), в этом API мы всегда увидим, когда прошел таймаут. Мы не всегда сможем определить, был ли вызван signal() из-за одного пограничного случая (если и эта информация нужна, то можно её получить просто добавив флаг), но факт прохождения таймаута мы будем знать достоверно.
private ReentrantLock reentrantLock = new ReentrantLock( ); private Condition condition= reentrantLock.newCondition(); private volatile boolean running = false; private static final long PERIOD_MILLISECONDS = 5000; private Thread calculationThread = new Thread( new Runnable() { public void run() { log.info( "Calculation thread started." ); for (;;) { // todo : здесь собственно идёт полезная работа потока reentrantLock.lock(); try { boolean awaitResult = false; // в этом месте мы проверяем running для ситуации, когда stopThread() // был вызван в момент, когда этот поток не находился в ожидании, и вызов // signal() не привел ни к чему if ( !running ) break; try { // При вызове await() занятая нами блокировка будет освобождена // Но при возврате управления метод await() снова должен будет взять блокировку // Интересный механизм, позволяющий нам точно знать, в какой последовательности // будут выполнены инструкции после вызова signal() и собственно пробуждение потока awaitResult = condition.await(PERIOD_MILLISECONDS, TimeUnit.MILLISECONDS ); } catch ( InterruptedException e ) { log.error( "Calculation thread interrupted", e ); break; } // running проверять обязательно, поскольку awaitResult может быть false // даже в случае вызова signal (если к этому времени подошел таймаут) - проверено // Это и есть тот самый пограничный случай if (awaitResult || !running ) break; } finally { reentrantLock.unlock(); } } log.info( "Calculation thread stopped." ); } } ); private void startCalculationThread() { Assert.assertTrue( !running ); log.info( "Starting calculation thread.." ); running = true; calculationThread.start(); } private void stopCalculationThread() { Assert.assertTrue( running ); log.info( "Stopping calculation thread.." ); reentrantLock.lock(); try { running = false; // Посылаем сигнал в наш condition object // Если в это время поток ждет на вызове await(), то он возобновит выполнение // НО только после того, как await() получит блокировку // Таким образом, сначала будет выполнен весь код после вызова signal() - // в нашем случае это запись в лог сообщения, и только потом await() получит блокировку и вернёт управление condition.signal(); log.info("Calculation thread signalled to stop." ); } finally { reentrantLock.unlock(); } // ожидаем завершения потока calculationThread.join(); } |
1