Skip to content

Lab 12

In this last lab sessions you will be practicing profiling, decompiling and obfuscation. For the exercises in this last lab session, you will be working with the PrimeChecker project, available on GitLab.

Profiling

  • Profiling is a reliable way of finding out which part of your code is responsible for factual resource consumption.
  • In the context of this course we're mostly interested in sampling based techniques to measure which methods consume most CPU cycles.

Flame graphs with IntelliJ

  • Flame graphs are a visual representation of CPU time, based on periodically polled call stacks.
  • For better interpretability the stacks are sorted and tinted, resulting in a flame-like structure.
  • IntelliJ has a built-in profiler, which automatically produces flame graphs from collected call stack samples.

Your turn

  • Before anything can be profiled, we need a program that runs a bit longer than just a handful of method calls.
  • Revisit the tests of the previously used PrimeChecker project and add a new test, that counts how many Prime Numbers exist below 10000. It should find 1229:
    @Test
    public void testPrimesBelow10000() {
      Assert.assertEquals(1229, PrimeChecker.countPrimeNumbersBelow(10000));
    }
    
  • Instead of running the code with maven, use the green triange left of the test. Do not use a simple click, but a right-click, followed by Profile with IntelliJ profiler...
  • The test will execute as usual, but intelliJ will show a popup: "Profiler data is ready" and an "Open" option.
    • Before you continue, write down the test execution time, i.e. how many milliseconds it took to execute the test.
  • Click on "Open". A flame graph will appear. Inspect the flame graph and identify which of your method calls (boxes in orange) consumes most resources.
  • Navigate back to your production code. Several lines now showcase a flame symbol on the side.
    • Inspect which lines are considered most consuming. You should find a single line that is responsible for the vast majority of CPU consumption.
    • Where possible, try to optimize your code, by improving / removing time-consuming lines.
  • Execute your code again: Compare the test duration to the one measured before. Validate there is a significant speedup.

Code decompiling

In class, you've seen an example of how to use the CFR (Class File Reader) tool.

Class File Reader

  • The CFR can be applied on any java bytecode. The easiest way to obtain sample bytecode to work with is by decompiling your own code.
  • The CFR is free for download on the developer's website

Your turn

See if you can decompile one of your own Halma classes !

  1. Use mvn clean package to produce a jar file.
  2. Extract the contents of your jar file and find the class file for any non-trivial class, e.g. your Controller implementation.
  3. Use CFR to decompile the class and reconstruct the .java source code file.
  4. Side by side compare your original source-code with the source code produced by the decompiler.

Use a visual comparison tool

icdiff is a neat command line tool to quickly spot deltas between two files.

(Bonus: If you like recursion, try to decompile the decompiler with the decompiler!)

Code Obfuscation

Obfuscators are tools to mitigate the risk of code decompilation for unwanted or unauthorized code inspection.

ProGuard

  • ProGuard is a free tool to replace (amongst others) all occurrences of variable names, by similar sounding, our visually similar identifiers.
  • This way, once a software release is obfuscated, it can be hardly interpreted or reverse-engineered by a human.
  • ProGuard is free to download on the project GitHub site.

Your Turn

  1. Obfuscate your own halma jar with ProGuard. A couple of things to keep in mind:
    • You need a configuration file (see course material for a sample). Make sure to point to the correct launcher class (if the main method is obfuscated, your code cannot be executed any more).
    • The ProGuard directly operates on a jar file. Make sure your initial halma jar file is operational.
  2. Try to decompile your jar.
    • Extract the jar and find the class files.
    • Decompile some class files with CFR.
    • Compare the obfuscated code to the original source code.
  3. Verify your obfuscated jar file: it must be still operational.
  4. (Bonus: Add ProGuard to your gitlab.ci configuration, so the jar file offered by GitLab's pages job is always protected against de-compilation.)