Skip to content

Latest commit

 

History

History
342 lines (218 loc) · 13.2 KB

HACKING.md

File metadata and controls

342 lines (218 loc) · 13.2 KB

Profiling the compiler

Profiling with Async profiler

IDEA Ultimate contains an Async sampling profiler. As of IDEA 2018.3 Async sampling profiler is still an experimental feature, so use Ctrl-Alt-Shift-/ on Linux, Cmd-Alt-Shift-/ on macOS to activate it. Then start compilation in CLI with --no-daemon and -Porg.gradle.workers.max=1 flags (running Gradle task with the profiler doesn't seem to work properly) and attach to the running process using "Run/Attach Profiler to Local Process" menu item.

Select "K2NativeKt" or "org.jetbrains.kotlin.cli.utilities.MainKt" process. On completion profiler will produce flame diagram which could be navigated with the mouse (click-drag moves, wheel scales). More RAM in IDE (>4G) could be helpful when analyzing longer runs. As Async is a sampling profiler, to get sensible coverage longer runs are important.

Profiling with YourKit

Unlike Async profiler in IDEA, YourKit can work as an exact profiler and provide complete coverage of all methods along with exact invocation counters.

Install the YourKit profiler for your platform from https://www.yourkit.com/java/profiler. Set AGENT variable to the JVMTI agent provided by YourKit, like

    export AGENT=/Applications/YourKit-Java-Profiler-2018.04.app/Contents/Resources/bin/mac/libyjpagent.jnilib

To profile standard library compilation:

    ./gradlew -PstdLibJvmArgs="-agentpath:$AGENT=probe_disable=*,listen=all,tracing"  dist

To profile platform libraries start build of proper target like this:

    ./gradlew -PplatformLibsJvmArgs="-agentpath:$AGENT=probe_disable=*,listen=all,tracing"  ios_arm64PlatformLibs

To profile standalone code compilation use:

    JAVA_OPTS="-agentpath:$AGENT=probe_disable=*,listen=all,tracing" ./dist/bin/konanc file.kt

Then attach to the desired application in YourKit GUI and use CPU tab to inspect CPU consuming methods. Saving the trace may be needed for more analysis. Adjusting -Xmx in $HOME/.yjp/ui.ini could help with the big traces.

To perform memory profiling follow the steps above, and after attachment to the running process use "Start Object Allocation Recording" button. See https://www.yourkit.com/docs/java/help/allocations.jsp for more details.

Compiler Gradle options

There are several gradle flags one can use for Konan build.

  • -Pbuild_flags passes flags to the compiler used to build stdlib

      ./gradlew -Pbuild_flags="--disable lower_inline --print_ir" stdlib
    
  • -Pshims compiles LLVM interface with tracing "shims". Allowing one to trace the LLVM calls from the compiler. Make sure to rebuild the project.

      ./gradlew -Pshims=true dist
    

Compiler environment variables

  • KONAN_DATA_DIR changes .konan local data directory location ($HOME/.konan by default). Works both with cli compiler and gradle plugin

Testing

Compiler integration tests

To run blackbox compiler tests from JVM Kotlin use (takes time):

./gradlew run_external

To update the blackbox compiler tests set TeamCity build number in gradle.properties:

testKotlinVersion=<build number>
  • -Pfilter allows one to choose test files to run.

      ./gradlew -Pfilter=overflowLong.kt run_external
    
  • -Pprefix allows one to choose external test directories to run. Only tests from directories with given prefix will be executed.

      ./gradlew -Pprefix=build_external_compiler_codegen_box_cast run_external
    
  • -Ptest_flags passes flags to the compiler used to compile tests

      ./gradlew -Ptest_flags="--time" backend.native:tests:array0
    
  • -Ptest_target specifies cross target for a test run.

      ./gradlew -Ptest_target=raspberrypi backend.native:tests:array0
    
  • -Premote=user@host sets remote test execution login/hostname. Good for cross compiled tests.

      ./gradlew -Premote=kotlin@111.22.33.444 backend.native:tests:run
    
  • -Ptest_verbose enables printing compiler args and other helpful information during a test execution.

      ./gradlew -Ptest_verbose :backend.native:tests:mpp_optional_expectation
    
  • -Ptest_two_stage enables two-stage compilation of tests. If two-stage compilation is enabled, test sources are compiled into a klibrary and then a final native binary is produced from this klibrary using the -Xinclude compiler flag.

      ./gradlew -Ptest_two_stage backend.native:tests:array0
    
  • -Ptest_with_cache_kind=static|dynamic enables using caches during testing.

Runtime unit tests

To run runtime unit tests on the host machine for both mimalloc and the standard allocator:

./gradlew hostRuntimeTests

To run tests for only one of these two allocators, run hostStdAllocRuntimeTests or hostMimallocRuntimeTests.

We use Google Test to execute the runtime unit tests. The build automatically fetches the specified Google Test revision to runtime/googletest. It is possible to manually modify the downloaded GTest sources for debug purposes; the build will not overwrite them by default.

To forcibly redownload Google Test when running tests, use the corresponding project property:

 ./gradlew hostRuntimeTests -Prefresh-gtest

or run the downloadGTest task directly with the --refresh CLI key:

./gradlew downloadGTest --refresh

To use a local GTest copy instead of the downloaded one, add the following line to runtime/build.gradle.kts:

googletest.useLocalSources("<path to local GTest sources>")

Performance measurement

Firstly, it's necessary to build analyzer tool to have opportunity to compare different performance results:

cd tools/benchmarksAnalyzer
../../gradlew build

To measure performance of Kotlin/Native compiler on existing benchmarks:

cd performance
../gradlew :konanRun

NOTE: konanRun task needs built compiler and libs. To test against working tree make sure to run

./gradlew dist distPlatformLibs

before konanRun

konanRun task can be run separately for one/several benchmark applications:

cd performance
../gradlew :cinterop:konanRun

konanRun task has parameter filter which allows to run only some subset of benchmarks:

cd performance
../gradlew :cinterop:konanRun --filter=struct,macros

Or you can use filterRegex if you want to specify the filter as regexes:

cd performance
../gradlew :ring:konanRun --filterRegex=String.*,Loop.*

There us also verbose mode to follow progress of running benchmarks

cd performance
../gradlew :cinterop:konanRun --verbose

> Task :performance:cinterop:konanRun
[DEBUG] Warm up iterations for benchmark macros
[DEBUG] Running benchmark macros
...

There are also tasks for running benchmarks on JVM (pay attention, some benchmarks e.g. cinterop benchmarks can't be run on JVM)

cd performance
../gradlew :jvmRun

Files with results of benchmarks run are saved in performance/build/nativeReport.json for konanRun and jvmReport.json for jvmRun. You can change the output filename by setting the nativeJson property for konanRun and jvmJson for jvmRun:

cd performance
../gradlew :ring:konanRun --filter=String.*,Loop.* -PnativeJson=stringsAndLoops.json

You can use the compilerArgs property to pass flags to the compiler used to compile the benchmarks:

cd performance
../gradlew :konanRun -PcompilerArgs="--time -g"

To compare different results run benchmarksAnalyzer tool:

cd tools/benchmarksAnalyzer/build/bin/<target>/benchmarksAnalyzerReleaseExecutable/
./benchmarksAnalyzer.kexe <file1> <file2>

Tool has several renders which allow produce output report in different forms (text, html, etc.). To set up render use flag --render/-r. Output can be redirected to file with flag --output/-o. To get detailed information about supported options, please use --help/-h.

Analyzer tool can compare both local files and files placed on Artifactory/TeamCity.

File description stored on Artifactory

artifactory:<build number>:<target (Linux|Windows10|MacOSX)>:<filename>

Example

artifactory:1.2-dev-7942:Windows10:nativeReport.json

File description stored on TeamCity

 teamcity:<build locator>:<filename>

Example

 teamcity:id:42491947:nativeReport.json

Pay attention, user and password information(with flag -u <username>:<password>) should be provided to get data from TeamCity.

Composite build and testing

If you have a fix spanning both Kotlin and Kotlin/native workspaces you need to be able to test Kotlin/Native composite build. Here's how to do it manually:

Have a composite build with the proper Kotlin tag.

Find the version of Kotlin the current native is guaranteed to build with. The version is specified in kotlin-native/gradle.properties. For example:

kotlinVersion=1.3.70-dev-1526

Checkout kotlin workspace to tag build-1.3.70-dev-1526. Make sure its path ends with .../kotlin. Otherwise issues will arise. Direct kotlin-native build to the kotlin with kotlinProjectPath in native's gradle.properties.

Now you have the kotlin + kotlin-native combination that is known to build. Apply your fix on top of both workspaces and run

$ ./gradlew dist

in kotlin-native to check the buildability.

Testing native

For a quick check use:

$ ./gradlew sanity 2>&1 | tee log

For a longer, more thorough testing build the complete build. Make sure you are running it on a macOS.

Have a complete build:

$ ./gradlew bundle # includes dist as its part

then run two test sets:

$ ./gradlew backend.native:tests:run 2>&1 | tee log

$ ./gradlew backend.native:tests:runExternal -Ptest_two_stage=true 2>&1 | tee log

LLVM

See BUILDING_LLVM.md if you want to build and use your own LLVM distribution instead of provided one.

Using different LLVM distributions as part of Kotlin/Native compilation pipeline.

llvmHome.<HOST_NAME> variable in <distribution_location>/konan/konan.properties controls which LLVM distribution Kotlin/Native will use in its compilation pipeline. You can replace its value with either $llvm.<HOST_NAME>.{dev, user} to use one of predefined distributions or pass an absolute to your own distribution. Don't forget to set llvmVersion.<HOST_NAME> to the version of your LLVM distribution.

Example. Using LLVM from an absolute path.

Assuming LLVM distribution is installed at /usr path, one can specify a path to it with the -Xoverride-konan-properties option:

konanc main.kt -Xoverride-konan-properties=llvmHome.linux_x64=/usr

Playing with compilation pipeline.

Following compiler phases control different parts of LLVM pipeline:

  1. LinkBitcodeDependencies. Linkage of produced bitcode with runtime and some other dependencies.
  2. BitcodeOptimization. Running LLVM optimization pipeline.
  3. ObjectFiles. Compilation of bitcode with Clang.

For example, pass -Xdisable-phases=BitcodeOptimization to skip optimization pipeline. Note that disabling LinkBitcodeDependencies or ObjectFiles will break compilation pipeline.

Compiler takes options for Clang from konan.properties file by combining clangFlags.<TARGET> and clang<Noopt/Opt/Debug>Flags.<TARGET> properties. Use -Xoverride-konan-properties=<key_1=value_1; ...;key_n=value_n> flag to override default values.

Please note:

  1. Kotlin Native passes bitcode files to Clang instead of C or C++, so many flags won't work.
  2. -cc1 -emit-obj should be passed because Kotlin/Native calls linker by itself.
  3. Use clang -cc1 -help to see a list of available options.

Another useful compiler option is -Xtemporary-files-dir=<PATH> which allows to specify a directory for intermediate compiler artifacts like bitcode and object files.

Example 1. Bitcode right after IR to Bitcode translation.

konanc main.kt -produce bitcode -o bitcode.bc

Example 2. Bitcode after LLVM optimizations.

konanc main.kt -Xtemporary-files-dir=<PATH> -o <OUTPUT_NAME>

<PATH>/<OUTPUT_NAME>.kt.bc will contain bitcode after LLVM optimization pipeline.

Example 3. Replace predefined LLVM pipeline with Clang options.

CLANG_FLAGS="clangFlags.macos_x64=-cc1 -emit-obj;clangNooptFlags.macos_x64=-O2"
konanc main.kt -Xdisable-phases=BitcodeOptimization -Xoverride-konan-properties="$CLANG_FLAGS"

Running Clang the same way Kotlin/Native compiler does

Kotlin/Native compiler (including cinterop tool) has machinery that manages LLVM, Clang and native SDKs for supported targets and runs bundled Clang with proper arguments. To utilize this machinery, use $dist/bin/run_konan clang $tool $target $arguments, e.g.

$dist/bin/run_konan clang clang ios_arm64 1.c

will print and run the following command:

~/.konan/dependencies/clang-llvm-apple-8.0.0-darwin-macos/bin/clang \
    -B/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin \
    -fno-stack-protector -stdlib=libc++ -arch arm64 \
    -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS13.5.sdk \
    -miphoneos-version-min=9.0 1.c

The similar helper is available for LLVM tools, $dist/bin/run_konan llvm $tool $arguments.