增加自定義findbugs規(guī)則集
增加自定義檢測(cè)模式的一般流程
上層接口和父類
Priority接口
public interface Priorities {
public static final int IGNORE_PRIORITY = 5;//忽略bug
public static final int EXP_PRIORITY = 4;
public static final int LOW_PRIORITY = 3;//低優(yōu)先級(jí)
public static final int NORMAL_PRIORITY = 2;//普通優(yōu)先級(jí)
public static final int HIGH_PRIORITY = 1;//高優(yōu)先級(jí)
}
Detector接口
/**
* bug pattern檢測(cè)必須要實(shí)現(xiàn)該接口
*/
public interface Detector extends Priorities {
/**
* Visit the ClassContext for a class which should be analyzed for instances
* of bug patterns.
*/
public void visitClassContext(ClassContext classContext);
/**
* This method is called after all classes to be visited. It should be used
* by any detectors which accumulate information over all visited classes to
* generate results.
*/
public void report();
}
BugInstance類
/**
* @param detector
* the Detector that is reporting the BugInstance
* @param type
* the bug type
*/
public BugInstance(Detector detector, String type, int priority) {
this(type, priority);
if (detector != null) {
// Adjust priority if required
String detectorName = detector.getClass().getName();
adjustForDetector(detectorName);
}
}
Bytecode 框架
- 所有的bug detector都是用bytecode分析
- 大部分的detector用以下技術(shù)實(shí)現(xiàn)
- inspect class/method/field
- micropattern:simple bytecode pattern
- stack-based pattern
- dataflow analysis
- interprocedural analysis
1. Inspect class/method/field
某些detector不需要code 分析
1. 發(fā)現(xiàn)類override equals()方法沒(méi)重寫hashCode()方法
2. 方法命名錯(cuò)誤(hashCode()寫成了hashcode()方法)
2. Micropatterns: simple bytecode patterns
synchronized (lock) { ALOAD 0
lock.wait(); GETFIELD A.lock
... DUP
} ASTORE 1
MONITORENTER
ALOAD 0
GETFIELD A.lock
INVOKEVIRTUAL
Object.wait()V
3. Stack-based patterns
- Micropatterns where the values on the operand stack are significant
- Example:
- As seen earlier: look for monitorenter on constant String value
- Typical implementation strategy:
- Inquire about values on operand stack
- Warn when suspicious instruction sequence/stack values
seen
一般性檢測(cè)器
BytecodeScanningDetector
對(duì)于掃描字節(jié)碼的需求聂抢,一般是擴(kuò)展這個(gè)類蜓谋。
Visit
有很多不同參數(shù)的方法。表示訪問(wèn)類枕面、或者代碼卵慰、方法等時(shí)候會(huì)調(diào)用該方法。
一般用該方法進(jìn)行訪問(wèn)前的初始化**工作**。
public void visit(Code obj) 分析方法內(nèi)容時(shí)調(diào)用visit(Code) 方法大溜,往往用于分析方法代碼前進(jìn)行初始化工作
public void visit(JavaClass obj) 分析該類之前,調(diào)用該方法估脆。往往用于取得類的信息
public void visitField(Field obj) 分析類的屬性前钦奋,調(diào)用該方法,往往用于取得類的屬性信息
分析字節(jié)碼
>>例1
public void sawOpcode(int seen)
>>在分析方法正文中的每一個(gè)操作碼時(shí)調(diào)用sawOpcode(int)方法疙赠。
>> seen就是每條的操作碼付材,操作碼在反編譯后都能看到
指令碼都是該類的常量,可以找到
public void show();
Code:
0: getstatic #29; //Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #48; //String ssssssssssssss00s
5: invokevirtual #35; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
>>取得該指令對(duì)于的類
getClassConstantOperand()
對(duì)于第5行:可以取到j(luò)ava/io/PrintStream
>>取得該指令對(duì)應(yīng)的類執(zhí)行的方法的名字
getNameConstantOperand()
對(duì)于第5行:可以取到println
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
例2
匯編代碼:
public void doBadStuff();
Code:
0: invokestatic #2; //Method java/lang/System.gc:()V
3: return
查找調(diào)用了System.gc的代碼
public void sawOpcode(int seen) {
if (seen == INVOKESTATIC) {
if (getClassConstantOperand().equals("java/lang/System")
&& getNameConstantOperand().equals("gc")) {
bugReporter.reportBug(new BugInstance("SYSTEM_GC", NORMAL_PRIORITY)
.addClassAndMethod(this)
.addSourceLine(this));
}
}
}
}
public void sawMethod()
>>每分析一個(gè)方法前圃阳,都會(huì)調(diào)用該方法
>>取得類的名字
String className = super.getClassName().replaceAll("/", ".");
>>取得方法的名字
this.getMethod().getName()
>>取得方法的返回類型
String returnType = this.getMethod().getReturnType().toString();
>>取得方法是否為靜態(tài)
boolean isStatic = this.getMethod().isStatic();
>>取得方法是否為公開的
boolean isPublic = this.getMethod().isPublic();
>>例如:
public void sawMethod() {
if (isPublicStaticMethord) {
return;
}
//class name: demo/First|| methord name :show|| ReturnType() name : //demo.Second //accce flag= 1
String className = super.getClassName().replaceAll("/", ".");
String returnType = this.getMethod().getReturnType().toString();
boolean isStatic = this.getMethod().isStatic();
boolean isPublic = this.getMethod().isPublic();
//單例判斷
if (isPublic && isStatic) {//如果為公有的靜態(tài)的
if (className != null && className.equals(returnType)) {//如果返回值就是本類
isPublicStaticMethord = true;
}
}
}
生成報(bào)表
1厌衔、構(gòu)造函數(shù)里面會(huì)傳遞報(bào)表參數(shù)
public SingletonDector(BugReporter bugReporter) {
this.bugReporter= bugReporter;
}
2.在恰當(dāng)?shù)牡胤秸{(diào)用報(bào)表
一般是在visitXXX sawXXX地方檢查的時(shí)候調(diào)用
bugReporter.reportBug(newBugInstance("MULTITHREAD_SINGLETON",NORMAL_PRIORITY)
.addClass(this)) ;
>>MULTITHREAD_SINGLETON
就是配置的報(bào)表的名字
>>addXXX
就是傳遞給報(bào)表的屬性,在報(bào)表里面是可以取到的