[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: Tightening Exception-based Control flow graph
John Plond JORGENSEN wrote:
"vranganath" == Venkatesh Prasad Ranganath <vranganath@cox.net> writes:
vranganath> Laurie Hendren wrote:
hendren> Could you please give us a concrete example of what
hendren> you are doing and why you are doing it. John
hendren> Jorgensen was working on improving our exception
hendren> edges in Soot and we would like to see how (or if)
hendren> that relates to his work.
I essentially pruned from the CFGs edges to exception handlers
from protected statements that could not possibly throw the type
of exception that the handler catches. Intuitively, you would
think that such an analysis would also accomplish your goal: if
exceptions from the nested synchronized block are always caught
by the nested block's exception handler, then there will be no
edge to the outer block's handler from statements in the inner
synchronized block.
Yes.
But the handlers for synchronized blocks catch Throwable,
i.e. they catch absolutely any exception, including asynchronous
ones. As a result of this, the pruned graphs I produce can still
include edges from the inner block to the outer handler, though
I'll concede that this is of dubious utility. In the case of
synchronized blocks generated by Sun's javac since Java 1.3 (when
javac started producing catch clauses that include themselves in
their own protected region), I think my pruned control flow graphs could
potentially produce the same CFGs as your modification. though
they don't not right now because of a bug in which your
proposal revealed (the bug is that in the Jimple produced for
such a self-protecting handler, the initial assignment of the
caught exception to a local is not necessarily included in the
protected region).
The bug does exist :-) About the type of the exception, I do not see how the catching of the most generic type of exception
will influence the proposed changes. In a nested try block catching Throwable, any exception thrown in it will be trapped by
it's handler before propogating it to the enclosing handler (if any and if at all it does propogate). Just based on Type
equality and try-catch semantics it should be safe to remove such dubious nesting.
Your proposal would affect the handling of bytecode generated for
synchronized blocks by older compilers, and code generated for
try blocks that have nothing to do with synchronization. In
those cases, I'm still not sure if we can do away with the edges
from the inner try block to the outer catch.
For the first part of your statement, it depends on Soot's user base. If everyone is using Soot with Java2 then it makes your
life simple to just support Java2 compilers. As for the code generated for try blocks, I do not quite see how will the
splitting of exception table affect these blocks.
I'll try to explain the rationale for what I did with an
example, based on yours:
void synchUpdate(int[] values, int index,
LockObject inner, LockObject outer) {
synchronized (outer) {
synchronized(inner) {
inner.value = index;
int i = values[index];
outer.value = i;
}
}
}
The code generated for that Java is roughly equivalent to the
following try blocks, with comments indicating operations which
cannot be represented explicitly in Java source (this code
corresponds to what Sun's javac produced before Java 1.3):
void tryUpdate(int[] values, int index,
LockObject inner, LockObject outer) {
try {
// entermonitor(outer)
try {
// entermonitor(inner)
inner.value = index;
int i = values[index];
outer.value = i;
// exitmonitor(inner)
} catch (Throwable t) {
// exitmonitor(inner)
throw t;
}
// exitmonitor(outer)
} catch (Throwable t) {
// exitmonitor(outer)
throw t;
}
}
Now imagine that the assignment to i in the inner try block
throws an ArrayIndexOutOfBoundsException. The exception gets
caught by the inner catch block. But before its exitmonitor
statement gets executed, some asynchronous exception occurs ---
the VM throws InternalError or some other thread inflicts our
thread with ThreadDeath. As a result, the outer exception
handler is invoked before any statements in the inner handler are
completed. In this case, that means the inner monitor might not
be exited before the outer one is, a Bad Thing which is
presumably the reason that javac now produces code where an
exception raised by the handler reinvokes the same handler.
Yes, but the proposed splitting of exception table does not break this structure in any way. So, I think I am missing your point.
As for code generated for other try blocks, we want to ensure
that we do not lose the ability to recognize that in the
following code it is possible for "b" to get set to 1 even though
"a" remains 0:
static int a = 0;
static int b = 0;
void tryUpdate(int[] values, int index,
LockObject inner, LockObject outer) {
try {
try {
inner.value = index;
int i = values[index];
outer.value = i;
} catch (Throwable t) {
a = 1;
}
} catch (Throwable t) {
b = 1;
}
}
I do not quite understand this too. Could you please elaborate how the proposed changes will prevent this situation? Cos,
the following would be the jimple after the proposed changes and it does not change the order or the way exceptions will be
handled except for the pruning the try blocks.
label0:
r2.<t: int value> = i0;
i1 = r1[i0];
r3.<t: int value> = i1;
label1:
goto label3;
label2:
$r4 := @caughtexception;
r5 = $r4;
z2 = 1;
label3:
goto label5;
label4:
$r6 := @caughtexception;
r7 = $r6;
z3 = 1;
label5:
return;
catch java.lang.Throwable from label0 to label1 with label2;
catch java.lang.Throwable from label1 to label3 with label4;
Can you please explain the above 2 scenarios, with a Jimple example, as to why the proposed changes is unsound? I am really
interested as I am wondering if I am missing something from the picture.
waiting for reply,
--
Venkatesh Prasad Ranganath,
Dept. Computing and Information Science,
Kansas State University, US.
web: http://www.cis.ksu.edu/~rvprasad