Skip to content

Commit

Permalink
Fix thread safety crash
Browse files Browse the repository at this point in the history
This allows position to be thread local.

Fixes square#2084
  • Loading branch information
pyricau authored and tildehacker committed Nov 8, 2021
1 parent 9ef2ff2 commit 0c43e29
Showing 1 changed file with 123 additions and 106 deletions.
229 changes: 123 additions & 106 deletions shark-graph/src/main/java/shark/internal/ClassFieldsReader.kt
Original file line number Diff line number Diff line change
Expand Up @@ -28,143 +28,160 @@ internal class ClassFieldsReader(
private val classFieldBytes: ByteArray
) {

private var position = 0

fun classDumpStaticFields(indexedClass: IndexedClass): List<StaticFieldRecord> {
position = indexedClass.fieldsIndex
val staticFieldCount = readUnsignedShort()
val staticFields = ArrayList<StaticFieldRecord>(staticFieldCount)
for (i in 0 until staticFieldCount) {
val nameStringId = readId()
val type = readUnsignedByte()
val value = readValue(type)
staticFields.add(
StaticFieldRecord(
nameStringId = nameStringId,
type = type,
value = value
return read(initialPosition = indexedClass.fieldsIndex) {
val staticFieldCount = readUnsignedShort()
val staticFields = ArrayList<StaticFieldRecord>(staticFieldCount)
for (i in 0 until staticFieldCount) {
val nameStringId = readId()
val type = readUnsignedByte()
val value = readValue(type)
staticFields.add(
StaticFieldRecord(
nameStringId = nameStringId,
type = type,
value = value
)
)
)
}
staticFields
}
return staticFields
}

fun classDumpFields(indexedClass: IndexedClass): List<FieldRecord> {
position = indexedClass.fieldsIndex

skipStaticFields()
return read(initialPosition = indexedClass.fieldsIndex) {
skipStaticFields()

val fieldCount = readUnsignedShort()
val fields = ArrayList<FieldRecord>(fieldCount)
for (i in 0 until fieldCount) {
fields.add(FieldRecord(nameStringId = readId(), type = readUnsignedByte()))
val fieldCount = readUnsignedShort()
val fields = ArrayList<FieldRecord>(fieldCount)
for (i in 0 until fieldCount) {
fields.add(FieldRecord(nameStringId = readId(), type = readUnsignedByte()))
}
fields
}
return fields
}

fun classDumpHasReferenceFields(indexedClass: IndexedClass): Boolean {
position = indexedClass.fieldsIndex
skipStaticFields()
val fieldCount = readUnsignedShort()
for (i in 0 until fieldCount) {
position += identifierByteSize
val type = readUnsignedByte()
if (type == PrimitiveType.REFERENCE_HPROF_TYPE) {
return true
return read(initialPosition = indexedClass.fieldsIndex) {
skipStaticFields()
val fieldCount = readUnsignedShort()
for (i in 0 until fieldCount) {
position += identifierByteSize
val type = readUnsignedByte()
if (type == PrimitiveType.REFERENCE_HPROF_TYPE) {
return@read true
}
}
return@read false
}
return false
}

private fun skipStaticFields() {
val staticFieldCount = readUnsignedShort()
for (i in 0 until staticFieldCount) {
position += identifierByteSize
val type = readUnsignedByte()
position += if (type == PrimitiveType.REFERENCE_HPROF_TYPE) {
identifierByteSize
} else {
PrimitiveType.byteSizeByHprofType.getValue(type)
private val readInFlightThreadLocal = object : ThreadLocal<ReadInFlight>() {
override fun initialValue() = ReadInFlight()
}

private fun <R> read(
initialPosition: Int,
block: ReadInFlight.() -> R
): R {
val readInFlight = readInFlightThreadLocal.get()
readInFlight.position = initialPosition
return readInFlight.run(block)
}

private inner class ReadInFlight {
var position = 0

fun skipStaticFields() {
val staticFieldCount = readUnsignedShort()
for (i in 0 until staticFieldCount) {
position += identifierByteSize
val type = readUnsignedByte()
position += if (type == PrimitiveType.REFERENCE_HPROF_TYPE) {
identifierByteSize
} else {
PrimitiveType.byteSizeByHprofType.getValue(type)
}
}
}
}

private fun readValue(type: Int): ValueHolder {
return when (type) {
PrimitiveType.REFERENCE_HPROF_TYPE -> ReferenceHolder(readId())
BOOLEAN_TYPE -> BooleanHolder(readBoolean())
CHAR_TYPE -> CharHolder(readChar())
FLOAT_TYPE -> FloatHolder(readFloat())
DOUBLE_TYPE -> DoubleHolder(readDouble())
BYTE_TYPE -> ByteHolder(readByte())
SHORT_TYPE -> ShortHolder(readShort())
INT_TYPE -> IntHolder(readInt())
LONG_TYPE -> LongHolder(readLong())
else -> throw IllegalStateException("Unknown type $type")
fun readValue(type: Int): ValueHolder {
return when (type) {
PrimitiveType.REFERENCE_HPROF_TYPE -> ReferenceHolder(readId())
BOOLEAN_TYPE -> BooleanHolder(readBoolean())
CHAR_TYPE -> CharHolder(readChar())
FLOAT_TYPE -> FloatHolder(readFloat())
DOUBLE_TYPE -> DoubleHolder(readDouble())
BYTE_TYPE -> ByteHolder(readByte())
SHORT_TYPE -> ShortHolder(readShort())
INT_TYPE -> IntHolder(readInt())
LONG_TYPE -> LongHolder(readLong())
else -> throw IllegalStateException("Unknown type $type")
}
}
}

private fun readByte(): Byte {
return classFieldBytes[position++]
}
fun readByte(): Byte {
return classFieldBytes[position++]
}

private fun readInt(): Int {
return (classFieldBytes[position++].toInt() and 0xff shl 24) or
(classFieldBytes[position++].toInt() and 0xff shl 16) or
(classFieldBytes[position++].toInt() and 0xff shl 8) or
(classFieldBytes[position++].toInt() and 0xff)
}
fun readInt(): Int {
return (classFieldBytes[position++].toInt() and 0xff shl 24) or
(classFieldBytes[position++].toInt() and 0xff shl 16) or
(classFieldBytes[position++].toInt() and 0xff shl 8) or
(classFieldBytes[position++].toInt() and 0xff)
}

private fun readLong(): Long {
return (classFieldBytes[position++].toLong() and 0xff shl 56) or
(classFieldBytes[position++].toLong() and 0xff shl 48) or
(classFieldBytes[position++].toLong() and 0xff shl 40) or
(classFieldBytes[position++].toLong() and 0xff shl 32) or
(classFieldBytes[position++].toLong() and 0xff shl 24) or
(classFieldBytes[position++].toLong() and 0xff shl 16) or
(classFieldBytes[position++].toLong() and 0xff shl 8) or
(classFieldBytes[position++].toLong() and 0xff)
}
fun readLong(): Long {
return (classFieldBytes[position++].toLong() and 0xff shl 56) or
(classFieldBytes[position++].toLong() and 0xff shl 48) or
(classFieldBytes[position++].toLong() and 0xff shl 40) or
(classFieldBytes[position++].toLong() and 0xff shl 32) or
(classFieldBytes[position++].toLong() and 0xff shl 24) or
(classFieldBytes[position++].toLong() and 0xff shl 16) or
(classFieldBytes[position++].toLong() and 0xff shl 8) or
(classFieldBytes[position++].toLong() and 0xff)
}

private fun readShort(): Short {
return ((classFieldBytes[position++].toInt() and 0xff shl 8) or
(classFieldBytes[position++].toInt() and 0xff)).toShort()
}
fun readShort(): Short {
return ((classFieldBytes[position++].toInt() and 0xff shl 8) or
(classFieldBytes[position++].toInt() and 0xff)).toShort()
}

private fun readUnsignedShort(): Int {
return readShort().toInt() and 0xFFFF
}
fun readUnsignedShort(): Int {
return readShort().toInt() and 0xFFFF
}

private fun readUnsignedByte(): Int {
return readByte().toInt() and 0xFF
}
fun readUnsignedByte(): Int {
return readByte().toInt() and 0xFF
}

private fun readId(): Long {
// As long as we don't interpret IDs, reading signed values here is fine.
return when (identifierByteSize) {
1 -> readByte().toLong()
2 -> readShort().toLong()
4 -> readInt().toLong()
8 -> readLong()
else -> throw IllegalArgumentException("ID Length must be 1, 2, 4, or 8")
fun readId(): Long {
// As long as we don't interpret IDs, reading signed values here is fine.
return when (identifierByteSize) {
1 -> readByte().toLong()
2 -> readShort().toLong()
4 -> readInt().toLong()
8 -> readLong()
else -> throw IllegalArgumentException("ID Length must be 1, 2, 4, or 8")
}
}
}

private fun readBoolean(): Boolean {
return readByte()
.toInt() != 0
}
fun readBoolean(): Boolean {
return readByte()
.toInt() != 0
}

private fun readChar(): Char {
return readShort().toChar()
}
fun readChar(): Char {
return readShort().toChar()
}

private fun readFloat(): Float {
return Float.fromBits(readInt())
}
fun readFloat(): Float {
return Float.fromBits(readInt())
}

private fun readDouble(): Double {
return Double.fromBits(readLong())
fun readDouble(): Double {
return Double.fromBits(readLong())
}
}

companion object {
Expand Down

0 comments on commit 0c43e29

Please sign in to comment.