Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions docs/jlearch/execution-state-changes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Changes in Execution State
Comment thread
CaelmBleidd marked this conversation as resolved.
Comment thread
Atos1337 marked this conversation as resolved.

```mermaid
classDiagram
class StateAnalyticsProperties{
+int depth
+int visitedAfterLastFork
+int visitedBeforeLastFork
+int stmtsSinceLastCovered
+ExecutionState? parent
+long executingTime
+double reward
+List~Double~ features
-boolean isFork
-boolean isVisitedNew
-int successorDepth
-int successorVisitedAfterLastFork
-int successorVisitedBeforeLastFork
-int successorStmtSinceLastCovered

+updateIsVisitedNew()
+updateIsFork()
}
ExecutionState o-- StateAnalyticsProperties
```

`StateAnalyticsProperties` maintains properties of `ExecutionState`, which don't need for symbolic execution, but need for `JLearch`.

* `depth: Int` - number of forks on the state's path excluded current state, if it is fork. In this case, fork is a state with more than one successor excluded implicit `NPE` branches.
* `visitedAfterLastFork: Int` - number of `stmt`, that was visited by `states` on this state's path after the last fork in first time.
* `visitedBeforeLastFork: Int` - number of `stmt`, that was visited by `states` on this state's path before the last fork in first time.
* `stmtsSinceLastCovered: Int` - number of `states` on this state's path after the last state that visited any `stmt` in first time.
* `parent: ExecutionState?` - parent of current `state`. If `UtSettings.featureProcess == false`, then it is always null, because we don't need this field in this case. If it is not null, then we can't delete `state` until all successors of this state will be deleted, which may cause memory issue.
* `executingTime: Long` - amount of time, during which this state was traversed.
* `reward: Double?` - calculated reward of this state
* `features: List<Double>` - list of extracted features for this state

Field with `successor` prefix is used for a constructor of successor properties.

* `updateIsFork()` - set `isFork` on true. This method is called when traversing of `stmt` produces more than one explicit state. Now it may be during the traversing of `IfStmt`, `SwitchStmt`, `AssignStmt` or `InvokeStmt`.
* `updateIsCoveredNew()` - set `isVisitedNew` on true, set `stmtsSinceLastCovered` on zero and increase `visitedAfterLastFork` on 1. This method is called in `UtBotSymbolicEngine` after new state `s` is polled and `s.stmt` was not visited yet.
17 changes: 17 additions & 0 deletions docs/jlearch/features.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Collecting features

Now we collect 13 features, that will be described in original [paper](https://files.sri.inf.ethz.ch/website/papers/ccs21-learch.pdf), except constraint representation, but it can be extended.

* `stack` - size of state’s current call stack.
* `successor` - number of successors of state’s current basic block.
* `testCase` - number of test cases generated so far
* `coverageByBranch` - number of instructions, which was covered first time on our last branch
* `coverageByPath` - number of instructions, which was covered first time on our path
* `depth` - number of forks already performed along state’s path.
* `cpicnt` - number of instructions visited in state's current function.
* `icnt` - number of times for which st ate’s current instruction has
been visited
* `covNew` - number of instructions executed by st ate since the last
time a new instruction is covered
* `subpath` - number of times for which st ate’s subpaths have been
visited. The length of the subpaths can be 1, 2, 4, or 8 respectively
152 changes: 152 additions & 0 deletions docs/jlearch/jlearch-architecture.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
# JLearch architecture

# Global Class Diagram

```mermaid
classDiagram
class FeatureProcessor{
dumpFeatures()
}
<<interface>> FeatureProcessor
class TraverseGraphStatistics{
onVisit(ExecutionState)
onTraversed(ExecutionState)
}
class InterproceduralUnitGraph
class FeatureExtractorFactory
<<interface>> FeatureExtractorFactory
class FeatureProcessorFactory
<<interface>> FeatureProcessorFactory
class EngineAnalyticsContext
class UtBotSymbolicEngine
class NNRewardGuidedSelectorFactory
<<interface>> NNRewardGuidedSelectorFactory
class FeatureExtractor{
extractFeatures(ExecutionState)
}
<<interface>> FeatureExtractor

UtBotSymbolicEngine ..> EngineAnalyticsContext
EngineAnalyticsContext o-- FeatureProcessorFactory
EngineAnalyticsContext o-- FeatureExtractorFactory
EngineAnalyticsContext o-- NNRewardGuidedSelectorFactory

FeatureProcessor --|> TraverseGraphStatistics
InterproceduralUnitGraph o-- TraverseGraphStatistics
UtBotSymbolicEngine *-- FeatureProcessor
UtBotSymbolicEngine *-- InterproceduralUnitGraph

class Predictors
class NNStateRewardPredictor
class NNRewardGuidedSelector


class GreedySearch

class BasePathSelector

GreedySearch --|> BasePathSelector
NNRewardGuidedSelector --|> GreedySearch

UtBotSymbolicEngine *-- BasePathSelector

Predictors o-- NNStateRewardPredictor
NNRewardGuidedSelector ..> Predictors
NNRewardGuidedSelector *-- FeatureExtractor

NNStateRewardPredictorSmile --|> NNStateRewardPredictor
NNStateRewardPredictorTorch --|> NNStateRewardPredictor

NNStateRewardGuidedSelectorWithRecalculationWeight --|> NNRewardGuidedSelector
NNStateRewardGuidedSelectorWithoutRecalculationWeight --|> NNRewardGuidedSelector
```

This diagram doesn't illustrate some details, so read them below.

# FeatureProcessor

It is interface in framework-module, that allows to use implementation from analytics module.

* `dumpFeatures(state: ExecutionState)` - dump features and rewards in some format on disk. Called at the end of traverse in `UtBotSymbolicEngine`

## Implementation class diagram

```mermaid
classDiagram
class FeatureProcessorWithStatesRepetition{
-Map~Int, FeatureList~ dumpedStates
-Set~Stmt~ visitedStmts
-List~TestCase~ testCases
-int generatedTestCases
dumpFeatures()
}

class FeatureExtractor{
extractFeatures(ExecutionState)
}

class TraverseGraphStatistics{
onVisit(ExecutionState)
onTraversed(ExecutionState)
}

class RewardEstimator{
calculateRewards(List~TestCase~)
}

class TestCase{
+List<State> states
+int newCoverage
+int testIndex
}

FeatureProcessorWithStatesRepetition --|> TraverseGraphStatistics
FeatureProcessorWithStatesRepetition o-- FeatureExtractor
FeatureProcessorWithStatesRepetition o-- RewardEstimator
FeatureProcessorWithStatesRepetition ..> EngineAnalyticsContext

```

`State = Pair<Int, Long>`

`FeatureList = List<Double`

## RewardEstimator

Maintains calculation of reward.

* `calculateRewards(List<TestCase>): Map<Int, Double>` - calculates `coverage` for each state and `time` for each state. `Coverage` - sum of `newCoverage` by `TestCase` that contains its state. `Time` - sum of `state.executingTime` by all states, that has this state on its path. Then calculates `reward(coverage, time)`.

## FeatureProcessorWithStatesRepetition

* `onVisit(state: ExecutionState)` - extractFeatures for state
* `onTraversed(state: ExecutionState)` - create `TestCase`, so we go from `state` to `state.parent` while it is not root, for each `state` on path add its features to `dumpedStates`, calculate coverage of its `TestCase`, increment `generatedTestCases` on 1 and add new `TestCase` in `testCases`.
* `dumpFeatures()` - call `RewardEstimator.calculateRewards()` and write `csv` file for each `TestCase` in format: `newCov,features,reward` for each `state` in it. `newCov` - flag that indicates whether this `TestCase` cover something new or not. So in this approach, each `state` will be written as many times as the number of `TestCase` that has it.
For creating `FeatureExtractor`, it uses `FeatureExtractorFactory` from `EngineAnalyticsContext`.

# FeatureExtractor

It is interface in framework-module, that allows to use implementation from analytics module.
* `extractFeatures(state: ExecutionState)` - create features list for state and store it in `state.features`. Now we extract all features, which were described in [paper](https://files.sri.inf.ethz.ch/website/papers/ccs21-learch.pdf). In feature, we can extend the feature list by other features, for example, NeuroSMT.

# NNStateRewardPredictor

Interface for reward predictors. Now it has two implementations in `analytics` module:

* `NNStateRewardPredictorSmile`: it uses our own format to store feedforward neural network, and it uses `Smile` library to do multiplication of matrix.
* `NNStateRewardPredictorTorch`: it assumed that a model is any type of model in `pt` format. It uses the `Deep Java library` to use such models.

It should be created at the beginning of work and stored at `Predictors` class to be used in `NNRewardGuidedSelector` from the `framework` module.


# NNStateRewardGuidedSelector

It uses an `EngineAnalyticsContext` to create `FeatureExtractor`.
We override `ExecutionState.weight` as `NNStateRewardPredictor.predict(this.features)`.
We have two different implementantions:
* `NNStateRewardGuidedSelectorWithRecalculation`: we recalculate reward every time, so in `ExecutionState.weight` we extract features and call predict.
* `NNStateRewardGuidedSlectorWithoutRecalculation`: we extract features in `offerImpl`, calculate `reward` and store it in `ExecutionState.reward` without recalculation it every time.

# EngineAnalyticsContext

It is an object that should be filled by factories in the beginning of work to allow objects from the `framework` module using objects from `analytics` module.
93 changes: 93 additions & 0 deletions docs/jlearch/new-heuristical-path-selectors.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# GreedySearch

```mermaid
classDiagram
class GreedySearch{
-Set~ExecutionState~ states
+ExecutionState.weight
}
GreedySearch --|> BasePathSelector
```
Base methods such as `offer` or `remove` is implemented pretty simple and just a delegation to `states`.

In `peekImpl` we find the set of `states` with maximum `weight` and peek random among them, so to use this class in implementation of some `pathSelector`, you just need to override an `ExecutionState.weight`.

# SubpathStatistics

```mermaid
classDiagram
class SubpathStatistics{
+int index
-Map~Subpath, Int~ subpathCount
subpathCount(ExecutionState)
}
class TraverseGraphStatistics{
onVisit(ExecutionState)
}

SubpathStatistics --|> TraverseGraphStatistics
TraverseGraphStatistics o-- InterProceduralUnitGraph
```
`Subpath` = `List<Edge>`

This class maintains frequency of each subpath with length `2^index`, which is presented as `List<Edge>`, in a certain instance of `InterproceduralUnitGraph`

* `onVisit(state: ExecutionState)` - we calculate subpath of this state and increment its frequency on `1`
* `subpathCount(state: ExecutionState)` - we calculate subpath of this state and return its frequency

# SubpathGuidedSelector

```mermaid
classDiagram
SubpathGuidedSelector o-- SubpathStatistics
SubpathGuidedSelector --|> GreedySearch
```

Inspired by [paper](http://pxzhang.cn/paper/concolic_testing/oopsla13-pgse.pdf).

We override `ExecutionState.weight` as `-StatementStatistics.subpathCount(this)`, so we pick the `state`, which `subpath` is less traveled.

# StatementStatistics

```mermaid
classDiagram
class StatementStatistics{
-Map~Stmt, Int~ statementsCount
-Map~SootMethod, Int~ statementsInMethodCount
+statementCount(ExecutionState)
+statementsInMethodCount(ExecutionState)
}

class TraverseGraphStatistics{
onVisit(ExecutionState)
}

StatementStatistics --|> TraverseGraphStatistics
TraverseGraphStatistics o-- InterProceduralUnitGraph
```

This class maintains frequency of each `Stmt` and number of `Stmt`, that was visited in some `SootMethod`, on a certain instance of `InterproceduralUnitGraph`.

* `onVisit(state: ExecutionState)` - increment frequency of state's `stmt` on 1. If we visit this `stmt` for the first time, then increment number of `Stmt`, that we visit in the current state's `method`, on 1.
* `statementCount(state: ExecutionState)` - get a frequency of state's `stmt`
* `statementsInMethodCount(state: ExecutionState)` - get number of `stmt`, that was visited in the current state's `method`.

# CPInstSelector

```mermaid
classDiagram
CPInstSelector o-- StatementStatistics
CPInstSelector --|> NonUniformRandomSearch
```

Override `ExecutionState.cost` as `StatementStatistics.statementInMethodCount(this)`, so we are more likely to explore the least explored `method`.

# ForkDepthSelector

```mermaid
classDiagram
ForkDepthSelector --|> NonUniformRandomSearch
```

Override `ExecutionState.cost` as `ExecutionState.depth`, so we are more likely to explore the least deep `state` in terms of the number of forks on its path.

4 changes: 4 additions & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,8 @@ eclipse_aether_version=1.1.0
maven_wagon_version=3.5.1
maven_plugin_api_version=3.8.5
maven_plugin_tools_version=3.6.4
javacpp_version=1.5.3
jsoup_version=1.7.2
djl_api_version=0.17.0
pytorch_native_version=1.9.1
Comment thread
Atos1337 marked this conversation as resolved.
# soot also depends on asm, so there could be two different versions
25 changes: 23 additions & 2 deletions utbot-analytics/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ String classifier = osName + "-x86_64"

evaluationDependsOn(':utbot-framework')
compileTestJava.dependsOn tasks.getByPath(':utbot-framework:testClasses')

dependencies {
implementation(project(":utbot-framework"))
implementation(project(':utbot-instrumentation'))
Expand All @@ -29,6 +30,7 @@ dependencies {

implementation group: 'org.bytedeco', name: 'arpack-ng', version: "3.7.0-1.5.4", classifier: "$classifier"
implementation group: 'org.bytedeco', name: 'openblas', version: "0.3.10-1.5.4", classifier: "$classifier"
implementation group: 'org.bytedeco', name: 'javacpp', version: javacpp_version, classifier: "$classifier"

implementation group: 'tech.tablesaw', name: 'tablesaw-core', version: '0.38.2'
implementation group: 'tech.tablesaw', name: 'tablesaw-jsplot', version: '0.38.2'
Expand All @@ -37,13 +39,19 @@ dependencies {

implementation group: 'com.github.javaparser', name: 'javaparser-core', version: '3.22.1'

implementation group: 'org.jsoup', name: 'jsoup', version: jsoup_version

implementation "ai.djl:api:$djl_api_version"
implementation "ai.djl.pytorch:pytorch-engine:$djl_api_version"
implementation "ai.djl.pytorch:pytorch-native-auto:$pytorch_native_version"

testCompile project(':utbot-framework').sourceSets.test.output
}

test {

useJUnitPlatform{
excludeTags 'Summary'
useJUnitPlatform {
excludeTags 'Summary'
}

}
Expand All @@ -54,4 +62,17 @@ processResources {
into "models"
}
}
}

jar {
dependsOn classes
manifest {
attributes 'Main-Class': 'org.utbot.QualityAnalysisKt'
}

from {
configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
}

duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package org.utbot.features

import org.utbot.analytics.FeatureExtractor
import org.utbot.analytics.FeatureExtractorFactory
import org.utbot.engine.InterProceduralUnitGraph

/**
* Implementation of feature extractor factory
*/
class FeatureExtractorFactoryImpl : FeatureExtractorFactory {
override operator fun invoke(graph: InterProceduralUnitGraph): FeatureExtractor = FeatureExtractorImpl(graph)
}
Loading