graal icon indicating copy to clipboard operation
graal copied to clipboard

[GR-39182] set of @CEntryPoint functions to enrich evaluation functionality in IDE.

Open minamoto79 opened this issue 3 years ago • 5 comments

Set of functions to enrich evaluation in IDE debugger.

String functions:

  • svm_dbg_string_to_utf8
  • svm_dbg_utf8_to_string

Array operations:

  • svm_dbg_create_array
  • svm_dbg_array_set
  • svm_dbg_array_get

Class resolving functions:

  • svm_dbg_classloader
  • svm_dbg_class_for_name
  • svm_dbg_allocate_object
  • svm_dbg_class_modifier
  • svm_dbg_class_fields
  • svm_dbg_class_methods
  • svm_dbg_class_super
  • svm_dbg_class_interfaces

Garbage collection functions:

  • svm_dbg_pin_object
  • svm_dbg_unpin_object

Misc:

  • svm_dbg_null

minamoto79 avatar Jul 14 '22 13:07 minamoto79

Thank you for your pull request and welcome to our community! To contribute, please sign the Oracle Contributor Agreement (OCA). The following contributors of this PR have not signed the OCA:

To sign the OCA, please create an Oracle account and sign the OCA in Oracle's Contributor Agreement Application.

When singing the OCA, please provide your GitHub username. After signing the OCA and getting an OCA approval from Oracle, this PR will be automatically updated.

I don't think these entry points should return objects, in fact I consider it an oversight that this currently does not lead to failure. The return values would be absolute addresses, but that is undefined behavior and it would be better to explicitly return pointers, like which are being passed for arguments -- I suppose there, we enforce non-object types.

Do you mean wrap return "objects" with Word.objectToUntrackedPointer?

minamoto79 avatar Jul 15 '22 13:07 minamoto79

Yes (more like unwrap :wink:)

peter-hofer avatar Jul 15 '22 14:07 peter-hofer

Yes (more like unwrap 😉)

Thank you, I've changed return types/values.

minamoto79 avatar Jul 18 '22 05:07 minamoto79

Like @christianhaeubl has already noted, we believe JNI is a better fit to support comprehensive debugging. The existing debug helper functions are intended as simple tools for direct command-line debugging.

Many of the functions in this PR already exist in JNI: string conversions, array access, class and member lookups, and object allocation. In JNI, objects are referred to by handles, which is more robust than addresses which can change during GC or pinning objects which needs bookkeeping. You can use JNI handle frames (PushLocalFrame) for successive JNI calls to clean up all their handles at once afterwards. Note that handles are thread-local by default and have to be recreated as global (NewGlobalRef) to be used in other threads (in which case they have to be explicitly destroyed later). JNI also offers functions for efficient access of primitive arrays.

Unlike for a Java application on HotSpot, our reflection dictionary (which you access here via svm_dbg_class_for_name, svm_dbg_class_fields and svm_dbg_class_methods) is not complete, but contains only elements which have been registered for reflective access beforehand (see our documentation on reflect-config.json). The same applies to the JNI dictionary (jni-config.json), and the reflection and JNI dictionaries can be different (and typically are). This means that any debug helper methods that you want to call via JNI must be registered during the image build, and that you can't call arbitrary methods or access arbitrary fields of arbitrary classes. This wouldn't be possible without JNI either though, because fields or methods or entire classes can be proven unreachable and excluded during the image build, and methods can be inlined at all call sites without ever being compiled individually.

Regarding GC and other VM operations, you will still need to consider which threads are suspended by the debugger because doing a JNI call or just single-stepping while other threads are suspended can block until another thread completes a pending VM operation. Another restriction is that the debugger should not be used on the JNI implementation itself (or any other VM internals, really) because this will most likely cause it to change data structures as they are used (particularly the handle tables).

In order to get started, we need a concept for a debugger context. I'm assuming that we always start out at a breakpoint in Java code, while JNI functions are intended to be called from native code. We could try putting the thread into native mode, or we could use the Java implementations of the JNI functions directly -- both approaches have their pitfalls and likely need some changes on our side. Then, we need to get the thread's JNI environment (this one is straightforward) and a way to create JNI handles for the object variables that we can access in the debugger. I'll have to experiment a bit to figure out how to best approach this and will also have a closer look at how/if NativeJDB does it.

Please let me know what you think and if there's something more we need to consider here.

peter-hofer avatar Jul 28 '22 12:07 peter-hofer