From 310825e2cc2b80704be0bff1cb0352d2afc1e14c Mon Sep 17 00:00:00 2001 From: Aaron Culliney Date: Tue, 12 Nov 2019 09:17:22 -0800 Subject: [PATCH] Route Android logging to native side and timestamp logs --- .../org/deadc0de/apple2ix/Apple2Activity.java | 47 +++++++++++++++---- .../deadc0de/apple2ix/Apple2CrashHandler.java | 28 +++++------ .../apple2ix/Apple2DiskChooserActivity.java | 8 ++-- .../deadc0de/apple2ix/Apple2DisksMenu.java | 6 +-- .../apple2ix/Apple2JoystickCalibration.java | 4 +- .../apple2ix/Apple2KeyboardSettingsMenu.java | 2 +- .../apple2ix/Apple2KeypadChooser.java | 2 +- .../org/deadc0de/apple2ix/Apple2MainMenu.java | 12 ++--- .../deadc0de/apple2ix/Apple2Preferences.java | 24 +++++----- .../deadc0de/apple2ix/Apple2SettingsMenu.java | 2 +- .../org/deadc0de/apple2ix/Apple2Utils.java | 24 +++++----- .../org/deadc0de/apple2ix/Apple2View.java | 16 +++---- Android/jni/jnihooks.c | 34 ++++++++++++++ src/meta/log.c | 36 ++++++++++++-- src/meta/log.h | 12 +++++ 15 files changed, 181 insertions(+), 76 deletions(-) diff --git a/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2Activity.java b/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2Activity.java index da1163c5..3fbd06e7 100644 --- a/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2Activity.java +++ b/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2Activity.java @@ -30,6 +30,7 @@ import android.widget.Toast; import org.deadc0de.apple2ix.basic.BuildConfig; import org.deadc0de.apple2ix.basic.R; +import org.json.JSONObject; import java.io.IOException; import java.io.InputStream; @@ -96,6 +97,8 @@ public class Apple2Activity extends AppCompatActivity implements Apple2DiskChoos private static native void nativeReboot(int resetState); + private static native void nativeLogMessage(String jsonStr); + public final static boolean isNativeBarfed() { return sNativeBarfed; } @@ -118,7 +121,7 @@ public class Apple2Activity extends AppCompatActivity implements Apple2DiskChoos } super.onCreate(savedInstanceState); - Log.e(TAG, "onCreate()"); + logMessage(LogType.ERROR, TAG, "onCreate()"); // placeholder view on initial launch if (mView == null) { @@ -127,14 +130,14 @@ public class Apple2Activity extends AppCompatActivity implements Apple2DiskChoos Apple2CrashHandler.getInstance().initializeAndSetCustomExceptionHandler(this); if (sNativeBarfed) { - Log.e(TAG, "NATIVE BARFED...", sNativeBarfedThrowable); + logMessage(LogType.ERROR, TAG, "NATIVE BARFED : " + sNativeBarfedThrowable.getMessage()); return; } int sampleRate = DevicePropertyCalculator.getRecommendedSampleRate(this); int monoBufferSize = DevicePropertyCalculator.getRecommendedBufferSize(this, /*isStereo:*/false); int stereoBufferSize = DevicePropertyCalculator.getRecommendedBufferSize(this, /*isStereo:*/true); - Log.d(TAG, "Device sampleRate:" + sampleRate + " mono bufferSize:" + monoBufferSize + " stereo bufferSize:" + stereoBufferSize); + logMessage(LogType.DEBUG, TAG, "Device sampleRate:" + sampleRate + " mono bufferSize:" + monoBufferSize + " stereo bufferSize:" + stereoBufferSize); String dataDir = Apple2Utils.getDataDir(this); nativeOnCreate(dataDir, sampleRate, monoBufferSize, stereoBufferSize); @@ -185,7 +188,7 @@ public class Apple2Activity extends AppCompatActivity implements Apple2DiskChoos Apple2Utils.exposeAPKAssets(Apple2Activity.this); if (externalStoragePermission) { Apple2Utils.exposeAPKAssetsToExternal(Apple2Activity.this); - Log.d(TAG, "Finished first time copying #1..."); + logMessage(LogType.DEBUG, TAG, "Finished first time copying #1..."); if (!(boolean)Apple2Preferences.getJSONPref(Apple2Preferences.PREF_DOMAIN_INTERFACE, Apple2Preferences.PREF_RELEASE_NOTES, false)) { Runnable myRunnable = new Runnable() { @@ -232,7 +235,7 @@ public class Apple2Activity extends AppCompatActivity implements Apple2DiskChoos // perform migration(s) and assets exposure now Apple2Utils.migrateToExternalStorage(Apple2Activity.this); Apple2Utils.exposeAPKAssetsToExternal(Apple2Activity.this); - Log.d(TAG, "Finished first time copying #2..."); + logMessage(LogType.DEBUG, TAG, "Finished first time copying #2..."); } // else ... we keep nagging on app startup ... } else { super.onRequestPermissionsResult(requestCode, permissions, grantResults); @@ -253,7 +256,7 @@ public class Apple2Activity extends AppCompatActivity implements Apple2DiskChoos break; } - Log.d(TAG, "onResume()"); + logMessage(LogType.DEBUG, TAG, "onResume()"); showSplashScreen(/*dismissable:*/true); if (!mSwitchingToPortrait.get()) { Apple2CrashHandler.getInstance().checkForCrashes(this); // NOTE : needs to be called again to clean-up @@ -320,10 +323,10 @@ public class Apple2Activity extends AppCompatActivity implements Apple2DiskChoos if (isEmulationPaused()) { Apple2Preferences.save(this); } else { - Log.d(TAG, "Letting native save preferences..."); + logMessage(LogType.DEBUG, TAG, "Letting native save preferences..."); } - Log.d(TAG, "onPause()"); + logMessage(LogType.DEBUG, TAG, "onPause()"); if (mView != null) { mView.onPause(); } @@ -415,7 +418,7 @@ public class Apple2Activity extends AppCompatActivity implements Apple2DiskChoos } catch (InterruptedIOException e) { /* EINTR, EAGAIN */ } catch (IOException e) { - Log.e(TAG, "OOPS could not load release_notes.txt!", e); + logMessage(LogType.ERROR, TAG, "OOPS could not load release_notes.txt : " + e.getMessage()); } finally { if (is != null) { try { @@ -620,4 +623,30 @@ public class Apple2Activity extends AppCompatActivity implements Apple2DiskChoos } }.run(); } + + public enum LogType { + // Constants match + VERBOSE(2), + DEBUG(3), + INFO(4), + WARN(5), + ERROR(6); + private int type; + + LogType(int type) { + this.type = type; + } + } + + public static void logMessage(LogType type, String tag, String mesg) { + JSONObject map = new JSONObject(); + try { + map.put("type", type.type); + map.put("tag", tag); + map.put("mesg", mesg); + nativeLogMessage(map.toString()); + } catch (Exception e) { + Log.e(TAG, "OOPS: " + e.getMessage()); + } + } } diff --git a/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2CrashHandler.java b/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2CrashHandler.java index 34bdc54e..def92ce2 100644 --- a/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2CrashHandler.java +++ b/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2CrashHandler.java @@ -221,24 +221,24 @@ public class Apple2CrashHandler { // here we assume that the crash data was previously sent via email ... if not then we lost it =P - Log.d(TAG, "Cleaning up crash data ..."); + Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "Cleaning up crash data ..."); int idx = 0; File[] nativeCrashes = _nativeCrashFiles(activity); for (File crash : nativeCrashes) { if (!crash.delete()) { - Log.d(TAG, "Could not unlink crash : " + crash); + Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "Could not unlink crash : " + crash); } File processed = new File(_dumpPath2ProcessedPath(crash.getAbsolutePath())); if (!processed.delete()) { - Log.d(TAG, "Could not unlink processed : " + processed); + Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "Could not unlink processed : " + processed); } } File javaCrashFile = _javaCrashFile(activity); if (!javaCrashFile.delete()) { - Log.d(TAG, "Could not unlink java crash : " + javaCrashFile); + Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "Could not unlink java crash : " + javaCrashFile); } // remove previous log file @@ -339,7 +339,7 @@ public class Apple2CrashHandler { for (File crash : nativeCrashes) { String crashPath = crash.getAbsolutePath(); - Log.d(TAG, "Processing crash : " + crashPath); + Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "Processing crash : " + crashPath); String processedPath = _dumpPath2ProcessedPath(crashPath); try { @@ -350,7 +350,7 @@ public class Apple2CrashHandler { StringBuilder crashData = new StringBuilder(); if (!Apple2Utils.readEntireFile(new File(processedPath), crashData)) { - Log.e(TAG, "Error processing crash : " + crashPath); + Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "Error processing crash : " + crashPath); } allCrashData.append(">>>>>>> NATIVE CRASH [").append(crashPath).append("]\n"); allCrashData.append(crashData); @@ -410,9 +410,9 @@ public class Apple2CrashHandler { StringBuilder javaCrashData = new StringBuilder(); File javaCrashFile = _javaCrashFile(activity); if (javaCrashFile.exists()) { - Log.d(TAG, "Reading java crashes file"); + Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "Reading java crashes file"); if (!Apple2Utils.readEntireFile(javaCrashFile, javaCrashData)) { - Log.e(TAG, "Error processing java crash : " + javaCrashFileName); + Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "Error processing java crash : " + javaCrashFileName); } } @@ -437,7 +437,7 @@ public class Apple2CrashHandler { try { obj = new JSONObject(jsonData.toString()); } catch (JSONException e) { - Log.e(TAG, "Error reading preferences : " + e); + Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "Error reading preferences : " + e); } if (obj != null) { summary.append("PREFS:\n"); @@ -511,7 +511,7 @@ public class Apple2CrashHandler { } catch (InterruptedIOException ie) { /* EINTR, EAGAIN ... */ } catch (IOException e) { - Log.e(TAG, "Exception attempting to write data : " + e); + Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "Exception attempting to write data : " + e); } try { @@ -564,7 +564,7 @@ public class Apple2CrashHandler { } else { allCrashFile = new File(Apple2Utils.getDataDir(activity), "apple2ix_crash.txt"); } - Log.d(TAG, "Writing all crashes to temp file : " + allCrashFile.getAbsolutePath()); + Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "Writing all crashes to temp file : " + allCrashFile.getAbsolutePath()); return allCrashFile; } @@ -588,14 +588,14 @@ public class Apple2CrashHandler { File allCrashFile = _getCrashFile(activity); Apple2Utils.writeFile(allCrashData, allCrashFile); if (!allCrashFile.setReadable(true, /*ownerOnly:*/false)) { - Log.d(TAG, "Oops, could not set file data readable!"); + Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "Oops, could not set file data readable!"); } emailIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(allCrashFile)); - Log.d(TAG, "STARTING CHOOSER FOR EMAIL ..."); + Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "STARTING CHOOSER FOR EMAIL ..."); activity.startActivity(Intent.createChooser(emailIntent, "Send email")); - Log.d(TAG, "AFTER START ACTIVITY ..."); + Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "AFTER START ACTIVITY ..."); } diff --git a/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2DiskChooserActivity.java b/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2DiskChooserActivity.java index f1c3a04e..99196df2 100644 --- a/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2DiskChooserActivity.java +++ b/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2DiskChooserActivity.java @@ -55,7 +55,7 @@ public class Apple2DiskChooserActivity extends AppCompatActivity { resolver.takePersistableUriPermission(uri, (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION)); pfd = resolver.openFileDescriptor(uri, "rw"); } catch (Throwable t) { - Log.e(TAG, "OOPS, could not get appropriate access to URI ( " + uri + " ) : " + t); + Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "OOPS, could not get appropriate access to URI ( " + uri + " ) : " + t); } return pfd; @@ -83,7 +83,7 @@ public class Apple2DiskChooserActivity extends AppCompatActivity { fileName = returnCursor.getString(nameIndex); } catch (Throwable t) { - Log.e(TAG, "OOPS, could not get filename from URI ( " + uri + " ) : " + t); + Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "OOPS, could not get filename from URI ( " + uri + " ) : " + t); } return fileName; @@ -118,7 +118,7 @@ public class Apple2DiskChooserActivity extends AppCompatActivity { boolean ran = b.getBoolean("ran"); if (ran) { - Log.e(TAG, "OOPS, already ran..."); + Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "OOPS, already ran..."); finish(); return; } @@ -139,7 +139,7 @@ public class Apple2DiskChooserActivity extends AppCompatActivity { startActivityForResult(pickIntent, EDIT_REQUEST_CODE); } } catch (Throwable t) { - Log.e(TAG, "OOPS : " + t); + Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "OOPS : " + t); setResult(RESULT_CANCELED); finish(); } diff --git a/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2DisksMenu.java b/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2DisksMenu.java index 575e0400..9b965c19 100644 --- a/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2DisksMenu.java +++ b/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2DisksMenu.java @@ -369,7 +369,7 @@ public class Apple2DisksMenu implements Apple2MenuView { try { diskArgs.pfd.close(); } catch (IOException ioe) { - Log.e(TAG, "Error attempting to close PFD : " + ioe); + Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "Error attempting to close PFD : " + ioe); } } diskArgs.pfd = null; @@ -381,7 +381,7 @@ public class Apple2DisksMenu implements Apple2MenuView { } } catch (Throwable t) { - Log.d(TAG, "OOPS: " + t); + Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "OOPS: " + t); } } @@ -739,7 +739,7 @@ public class Apple2DisksMenu implements Apple2MenuView { @Override public void onItemClick(AdapterView parent, View view, final int position, long id) { if (isDirectory[position]) { - Log.d(TAG, "Descending to path : " + filePaths[position]); + Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "Descending to path : " + filePaths[position]); if (parentIsRootPath && !new File(filePaths[position]).isAbsolute()) { pushPathStack(parentDisksDir + File.separator + filePaths[position]); } else { diff --git a/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2JoystickCalibration.java b/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2JoystickCalibration.java index 3293ae65..93f42d58 100644 --- a/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2JoystickCalibration.java +++ b/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2JoystickCalibration.java @@ -108,7 +108,7 @@ public class Apple2JoystickCalibration implements Apple2MenuView { joystickPollerThread = new Thread() { @Override public void run() { - Log.i(TAG, "Starting joystick poll thread..."); + Apple2Activity.logMessage(Apple2Activity.LogType.INFO, TAG, "Starting joystick poll thread..."); try { while (true) { long cxy = nativePollJoystick(); @@ -139,7 +139,7 @@ public class Apple2JoystickCalibration implements Apple2MenuView { Thread.sleep(100); } } catch (Exception e) { - Log.i(TAG, "Stopping joystick poll thread..."); + Apple2Activity.logMessage(Apple2Activity.LogType.INFO, TAG, "Stopping joystick poll thread..."); } } }; diff --git a/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2KeyboardSettingsMenu.java b/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2KeyboardSettingsMenu.java index 5e1f0a3e..176b1a81 100644 --- a/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2KeyboardSettingsMenu.java +++ b/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2KeyboardSettingsMenu.java @@ -216,7 +216,7 @@ public class Apple2KeyboardSettingsMenu extends Apple2AbstractMenu { File keyboardDir = new File(Apple2Utils.getDataDir(activity) + File.separator + "keyboards"); files = keyboardDir.listFiles(kbdJsonFilter); if (files == null) { - Log.e(TAG, "OOPS, could not read keyboard data directory"); + Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "OOPS, could not read keyboard data directory"); return; } } diff --git a/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2KeypadChooser.java b/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2KeypadChooser.java index dbe6b8a9..66132919 100644 --- a/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2KeypadChooser.java +++ b/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2KeypadChooser.java @@ -89,7 +89,7 @@ public class Apple2KeypadChooser implements Apple2MenuView { } String asciiStr = asciiRepresentation(mActivity, ascii); - Log.d(TAG, "ascii:'" + asciiStr + "' scancode:" + scancode); + Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "ascii:'" + asciiStr + "' scancode:" + scancode); if (ascii == ' ') { ascii = Apple2KeyboardSettingsMenu.ICONTEXT_VISUAL_SPACE; } diff --git a/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2MainMenu.java b/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2MainMenu.java index 6ef0401c..28ff0de7 100644 --- a/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2MainMenu.java +++ b/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2MainMenu.java @@ -179,7 +179,7 @@ public class Apple2MainMenu { mainMenuView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView parent, View view, int position, long id) { - Log.d(TAG, "position:" + position + " tapped..."); + Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "position:" + position + " tapped..."); SETTINGS setting = SETTINGS.values()[position]; setting.handleSelection(Apple2MainMenu.this); } @@ -337,12 +337,12 @@ public class Apple2MainMenu { try { diskArgs.pfd.close(); // at this point diskArgs.pfd !null } catch (IOException ioe) { - Log.e(TAG, "Error attempting to close PFD : " + ioe); + Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "Error attempting to close PFD : " + ioe); } diskArgs.pfd = null; } catch (Exception e) { - Log.e(TAG, "OOPS: " + e); + Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "OOPS: " + e); } return restored; @@ -386,7 +386,7 @@ public class Apple2MainMenu { pfds[i] = Apple2DiskChooserActivity.openFileDescriptorFromUri(activity, uri); if (pfds[i] == null) { - Log.e(TAG, "Did not find URI for drive #" + i + " specified in " + SAVE_FILE + " file : " + diskPath); + Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "Did not find URI for drive #" + i + " specified in " + SAVE_FILE + " file : " + diskPath); } else { int fd = pfds[i].getFd(); map.put(fdKeys[i], fd); @@ -394,7 +394,7 @@ public class Apple2MainMenu { } else { boolean exists = new File(diskPath).exists(); if (!exists) { - Log.e(TAG, "Did not find path for drive #" + i + " specified in " + SAVE_FILE + " file : " + diskPath); + Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "Did not find path for drive #" + i + " specified in " + SAVE_FILE + " file : " + diskPath); } } } @@ -407,7 +407,7 @@ public class Apple2MainMenu { pfds[i].close(); } } catch (IOException ioe) { - Log.e(TAG, "Error attempting to close PFD #" + i + " : " + ioe); + Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "Error attempting to close PFD #" + i + " : " + ioe); } } map = new JSONObject(jsonString); diff --git a/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2Preferences.java b/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2Preferences.java index 25d6c397..431328d4 100644 --- a/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2Preferences.java +++ b/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2Preferences.java @@ -103,7 +103,7 @@ public class Apple2Preferences { key = menu.getPrefKey(); val = map.get(key); } catch (JSONException e) { - Log.d(TAG, "Did not find value for domain:" + menu.getPrefDefault() + " key:" + key); + Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "Did not find value for domain:" + menu.getPrefDefault() + " key:" + key); } if (val == null && key != null) { val = menu.getPrefDefault(); @@ -123,7 +123,7 @@ public class Apple2Preferences { map = _prefDomain(domain); val = map.get(key); } catch (JSONException e) { - Log.d(TAG, "Did not find value for domain:" + domain + " key:" + key); + Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "Did not find value for domain:" + domain + " key:" + key); } if (val == null) { val = defaultVal; @@ -143,17 +143,17 @@ public class Apple2Preferences { try { return (float) obj; } catch (ClassCastException e) { - Log.d(TAG, "could not cast object as float"); + Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "could not cast object as float"); } try { return (float) ((double) obj); } catch (ClassCastException e) { - Log.d(TAG, "could not cast object as double"); + Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "could not cast object as double"); } try { return (float) ((int) obj); } catch (ClassCastException e) { - Log.d(TAG, "could not cast object as int"); + Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "could not cast object as int"); } return (float) ((long) obj); } @@ -165,17 +165,17 @@ public class Apple2Preferences { try { return (int) obj; } catch (ClassCastException e) { - Log.d(TAG, "could not cast object as int"); + Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "could not cast object as int"); } try { return (int) ((long) obj); } catch (ClassCastException e) { - Log.d(TAG, "could not cast object as long"); + Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "could not cast object as long"); } try { return (int) ((float) obj); } catch (ClassCastException e) { - Log.d(TAG, "could not cast object as float"); + Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "could not cast object as float"); } return (int) ((double) obj); } @@ -249,7 +249,7 @@ public class Apple2Preferences { jsonArray = (JSONArray) getJSONPref(PREF_DOMAIN_JOYSTICK, "kpAxisRosetteChars", null); if (jsonArray == null || jsonArray.length() != Apple2KeypadSettingsMenu.ROSETTE_SIZE) { - Log.e(TAG, "Oops, kpAxisRosetteChars is not expected length"); + Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "Oops, kpAxisRosetteChars is not expected length"); } else { for (int i = 0; i < Apple2KeypadSettingsMenu.ROSETTE_SIZE; i++) { Apple2KeypadSettingsMenu.KeyTuple tuple = axisRosette.get(i); @@ -259,7 +259,7 @@ public class Apple2Preferences { jsonArray = (JSONArray) getJSONPref(PREF_DOMAIN_JOYSTICK, "kpAxisRosetteScancodes", null); if (jsonArray == null || jsonArray.length() != Apple2KeypadSettingsMenu.ROSETTE_SIZE) { - Log.e(TAG, "Oops, kpAxisRosetteScancodes is not expected length"); + Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "Oops, kpAxisRosetteScancodes is not expected length"); } else { for (int i = 0; i < Apple2KeypadSettingsMenu.ROSETTE_SIZE; i++) { Apple2KeypadSettingsMenu.KeyTuple tuple = axisRosette.get(i); @@ -333,7 +333,7 @@ public class Apple2Preferences { StringBuilder jsonString = new StringBuilder(); if (!Apple2Utils.readEntireFile(prefsFile, jsonString)) { - Log.d(TAG, "Oops, could not read JSON file : " + prefsFile); + Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "Oops, could not read JSON file : " + prefsFile); } try { @@ -359,7 +359,7 @@ public class Apple2Preferences { try { jsonString = sSettings.toString(2); } catch (JSONException e) { - Log.w(TAG, "Error attempting to pretty-print JSON : " + e); + Apple2Activity.logMessage(Apple2Activity.LogType.WARN, TAG, "Error attempting to pretty-print JSON : " + e); ex = e; jsonString = sSettings.toString(); } diff --git a/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2SettingsMenu.java b/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2SettingsMenu.java index 0c9f0df1..8448f999 100644 --- a/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2SettingsMenu.java +++ b/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2SettingsMenu.java @@ -425,7 +425,7 @@ public class Apple2SettingsMenu extends Apple2AbstractMenu { activity.runOnUiThread(new Runnable() { @Override public void run() { - Log.d(TAG, "About to NPE : " + str[0].length()); + Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "About to NPE : " + str[0].length()); } }); } diff --git a/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2Utils.java b/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2Utils.java index 8db699b6..4d82d534 100644 --- a/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2Utils.java +++ b/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2Utils.java @@ -61,7 +61,7 @@ public class Apple2Utils { } catch (InterruptedIOException ie) { /* EINTR, EAGAIN ... */ } catch (IOException e) { - Log.d(TAG, "Error reading file at path : " + file.toString()); + Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "Error reading file at path : " + file.toString()); } try { @@ -89,7 +89,7 @@ public class Apple2Utils { } catch (InterruptedIOException ie) { /* EINTR, EAGAIN ... */ } catch (IOException e) { - Log.e(TAG, "Exception attempting to write data : " + e); + Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "Exception attempting to write data : " + e); } try { @@ -182,7 +182,7 @@ public class Apple2Utils { if (!externalDir.exists()) { boolean made = externalDir.mkdirs(); if (!made) { - Log.d(TAG, "WARNING: could not make directory : " + sExternalFilesDir); + Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "WARNING: could not make directory : " + sExternalFilesDir); break; } } @@ -218,7 +218,7 @@ public class Apple2Utils { PackageInfo pi = pm.getPackageInfo(activity.getPackageName(), 0); sDataDir = pi.applicationInfo.dataDir; } catch (PackageManager.NameNotFoundException e) { - Log.e(TAG, "" + e); + Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "" + e); if (sDataDir == null) { sDataDir = "/data/local/tmp"; } @@ -285,7 +285,7 @@ public class Apple2Utils { recursivelyDelete(new File(new File(sDataDir, "disks").getAbsolutePath(), "logo")); recursivelyDelete(new File(new File(sDataDir, "disks").getAbsolutePath(), "miscgame")); - Log.d(TAG, "First time copying stuff-n-things out of APK for ease-of-NDK access..."); + Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "First time copying stuff-n-things out of APK for ease-of-NDK access..."); getExternalStorageDirectory(activity); recursivelyCopyAPKAssets(activity, /*from APK directory:*/"disks", /*to location:*/new File(sDataDir, "disks").getAbsolutePath(), true); @@ -321,7 +321,7 @@ public class Apple2Utils { } } if (!file.delete()) { - Log.d(TAG, "Failed to delete file: " + file); + Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "Failed to delete file: " + file); } } @@ -338,7 +338,7 @@ public class Apple2Utils { } catch (InterruptedIOException e) { /* EINTR, EAGAIN ... */ } catch (IOException e) { - Log.d(TAG, "OOPS exception attempting to list APK files at : " + srcFileOrDir + " : " + e); + Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "OOPS exception attempting to list APK files at : " + srcFileOrDir + " : " + e); } try { @@ -350,7 +350,7 @@ public class Apple2Utils { } while (attempts < maxAttempts); if (files == null) { - Log.d(TAG, "OOPS, could not list APK assets at : " + srcFileOrDir); + Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "OOPS, could not list APK assets at : " + srcFileOrDir); return; } @@ -359,7 +359,7 @@ public class Apple2Utils { File dstPath = new File(dstFileOrDir); if (!dstPath.mkdirs()) { if (!dstPath.exists()) { - Log.d(TAG, "OOPS, could not mkdirs on " + dstPath); + Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "OOPS, could not mkdirs on " + dstPath); return; } } @@ -387,7 +387,7 @@ public class Apple2Utils { } catch (InterruptedIOException e) { /* EINTR, EAGAIN */ } catch (IOException e) { - Log.e(TAG, "Failed to copy asset file: " + srcFileOrDir, e); + Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "Failed to copy asset file: " + srcFileOrDir + " : " + e.getMessage()); } finally { if (is != null) { try { @@ -462,7 +462,7 @@ public class Apple2Utils { } } } catch (Exception e) { - Log.e(TAG, "OOPS : {e}"); + Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "OOPS : {e}"); } } @@ -478,7 +478,7 @@ public class Apple2Utils { } catch (InterruptedIOException e) { // EINTR, EAGAIN ... } catch (IOException e) { - Log.d(TAG, "OOPS exception attempting to copy emulator state file : " + e); + Apple2Activity.logMessage(Apple2Activity.LogType.DEBUG, TAG, "OOPS exception attempting to copy emulator state file : " + e); } try { diff --git a/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2View.java b/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2View.java index 1546bf76..80903c0d 100644 --- a/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2View.java +++ b/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2View.java @@ -136,7 +136,7 @@ class Apple2View extends GLSurfaceView implements InputManagerCompat.InputDevice private static int EGL_CONTEXT_CLIENT_VERSION = 0x3098; public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig) { - Log.w(TAG, "creating OpenGL ES 2.0 context"); + Apple2Activity.logMessage(Apple2Activity.LogType.WARN, TAG, "creating OpenGL ES 2.0 context"); checkEglError("Before eglCreateContext", egl); int[] attrib_list = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE}; EGLContext context = egl.eglCreateContext(display, eglConfig, EGL10.EGL_NO_CONTEXT, attrib_list); @@ -152,7 +152,7 @@ class Apple2View extends GLSurfaceView implements InputManagerCompat.InputDevice private static void checkEglError(String prompt, EGL10 egl) { int error; while ((error = egl.eglGetError()) != EGL10.EGL_SUCCESS) { - Log.e(TAG, String.format("%s: EGL error: 0x%x", prompt, error)); + Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, String.format("%s: EGL error: 0x%x", prompt, error)); } } @@ -204,9 +204,9 @@ class Apple2View extends GLSurfaceView implements InputManagerCompat.InputDevice // Now return the "best" one EGLConfig best = chooseConfig(egl, display, configs); if (best == null) { - Log.e(TAG, "OOPS! Did not pick an EGLConfig. What device are you using?! Android will now crash this app..."); + Apple2Activity.logMessage(Apple2Activity.LogType.ERROR, TAG, "OOPS! Did not pick an EGLConfig. What device are you using?! Android will now crash this app..."); } else { - Log.w(TAG, "Using EGL CONFIG : "); + Apple2Activity.logMessage(Apple2Activity.LogType.WARN, TAG, "Using EGL CONFIG : "); printConfig(egl, display, best); } return best; @@ -245,9 +245,9 @@ class Apple2View extends GLSurfaceView implements InputManagerCompat.InputDevice private void printConfigs(EGL10 egl, EGLDisplay display, EGLConfig[] configs) { int numConfigs = configs.length; - Log.w(TAG, String.format("%d configurations", numConfigs)); + Apple2Activity.logMessage(Apple2Activity.LogType.WARN, TAG, String.format("%d configurations", numConfigs)); for (int i = 0; i < numConfigs; i++) { - Log.w(TAG, String.format("Configuration %d:\n", i)); + Apple2Activity.logMessage(Apple2Activity.LogType.WARN, TAG, String.format("Configuration %d:\n", i)); printConfig(egl, display, configs[i]); } } @@ -328,9 +328,9 @@ class Apple2View extends GLSurfaceView implements InputManagerCompat.InputDevice int attribute = attributes[i]; String name = names[i]; if (egl.eglGetConfigAttrib(display, config, attribute, value)) { - Log.w(TAG, String.format(" %s: %d\n", name, value[0])); + Apple2Activity.logMessage(Apple2Activity.LogType.WARN, TAG, String.format(" %s: %d\n", name, value[0])); } else { - // Log.w(TAG, String.format(" %s: failed\n", name)); + // Apple2Activity.logMessage(Apple2Activity.LogType.WARN, TAG, String.format(" %s: failed\n", name)); while (egl.eglGetError() != EGL10.EGL_SUCCESS) ; } } diff --git a/Android/jni/jnihooks.c b/Android/jni/jnihooks.c index b6bf6e32..1cbe400b 100644 --- a/Android/jni/jnihooks.c +++ b/Android/jni/jnihooks.c @@ -627,3 +627,37 @@ jlong Java_org_deadc0de_apple2ix_Apple2JoystickCalibration_nativePollJoystick(JN return cxy; } +void Java_org_deadc0de_apple2ix_Apple2Activity_nativeLogMessage(JNIEnv *env, jclass cls, jstring jJsonString) { +#if TESTING + return NULL; +#endif + + const char *jsonString = (*env)->GetStringUTFChars(env, jJsonString, NULL); + + JSON_ref jsonData = NULL; + bool ret = json_createFromString(jsonString, &jsonData); + assert(ret > 0); + + (*env)->ReleaseStringUTFChars(env, jJsonString, jsonString); jsonString = NULL; + + long type = LOG_TYPE_INFO; + json_mapParseLongValue(jsonData, "type", &type, 10); + + char *tag = NULL; + json_mapCopyStringValue(jsonData, "tag", &tag); + + char *mesg = NULL; + json_mapCopyStringValue(jsonData, "mesg", &mesg); + + log_taggedOutputString((log_type_t)type, tag, mesg); + + if (tag) { + FREE(tag); + } + if (mesg) { + FREE(mesg); + } + + json_destroy(&jsonData); +} + diff --git a/src/meta/log.c b/src/meta/log.c index 2b7c4977..7ea0cebb 100644 --- a/src/meta/log.c +++ b/src/meta/log.c @@ -95,14 +95,25 @@ void log_init(void) { } void log_outputString(const char * const str) { + log_taggedOutputString(LOG_TYPE_INFO, NULL, str); +} + +void log_taggedOutputString(log_type_t type, const char * const tag, const char * const str) { if (UNLIKELY(!str)) { return; } +#if defined(NDEBUG) + // TODO : make logging level dynamic+configurable ... + if (type <= LOG_TYPE_DEBUG) { + return; + } +#endif + #if DO_STDERR_LOGGING { #if defined(__ANDROID__) && !defined(NDEBUG) - __android_log_print(ANDROID_LOG_ERROR, "apple2ix", "%s", str); + __android_log_print(type, tag ? tag : "apple2ix", "%s", str); #else fprintf(stderr, "%s\n", str); #endif @@ -113,14 +124,31 @@ void log_outputString(const char * const str) { return; } + // calculate timestamp ... + char timestamp[32]; + { + time_t secs = time(NULL); + struct tm timeinfo = { 0 }; + struct tm *t = gmtime_r(&secs, &timeinfo); + assert(t != NULL); + size_t count = strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S", &timeinfo); + assert(count == 19); + } + + char *buf = NULL; + ASPRINTF(&buf, "%s %s %s", timestamp, tag ? tag : "-", str); + if (UNLIKELY(!buf)) { + return; + } + pthread_mutex_lock(&log_mutex); - size_t expected_bytescount = strlen(str); + size_t expected_bytescount = strlen(buf); size_t bytescount = 0; do { ssize_t byteswritten = 0; - TEMP_FAILURE_RETRY(byteswritten = write(logFd, str+bytescount, expected_bytescount-bytescount)); + TEMP_FAILURE_RETRY(byteswritten = write(logFd, buf+bytescount, expected_bytescount-bytescount)); if (UNLIKELY(byteswritten <= 0)) { break; // OOPS ! } @@ -139,6 +167,8 @@ void log_outputString(const char * const str) { } } while (1); + FREE(buf); + if (UNLIKELY(bytescount != expected_bytescount)) { // logging is b0rked, shut it down ... _log_stopLogging(); diff --git a/src/meta/log.h b/src/meta/log.h index 0cd24640..04fcb2a7 100644 --- a/src/meta/log.h +++ b/src/meta/log.h @@ -16,6 +16,15 @@ extern "C" { #endif +// NOTE: These match Android settings +typedef enum log_type_t { + LOG_TYPE_VERBOSE = 2, + LOG_TYPE_DEBUG, + LOG_TYPE_INFO, + LOG_TYPE_WARN, + LOG_TYPE_ERROR, +} log_type_t; + #if VIDEO_OPENGL extern GLenum safeGLGetError(void); #else @@ -29,6 +38,9 @@ void log_init(void); // print a string to the log file. Terminating '\n' is added. void log_outputString(const char * const str); +// print a tag + string to the log file. Terminating '\n' is added. +void log_taggedOutputString(log_type_t type, const char * const tag, const char * const str); + #define _MYFILE_ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) #define _SIMPLE_LOG(...) \