Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
out/
*.class
lib/
test/
21 changes: 21 additions & 0 deletions FakeTimeFileTest.java
Original file line number Diff line number Diff line change
@@ -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.offset.file", 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());
}
}
}
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

```
Expand All @@ -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.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.offset.file=/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.
49 changes: 46 additions & 3 deletions src/FakeTimeAgent.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;");
Expand All @@ -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.offset.file");
//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;
}

Expand Down
3 changes: 3 additions & 0 deletions test_linux.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
7 changes: 5 additions & 2 deletions test_linux_64.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
3 changes: 3 additions & 0 deletions test_mac.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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