Skip to content

Commit a9fcc77

Browse files
authored
Merge pull request #3 from GlebSolovev/extractor-features-dev
Extend extract-bitcode analysis with various features
2 parents 42723d1 + 355fff3 commit a9fcc77

File tree

9 files changed

+659
-169
lines changed

9 files changed

+659
-169
lines changed

.github/workflows/checks.yaml

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
name: Checks
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
pull_request:
8+
branches:
9+
- '*'
10+
11+
jobs:
12+
gradle:
13+
strategy:
14+
matrix:
15+
os: [ ubuntu-latest, macos-latest ]
16+
runs-on: ${{ matrix.os }}
17+
env:
18+
GRADLE_PUBLISH_KEY: ${{ secrets.GRADLE_PUBLISH_KEY }}
19+
GRADLE_PUBLISH_SECRET: ${{ secrets.GRADLE_PUBLISH_SECRET }}
20+
if: ${{ !contains(github.event.head_commit.message, 'ci skip') }}
21+
steps:
22+
- name: Checkout Repo
23+
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4
24+
- name: Cache Gradle Caches
25+
uses: gradle/gradle-build-action@v2
26+
27+
- name: Set up LLVM for `llvm-dis` tool
28+
uses: KyleMayes/install-llvm-action@v1
29+
with:
30+
version: '14.0'
31+
- name: Set up Python
32+
uses: actions/setup-python@v4
33+
with:
34+
python-version: '3.10'
35+
cache: 'pip'
36+
- name: Install Python dependencies
37+
run: pip install -r requirements.txt
38+
39+
- name: Run Gradle verification tasks
40+
run: ./gradlew preMerge --continue # execute each of subtasks even if one fails
41+
42+
- name: Test `decompileBitcode` task
43+
run: |
44+
./gradlew :example:decompileBitcode --output "build/bitcode/bitcode.ll"
45+
test -f example/build/bitcode/bitcode.ll
46+
if: success()
47+
48+
- name: Test `extractBitcode` task
49+
run: |
50+
./gradlew :example:extractBitcode --function 'kfun:#main(){}' --output "build/bitcode/main-bitcode.ll"
51+
test -f example/build/bitcode/main-bitcode.ll
52+
if: success()
53+
54+
- name: Test debug `extractBitcodeDebug` task
55+
run: |
56+
./gradlew :example:extractBitcodeDebug --function 'kfun:#main(){}' --output "build/bitcode/main-bitcode-debug.ll"
57+
test -f example/build/bitcode/main-bitcode-debug.ll
58+
if: success()
59+
60+
- name: Test standalone `extractSomeBitcode` task
61+
run: |
62+
./gradlew :example:extractSomeBitcode --function 'kfun:#main(){}' --input "build/bitcode/bitcode.ll" --output "build/bitcode/main-bitcode-by-standalone.ll"
63+
test -f example/build/bitcode/main-bitcode-by-standalone.ll
64+
if: success()
65+
66+
- name: Test multiple functions to extract
67+
run: |
68+
./gradlew :example:extractSomeBitcode --function 'kfun:#main(){}' --function 'kfun:#main(){}' --function 'ThrowIllegalArgumentException' --function 'non-existent' --input "build/bitcode/bitcode.ll" --output "build/bitcode/multiple-functions.ll" --verbose
69+
test -f example/build/bitcode/multiple-functions.ll
70+
if: success()
71+
72+
- name: Test extraction by function patterns
73+
run: |
74+
./gradlew :example:extractSomeBitcode --function-pattern '.*#main.*' --function-pattern '.*#mai.*' --function-pattern 'ThrowIllegalArgumentException' --function-pattern 'non-existent' --input "build/bitcode/bitcode.ll" --output "build/bitcode/patterns.ll" --verbose
75+
test -f example/build/bitcode/patterns.ll
76+
if: success()
77+
78+
- name: Test ignore by function patterns
79+
run: |
80+
./gradlew :example:extractSomeBitcode --function 'kfun:#main(){}' --function 'ThrowIllegalArgumentException' --ignore-function-pattern '.*#main.*' --ignore-function-pattern 'non-existent' --input "build/bitcode/bitcode.ll" --output "build/bitcode/ignore-patterns.ll" --verbose
81+
test -f example/build/bitcode/ignore-patterns.ll
82+
if: success()
83+
84+
- name: Test extraction by line patterns
85+
run: |
86+
./gradlew :example:extractSomeBitcode --line-pattern '.*kfun:.*#hashCode\(\)\{\}kotlin\.Int' --line-pattern 'unreachable' --line-pattern 'non-existent' --input "build/bitcode/bitcode.ll" --output "build/bitcode/line-patterns.ll" --verbose
87+
test -f example/build/bitcode/line-patterns.ll
88+
if: success()
89+
90+
# warning: this test depends on the build.gradle.kts configuration
91+
- name: Test standalone `decompileSomeBitcode` task customization
92+
run: |
93+
./gradlew :example:decompileBitcode
94+
./gradlew :example:decompileSomeBitcode --input "build/bitcode/releaseSources/out.bc"
95+
test -f example/build/bitcode/standalone-bitcode.ll
96+
if: success()
97+
98+
# warning: this test depends on the build.gradle.kts configuration
99+
- name: Test custom ExtractBitcodeTask
100+
run: |
101+
./gradlew :example:extractCustomBitcode --output "build/bitcode/custom-bitcode.ll"
102+
test -f example/build/bitcode/custom-bitcode.ll
103+
if: success()

.github/workflows/pre-merge.yaml

Lines changed: 0 additions & 64 deletions
This file was deleted.

example/build.gradle.kts

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import com.glebsolovev.kotlin.bitcodetools.gradle.plugin.DecompileBitcodeTask
2+
import com.glebsolovev.kotlin.bitcodetools.gradle.plugin.ExtractBitcodeTask
3+
14
plugins {
25
kotlin("multiplatform")
36
id("com.glebsolovev.kotlin.bitcodetools.gradle.plugin")
@@ -51,6 +54,13 @@ kotlin {
5154
}
5255
}
5356

57+
/* Configure the bitcode-analysis pipeline for your Kotlin/Native project:
58+
* `decompileBitcode` and `decompileDebugBitcode` tasks;
59+
* `extractBitcode` and `extractDebugBitcode` tasks.
60+
* They are connected to the project and to each other,
61+
* so do not use them to analyze some stand-alone code.
62+
*/
63+
5464
decompileBitcodeConfig {
5565
val hostOs: String = System.getProperty("os.name")
5666
val arch: String = System.getProperty("os.arch")
@@ -62,7 +72,6 @@ decompileBitcodeConfig {
6272
else -> throw GradleException("Unsupported target platform: $hostOs / $arch")
6373
}
6474
linkDebugTaskName = linkTaskName.replace("Release", "Debug")
65-
tmpArtifactsDirectoryPath = "build/bitcode"
6675
setCompilerFlags = { compilerFlags ->
6776
kotlin {
6877
listOf(macosX64(), macosArm64(), mingwX64(), linuxX64()).forEach {
@@ -73,10 +82,49 @@ decompileBitcodeConfig {
7382
}
7483
println("[example] following compiler flags are set: $compilerFlags")
7584
}
85+
artifactsDirectoryPath = "build/bitcode"
7686
llOutputFileName = "example-bitcode.ll"
7787
llDebugOutputFileName = "example-bitcode-debug.ll"
7888
}
7989

8090
extractFromDecompiledBitcodeConfig {
91+
functionNames = listOf("kfun:#main(){}", "ThrowIllegalArgumentException")
92+
functionPatterns = listOf(".*main.*")
93+
linePatterns = listOf(
94+
"%2 = icmp eq i64 %1, 0",
95+
".*call.*kfun:.*#hashCode\\(\\)\\{\\}kotlin\\.Int.*"
96+
)
97+
ignorePatterns = listOf("kfun:kotlin.*")
98+
recursionDepth = 1u
99+
verbose = true
100+
}
101+
102+
/* The stand-alone tasks are already registered.
103+
* However, you can make them more convenient:
104+
* configure the default values of the most frequently used arguments here and
105+
* define others & override the configured ones in the command line.
106+
*/
107+
108+
tasks.named<DecompileBitcodeTask>("decompileSomeBitcode") {
109+
outputFilePath = "build/bitcode/standalone-bitcode.ll"
110+
}
111+
112+
tasks.named<ExtractBitcodeTask>("extractSomeBitcode") {
81113
recursionDepth = 1u
114+
inputFilePath = "build/bitcode/standalone-bitcode.ll"
115+
verbose = true
116+
}
117+
118+
/* Register new custom ExtractBitcodeTask &
119+
* configure it with the full power of Gradle.
120+
*/
121+
122+
tasks.register<ExtractBitcodeTask>("extractCustomBitcode") {
123+
dependsOn("decompileBitcode")
124+
doFirst {
125+
println("It's impolite not to say hello at the very beginning. So, hello!")
126+
}
127+
inputFilePath = "build/bitcode/bitcode.ll"
128+
outputFilePath = "build/bitcode/custom-bitcode.ll"
129+
functionNames = listOf("kfun:#main(){}")
82130
}

plugin-build/plugin/src/main/java/com/glebsolovev/kotlin/bitcodetools/gradle/plugin/BitcodeAnalysisPlugin.kt

Lines changed: 49 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ abstract class BitcodeAnalysisPlugin : Plugin<Project> {
3131
""
3232
}If you still would like to overcome this behaviour, ${
3333
""
34+
}use the standalone `extractSomeBitcode` task / ${
35+
""
3436
}register a custom `ExtractBitcodeTask` task and run it instead."
3537

3638
const val GROUP_NAME = "bitcode analysis"
@@ -40,25 +42,28 @@ abstract class BitcodeAnalysisPlugin : Plugin<Project> {
4042
val (decompileBitcodeExtension, extractBitcodeExtension) = project.createExtensions()
4143
project.registerStandaloneBitcodeTasks()
4244
project.afterEvaluate {
43-
val tmpArtifactsDirectory = resolveToRelativeDirectory(decompileBitcodeExtension.tmpArtifactsDirectoryPath)
44-
listOf(
45-
resolveReleasePipelineParameters(
46-
tmpArtifactsDirectory,
47-
decompileBitcodeExtension,
48-
extractBitcodeExtension
49-
),
50-
resolveDebugPipelineParameters(
51-
tmpArtifactsDirectory,
52-
decompileBitcodeExtension,
53-
extractBitcodeExtension
45+
val artifactsDirectory = resolveToRelativeDirectory(decompileBitcodeExtension.artifactsDirectoryPath)
46+
buildList {
47+
add(
48+
resolveReleasePipelineParameters(
49+
artifactsDirectory,
50+
decompileBitcodeExtension,
51+
extractBitcodeExtension
52+
)
5453
)
55-
).forEach {
56-
it.registerTasks(
54+
if (decompileBitcodeExtension.linkDebugTaskName != null) {
55+
add(
56+
resolveDebugPipelineParameters(
57+
artifactsDirectory,
58+
decompileBitcodeExtension,
59+
extractBitcodeExtension
60+
)
61+
)
62+
}
63+
}.forEach { pipeline ->
64+
pipeline.registerTasks(
5765
project,
58-
extractBitcodeTaskParameters = ExtractBitcodeTaskParameters(
59-
functionToExtractName = extractBitcodeExtension.functionToExtractName,
60-
recursionDepth = extractBitcodeExtension.recursionDepth
61-
),
66+
extractBitcodeTaskParameters = extractBitcodeExtension.toExtractBitcodeTaskParameters(),
6267
setCompilerFlags = decompileBitcodeExtension.setCompilerFlags
6368
)
6469
}
@@ -109,8 +114,12 @@ abstract class BitcodeAnalysisPlugin : Plugin<Project> {
109114
dependsOn(decompileBitcodeTaskName)
110115
inputFilePath = llFilePath
111116
outputFilePath = extractedBitcodeFilePath
112-
functionToExtractName = taskParameters.functionToExtractName
117+
functionNames = taskParameters.functionNames
118+
functionPatterns = taskParameters.functionPatterns
119+
linePatterns = taskParameters.linePatterns
120+
ignorePatterns = taskParameters.ignorePatterns
113121
recursionDepth = taskParameters.recursionDepth
122+
verbose = taskParameters.verbose
114123
doFirst {
115124
if (inputFilePath.orNull != llFilePath) {
116125
throw BitcodeAnalysisException(PIPELINE_VIOLATION_ERROR_MESSAGE)
@@ -140,46 +149,55 @@ abstract class BitcodeAnalysisPlugin : Plugin<Project> {
140149
}
141150

142151
private fun Project.resolveReleasePipelineParameters(
143-
tmpArtifactsDirectory: Directory,
152+
artifactsDirectory: Directory,
144153
decompileBitcodeExtension: DecompileBitcodeExtension,
145154
extractBitcodeExtension: ExtractBitcodeExtension,
146155
): BitcodeAnalysisPipelineParameters {
147-
val bitcodeSourcesDirectory = tmpArtifactsDirectory.dir(RELEASE_BITCODE_SOURCES_DIR_NAME)
156+
val bitcodeSourcesDirectory = artifactsDirectory.dir(RELEASE_BITCODE_SOURCES_DIR_NAME)
148157
return BitcodeAnalysisPipelineParameters(
149158
linkTaskName = decompileBitcodeExtension.linkTaskName,
150159
decompileBitcodeTaskName = RELEASE_DECOMPILE_BITCODE_TASK_NAME,
151160
extractBitcodeTaskName = RELEASE_EXTRACT_BITCODE_TASK_NAME,
152161
bitcodeSourcesDirectory = bitcodeSourcesDirectory,
153162
bcFilePath = resolveToRelativePath(bitcodeSourcesDirectory, decompileBitcodeExtension.bcInputFileName),
154-
llFilePath = resolveToRelativePath(tmpArtifactsDirectory, decompileBitcodeExtension.llOutputFileName),
163+
llFilePath = resolveToRelativePath(artifactsDirectory, decompileBitcodeExtension.llOutputFileName),
155164
extractedBitcodeFilePath = resolveToRelativePath(
156-
tmpArtifactsDirectory,
165+
artifactsDirectory,
157166
extractBitcodeExtension.outputFileName
158167
)
159168
)
160169
}
161170

162171
private fun Project.resolveDebugPipelineParameters(
163-
tmpArtifactsDirectory: Directory,
172+
artifactsDirectory: Directory,
164173
decompileBitcodeExtension: DecompileBitcodeExtension,
165174
extractBitcodeExtension: ExtractBitcodeExtension,
166175
): BitcodeAnalysisPipelineParameters {
167-
val bitcodeSourcesDirectory = tmpArtifactsDirectory.dir(DEBUG_BITCODE_SOURCES_DIR_NAME)
176+
val bitcodeSourcesDirectory = artifactsDirectory.dir(DEBUG_BITCODE_SOURCES_DIR_NAME)
168177
return BitcodeAnalysisPipelineParameters(
169178
linkTaskName = decompileBitcodeExtension.linkDebugTaskName
170179
?: error("debug pipeline can be registered only if `linkDebugTaskName` is set"),
171180
decompileBitcodeTaskName = DEBUG_DECOMPILE_BITCODE_TASK_NAME,
172181
extractBitcodeTaskName = DEBUG_EXTRACT_BITCODE_TASK_NAME,
173182
bitcodeSourcesDirectory = bitcodeSourcesDirectory,
174183
bcFilePath = resolveToRelativePath(bitcodeSourcesDirectory, decompileBitcodeExtension.bcInputFileName),
175-
llFilePath = resolveToRelativePath(tmpArtifactsDirectory, decompileBitcodeExtension.llDebugOutputFileName),
184+
llFilePath = resolveToRelativePath(artifactsDirectory, decompileBitcodeExtension.llDebugOutputFileName),
176185
extractedBitcodeFilePath = resolveToRelativePath(
177-
tmpArtifactsDirectory,
186+
artifactsDirectory,
178187
extractBitcodeExtension.debugOutputFileName
179188
)
180189
)
181190
}
182191

192+
private fun ExtractBitcodeExtension.toExtractBitcodeTaskParameters() = ExtractBitcodeTaskParameters(
193+
functionNames = functionNames.get(),
194+
functionPatterns = functionPatterns.get(),
195+
linePatterns = linePatterns.get(),
196+
ignorePatterns = ignorePatterns.get(),
197+
recursionDepth = recursionDepth,
198+
verbose = verbose
199+
)
200+
183201
private fun Project.resolveToRelativeDirectory(directoryPath: String) =
184202
layout.projectDirectory.dir(directoryPath)
185203

@@ -204,7 +222,11 @@ abstract class BitcodeAnalysisPlugin : Plugin<Project> {
204222
}
205223

206224
private data class ExtractBitcodeTaskParameters(
207-
val functionToExtractName: String,
225+
val functionNames: List<String>,
226+
val functionPatterns: List<String>,
227+
val linePatterns: List<String>,
228+
val ignorePatterns: List<String>,
208229
val recursionDepth: UInt,
230+
val verbose: Boolean,
209231
)
210232
}

0 commit comments

Comments
 (0)