Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Static memory snapshot: add support for Unsafe, VarHandle, and AtomicFieldUpdater #429

Merged
merged 27 commits into from
Jan 22, 2025

Conversation

dmitrii-artuhov
Copy link
Collaborator

@dmitrii-artuhov dmitrii-artuhov commented Nov 29, 2024

Partly resolves issue #389.

This PR is aimed to add support for tracking fields that are accessed via:

  • Unsafe API
  • VarHandle API
  • Java's AtomicFieldUpdater

Notably,System.arraycopy(..) is currently not supported.

@dmitrii-artuhov dmitrii-artuhov changed the base branch from develop to trace-debugger-static-memory November 29, 2024 16:28
@dmitrii-artuhov dmitrii-artuhov force-pushed the trace-debugger-system-calls branch from 881a74a to fb813ab Compare December 2, 2024 21:10
@dmitrii-artuhov dmitrii-artuhov changed the title Static memory snapshot: add support for System.arraycopy, Unsafe, VarHandle, AFU, kotlinx.atomicfu, and reflexivity Static memory snapshot: add support for System.arraycopy, Unsafe, VarHandle, AFU, kotlinx.atomicfu Dec 2, 2024
@dmitrii-artuhov dmitrii-artuhov changed the title Static memory snapshot: add support for System.arraycopy, Unsafe, VarHandle, AFU, kotlinx.atomicfu Static memory snapshot: add support for Unsafe, VarHandle, AFU, kotlinx.atomicfu Dec 9, 2024
@dmitrii-artuhov dmitrii-artuhov marked this pull request as ready for review December 12, 2024 11:13
@dmitrii-artuhov dmitrii-artuhov requested a review from eupp December 12, 2024 11:13
Base automatically changed from trace-debugger-static-memory to develop December 12, 2024 11:17
@ndkoval
Copy link
Collaborator

ndkoval commented Dec 12, 2024

Please note that you don't need to have special support for atomicfu -- it compiles to either AFU or VarHandle

@dmitrii-artuhov dmitrii-artuhov force-pushed the trace-debugger-system-calls branch from d95fd4d to 2fd1a05 Compare December 12, 2024 13:32
@@ -25,6 +25,57 @@ import java.util.*
import kotlin.coroutines.*
import kotlin.coroutines.intrinsics.*

fun shouldTransformClass(className: String): Boolean {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems the code was moved from somewhere, but I do not see it removed in the original location.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, I would suggest keeping the transformation-related logic in the corresponding package.

@@ -37,11 +37,22 @@ internal object AtomicFieldUpdaterNames {
val offsetField = updater.javaClass.getDeclaredField("offset")
val offset = UNSAFE.getLong(updater, UNSAFE.objectFieldOffset(offsetField))

return findFieldNameByOffsetViaUnsafe(targetType, offset)
return findFieldNameByOffsetViaUnsafe(targetType, offset).let { fieldName ->
if (fieldName != null) AtomicFieldUpdaterDescriptor(targetType, fieldName)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please always add { .. } when the else branch is present.

Copy link
Collaborator

@eupp eupp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please also don't forget to rebase on develop.

isVarHandle(owner) &&
(
VarHandleNames.isInstanceVarHandle(owner) && staticMemorySnapshot.isTracked(params.firstOrNull()) ||
VarHandleNames.isArrayVarHandle(owner) && staticMemorySnapshot.isTracked(params.firstOrNull()) ||
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you check staticMemorySnapshot.isTracked in case of VarHandle API, but not in case of AFU or Unsafe APIs?

Also, wouldn't the staticMemorySnapshot.trackField methods check itself if the object is tracked or not?

else {
null
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using let is overkill here.

val fieldName = findFieldNameByOffsetViaUnsafe(targetType, offset) ?: return null
return AtomicFieldUpdaterDescriptor(targetType, fieldName)

private fun getDeclaringClass(obj: Any?, className: String, fieldName: String): Class<*> {
return Class.forName(className).let {
private fun getDeclaringClass(obj: Any?, clazz: Class<*>, fieldName: String): Class<*> {
return clazz.let {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

using let is overkill here

}

fun trackField(obj: Any?, accessClassName: String, fieldName: String) {
if (obj != null && obj !in trackedObjects) return
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please replace this line and all related lines with isTracked(obj)

override fun verifyResults(scenario: ExecutionScenario?, results: ExecutionResult?): Boolean {
checkForExceptions(results)
check(intArray === refIntArray)
check(intArray.contentEquals(intArrayOf(2, 1, 4, 3, 6, 5, 8, 7, 10, 9)))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please extract intArrayOf(2, 1, 4, 3, 6, 5, 8, 7, 10, 9) into separate variable

override fun verifyResults(scenario: ExecutionScenario?, results: ExecutionResult?): Boolean {
checkForExceptions(results)
check(intList === refIntList)
check(intList.toIntArray().contentEquals(intArrayOf(2, 1, 4, 3, 6, 5, 8, 7, 10, 9)))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please extract intArrayOf(2, 1, 4, 3, 6, 5, 8, 7, 10, 9) into separate variable

@dmitrii-artuhov dmitrii-artuhov force-pushed the trace-debugger-system-calls branch from 2ef2af9 to dce3e40 Compare January 13, 2025 16:48
@dmitrii-artuhov dmitrii-artuhov requested a review from eupp January 14, 2025 12:51
Copy link
Collaborator

@eupp eupp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Few final nitpicks.

@dmitrii-artuhov dmitrii-artuhov requested a review from eupp January 16, 2025 17:09
@eupp eupp requested a review from ndkoval January 16, 2025 20:49
@eupp
Copy link
Collaborator

eupp commented Jan 16, 2025

@ndkoval I am good with the current state of this PR. What about you? Should we merge it?

@ndkoval ndkoval changed the title Static memory snapshot: add support for Unsafe, VarHandle, AFU, kotlinx.atomicfu Static memory snapshot: add support for Unsafe, VarHandle, and AtomicFieldUpdater Jan 17, 2025

private val arrayValue = intArrayOf(2, 1, 4, 3, 6, 5, 8, 7, 10, 9)

// TODO: parallel operations are not supported because of java.lang.ClassCastException:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this TODO be left?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Arrays API has parallel* versions of methods, I added this todo to add them later, when I fix the issue with System.arraycopy.

I updated the TODO message a little bit.

@@ -24,7 +24,7 @@ import java.util.concurrent.atomic.AtomicReferenceFieldUpdater
internal object AtomicFieldUpdaterNames {

@Suppress("DEPRECATION")
internal fun getAtomicFieldUpdaterName(updater: Any): String? {
internal fun getAtomicFieldUpdaterName(updater: Any): AtomicFieldUpdaterDescriptor? {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the function be renamed?

else it.findField(fieldName).declaringClass
}
private fun getDeclaringClass(obj: Any?, clazz: Class<*>, fieldName: String): Class<*> {
return if (obj != null) clazz
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When using both clauses in if, please wrap the branches with brackets, it makes the flow more visible when reading the code

staticMemorySnapshot.trackField(obj, afuDesc.targetType, afuDesc.fieldName)
}
// TODO: System.arraycopy
// TODO: reflexivity
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reflection

@eupp eupp merged commit 0cbfcaf into develop Jan 22, 2025
16 checks passed
@eupp eupp deleted the trace-debugger-system-calls branch January 22, 2025 16:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants