用于在Java中查找Shared Mutable数据错误的工具 [英] Tools for finding Shared Mutable data bugs in Java
问题描述
我有一个庞大的遗留系统需要维护。代码库使用遍布各处的线程,这些线程共享大量可变数据。我知道,听起来不错。无论如何,不回答从头开始重写整个应用程序或者我会投票给你:-)我试图在代码库上运行一些静态分析工具,但这些似乎都没有抓住这种情况发生了很多在我们的源代码中:多个线程正在读取和写入未标记为volatile或同步的变量。通常这发生在runFlag类型的变量上。这方面的一个例子是在Effective Java第2版第260页:
I have a large legacy system to maintain. The codebase uses threads all over the place and those threads share a lot of mutable data. I know, sounds bad. Anyway, don't answer "rewrite the whole application from scratch" or I'll vote you down :-) I have tried to run some static analysis tools on the codebase, but none of those seem to catch this case which occurs a lot in our source code: multiple threads are reading and writing variables which are not marked as volatile or synchronized at all. Typically this occurs on "runFlag"-type variables. An example of this is on Effective Java 2nd edition page 260:
public class StopThread
{
private static boolean stopRequested;
public static void main(String[] args) throws InterruptedException
{
Thread backgroundThread = new Thread(new Runnable()
{
public void run()
{
int i = 0;
while (!stopRequested)
{
i++;
}
}
});
backgroundThread.start();
Thread.sleep(1000);
stopRequested = true;
}
}
这个例子永远不会在Windows / Linux上完成 - 服务器提供给Sun JVM的启动参数。那么,是否有任何(半)自动方式来查找这些问题,或者我是否必须完全依赖代码审查?
This example never finishes on Windows/Linux with "-server" startup parameter given to Sun JVM. So, is there any (semi-)automatic way to find these issues, or do I have to rely totally on code reviews?
推荐答案
< Chris Grindstaff写了一篇文章 FindBugs,第2部分:编写自定义探测器其中他描述了如何使用 BCEL 添加您自己的规则。 (BCEL不是唯一的字节码库 - 但它是FindBugs使用的字节码库。)
Chris Grindstaff wrote an article FindBugs, Part 2: Writing custom detectors in which he describes how to use the BCEL to add your own rules. (BCEL isn't the only bytecode library - but it is the one used by FindBugs.)
下面的代码会发出方法访问静态方法或字段的任何情况。您可以在任何实现 Runnable 的类型上运行它。
The code below emits any cases where a method accesses a static method or field. You could run it on any type that implements Runnable.
public class StaticInvocationFinder extends EmptyVisitor {
@Override
public void visitMethod(Method obj) {
System.out.println("==========================");
System.out.println("method:" + obj.getName());
Code code = obj.getCode();
InstructionList instructions = new InstructionList(code.getCode());
for (Instruction instruction : instructions.getInstructions()) {
// static field or method
if (Constants.INVOKESTATIC == instruction.getOpcode()) {
if (instruction instanceof InvokeInstruction) {
InvokeInstruction invokeInstruction = (InvokeInstruction) instruction;
ConstantPoolGen cpg = new ConstantPoolGen(obj
.getConstantPool());
System.out.println("static access:"
+ invokeInstruction.getMethodName(cpg));
System.out.println(" on type:"
+ invokeInstruction.getReferenceType(cpg));
}
}
}
instructions.dispose();
}
public static void main(String[] args) throws Exception {
JavaClass javaClass = Repository.lookupClass("StopThread$1");
StaticInvocationFinder visitor = new StaticInvocationFinder();
DescendingVisitor classWalker = new DescendingVisitor(javaClass,
visitor);
classWalker.visit();
}
}
此代码会发出以下内容:
This code emits the following:
==========================
method:<init>
==========================
method:run
static access:access$0
on type:StopThread
然后可以扫描类型 StopThread ,找到该字段并检查它是否 volatile 。
It would be possible to then scan the type StopThread, find the field and check to see if it is volatile.
检查同步是可能的,但由于多个MONITOREXIT条件可能会变得棘手。向上调用堆栈也很困难,但这不是一个小问题。但是,我认为检查错误模式是否相对容易,如果它已经一致地实现。
Checking for synchronization is possible, but might get tricky due to multiple MONITOREXIT conditions. Walking up call stacks could be difficult too, but then this isn't a trivial problem. However, I think it would be relatively easy to check for a bug pattern if it has been implemented consistently.
BCEL看起来很难记录并且非常毛茸茸直到找到 BCELifier 类。如果你在一个类上运行它,它会向你发出如何在BCEL中构建类的Java源代码。在 StopThread 上运行它可以生成 access $ 0 合成访问器:
BCEL looks scantly documented and really hairy until you find the BCELifier class. If you run it on a class, it spits out Java source of how you would build the class in BCEL. Running it on StopThread gives this for generating the access$0 synthetic accessor:
private void createMethod_2() {
InstructionList il = new InstructionList();
MethodGen method = new MethodGen(ACC_STATIC | ACC_SYNTHETIC, Type.BOOLEAN, Type.NO_ARGS, new String[] { }, "access$0", "StopThread", il, _cp);
InstructionHandle ih_0 = il.append(_factory.createFieldAccess("StopThread", "stopRequested", Type.BOOLEAN, Constants.GETSTATIC));
il.append(_factory.createReturn(Type.INT));
method.setMaxStack();
method.setMaxLocals();
_cg.addMethod(method.getMethod());
il.dispose();
}
这篇关于用于在Java中查找Shared Mutable数据错误的工具的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!