mirror of
https://github.com/AngelAuraMC/Amethyst-Android.git
synced 2025-09-16 16:16:04 -04:00
Stdio logging through C
This commit is contained in:
parent
4115b997f8
commit
fbe587bea5
@ -60,7 +60,13 @@ public class LoggerView extends ConstraintLayout {
|
|||||||
mToggleButton.setOnCheckedChangeListener(
|
mToggleButton.setOnCheckedChangeListener(
|
||||||
(compoundButton, isChecked) -> {
|
(compoundButton, isChecked) -> {
|
||||||
mLogTextView.setVisibility(isChecked ? VISIBLE : GONE);
|
mLogTextView.setVisibility(isChecked ? VISIBLE : GONE);
|
||||||
if(!isChecked) mLogTextView.setText("");
|
if(isChecked) {
|
||||||
|
Logger.setLogListener(mLogListener);
|
||||||
|
}else{
|
||||||
|
mLogTextView.setText("");
|
||||||
|
Logger.setLogListener(null); // Makes the JNI code be able to skip expensive logger callbacks
|
||||||
|
// NOTE: was tested by rapidly smashing the log on/off button, no sync issues found :)
|
||||||
|
}
|
||||||
});
|
});
|
||||||
mToggleButton.setChecked(false);
|
mToggleButton.setChecked(false);
|
||||||
|
|
||||||
@ -80,7 +86,6 @@ public class LoggerView extends ConstraintLayout {
|
|||||||
});
|
});
|
||||||
|
|
||||||
};
|
};
|
||||||
Logger.getInstance().setLogListener(mLogListener);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,7 @@ public class JavaGUILauncherActivity extends BaseActivity implements View.OnTouc
|
|||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setContentView(R.layout.activity_java_gui_launcher);
|
setContentView(R.layout.activity_java_gui_launcher);
|
||||||
|
|
||||||
Logger.getInstance().reset();
|
Logger.begin(new File(Tools.DIR_GAME_HOME, "latestlog.txt").getAbsolutePath());
|
||||||
mTouchCharInput = findViewById(R.id.awt_touch_char);
|
mTouchCharInput = findViewById(R.id.awt_touch_char);
|
||||||
mTouchCharInput.setCharacterSender(new AwtCharSender());
|
mTouchCharInput.setCharacterSender(new AwtCharSender());
|
||||||
|
|
||||||
|
@ -2,75 +2,24 @@ package net.kdt.pojavlaunch;
|
|||||||
|
|
||||||
import androidx.annotation.Keep;
|
import androidx.annotation.Keep;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.PrintStream;
|
|
||||||
import java.lang.ref.WeakReference;
|
|
||||||
|
|
||||||
/** Singleton class made to log on one file
|
/** Singleton class made to log on one file
|
||||||
* The singleton part can be removed but will require more implementation from the end-dev
|
* The singleton part can be removed but will require more implementation from the end-dev
|
||||||
*/
|
*/
|
||||||
@Keep
|
@Keep
|
||||||
public class Logger {
|
public class Logger {
|
||||||
private static volatile Logger sLoggerSingleton = null;
|
|
||||||
|
|
||||||
/* Instance variables */
|
private static final Logger dummyLogger = new Logger();
|
||||||
private final File mLogFile;
|
|
||||||
private PrintStream mLogStream;
|
|
||||||
private WeakReference<eventLogListener> mLogListenerWeakReference = null;
|
|
||||||
|
|
||||||
/* No public construction */
|
|
||||||
private Logger(){
|
|
||||||
mLogFile = new File(Tools.DIR_GAME_HOME, "latestlog.txt");
|
|
||||||
// Make a new instance of the log file
|
|
||||||
// Default PrintStream constructor will overwrite the file for us
|
|
||||||
try {
|
|
||||||
mLogStream = new PrintStream(mLogFile.getAbsolutePath());
|
|
||||||
}catch (IOException e){e.printStackTrace();}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Logger getInstance(){
|
public static Logger getInstance(){
|
||||||
if(sLoggerSingleton == null){
|
return dummyLogger;
|
||||||
synchronized(Logger.class){
|
|
||||||
if(sLoggerSingleton == null){
|
|
||||||
sLoggerSingleton = new Logger();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return sLoggerSingleton;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/** Print the text to the log file if not censored */
|
/** Print the text to the log file if not censored */
|
||||||
public void appendToLog(String text){
|
public static native void appendToLog(String text);
|
||||||
if(shouldCensorLog(text)) return;
|
|
||||||
appendToLogUnchecked(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Print the text to the log file, no china censoring there */
|
|
||||||
public void appendToLogUnchecked(String text){
|
|
||||||
mLogStream.println(text);
|
|
||||||
notifyLogListener(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Reset the log file, effectively erasing any previous logs */
|
/** Reset the log file, effectively erasing any previous logs */
|
||||||
public void reset(){
|
public static native void begin(String logFilePath);
|
||||||
try{
|
|
||||||
//Refer to line 30
|
|
||||||
mLogStream = new PrintStream(mLogFile.getAbsolutePath());
|
|
||||||
}catch (IOException e){ e.printStackTrace();}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Perform various checks to see if the log is safe to print
|
|
||||||
* Subclasses may want to override this behavior
|
|
||||||
* @param text The text to check
|
|
||||||
* @return Whether the log should be censored
|
|
||||||
*/
|
|
||||||
private static boolean shouldCensorLog(String text){
|
|
||||||
return text.contains("Session ID is");
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Small listener for anything listening to the log */
|
/** Small listener for anything listening to the log */
|
||||||
public interface eventLogListener {
|
public interface eventLogListener {
|
||||||
@ -78,18 +27,5 @@ public class Logger {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Link a log listener to the logger */
|
/** Link a log listener to the logger */
|
||||||
public void setLogListener(eventLogListener logListener){
|
public static native void setLogListener(eventLogListener logListener);
|
||||||
this.mLogListenerWeakReference = new WeakReference<>(logListener);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Notifies the event listener, if it exists */
|
|
||||||
private void notifyLogListener(String text){
|
|
||||||
if(mLogListenerWeakReference == null) return;
|
|
||||||
eventLogListener logListener = mLogListenerWeakReference.get();
|
|
||||||
if(logListener == null){
|
|
||||||
mLogListenerWeakReference = null;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
logListener.onEventLogged(text);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -131,7 +131,7 @@ public class MainActivity extends BaseActivity implements ControlButtonMenuListe
|
|||||||
drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
|
drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Logger.getInstance().reset();
|
Logger.begin(new File(Tools.DIR_GAME_HOME, "latestlog.txt").getAbsolutePath());
|
||||||
// FIXME: is it safe for multi thread?
|
// FIXME: is it safe for multi thread?
|
||||||
GLOBAL_CLIPBOARD = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
|
GLOBAL_CLIPBOARD = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
|
||||||
touchCharInput.setCharacterSender(new LwjglCharSender());
|
touchCharInput.setCharacterSender(new LwjglCharSender());
|
||||||
|
@ -35,7 +35,6 @@ public class MultiRTUtils {
|
|||||||
private static final File RUNTIME_FOLDER = new File(Tools.MULTIRT_HOME);
|
private static final File RUNTIME_FOLDER = new File(Tools.MULTIRT_HOME);
|
||||||
private static final String JAVA_VERSION_STR = "JAVA_VERSION=\"";
|
private static final String JAVA_VERSION_STR = "JAVA_VERSION=\"";
|
||||||
private static final String OS_ARCH_STR = "OS_ARCH=\"";
|
private static final String OS_ARCH_STR = "OS_ARCH=\"";
|
||||||
private static String sSelectedRuntimeName;
|
|
||||||
|
|
||||||
public static List<Runtime> getRuntimes() {
|
public static List<Runtime> getRuntimes() {
|
||||||
if(!RUNTIME_FOLDER.exists() && !RUNTIME_FOLDER.mkdirs()) {
|
if(!RUNTIME_FOLDER.exists() && !RUNTIME_FOLDER.mkdirs()) {
|
||||||
|
@ -96,7 +96,6 @@ public class JREUtils {
|
|||||||
public static void redirectAndPrintJRELog() {
|
public static void redirectAndPrintJRELog() {
|
||||||
|
|
||||||
Log.v("jrelog","Log starts here");
|
Log.v("jrelog","Log starts here");
|
||||||
JREUtils.logToLogger(Logger.getInstance());
|
|
||||||
new Thread(new Runnable(){
|
new Thread(new Runnable(){
|
||||||
int failTime = 0;
|
int failTime = 0;
|
||||||
ProcessBuilder logcatPb;
|
ProcessBuilder logcatPb;
|
||||||
@ -545,7 +544,6 @@ public class JREUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
public static native int chdir(String path);
|
public static native int chdir(String path);
|
||||||
public static native void logToLogger(final Logger logger);
|
|
||||||
public static native boolean dlopen(String libPath);
|
public static native boolean dlopen(String libPath);
|
||||||
public static native void setLdLibraryPath(String ldLibraryPath);
|
public static native void setLdLibraryPath(String ldLibraryPath);
|
||||||
public static native void setupBridgeWindow(Object surface);
|
public static native void setupBridgeWindow(Object surface);
|
||||||
|
@ -5,46 +5,74 @@
|
|||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <xhook.h>
|
#include <xhook.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
//
|
//
|
||||||
// Created by maks on 17.02.21.
|
// Created by maks on 17.02.21.
|
||||||
//
|
//
|
||||||
static JavaVM *stdiois_jvm;
|
|
||||||
static volatile jmethodID log_cbMethod;
|
|
||||||
static volatile jobject log_cbObject;
|
|
||||||
static volatile jobject exitTrap_ctx;
|
static volatile jobject exitTrap_ctx;
|
||||||
static volatile jclass exitTrap_exitClass;
|
static volatile jclass exitTrap_exitClass;
|
||||||
static volatile jmethodID exitTrap_staticMethod;
|
static volatile jmethodID exitTrap_staticMethod;
|
||||||
static JavaVM *exitTrap_jvm;
|
static JavaVM *exitTrap_jvm;
|
||||||
|
|
||||||
|
static JavaVM *stdiois_jvm;
|
||||||
static int pfd[2];
|
static int pfd[2];
|
||||||
static pthread_t logger;
|
static pthread_t logger;
|
||||||
|
static jmethodID logger_onEventLogged;
|
||||||
|
static volatile jobject logListener = NULL;
|
||||||
|
static int latestlog_fd = -1;
|
||||||
|
|
||||||
|
static bool recordBuffer(char* buf, ssize_t len) {
|
||||||
|
if(strstr(buf, "Session ID is")) return false;
|
||||||
|
if(latestlog_fd != -1) {
|
||||||
|
write(latestlog_fd, buf, len);
|
||||||
|
fdatasync(latestlog_fd);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, __attribute((unused)) void* reserved) {
|
||||||
|
stdiois_jvm = vm;
|
||||||
|
JNIEnv *env;
|
||||||
|
(*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_4);
|
||||||
|
jclass eventLogListener = (*env)->FindClass(env, "net/kdt/pojavlaunch/Logger$eventLogListener");
|
||||||
|
logger_onEventLogged = (*env)->GetMethodID(env, eventLogListener, "onEventLogged", "(Ljava/lang/String;)V");
|
||||||
|
return JNI_VERSION_1_4;
|
||||||
|
}
|
||||||
|
|
||||||
static void *logger_thread() {
|
static void *logger_thread() {
|
||||||
JNIEnv *env;
|
JNIEnv *env;
|
||||||
jstring str;
|
jstring writeString;
|
||||||
(*stdiois_jvm)->AttachCurrentThread(stdiois_jvm, &env, NULL);
|
(*stdiois_jvm)->AttachCurrentThread(stdiois_jvm, &env, NULL);
|
||||||
ssize_t rsize;
|
ssize_t rsize;
|
||||||
char buf[2048];
|
char buf[2050];
|
||||||
while((rsize = read(pfd[0], buf, sizeof(buf)-1)) > 0) {
|
while((rsize = read(pfd[0], buf, sizeof(buf)-1)) > 0) {
|
||||||
if(buf[rsize-1]=='\n') {
|
if(buf[rsize-1]=='\n') {
|
||||||
rsize=rsize-1;
|
rsize=rsize-1;
|
||||||
}
|
}
|
||||||
buf[rsize]=0x00;
|
buf[rsize]=0x00;
|
||||||
str = (*env)->NewStringUTF(env,buf);
|
if(recordBuffer(buf, rsize) && logListener != NULL) {
|
||||||
(*env)->CallVoidMethod(env, log_cbObject, log_cbMethod, str);
|
writeString = (*env)->NewStringUTF(env, buf);
|
||||||
(*env)->DeleteLocalRef(env,str);
|
(*env)->CallVoidMethod(env, logListener, logger_onEventLogged, writeString);
|
||||||
|
(*env)->DeleteLocalRef(env, writeString);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
(*env)->DeleteGlobalRef(env, log_cbMethod);
|
|
||||||
(*env)->DeleteGlobalRef(env, log_cbObject);
|
|
||||||
(*stdiois_jvm)->DetachCurrentThread(stdiois_jvm);
|
(*stdiois_jvm)->DetachCurrentThread(stdiois_jvm);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
JNIEXPORT void JNICALL
|
JNIEXPORT void JNICALL
|
||||||
Java_net_kdt_pojavlaunch_utils_JREUtils_logToLogger(JNIEnv *env, __attribute((unused)) jclass clazz, jobject javaLogger) {
|
Java_net_kdt_pojavlaunch_Logger_begin(JNIEnv *env, __attribute((unused)) jclass clazz, jstring logPath) {
|
||||||
// TODO: implement logToActivity()
|
// TODO: implement logToActivity()
|
||||||
jclass loggableActivityClass = (*env)->FindClass(env,"net/kdt/pojavlaunch/Logger");
|
if(latestlog_fd != -1) {
|
||||||
log_cbMethod = (*env)->GetMethodID(env, loggableActivityClass, "appendToLog", "(Ljava/lang/String;)V");
|
int localfd = latestlog_fd;
|
||||||
(*env)->GetJavaVM(env,&stdiois_jvm);
|
latestlog_fd = -1;
|
||||||
log_cbObject = (*env)->NewGlobalRef(env, javaLogger);
|
close(localfd);
|
||||||
|
}
|
||||||
|
jclass ioeClass = (*env)->FindClass(env, "java/io/IOException");
|
||||||
|
|
||||||
|
|
||||||
setvbuf(stdout, 0, _IOLBF, 0); // make stdout line-buffered
|
setvbuf(stdout, 0, _IOLBF, 0); // make stdout line-buffered
|
||||||
setvbuf(stderr, 0, _IONBF, 0); // make stderr unbuffered
|
setvbuf(stderr, 0, _IONBF, 0); // make stderr unbuffered
|
||||||
@ -54,17 +82,26 @@ Java_net_kdt_pojavlaunch_utils_JREUtils_logToLogger(JNIEnv *env, __attribute((un
|
|||||||
dup2(pfd[1], 1);
|
dup2(pfd[1], 1);
|
||||||
dup2(pfd[1], 2);
|
dup2(pfd[1], 2);
|
||||||
|
|
||||||
|
/* open latestlog.txt for writing */
|
||||||
|
const char* logFilePath = (*env)->GetStringUTFChars(env, logPath, NULL);
|
||||||
|
latestlog_fd = open(logFilePath, O_WRONLY | O_TRUNC | O_CREAT | O_NOATIME, 666);
|
||||||
|
if(latestlog_fd == -1) {
|
||||||
|
latestlog_fd = 0;
|
||||||
|
(*env)->ThrowNew(env, ioeClass, strerror(errno));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
(*env)->ReleaseStringUTFChars(env, logPath, logFilePath);
|
||||||
|
|
||||||
/* spawn the logging thread */
|
/* spawn the logging thread */
|
||||||
if(pthread_create(&logger, 0, logger_thread, 0) != 0) {
|
int result = pthread_create(&logger, 0, logger_thread, 0);
|
||||||
jstring str = (*env)->NewStringUTF(env,"Failed to start logging!");
|
if(result != 0) {
|
||||||
(*env)->CallVoidMethod(env, log_cbObject, log_cbMethod, str);
|
close(latestlog_fd);
|
||||||
(*env)->DeleteLocalRef(env,str);
|
(*env)->ThrowNew(env, ioeClass, strerror(result));
|
||||||
(*env)->DeleteGlobalRef(env, log_cbMethod);
|
|
||||||
(*env)->DeleteGlobalRef(env, log_cbObject);
|
|
||||||
}
|
}
|
||||||
pthread_detach(logger);
|
pthread_detach(logger);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void (*old_exit)(int code);
|
void (*old_exit)(int code);
|
||||||
void custom_exit(int code) {
|
void custom_exit(int code) {
|
||||||
if(code != 0) {
|
if(code != 0) {
|
||||||
@ -87,3 +124,25 @@ JNIEXPORT void JNICALL Java_net_kdt_pojavlaunch_utils_JREUtils_setupExitTrap(JNI
|
|||||||
xhook_register(".*\\.so$", "exit", custom_exit, (void **) &old_exit);
|
xhook_register(".*\\.so$", "exit", custom_exit, (void **) &old_exit);
|
||||||
xhook_refresh(1);
|
xhook_refresh(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JNIEXPORT void JNICALL Java_net_kdt_pojavlaunch_Logger_appendToLog(JNIEnv *env, __attribute((unused)) jclass clazz, jstring text) {
|
||||||
|
jsize appendStringLength = (*env)->GetStringUTFLength(env, text);
|
||||||
|
char newChars[appendStringLength+2];
|
||||||
|
(*env)->GetStringUTFRegion(env, text, 0, appendStringLength, newChars);
|
||||||
|
newChars[appendStringLength] = '\n';
|
||||||
|
newChars[appendStringLength+1] = 0;
|
||||||
|
if(recordBuffer(newChars, appendStringLength+1) && logListener != NULL) {
|
||||||
|
(*env)->CallVoidMethod(env, logListener, logger_onEventLogged, text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
JNIEXPORT void JNICALL
|
||||||
|
Java_net_kdt_pojavlaunch_Logger_setLogListener(JNIEnv *env, __attribute((unused)) jclass clazz, jobject log_listener) {
|
||||||
|
jobject logListenerLocal = logListener;
|
||||||
|
if(log_listener == NULL) {
|
||||||
|
logListener = NULL;
|
||||||
|
}else{
|
||||||
|
logListener = (*env)->NewGlobalRef(env, log_listener);
|
||||||
|
}
|
||||||
|
if(logListenerLocal != NULL) (*env)->DeleteGlobalRef(env, logListenerLocal);
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user