Getting a reference to caller object

P

puchacz

Hi,

Is it possible to get a reference to caller object (and recursively
all the way up the call stack)? I know about Thread.dumpStack() that
calls new Exception() internally and about sun.reflect.Reflection
class, but you can only get information about classes, method names
and lines - not reference to live objects that in turn can be
inspected using reflection for all field values etc. I know in general
it is probably possible, because this is what debuggers do when
showing stack frame.

What would it take to get this information? It does not have to be
100% compatible with all versions of JDK. Anything in reflection
internal packages? Or maybe there's a library that already does it? To
what degree it is possible without (a) running the whole jvm in
special mode - like debug mode and (b) without processing the whole
program in AspectJ or Hibernate bytecode postprocessors style, and of
course (c) without rewriting the source to pass around "this"
everywhere?

Cheers,
Piotr
 
E

Eric Sosman

Hi,

Is it possible to get a reference to caller object (and recursively
all the way up the call stack)? [...]

Maybe. What do you mean by "caller object?" For example, in

public class Piotr {
public static void main(String[] unused) {
System.out.println(hello());
}
private static String hello() {
// What is the "caller object" here?
return "Bonjour";
}
}
[...] I know in general
it is probably possible, because this is what debuggers do when
showing stack frame.

The debuggers I have used will show the method associated with
each stack frame, and the class the method belongs to, and the source
code's line number (if it's available). I have not seen a debugger
that shows anything I would term a "caller object," so again: What
do you mean? More to the point, what problem are you trying to solve?
 
A

Arne Vajhøj

Is it possible to get a reference to caller object (and recursively
all the way up the call stack)? I know about Thread.dumpStack() that
calls new Exception() internally and about sun.reflect.Reflection
class, but you can only get information about classes, method names
and lines - not reference to live objects that in turn can be
inspected using reflection for all field values etc. I know in general
it is probably possible, because this is what debuggers do when
showing stack frame.

I don't think you can.
What would it take to get this information? It does not have to be
100% compatible with all versions of JDK. Anything in reflection
internal packages? Or maybe there's a library that already does it? To
what degree it is possible without (a) running the whole jvm in
special mode - like debug mode and (b) without processing the whole
program in AspectJ or Hibernate bytecode postprocessors style, and of
course (c) without rewriting the source to pass around "this"
everywhere?

I think you need to redesign so that you don't need this feature.

Arne
 
S

Stefan Ram

Eric Sosman said:
public class Piotr {
public static void main(String[] unused) {
System.out.println(hello()); (...)
// What is the "caller object" here?

The closest approximation to an answer to the above
question might be »Piotr.class«.

(But I do not know whether such an object is instanciated
when the class »Piotr« is initialized or only when such
an object is first needed.)
 
M

Mike Schilling

Eric Sosman said:
Hi,

Is it possible to get a reference to caller object (and recursively
all the way up the call stack)? [...]

Maybe. What do you mean by "caller object?" For example, in

public class Piotr {
public static void main(String[] unused) {
System.out.println(hello());
}
private static String hello() {
// What is the "caller object" here?
return "Bonjour";
}
}
[...] I know in general
it is probably possible, because this is what debuggers do when
showing stack frame.

The debuggers I have used will show the method associated with
each stack frame, and the class the method belongs to, and the source
code's line number (if it's available).

And the local variables for each frame, including "this" (if the frame has a
this.) I think the last of these is what's being asked for.
 
L

Lew

Eric said:
public class Piotr {
public static void main(String[] unused) {
System.out.println(hello()); (...)
// What is the "caller object" here?

Stefan said:
The closest approximation to an answer to the above
question might be »Piotr.class«.

(But I do not know whether such an object is instanciated
when the class »Piotr« is initialized or only when such
an object is first needed.)

The class object comes into existence when the class is loaded, but not all
class initialization happens then. The 'class' attribute is an attribute of
the class object, not the class object itself. The VM loader loads and
initializes the class 'Piotr' and calls its 'main()', and thus 'Piotr',
running 'main()', is the caller of 'hello()'.

The class is loaded upon first reference to it or its members, but not
necessarily initialized right then. For example, a reference to 'Piotr.class'
will load 'Piotr' if it wasn't already, but not initialize it.

The invocation of 'main()' will cause class initialization, so that will have
happened before the call to 'hello()'.
 
P

puchacz

Eric said:
       public class Piotr {
           public static void main(String[] unused) {
               System.out.println(hello());            (...)
               // What is the "caller object" here?
Stefan said:
   The closest approximation to an answer to the above
   question might be »Piotr.class«.
   (But I do not know whether such an object is instanciated
   when the class »Piotr« is initialized or only when such
   an object is first needed.)

The class object comes into existence when the class is loaded, but not all
class initialization happens then.  The 'class' attribute is an attribute of
the class object, not the class object itself.  The VM loader loads and
initializes the class 'Piotr' and calls its 'main()', and thus 'Piotr',
running 'main()', is the caller of 'hello()'.

The class is loaded upon first reference to it or its members, but not
necessarily initialized right then.  For example, a reference to 'Piotr..class'
will load 'Piotr' if it wasn't already, but not initialize it.

The invocation of 'main()' will cause class initialization, so that will have
happened before the call to 'hello()'.



Hi guys,

Thanks, but this is not what I want; I would like something like the
program below. Basically references to live objects, not class
definition objects. It is for easier troubleshooting of an existing
large production system, if something sets up a bad status, I would
like to know what caused it, we have a lot of information in JMX
already to click through, but not real stacks - and you cannot really
debug a live production system.

By debugger I mean debugger, like Eclipse debugger - you can see all
stack frames, local variables in them, and "this" local to each stack
frame, so you see values of fields in each object in stack frames,
from your breakpoint location all the way up to top level Thread.run()
or similar.

Cheers,
Piotr

class A {
int i, j;
void a() {
i = 1;
j = 2;
aa();
}

void aa() {
B b = new B();
b.b(this);
}
}

class B {
void b(Object o) {
// assuming Magic.getStackFrame(thread, nextFrame - null means we are
asking for the last frame in stack)

// getting first from stack frame
Frame frame1 = Magic.getStackFrame(Thread.currentThread(), null);

// THIS IS WHAT I WANT TO RETRIEVE, FROM HERE
assert(frame1.objectRef == o);
assert(A.class.getDeclaredField("i").get(frame1.objectRef) == 1);
assert(A.class.getDeclaredField("j").get(frame1.objectRef) == 2);
// TO HERE

// we were called from no arg method - this is retrieveable from
// Thread.dumpStack() actually
assert(frame1.method.equals(A.class.getMethod("aa", new Class[]
{Object.class})));

Frame frame2 = Magic.getStackFrame(Thread.currentThread(), frame1);
assert(frame2.objectRef == frame1.objectRef); // the same object,
also not retrievable using what I know
assert(frame2.objectRef == o);
assert(frame1.method.equals(A.class.getMethod("a", new Class[]{})));

Frame frame3 = Magic.getStackFrame(Thread.currentThread(), frame2);
assert(frame3.objectRef == null); // this method was called from
static method
assert(frame3.method.equals(C.getMethod("main"), new Class[]
{String[].class}));

Frame frame4 = Magic.getStackFrame(Thread.currentThread(), frame3);
assert(frame4 == null); // it was top level frame

// etc.
}
}

class C {
public static void main(String s[]) {
new A().a();
}
}
 
E

Eric Sosman

[...]
Thanks, but this is not what I want; I would like something like the
program below. Basically references to live objects, not class
definition objects. It is for easier troubleshooting of an existing
large production system, if something sets up a bad status, I would
like to know what caused it, we have a lot of information in JMX
already to click through, but not real stacks - and you cannot really
debug a live production system.

I don't think you'll get what you want, short of running the
code under a debugger. Perhaps the java.lang.instrument package
would help -- but using those facilities seems pretty close to (and
as intrusive as) using a debugger to begin with.
 
A

Arne Vajhøj

yes. You can all sorts of information.

See http://mindprod.com/jgloss/trace.html
for sample code.

Two quick questions:

1) what is an object?

2) what of the code below

Throwable t = new Throwable();
StackTraceElement[] es = t.getStackTrace();
for ( int i=0; i<es.length; i++ )
{
StackTraceElement e = es;
System.out.println( " in class:" + e.getClassName()
+ " in source file:" + e.getFileName()
+ " in method:" + e.getMethodName()
+ " at line:" + e.getLineNumber()
+ " " + ( e.isNativeMethod() ? "native" : "" ) );
}

finds "caller object"?

Arne
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,981
Messages
2,570,188
Members
46,732
Latest member
ArronPalin

Latest Threads

Top