2015-07-07 5 views
1

Я должен использовать любой код (без прямого изменения заданного кода) в начале и в конце каждого потока. Проще говоря, как я могу напечатать что-то в точках входа и выхода любого потока.Как определить, что поток начал использовать javassist?

Как это сделать с помощью javassist?

+0

Ваш вопрос не совсем ясен .... Вы спрашиваете, как обнаружить, что новый объект Thread создается (или запускается) в коде, который вы используете с javassist, заключается в том, что это или что-то еще? Предлагаю вам отредактировать вопрос более подробно! Также предоставляя некоторый код того, что вы пытаетесь сделать. Пожалуйста, проверьте http://stackoverflow.com/help/how-to-ask, чтобы узнать немного больше о том, как задавать вопросы на SO – pabrantes

+0

Да, это очень похоже на то, что вы поняли .. Я обновил вопрос. Пожалуйста, смотрите. – Curious

ответ

1

Короткий ответ

Вы можете сделать это путем создания ExprEditor и использовать его, чтобы изменить MethodCall S, которые соответствуют с начала и присоединиться объектов резьбы.

(очень) Длинный ответ (с кодом)

Прежде чем мы начнем, просто дайте мне сказать, что вы не должны быть запуганы длинный пост, большинство из них просто код и как только вы ломать вещи вниз, это довольно Легко понять!

Давайте заняты тогда ...

Представьте у вас есть следующий код: фиктивный

public class GuineaPig { 

public void test() throws InterruptedException { 
    Thread t = new Thread(new Runnable() { 

     @Override 
     public void run() { 
      for (int i = 0; i < 10; i++) 
       System.out.println(i); 
     } 
    }); 

    t.start(); 
    System.out.println("Sleeping 10 seconds"); 
    Thread.sleep(10 * 1000); 
    System.out.println("Done joining thread"); 
    t.join(); 

} 
} 

При запуске этого кода делает

new GuineaPig().test(); 

Вы получаете выход как (sleep system.out может отображаться в середине подсчета, так как он работает в основной нити):

Sleeping 10 seconds 
0 
1 
2 
3 
4 
5 
6 
7 
8 
9 
Done joining thread 

Наша цель состоит в том, чтобы создать код инжектора, который сделает изменение выходного сигнала на следующее:

Detected thread starting with id: 10 
Sleeping 10 seconds 
0 
1 
2 
3 
4 
5 
6 
7 
8 
9 
Done joining thread 
Detected thread joining with id: 10 

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

со всеми этими идеями в виде, мы создаем следующий инжектор:

ClassPool classPool = ClassPool.getDefault(); 
CtClass guineaPigCtClass = classPool.get(GuineaPig.class.getName()); 

    guineaPigCtClass.instrument(new ExprEditor() { 

     @Override 
     public void edit(MethodCall m) throws CannotCompileException { 
      CtMethod method = null; 
      try { 
       method = m.getMethod(); 
      } catch (NotFoundException e) { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
      } 
      String classname = method.getDeclaringClass().getName(); 
      String methodName = method.getName(); 
      if (classname.equals(Thread.class.getName()) 
        && methodName.equals("start")) { 
       m.replace("{ System.out.println(\"Detected thread starting with id: \" + ((Thread)$0).getId()); $proceed($$); } "); 
      } else if (classname.equals(Thread.class.getName()) 
        && methodName.equals("join")) { 
       m.replace("{ System.out.println(\"Detected thread joining with id: \" + ((Thread)$0).getId()); $proceed($$); } "); 
      } 

     } 
    }); 

    guineaPigCtClass 
      .writeFile("<Your root directory with the class files>"); 
} 

Так, что происходит в этом небольшом изящном куске коды? Мы используем ExprEdit для инструментария нашего класса GuineaPig (не нанося ему никакого вреда!) И перехватываем все вызовы методов.

Когда мы перехватываем вызов метода, сначала проверяем, является ли класс объявления класса Thread, если это так, это означает, что мы вызываем метод в объекте Thread. Затем переходим к проверке, является ли это одним из двух конкретных методов: start и join.

Когда происходит один из этих двух случаев, мы используем javassist highlevel API для замены кода.Замена легко обнаружить в коде, фактический код, предоставленный где это может быть немного сложнее, так что давайте разделить одну из этих линий, давайте возьмем, к примеру, линия, которая будет обнаруживать запуск темы:

{ System.out.println(\"Detected thread starting with id: \" + ((Thread)$0).getId()); $proceed($$); } "

  1. Сначала весь код внутри фигурных скобок, в противном случае Javassist не будет принимать это
  2. Тогда у вас есть System.out, который ссылается на $ 0. $ 0 - специальный параметр, который может использоваться в манипуляциях с javassist и представляет целевой объект вызова метода, в этом случае мы точно знаем, что это будет Thread.
  3. $ continue ($$) Это, наверное, самая сложная инструкция, если вы не знакомы с javassist, так как это все сахара javassist и никакой java вообще. $ continue - это то, как вы должны ссылаться на фактический вызов метода, который вы обрабатываете, и $$ ссылки на полный список аргументов, переданный вызову метода. В этом конкретном случае начало и объединение обоих будет иметь этот список пустым, тем не менее, я думаю, что лучше сохранить эту информацию.

Вы можете прочитать больше о специальных операторов в Javassist учебник, раздел 4.2 Altering a Method Body (поиск MethodCall части, извините нет якоря для этого подраздела)

Наконец, после всего этого кунг-фу мы пишем байткод нашего ctClass в папку класса (так перезаписывает существующий файл GuinePig.class), и когда мы исполняем его ... вуаля, выход теперь, что мы хотели :-)

Только окончательное предупреждение, имейте в виду, что этот инжектор довольно прост и не проверяет, был ли класс уже введен, поэтому вы можете получить несколько инъекций.