diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/ConcreteExecutor.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/ConcreteExecutor.kt index 5f80edec97..3194e48560 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/ConcreteExecutor.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/ConcreteExecutor.kt @@ -22,10 +22,13 @@ import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.FieldId import org.utbot.framework.plugin.api.ConcreteContextLoadingResult import org.utbot.framework.plugin.api.SpringRepositoryId +import org.utbot.framework.plugin.api.ExecutableId import org.utbot.framework.plugin.api.util.UtContext import org.utbot.framework.plugin.api.util.signature import org.utbot.instrumentation.instrumentation.Instrumentation +import org.utbot.instrumentation.instrumentation.execution.ResultOfInstrumentation import org.utbot.instrumentation.process.generated.ComputeStaticFieldParams +import org.utbot.instrumentation.process.generated.GetResultOfInstrumentationParams import org.utbot.instrumentation.process.generated.GetSpringRepositoriesParams import org.utbot.instrumentation.process.generated.InvokeMethodCommandParams import org.utbot.instrumentation.rd.InstrumentedProcess @@ -313,3 +316,12 @@ fun ConcreteExecutor<*, *>.computeStaticField(fieldId: FieldId): Result = kryoHelper.readObject(result.result) } } + +fun ConcreteExecutor<*, *>.getInstrumentationResult(methodUnderTest: ExecutableId): ResultOfInstrumentation = + runBlocking { + withProcess { + val params = GetResultOfInstrumentationParams(methodUnderTest.classId.name, methodUnderTest.signature) + val result = instrumentedProcessModel.getResultOfInstrumentation.startSuspending(lifetime, params) + kryoHelper.readObject(result.result) + } + } diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/et/TraceHandler.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/et/TraceHandler.kt index 6676be0da4..c31e403519 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/et/TraceHandler.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/et/TraceHandler.kt @@ -52,7 +52,7 @@ data class PutStaticInstruction( private data class ClassToMethod( val className: String, - val methodName: String + val methodSignature: String ) class ProcessingStorage { @@ -65,6 +65,8 @@ class ProcessingStorage { private val instructionsData = mutableMapOf() private val classToInstructionsCount = mutableMapOf() + private val methodIdToInstructionsIds = mutableMapOf>() + fun addClass(className: String): Int { val id = classToId.getOrPut(className) { classToId.size } idToClass.putIfAbsent(id, className) @@ -75,8 +77,8 @@ class ProcessingStorage { return classToId[className]!!.toLong() * SHIFT + localId } - fun addClassMethod(className: String, methodName: String): Int { - val classToMethod = ClassToMethod(className, methodName) + fun addClassMethod(className: String, methodSignature: String): Int { + val classToMethod = ClassToMethod(className, methodSignature) val id = classMethodToId.getOrPut(classToMethod) { classMethodToId.size } idToClassMethod.putIfAbsent(id, classToMethod) return id @@ -88,10 +90,11 @@ class ProcessingStorage { return className to localId } - fun addInstruction(id: Long, instructionData: InstructionData) { + fun addInstruction(id: Long, methodId: Int, instructionData: InstructionData) { instructionsData.computeIfAbsent(id) { val (className, _) = computeClassNameAndLocalId(id) classToInstructionsCount.merge(className, 1, Long::plus) + methodIdToInstructionsIds.getOrPut(methodId) { mutableListOf() }.add(id) instructionData } } @@ -103,6 +106,11 @@ class ProcessingStorage { return instructionsData.getValue(id) } + fun getInstructionsIds(className: String, methodSignature: String): List? { + val methodId = classMethodToId[ClassToMethod(className, methodSignature)] + return methodIdToInstructionsIds[methodId] + } + companion object { private const val SHIFT = 1.toLong().shl(32) // 2 ^ 32 } diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/et/TraceListStrategy.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/et/TraceListStrategy.kt index 157ea89605..d04f177219 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/et/TraceListStrategy.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/et/TraceListStrategy.kt @@ -27,7 +27,7 @@ class TraceListStrategy( methodVisitor: MethodVisitor ) { currentMethodSignature = name + descriptor - currentClassMethodId = storage.addClassMethod(className, name) + currentClassMethodId = storage.addClassMethod(className, currentMethodSignature) } override fun visitCode(mv: MethodVisitor, lvs: LocalVariablesSorter) { @@ -74,7 +74,7 @@ class TraceListStrategy( private fun processNewInstruction(mv: MethodVisitor, instructionData: InstructionData): MethodVisitor { val id = nextId() - storage.addInstruction(id, instructionData) + storage.addInstruction(id, currentClassMethodId, instructionData) return inserter.insertUtilityInstructions(mv, id) } diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/RemovingConstructFailsUtExecutionInstrumentation.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/RemovingConstructFailsUtExecutionInstrumentation.kt index 803f0b6e8b..359d2dd2e9 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/RemovingConstructFailsUtExecutionInstrumentation.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/RemovingConstructFailsUtExecutionInstrumentation.kt @@ -94,6 +94,9 @@ class RemovingConstructFailsUtExecutionInstrumentation( } } + override fun getResultOfInstrumentation(className: String, methodSignature: String): ResultOfInstrumentation = + delegateInstrumentation.getResultOfInstrumentation(className, methodSignature) + private fun shallowlyRemoveFailingCalls(model: UtModel): UtModel = when { model !is UtAssembleModel -> model model.instantiationCall.thrownConcreteException != null -> model.classId.defaultValueModel() diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/SimpleUtExecutionInstrumentation.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/SimpleUtExecutionInstrumentation.kt index bfbdabb159..82d8fe8e25 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/SimpleUtExecutionInstrumentation.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/SimpleUtExecutionInstrumentation.kt @@ -139,6 +139,9 @@ class SimpleUtExecutionInstrumentation( ) } + override fun getResultOfInstrumentation(className: String, methodSignature: String): ResultOfInstrumentation = + ResultOfInstrumentation(traceHandler.processingStorage.getInstructionsIds(className, methodSignature)) + override fun getStaticField(fieldId: FieldId): Result = delegateInstrumentation.getStaticField(fieldId).map { value -> UtModelConstructor.createOnlyUserClassesConstructor( diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/UtExecutionInstrumentation.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/UtExecutionInstrumentation.kt index 9b5217df55..5d9dc83b6c 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/UtExecutionInstrumentation.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/execution/UtExecutionInstrumentation.kt @@ -70,6 +70,8 @@ data class UtConcreteExecutionResult( } } +data class ResultOfInstrumentation(val instructionsIds: List?) + interface UtExecutionInstrumentation : Instrumentation { override fun invoke( clazz: Class<*>, @@ -88,6 +90,8 @@ interface UtExecutionInstrumentation : Instrumentation PreliminaryUtConcreteExecutionResult) -> PreliminaryUtConcreteExecutionResult ): UtConcreteExecutionResult + fun getResultOfInstrumentation(className: String, methodSignature: String): ResultOfInstrumentation + interface Factory : Instrumentation.Factory { override fun create(): TInstrumentation = create(SimpleInstrumentationContext()) diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/spring/SpringUtExecutionInstrumentation.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/spring/SpringUtExecutionInstrumentation.kt index eb24804aa9..fbac687286 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/spring/SpringUtExecutionInstrumentation.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/instrumentation/spring/SpringUtExecutionInstrumentation.kt @@ -18,6 +18,7 @@ import org.utbot.framework.plugin.api.util.SpringModelUtils.mockMvcPerformMethod import org.utbot.framework.plugin.api.util.jClass import org.utbot.framework.process.kryo.KryoHelper import org.utbot.instrumentation.instrumentation.ArgumentList +import org.utbot.instrumentation.instrumentation.execution.ResultOfInstrumentation import org.utbot.instrumentation.instrumentation.execution.PreliminaryUtConcreteExecutionResult import org.utbot.instrumentation.instrumentation.execution.UtConcreteExecutionData import org.utbot.instrumentation.instrumentation.execution.UtConcreteExecutionResult @@ -118,6 +119,9 @@ class SpringUtExecutionInstrumentation( } } + override fun getResultOfInstrumentation(className: String, methodSignature: String): ResultOfInstrumentation = + delegateInstrumentation.getResultOfInstrumentation(className, methodSignature) + override fun getStaticField(fieldId: FieldId): Result<*> = delegateInstrumentation.getStaticField(fieldId) private fun getRelevantBeans(clazz: Class<*>): Set = relatedBeansCache.getOrPut(clazz) { diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/InstrumentedProcessMain.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/InstrumentedProcessMain.kt index 12b2f996b3..f039ae0e2e 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/InstrumentedProcessMain.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/process/InstrumentedProcessMain.kt @@ -11,10 +11,12 @@ import org.utbot.framework.process.kryo.KryoHelper import org.utbot.instrumentation.agent.Agent import org.utbot.instrumentation.instrumentation.Instrumentation import org.utbot.instrumentation.instrumentation.coverage.CoverageInstrumentation +import org.utbot.instrumentation.instrumentation.execution.UtExecutionInstrumentation import org.utbot.instrumentation.process.generated.CollectCoverageResult import org.utbot.instrumentation.process.generated.InstrumentedProcessModel import org.utbot.instrumentation.process.generated.InvokeMethodCommandResult import org.utbot.instrumentation.process.generated.instrumentedProcessModel +import org.utbot.instrumentation.process.generated.GetResultOfInstrumentationResult import org.utbot.rd.IdleWatchdog import org.utbot.rd.ClientProtocolBuilder import org.utbot.rd.RdSettingsContainerFactory @@ -146,6 +148,13 @@ private fun InstrumentedProcessModel.setup(kryoHelper: KryoHelper, watchdog: Idl Agent.dynamicClassTransformer.addUserPaths(pathsToUserClasses) instrumentation.run { setupAdditionalRdResponses(kryoHelper, watchdog) } } + watchdog.measureTimeForActiveCall(getResultOfInstrumentation, "Getting instrumentation result") { params -> + HandlerClassesLoader.loadClass(params.className) + val result = (instrumentation as UtExecutionInstrumentation).getResultOfInstrumentation( + params.className, params.methodSignature + ) + GetResultOfInstrumentationResult(kryoHelper.writeObject(result)) + } watchdog.measureTimeForActiveCall(addPaths, "User and dependency classpath setup") { params -> pathsToUserClasses = params.pathsToUserClasses.split(File.pathSeparatorChar).toSet() HandlerClassesLoader.addUrls(pathsToUserClasses) diff --git a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/generated/InstrumentedProcessModel.Generated.kt b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/generated/InstrumentedProcessModel.Generated.kt index 2fe14c35d9..c2d67d0512 100644 --- a/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/generated/InstrumentedProcessModel.Generated.kt +++ b/utbot-instrumentation/src/main/kotlin/org/utbot/instrumentation/rd/generated/InstrumentedProcessModel.Generated.kt @@ -22,6 +22,7 @@ class InstrumentedProcessModel private constructor( private val _addPaths: RdCall, private val _warmup: RdCall, private val _setInstrumentation: RdCall, + private val _getResultOfInstrumentation: RdCall, private val _invokeMethodCommand: RdCall, private val _collectCoverage: RdCall, private val _computeStaticField: RdCall, @@ -35,6 +36,8 @@ class InstrumentedProcessModel private constructor( override fun registerSerializersCore(serializers: ISerializers) { serializers.register(AddPathsParams) serializers.register(SetInstrumentationParams) + serializers.register(GetResultOfInstrumentationParams) + serializers.register(GetResultOfInstrumentationResult) serializers.register(InvokeMethodCommandParams) serializers.register(InvokeMethodCommandResult) serializers.register(CollectCoverageParams) @@ -64,7 +67,7 @@ class InstrumentedProcessModel private constructor( } - const val serializationHash = 8567129171874407469L + const val serializationHash = -1792926381265506685L } override val serializersOwner: ISerializersOwner get() = InstrumentedProcessModel @@ -87,6 +90,11 @@ class InstrumentedProcessModel private constructor( */ val setInstrumentation: RdCall get() = _setInstrumentation + /** + * This command is sent to the instrumented process from the [ConcreteExecutor] to get instrumentation result + */ + val getResultOfInstrumentation: RdCall get() = _getResultOfInstrumentation + /** * The main process requests the instrumented process to execute a method with the given [signature], which declaring class's name is [className]. @@ -122,6 +130,7 @@ class InstrumentedProcessModel private constructor( _addPaths.async = true _warmup.async = true _setInstrumentation.async = true + _getResultOfInstrumentation.async = true _invokeMethodCommand.async = true _collectCoverage.async = true _computeStaticField.async = true @@ -133,6 +142,7 @@ class InstrumentedProcessModel private constructor( bindableChildren.add("addPaths" to _addPaths) bindableChildren.add("warmup" to _warmup) bindableChildren.add("setInstrumentation" to _setInstrumentation) + bindableChildren.add("getResultOfInstrumentation" to _getResultOfInstrumentation) bindableChildren.add("invokeMethodCommand" to _invokeMethodCommand) bindableChildren.add("collectCoverage" to _collectCoverage) bindableChildren.add("computeStaticField" to _computeStaticField) @@ -146,6 +156,7 @@ class InstrumentedProcessModel private constructor( RdCall(AddPathsParams, FrameworkMarshallers.Void), RdCall(FrameworkMarshallers.Void, FrameworkMarshallers.Void), RdCall(SetInstrumentationParams, FrameworkMarshallers.Void), + RdCall(GetResultOfInstrumentationParams, GetResultOfInstrumentationResult), RdCall(InvokeMethodCommandParams, InvokeMethodCommandResult), RdCall(CollectCoverageParams, CollectCoverageResult), RdCall(ComputeStaticFieldParams, ComputeStaticFieldResult), @@ -162,6 +173,7 @@ class InstrumentedProcessModel private constructor( print("addPaths = "); _addPaths.print(printer); println() print("warmup = "); _warmup.print(printer); println() print("setInstrumentation = "); _setInstrumentation.print(printer); println() + print("getResultOfInstrumentation = "); _getResultOfInstrumentation.print(printer); println() print("invokeMethodCommand = "); _invokeMethodCommand.print(printer); println() print("collectCoverage = "); _collectCoverage.print(printer); println() print("computeStaticField = "); _computeStaticField.print(printer); println() @@ -176,6 +188,7 @@ class InstrumentedProcessModel private constructor( _addPaths.deepClonePolymorphic(), _warmup.deepClonePolymorphic(), _setInstrumentation.deepClonePolymorphic(), + _getResultOfInstrumentation.deepClonePolymorphic(), _invokeMethodCommand.deepClonePolymorphic(), _collectCoverage.deepClonePolymorphic(), _computeStaticField.deepClonePolymorphic(), @@ -247,7 +260,7 @@ data class AddPathsParams ( /** - * #### Generated from [InstrumentedProcessModel.kt:29] + * #### Generated from [InstrumentedProcessModel.kt:38] */ data class CollectCoverageParams ( val clazz: ByteArray @@ -304,7 +317,7 @@ data class CollectCoverageParams ( /** - * #### Generated from [InstrumentedProcessModel.kt:33] + * #### Generated from [InstrumentedProcessModel.kt:42] */ data class CollectCoverageResult ( val coverageInfo: ByteArray @@ -361,7 +374,7 @@ data class CollectCoverageResult ( /** - * #### Generated from [InstrumentedProcessModel.kt:37] + * #### Generated from [InstrumentedProcessModel.kt:46] */ data class ComputeStaticFieldParams ( val fieldId: ByteArray @@ -418,7 +431,7 @@ data class ComputeStaticFieldParams ( /** - * #### Generated from [InstrumentedProcessModel.kt:41] + * #### Generated from [InstrumentedProcessModel.kt:50] */ data class ComputeStaticFieldResult ( val result: ByteArray @@ -475,7 +488,127 @@ data class ComputeStaticFieldResult ( /** - * #### Generated from [InstrumentedProcessModel.kt:45] + * #### Generated from [InstrumentedProcessModel.kt:18] + */ +data class GetResultOfInstrumentationParams ( + val className: String, + val methodSignature: String +) : IPrintable { + //companion + + companion object : IMarshaller { + override val _type: KClass = GetResultOfInstrumentationParams::class + + @Suppress("UNCHECKED_CAST") + override fun read(ctx: SerializationCtx, buffer: AbstractBuffer): GetResultOfInstrumentationParams { + val className = buffer.readString() + val methodSignature = buffer.readString() + return GetResultOfInstrumentationParams(className, methodSignature) + } + + override fun write(ctx: SerializationCtx, buffer: AbstractBuffer, value: GetResultOfInstrumentationParams) { + buffer.writeString(value.className) + buffer.writeString(value.methodSignature) + } + + + } + //fields + //methods + //initializer + //secondary constructor + //equals trait + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || other::class != this::class) return false + + other as GetResultOfInstrumentationParams + + if (className != other.className) return false + if (methodSignature != other.methodSignature) return false + + return true + } + //hash code trait + override fun hashCode(): Int { + var __r = 0 + __r = __r*31 + className.hashCode() + __r = __r*31 + methodSignature.hashCode() + return __r + } + //pretty print + override fun print(printer: PrettyPrinter) { + printer.println("GetResultOfInstrumentationParams (") + printer.indent { + print("className = "); className.print(printer); println() + print("methodSignature = "); methodSignature.print(printer); println() + } + printer.print(")") + } + //deepClone + //contexts +} + + +/** + * #### Generated from [InstrumentedProcessModel.kt:23] + */ +data class GetResultOfInstrumentationResult ( + val result: ByteArray +) : IPrintable { + //companion + + companion object : IMarshaller { + override val _type: KClass = GetResultOfInstrumentationResult::class + + @Suppress("UNCHECKED_CAST") + override fun read(ctx: SerializationCtx, buffer: AbstractBuffer): GetResultOfInstrumentationResult { + val result = buffer.readByteArray() + return GetResultOfInstrumentationResult(result) + } + + override fun write(ctx: SerializationCtx, buffer: AbstractBuffer, value: GetResultOfInstrumentationResult) { + buffer.writeByteArray(value.result) + } + + + } + //fields + //methods + //initializer + //secondary constructor + //equals trait + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || other::class != this::class) return false + + other as GetResultOfInstrumentationResult + + if (!(result contentEquals other.result)) return false + + return true + } + //hash code trait + override fun hashCode(): Int { + var __r = 0 + __r = __r*31 + result.contentHashCode() + return __r + } + //pretty print + override fun print(printer: PrettyPrinter) { + printer.println("GetResultOfInstrumentationResult (") + printer.indent { + print("result = "); result.print(printer); println() + } + printer.print(")") + } + //deepClone + //contexts +} + + +/** + * #### Generated from [InstrumentedProcessModel.kt:54] */ data class GetSpringRepositoriesParams ( val classId: ByteArray @@ -532,7 +665,7 @@ data class GetSpringRepositoriesParams ( /** - * #### Generated from [InstrumentedProcessModel.kt:49] + * #### Generated from [InstrumentedProcessModel.kt:58] */ data class GetSpringRepositoriesResult ( val springRepositoryIds: ByteArray @@ -589,7 +722,7 @@ data class GetSpringRepositoriesResult ( /** - * #### Generated from [InstrumentedProcessModel.kt:18] + * #### Generated from [InstrumentedProcessModel.kt:27] */ data class InvokeMethodCommandParams ( val classname: String, @@ -664,7 +797,7 @@ data class InvokeMethodCommandParams ( /** - * #### Generated from [InstrumentedProcessModel.kt:25] + * #### Generated from [InstrumentedProcessModel.kt:34] */ data class InvokeMethodCommandResult ( val result: ByteArray @@ -784,7 +917,7 @@ data class SetInstrumentationParams ( /** - * #### Generated from [InstrumentedProcessModel.kt:53] + * #### Generated from [InstrumentedProcessModel.kt:62] */ data class TryLoadingSpringContextResult ( val springContextLoadingResult: ByteArray diff --git a/utbot-rd/src/main/rdgen/org/utbot/rd/models/InstrumentedProcessModel.kt b/utbot-rd/src/main/rdgen/org/utbot/rd/models/InstrumentedProcessModel.kt index f9d3d76f50..46e95705d8 100644 --- a/utbot-rd/src/main/rdgen/org/utbot/rd/models/InstrumentedProcessModel.kt +++ b/utbot-rd/src/main/rdgen/org/utbot/rd/models/InstrumentedProcessModel.kt @@ -15,6 +15,15 @@ object InstrumentedProcessModel : Ext(InstrumentedProcessRoot) { field("useBytecodeTransformation", PredefinedType.bool) } + val GetResultOfInstrumentationParams = structdef { + field("className", PredefinedType.string) + field("methodSignature", PredefinedType.string) + } + + val GetResultOfInstrumentationResult = structdef { + field("result", array(PredefinedType.byte)) + } + val InvokeMethodCommandParams = structdef { field("classname", PredefinedType.string) field("signature", PredefinedType.string) @@ -70,6 +79,11 @@ object InstrumentedProcessModel : Ext(InstrumentedProcessRoot) { documentation = "The main process sends [instrumentation] to the instrumented process" } + call("GetResultOfInstrumentation", GetResultOfInstrumentationParams, GetResultOfInstrumentationResult).apply { + async + documentation = + "This command is sent to the instrumented process from the [ConcreteExecutor] to get instrumentation result" + } call("InvokeMethodCommand", InvokeMethodCommandParams, InvokeMethodCommandResult).apply { async documentation =