From 5568117d129c7c4801d3b4b6c11cf980200650be Mon Sep 17 00:00:00 2001 From: Sebastian Joergensen Date: Tue, 30 Apr 2019 15:06:33 +0200 Subject: [PATCH 1/4] Support for reading offset from a file --- FakeTimeFileTest.java | 21 +++++++++++++++++++ README.md | 18 ++++++++++++++++ src/FakeTimeAgent.c | 49 ++++++++++++++++++++++++++++++++++++++++--- test_linux.sh | 3 +++ test_linux_64.sh | 7 +++++-- test_mac.sh | 3 +++ 6 files changed, 96 insertions(+), 5 deletions(-) create mode 100644 FakeTimeFileTest.java diff --git a/FakeTimeFileTest.java b/FakeTimeFileTest.java new file mode 100644 index 0000000..5845f7f --- /dev/null +++ b/FakeTimeFileTest.java @@ -0,0 +1,21 @@ +import java.util.Calendar; +import java.lang.String; +import java.nio.file.Files; +import java.nio.file.Path; + +public class FakeTimeFileTest { + public static void main(String args[]) throws Throwable{ + final Path filePath = Files.createTempFile(null, null); + System.out.println("Using temporary file " + filePath.toString()); + + System.setProperty("faketime.file.path", filePath.toAbsolutePath().toString()); + + for(int i = 0; i < 10; i++) { + Files.write(filePath, String.valueOf(i * -86400).getBytes("UTF-8")); + + System.out.println("Time: " + System.currentTimeMillis()); + Calendar calendar = Calendar.getInstance(); + System.out.println("Date: " + calendar.getTime()); + } + } +} diff --git a/README.md b/README.md index e4d8cdd..5625e15 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,8 @@ gcc -fPIC -shared -I $JAVA_HOME/include -I $JAVA_HOME/include/linux -m32 -Wall s ### Step 2: Use it: +##### Option 1: Control offset with a Java system property + Run your Java program (say, org.test.Main) with these agent-specific extra arguments (see [issue #3](https://github.com/arvindsv/faketime/issues/3)), like this: ``` @@ -63,5 +65,21 @@ In your Java code, you can set the property **faketime.offset.seconds** to the n ``` System.setProperty("faketime.offset.seconds", "86400"); ``` + +OR + +##### Option 2: Control offset with an offset file + +You can set the property **faketime.file.path** to point to a file containing the offset in *seconds* (ASCII-encoded). This way you can alter the time using methods external to the Java process, i.e. through a mounted Kubernetes ConfigMap. + +``` +java -agentpath:/path/to/the/library/you/got/above \ + -Dfaketime.file.path=/path/to/a/text/file/containing/a/number + -XX:+UnlockDiagnosticVMOptions \ + -XX:DisableIntrinsic=_currentTimeMillis \ + -XX:CompileCommand=exclude,java/lang/System.currentTimeMillis \ + org.test.Main +``` + That's it! Take a look at [FakeTimeTest.java](https://github.com/arvindsv/faketime/blob/master/FakeTimeTest.java) if you need to see some Java code which uses it. diff --git a/src/FakeTimeAgent.c b/src/FakeTimeAgent.c index 88e98de..a497928 100644 --- a/src/FakeTimeAgent.c +++ b/src/FakeTimeAgent.c @@ -18,6 +18,34 @@ static jvmtiEnv *jvmti = NULL; static GlobalAgentData *gdata; static call__jlong originalMethodCurrentTimeInMillis = NULL; +// Read the offset in seconds from a text file and convert it to a long in milliseconds. +long readOffsetFromFile(const char* filePath) { + FILE* pFile = fopen(filePath, "r"); + + if (pFile == NULL) { + return 0L; + } + + // Find the size of the file + fseek(pFile, 0, SEEK_END); + long lSize = ftell(pFile); + rewind(pFile); + + // Allocate a buffer for the file contents, adding room for NULL-termination. + char* buffer = (char*) malloc(sizeof(char) * (lSize + 1)); + fread(buffer, sizeof(char), lSize, pFile); + fclose(pFile); + + buffer[lSize] = '\0'; // Make sure the string is null terminated + + // Convert the offset string to a long and multiply by 1000 to get milliseconds + long offset = atol(buffer) * 1000; + + free(buffer); + + return offset; +} + JNIEXPORT jlong JNICALL newCurrentTimeInMillis(JNIEnv* env, jclass jc) { jclass systemClass = (*env)->FindClass(env, "java/lang/System"); jmethodID getPropertyMethodId = (*env)->GetStaticMethodID(env, systemClass, "getProperty", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"); @@ -28,16 +56,31 @@ JNIEXPORT jlong JNICALL newCurrentTimeInMillis(JNIEnv* env, jclass jc) { jstring offsetPropertyName = (*env)->NewStringUTF(env, "faketime.offset.seconds"); jstring offsetPropertyDefault = (*env)->NewStringUTF(env, "0"); + jstring filePathPropertyName = (*env)->NewStringUTF(env, "faketime.file.path"); + //jstring fileCacheDurationPropertyName = (*env)->NewStringUTF(env, "faketime.file.cache_duration"); + //jstring fileCacheDurationPropertyDefault = (*env)->NewStringUTF(env, "10"); jstring offsetValue = (*env)->CallStaticObjectMethod(env, systemClass, getPropertyMethodId, offsetPropertyName, offsetPropertyDefault); + jstring filePath = (*env)->CallStaticObjectMethod(env, systemClass, getPropertyMethodId, filePathPropertyName, NULL); + //jstring fileCacheDuration = (*env)->CallStaticObjectMethod(env, systemClass, getPropertyMethodId, fileCacheDurationPropertyName, fileCacheDurationPropertyDefault); if ((*env)->ExceptionCheck(env)) return 0; - const char *offset = (*env)->GetStringUTFChars(env, offsetValue, NULL); + jlong offset; + if (filePath != NULL) { + // If filePath is defined, read the offset from that file (in milliseconds) + const char* filepathStr = (*env)->GetStringUTFChars(env, filePath, NULL); + offset = readOffsetFromFile(filepathStr); + (*env)->ReleaseStringUTFChars(env, filePath, filepathStr); + } else { + // if not, use the offset from system property "faketime.offset.seconds" + const char *offsetStr = (*env)->GetStringUTFChars(env, offsetValue, NULL); + offset = atol(offsetStr) * 1000; + (*env)->ReleaseStringUTFChars(env, offsetValue, offsetStr); + } jlong realTime = originalMethodCurrentTimeInMillis(env, jc); - jlong timeWithOffset = realTime + (atol(offset) * 1000); + jlong timeWithOffset = realTime + offset; - (*env)->ReleaseStringUTFChars(env, offsetValue, offset); return timeWithOffset; } diff --git a/test_linux.sh b/test_linux.sh index 358db47..405dc41 100755 --- a/test_linux.sh +++ b/test_linux.sh @@ -5,4 +5,7 @@ gcc -shared -I $JAVA_HOME/include -I $JAVA_HOME/include/linux -m32 -Wall src/Fak mkdir -p lib/linux && mv libfaketime.so lib/linux/ javac FakeTimeTest.java +javac FakeTimeFileTest.java + java -agentpath:./lib/linux/libfaketime.so FakeTimeTest +java -agentpath:./libfaketime.so FakeTimeFileTest diff --git a/test_linux_64.sh b/test_linux_64.sh index 186ccaa..dfec983 100755 --- a/test_linux_64.sh +++ b/test_linux_64.sh @@ -2,7 +2,10 @@ set -eu gcc -fPIC -shared -I $JAVA_HOME/include -I $JAVA_HOME/include/linux -m64 -Wall src/FakeTimeAgent.c -o libfaketime.so -mkdir -p lib/linux && mv libfaketime.so lib/linux/ +mkdir -p lib/linux && mv libfaketime.so lib/linux/ + +javac FakeTimeTest.java +javac FakeTimeFileTest.java -javac FakeTimeTest.java java -agentpath:./lib/linux/libfaketime.so FakeTimeTest +java -agentpath:./lib/linux/libfaketime.so FakeTimeFileTest diff --git a/test_mac.sh b/test_mac.sh index 0958d2c..8890a24 100755 --- a/test_mac.sh +++ b/test_mac.sh @@ -5,4 +5,7 @@ gcc -shared -I $JAVA_HOME/include -Wall src/FakeTimeAgent.c -o libfaketime.jnili mkdir -p lib/mac/ && mv libfaketime.jnilib lib/mac/ javac FakeTimeTest.java +javac FakeTimeFileTest.java + java -agentpath:./lib/mac/libfaketime.jnilib FakeTimeTest +java -agentpath:./lib/mac/libfaketime.jnilib FakeTimeFileTest From 6ef0a5ae04bc997e6316bbd086667a2311e54d88 Mon Sep 17 00:00:00 2001 From: Sebastian Joergensen Date: Tue, 30 Apr 2019 15:21:55 +0200 Subject: [PATCH 2/4] Rename file property to faketime.file.path --- FakeTimeFileTest.java | 2 +- README.md | 4 ++-- src/FakeTimeAgent.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/FakeTimeFileTest.java b/FakeTimeFileTest.java index 5845f7f..d57d841 100644 --- a/FakeTimeFileTest.java +++ b/FakeTimeFileTest.java @@ -8,7 +8,7 @@ public static void main(String args[]) throws Throwable{ final Path filePath = Files.createTempFile(null, null); System.out.println("Using temporary file " + filePath.toString()); - System.setProperty("faketime.file.path", filePath.toAbsolutePath().toString()); + System.setProperty("faketime.offset.file", filePath.toAbsolutePath().toString()); for(int i = 0; i < 10; i++) { Files.write(filePath, String.valueOf(i * -86400).getBytes("UTF-8")); diff --git a/README.md b/README.md index 5625e15..6d59826 100644 --- a/README.md +++ b/README.md @@ -70,11 +70,11 @@ OR ##### Option 2: Control offset with an offset file -You can set the property **faketime.file.path** to point to a file containing the offset in *seconds* (ASCII-encoded). This way you can alter the time using methods external to the Java process, i.e. through a mounted Kubernetes ConfigMap. +You can set the property **faketime.offset.file** to point to a file containing the offset in *seconds* (ASCII-encoded). This way you can alter the time using methods external to the Java process, i.e. through a mounted Kubernetes ConfigMap. ``` java -agentpath:/path/to/the/library/you/got/above \ - -Dfaketime.file.path=/path/to/a/text/file/containing/a/number + -Dfaketime.offset.file=/path/to/a/text/file/containing/a/number -XX:+UnlockDiagnosticVMOptions \ -XX:DisableIntrinsic=_currentTimeMillis \ -XX:CompileCommand=exclude,java/lang/System.currentTimeMillis \ diff --git a/src/FakeTimeAgent.c b/src/FakeTimeAgent.c index a497928..ddffae9 100644 --- a/src/FakeTimeAgent.c +++ b/src/FakeTimeAgent.c @@ -56,7 +56,7 @@ JNIEXPORT jlong JNICALL newCurrentTimeInMillis(JNIEnv* env, jclass jc) { jstring offsetPropertyName = (*env)->NewStringUTF(env, "faketime.offset.seconds"); jstring offsetPropertyDefault = (*env)->NewStringUTF(env, "0"); - jstring filePathPropertyName = (*env)->NewStringUTF(env, "faketime.file.path"); + jstring filePathPropertyName = (*env)->NewStringUTF(env, "faketime.offset.file"); //jstring fileCacheDurationPropertyName = (*env)->NewStringUTF(env, "faketime.file.cache_duration"); //jstring fileCacheDurationPropertyDefault = (*env)->NewStringUTF(env, "10"); From 4b338115b8af3f23078101c65e042453af199f3a Mon Sep 17 00:00:00 2001 From: Sebastian Joergensen Date: Tue, 30 Apr 2019 15:21:55 +0200 Subject: [PATCH 3/4] Rename file property to faketime.offset.file --- FakeTimeFileTest.java | 2 +- README.md | 4 ++-- src/FakeTimeAgent.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/FakeTimeFileTest.java b/FakeTimeFileTest.java index 5845f7f..d57d841 100644 --- a/FakeTimeFileTest.java +++ b/FakeTimeFileTest.java @@ -8,7 +8,7 @@ public static void main(String args[]) throws Throwable{ final Path filePath = Files.createTempFile(null, null); System.out.println("Using temporary file " + filePath.toString()); - System.setProperty("faketime.file.path", filePath.toAbsolutePath().toString()); + System.setProperty("faketime.offset.file", filePath.toAbsolutePath().toString()); for(int i = 0; i < 10; i++) { Files.write(filePath, String.valueOf(i * -86400).getBytes("UTF-8")); diff --git a/README.md b/README.md index 5625e15..6d59826 100644 --- a/README.md +++ b/README.md @@ -70,11 +70,11 @@ OR ##### Option 2: Control offset with an offset file -You can set the property **faketime.file.path** to point to a file containing the offset in *seconds* (ASCII-encoded). This way you can alter the time using methods external to the Java process, i.e. through a mounted Kubernetes ConfigMap. +You can set the property **faketime.offset.file** to point to a file containing the offset in *seconds* (ASCII-encoded). This way you can alter the time using methods external to the Java process, i.e. through a mounted Kubernetes ConfigMap. ``` java -agentpath:/path/to/the/library/you/got/above \ - -Dfaketime.file.path=/path/to/a/text/file/containing/a/number + -Dfaketime.offset.file=/path/to/a/text/file/containing/a/number -XX:+UnlockDiagnosticVMOptions \ -XX:DisableIntrinsic=_currentTimeMillis \ -XX:CompileCommand=exclude,java/lang/System.currentTimeMillis \ diff --git a/src/FakeTimeAgent.c b/src/FakeTimeAgent.c index a497928..ddffae9 100644 --- a/src/FakeTimeAgent.c +++ b/src/FakeTimeAgent.c @@ -56,7 +56,7 @@ JNIEXPORT jlong JNICALL newCurrentTimeInMillis(JNIEnv* env, jclass jc) { jstring offsetPropertyName = (*env)->NewStringUTF(env, "faketime.offset.seconds"); jstring offsetPropertyDefault = (*env)->NewStringUTF(env, "0"); - jstring filePathPropertyName = (*env)->NewStringUTF(env, "faketime.file.path"); + jstring filePathPropertyName = (*env)->NewStringUTF(env, "faketime.offset.file"); //jstring fileCacheDurationPropertyName = (*env)->NewStringUTF(env, "faketime.file.cache_duration"); //jstring fileCacheDurationPropertyDefault = (*env)->NewStringUTF(env, "10"); From 61e9842e74fdfa647a5043e080d618ed67018ce3 Mon Sep 17 00:00:00 2001 From: Sebastian Joergensen Date: Wed, 1 May 2019 10:55:01 +0200 Subject: [PATCH 4/4] add test/ and lib/ to .gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 2e5ccf5..5ffeb66 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ out/ *.class +lib/ +test/