link:https://github.com/lzaoral/JCProfilerNext/actions/workflows/ci.yml[image:https://github.com/lzaoral/JCProfilerNext/actions/workflows/ci.yml/badge.svg[Java CI with Gradle]]
JCProfilerNext is a complete rewrite of link:https://github.com/OpenCryptoProject/JCProfiler[OpenCryptoProject/JCProfiler] that provides a completely automated preprocessing, compilation, installation and profiling of JavaCard code on JavaCard smart cards or in the jCardSim simulator.
.Compatibility Matrix [cols="^1,^2"] |=== | JDK | Supported JavaCard versions
| 8 | 2.2.1+
| 9-11 | 3.0.1+
| 12-19 | 3.1+ |===
. Clone this repository.
. Run ./gradlew build
on Unix-like systems or gradlew.bat build
on Windows.
. Execute
.. directly using Gradle: ./gradlew run --args='--help'
.
.. directly using built JAR: java -jar build/libs/JCProfilerNext-1.0-SNAPSHOT.jar --help
.
.. directly from distribution archives in build/distributions
.
The tool can be executed in four different modes (select with --mode
):
custom
-- Instrument the applet with user-provided code snippets. The user has to do the measurements on their own.memory
-- Measure memory usage.time
(default) -- Measure elapsed time.stats
-- Collect API usage statistics.Time Example
Consider the `example` method in the following class. The outer loop is clearly
not constant since its iteration count depends on the input value. On the other
hand, the inner loop is constant.
[source,java]
----
package example;
import javacard.framework.*;
public class Example extends Applet {
public static final byte INS_EXAMPLE = (byte) 0xEE;
Example() { }
public static void install(byte[] bArray, short bOffset, byte bLength) {
new Example().register();
}
@Override
public void process(APDU apdu) throws ISOException {
if (selectingApplet())
return;
switch (apdu.getBuffer()[ISO7816.OFFSET_INS]) {
case INS_EXAMPLE:
example(apdu);
break;
default:
ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
}
}
private void example(APDU apdu) {
short count = Util.getShort(apdu.getBuffer(), ISO7816.OFFSET_CDATA);
for (short i = 0; i < count; i++) {
short tmp = 0;
for (short k = 0; k < 50; k++) {
tmp++;
}
}
}
}
----
Run as:
[source,console]
----
$ ./gradlew run --args="--work-dir example --jckit jc222_kit --executable example --ins 0xEE --data-regex 00[0-9A-F]{2} --repeat-count 100"
----
JCProfilerNext will process all sources in the `example` directory and will
also use this directory to store all intermediate results:
* `applet` - contains the built applet with corresponding CAP, JAR and JCA files
* `measurements.csv` - contains the profiled method signature, card ATR, elapsed time, input APDUs and corresponding raw measurements
* `measurements.html` - contains a visualisation of the measurements
* `sources_instr` - contains instrumented sources
* `sources_original` - contains original unchanged sources
* `sources_perf` - contains sources with inserted measurements as inline comments
If you open the corresponding link:https://lzaoral.github.io/JCProfilerNext/example-time.html[`measurements.html`]
produced by JCProfilerNext, you will see that some parts of the code are indeed
not constant, namely if you inspect the corresponding histograms of traps
after the `for` loops.
Memory Example
Consider the following source code:
package example;
import javacard.framework.*;
public class Example extends Applet {
private Example() {
// Transient Deselect
byte[] transientDeselect = JCSystem.makeTransientByteArray((short) 256, JCSystem.CLEAR_ON_DESELECT);
// Transient Reset
byte[] transientReset = JCSystem.makeTransientByteArray((short) 256, JCSystem.CLEAR_ON_RESET);
// Persistent
byte[] persistent = new byte[256];
}
public static void install(byte[] bArray, short bOffset, byte bLength) {
new Example().register();
}
@Override
public void process(APDU apdu) {}
JCProfilerNext will produce the same directory structure as in the time mode
above. The --executable
option is omitted because we want to measure memory
usage in the entry point class constructor. Note that the allocation of transient
deselect memory may also affect the amount of free transient memory and vice versa.
See link:https://lzaoral.github.io/JCProfilerNext/example-memory.html[`measurements.html`]
for visualisation of the measurements.
Stats Example
Consider the following source code:
[source,java]
----
import javacard.security.KeyPair;
class Example {
public Example() {
KeyPair kp = new KeyPair(KeyPair.ALG_RSA, (short) 2048);
kp.genKeyPair();
}
}
----
Run as:
[source,console]
----
$ ./gradlew run --args="--work-dir example --jckit jc222_kit --mode stats"
----
JCProfilerNext will process all sources in the `example` directory and produce
the `APIstatistics.csv` file. Note that if not all imports can be resolved,
the results may not be precise, and the tool will issue an appropriate warning.
[source,csv]
----
# package/outer type,type,member,frequency
javacard.security,KeyPair,,2
javacard.security,KeyPair,ALG_RSA,1
javacard.security,KeyPair,genKeyPair(),1
javacard.security,KeyPair,"KeyPair(byte,short)",1
----
Limitations
-----------
* Cards that require special communication procedures (e.g. `SecureChannel`) are not supported.
* It is not possible to use a different JDK to compile the JavaCard applets and run this project.
* Connection to wireless card terminals may occasionally fail.