mirror of
https://github.com/AngelAuraMC/Amethyst-Android.git
synced 2025-09-15 23:59:21 -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(
|
||||
(compoundButton, isChecked) -> {
|
||||
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);
|
||||
|
||||
@ -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);
|
||||
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.setCharacterSender(new AwtCharSender());
|
||||
|
||||
|
@ -2,75 +2,24 @@ package net.kdt.pojavlaunch;
|
||||
|
||||
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
|
||||
* The singleton part can be removed but will require more implementation from the end-dev
|
||||
*/
|
||||
@Keep
|
||||
public class Logger {
|
||||
private static volatile Logger sLoggerSingleton = null;
|
||||
|
||||
/* Instance variables */
|
||||
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();}
|
||||
|
||||
}
|
||||
private static final Logger dummyLogger = new Logger();
|
||||
|
||||
public static Logger getInstance(){
|
||||
if(sLoggerSingleton == null){
|
||||
synchronized(Logger.class){
|
||||
if(sLoggerSingleton == null){
|
||||
sLoggerSingleton = new Logger();
|
||||
}
|
||||
}
|
||||
}
|
||||
return sLoggerSingleton;
|
||||
return dummyLogger;
|
||||
}
|
||||
|
||||
|
||||
/** Print the text to the log file if not censored */
|
||||
public void appendToLog(String text){
|
||||
if(shouldCensorLog(text)) return;
|
||||
appendToLogUnchecked(text);
|
||||
}
|
||||
public static native void appendToLog(String 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 */
|
||||
public void reset(){
|
||||
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");
|
||||
}
|
||||
public static native void begin(String logFilePath);
|
||||
|
||||
/** Small listener for anything listening to the log */
|
||||
public interface eventLogListener {
|
||||
@ -78,18 +27,5 @@ public class Logger {
|
||||
}
|
||||
|
||||
/** Link a log listener to the logger */
|
||||
public 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);
|
||||
}
|
||||
public static native void setLogListener(eventLogListener logListener);
|
||||
}
|
||||
|
@ -131,7 +131,7 @@ public class MainActivity extends BaseActivity implements ControlButtonMenuListe
|
||||
drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
|
||||
|
||||
try {
|
||||
Logger.getInstance().reset();
|
||||
Logger.begin(new File(Tools.DIR_GAME_HOME, "latestlog.txt").getAbsolutePath());
|
||||
// FIXME: is it safe for multi thread?
|
||||
GLOBAL_CLIPBOARD = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
|
||||
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 String JAVA_VERSION_STR = "JAVA_VERSION=\"";
|
||||
private static final String OS_ARCH_STR = "OS_ARCH=\"";
|
||||
private static String sSelectedRuntimeName;
|
||||
|
||||
public static List<Runtime> getRuntimes() {
|
||||
if(!RUNTIME_FOLDER.exists() && !RUNTIME_FOLDER.mkdirs()) {
|
||||
|
@ -96,7 +96,6 @@ public class JREUtils {
|
||||
public static void redirectAndPrintJRELog() {
|
||||
|
||||
Log.v("jrelog","Log starts here");
|
||||
JREUtils.logToLogger(Logger.getInstance());
|
||||
new Thread(new Runnable(){
|
||||
int failTime = 0;
|
||||
ProcessBuilder logcatPb;
|
||||
@ -545,7 +544,6 @@ public class JREUtils {
|
||||
}
|
||||
}
|
||||
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 void setLdLibraryPath(String ldLibraryPath);
|
||||
public static native void setupBridgeWindow(Object surface);
|
||||
|
@ -5,46 +5,74 @@
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <xhook.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
//
|
||||
// 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 jclass exitTrap_exitClass;
|
||||
static volatile jmethodID exitTrap_staticMethod;
|
||||
static JavaVM *exitTrap_jvm;
|
||||
|
||||
static JavaVM *stdiois_jvm;
|
||||
static int pfd[2];
|
||||
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() {
|
||||
JNIEnv *env;
|
||||
jstring str;
|
||||
jstring writeString;
|
||||
(*stdiois_jvm)->AttachCurrentThread(stdiois_jvm, &env, NULL);
|
||||
ssize_t rsize;
|
||||
char buf[2048];
|
||||
char buf[2050];
|
||||
while((rsize = read(pfd[0], buf, sizeof(buf)-1)) > 0) {
|
||||
if(buf[rsize-1]=='\n') {
|
||||
rsize=rsize-1;
|
||||
}
|
||||
buf[rsize]=0x00;
|
||||
str = (*env)->NewStringUTF(env,buf);
|
||||
(*env)->CallVoidMethod(env, log_cbObject, log_cbMethod, str);
|
||||
(*env)->DeleteLocalRef(env,str);
|
||||
if(recordBuffer(buf, rsize) && logListener != NULL) {
|
||||
writeString = (*env)->NewStringUTF(env, buf);
|
||||
(*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);
|
||||
return NULL;
|
||||
}
|
||||
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()
|
||||
jclass loggableActivityClass = (*env)->FindClass(env,"net/kdt/pojavlaunch/Logger");
|
||||
log_cbMethod = (*env)->GetMethodID(env, loggableActivityClass, "appendToLog", "(Ljava/lang/String;)V");
|
||||
(*env)->GetJavaVM(env,&stdiois_jvm);
|
||||
log_cbObject = (*env)->NewGlobalRef(env, javaLogger);
|
||||
if(latestlog_fd != -1) {
|
||||
int localfd = latestlog_fd;
|
||||
latestlog_fd = -1;
|
||||
close(localfd);
|
||||
}
|
||||
jclass ioeClass = (*env)->FindClass(env, "java/io/IOException");
|
||||
|
||||
|
||||
setvbuf(stdout, 0, _IOLBF, 0); // make stdout line-buffered
|
||||
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], 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 */
|
||||
if(pthread_create(&logger, 0, logger_thread, 0) != 0) {
|
||||
jstring str = (*env)->NewStringUTF(env,"Failed to start logging!");
|
||||
(*env)->CallVoidMethod(env, log_cbObject, log_cbMethod, str);
|
||||
(*env)->DeleteLocalRef(env,str);
|
||||
(*env)->DeleteGlobalRef(env, log_cbMethod);
|
||||
(*env)->DeleteGlobalRef(env, log_cbObject);
|
||||
int result = pthread_create(&logger, 0, logger_thread, 0);
|
||||
if(result != 0) {
|
||||
close(latestlog_fd);
|
||||
(*env)->ThrowNew(env, ioeClass, strerror(result));
|
||||
}
|
||||
pthread_detach(logger);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void (*old_exit)(int code);
|
||||
void custom_exit(int code) {
|
||||
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_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