Polarbear0106 Polarbear0106 - 4 months ago 29
PHP Question

Creating ID for issues based on two parameters

For a while now I have been trying to alter my ACRA backend to include issue ID's so each issue doesn't show up as a single one, but shows up as one issue with a report count. Because of that I worked out this:

// Finds in array
function array_find($needle, $haystack) {
foreach($haystack as $k => $v) {
if (strstr($v, $needle) !== FALSE) {
return $k;
}
}
return FALSE;
}
function bicou_short_stack_trace($stack_trace, $package) {
$lines = explode("\n", $stack_trace);
if (array_find(": ", $lines) === FALSE && array_find($package, $lines) === FALSE) {
$value = $lines[0];
} else {
$value = "";
foreach ($lines as $id => $line) {
if (/*strpos($line, ": ") !== FALSE || */strpos($line, $package) !== FALSE
|| strpos($line, "Error") !== FALSE || strpos($line, "ACRA caught a") !== FALSE) {
$value .= $line . "<br />";
}
}
}
return $value;
}

function bicou_issue_id($stack_trace, $package) {
return md5(bicou_short_stack_trace($stack_trace, $package));
}


This is supposed to take the stacktrace and package and convert it to an issue ID that can be used over and over for the same issue whenever it is reported. So I caused an error in my app which transmits this:

PACKAGE_NAME = com.gamerscave.codingclicker
APP_VERSION_NAME = 1.1.5
LOGCAT = 07-14 15:51:20.891 V/MediaPlayer(31408): isPlaying: no active player
07-14 15:51:20.891 V/MediaPlayer-JNI(31408): isPlaying: 0
07-14 15:51:20.911 V/MediaPlayer(31408): isPlaying: no active player
07-14 15:51:20.911 V/MediaPlayer-JNI(31408): isPlaying: 0
07-14 15:51:20.931 V/MediaPlayer(31408): isPlaying: no active player
07-14 15:51:20.931 V/MediaPlayer-JNI(31408): isPlaying: 0
07-14 15:51:20.941 V/MediaPlayer(31408): isPlaying: no active player
07-14 15:51:20.941 V/MediaPlayer-JNI(31408): isPlaying: 0
07-14 15:51:20.961 V/MediaPlayer(31408): isPlaying: no active player
07-14 15:51:20.961 V/MediaPlayer-JNI(31408): isPlaying: 0
07-14 15:51:20.981 V/MediaPlayer(31408): isPlaying: no active player
07-14 15:51:20.981 V/MediaPlayer-JNI(31408): isPlaying: 0
07-14 15:51:22.651 W/SELinux ( 566): SELinux: seapp_context_lookup: seinfo=default, level=s0:c512,c768, pkgname=com.gamerscave.codingclicker
07-14 15:51:22.661 I/art ( 566): Late-enabling -Xcheck:jni
07-14 15:51:22.681 D/TimaKeyStoreProvider( 566): TimaSignature is unavailable
07-14 15:51:22.681 D/ActivityThread( 566): Added TimaKeyStore provider
--------- beginning of system
07-14 15:51:22.711 W/ResourcesManager( 566): getTopLevelResources: /data/app/com.gamerscave.codingclicker-2/base.apk / 1.0 running in com.gamerscave.codingclicker rsrc of package com.gamerscave.codingclicker
07-14 15:51:22.731 I/InjectionManager( 566): Inside getClassLibPath + mLibMap{0=, 1=}
07-14 15:51:22.741 I/InjectionManager( 566): Inside getClassLibPath caller
07-14 15:51:22.741 D/ResourcesManager( 566): For user 0 new overlays fetched Null
07-14 15:51:22.751 W/System ( 566): ClassLoader referenced unknown path: /data/app/com.gamerscave.codingclicker-2/lib/arm64
07-14 15:51:22.791 I/ACRA ( 566): ACRA is enabled for com.gamerscave.codingclicker, initializing...
07-14 15:51:22.851 D/InjectionManager( 566): InjectionManager
07-14 15:51:22.851 D/InjectionManager( 566): fillFeatureStoreMap com.gamerscave.codingclicker
07-14 15:51:22.851 I/InjectionManager( 566): Constructor com.gamerscave.codingclicker, Feature store :{}
07-14 15:51:22.851 I/InjectionManager( 566): featureStore :{}
07-14 15:51:22.851 W/SELinux ( 613): SELinux: seapp_context_lookup: seinfo=default, level=s0:c512,c768, pkgname=com.gamerscave.codingclicker:acra
07-14 15:51:22.861 I/art ( 613): Late-enabling -Xcheck:jni
07-14 15:51:22.861 W/ResourcesManager( 566): getTopLevelResources: /data/app/com.gamerscave.codingclicker-2/base.apk / 1.0 running in com.gamerscave.codingclicker rsrc of package com.gamerscave.codingclicker
07-14 15:51:22.861 W/ResourcesManager( 566): getTopLevelResources: /data/app/com.gamerscave.codingclicker-2/base.apk / 1.0 running in com.gamerscave.codingclicker rsrc of package com.gamerscave.codingclicker
07-14 15:51:22.881 D/TimaKeyStoreProvider( 613): TimaSignature is unavailable
07-14 15:51:22.881 D/ActivityThread( 613): Added TimaKeyStore provider
07-14 15:51:22.901 I/InjectionManager( 613): Inside getClassLibPath + mLibMap{0=, 1=}
07-14 15:51:22.901 W/ResourcesManager( 613): getTopLevelResources: /data/app/com.gamerscave.codingclicker-2/base.apk / 1.0 running in com.gamerscave.codingclicker rsrc of package com.gamerscave.codingclicker
07-14 15:51:22.901 D/ResourcesManager( 613): For user 0 new overlays fetched Null
07-14 15:51:22.911 I/InjectionManager( 613): Inside getClassLibPath caller
07-14 15:51:22.911 W/System ( 613): ClassLoader referenced unknown path: /data/app/com.gamerscave.codingclicker-2/lib/arm64
07-14 15:51:22.931 D/Activity( 566): performCreate Call Injection manager
07-14 15:51:22.941 I/InjectionManager( 566): dispatchOnViewCreated > Target : com.gamerscave.codingclicker.Splash isFragment :false
07-14 15:51:22.941 D/InjectionManager( 613): InjectionManager
07-14 15:51:22.941 D/InjectionManager( 613): fillFeatureStoreMap com.gamerscave.codingclicker
07-14 15:51:22.941 I/InjectionManager( 613): Constructor com.gamerscave.codingclicker, Feature store :{}
07-14 15:51:22.941 I/InjectionManager( 613): featureStore :{}
07-14 15:51:22.941 D/ViewRootImpl( 566): #1 mView = com.android.internal.policy.PhoneWindow$DecorView{f80c5e I.E...... R.....ID 0,0-0,0}
07-14 15:51:22.941 D/SecWifiDisplayUtil( 566): Metadata value : SecSettings2
07-14 15:51:22.951 D/OpenGLRenderer( 566): Use EGL_SWAP_BEHAVIOR_PRESERVED: true
07-14 15:51:23.051 D/libEGL ( 566): loaded /vendor/lib64/egl/libGLES_mali.so
07-14 15:51:23.071 D/libEGL ( 566): eglInitialize EGLDisplay = 0x7f97440178
07-14 15:51:23.071 I/OpenGLRenderer( 566): Initialized EGL, version 1.4
07-14 15:51:23.071 D/ ( 566): ro.exynos.dss isEnabled: 0
07-14 15:51:23.081 D/mali_winsys( 566): new_window_surface returns 0x3000, [1440x2560]-format:1
07-14 15:51:23.121 D/ViewRootImpl( 566): MSG_RESIZED_REPORT: ci=Rect(0, 0 - 0, 0) vi=Rect(0, 0 - 0, 0) or=1
07-14 15:51:23.181 D/ViewRootImpl( 566): #3 mView = null
07-14 15:51:23.191 E/ViewRootImpl( 566): sendUserActionEvent() mView == null
07-14 15:51:23.301 E/ViewRootImpl( 566): sendUserActionEvent() mView == null
07-14 15:51:25.941 I/Timeline( 566): Timeline: Activity_launch_request id:com.gamerscave.codingclicker time:414449072
07-14 15:51:26.021 W/ResourcesManager( 566): getTopLevelResources: /data/app/com.gamerscave.codingclicker-2/base.apk / 1.0 running in com.gamerscave.codingclicker rsrc of package com.gamerscave.codingclicker
07-14 15:51:26.171 D/AndroidRuntime( 566): Shutting down VM
07-14 15:51:26.171 E/ACRA ( 566): ACRA caught a RuntimeException for com.gamerscave.codingclicker
07-14 15:51:26.171 E/ACRA ( 566): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.gamerscave.codingclicker/com.gamerscave.codingclicker.Game}: android.view.InflateException: Binary XML file line #21: Binary XML file line #21: Error inflating class com.gamerscave.codingclicker.Clicker
07-14 15:51:26.171 E/ACRA ( 566): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3254)
07-14 15:51:26.171 E/ACRA ( 566): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3350)
07-14 15:51:26.171 E/ACRA ( 566): at android.app.ActivityThread.access$1100(ActivityThread.java:222)
07-14 15:51:26.171 E/ACRA ( 566): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1795)
07-14 15:51:26.171 E/ACRA ( 566): at android.os.Handler.dispatchMessage(Handler.java:102)
07-14 15:51:26.171 E/ACRA ( 566): at android.os.Looper.loop(Looper.java:158)
07-14 15:51:26.171 E/ACRA ( 566): at android.app.ActivityThread.main(ActivityThread.java:7229)
07-14 15:51:26.171 E/ACRA ( 566): at java.lang.reflect.Method.invoke(Native Method)
07-14 15:51:26.171 E/ACRA ( 566): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1230)
07-14 15:51:26.171 E/ACRA ( 566): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1120)
07-14 15:51:26.171 E/ACRA ( 566): Caused by: android.view.InflateException: Binary XML file line #21: Binary XML file line #21: Error inflating class com.gamerscave.codingclicker.Clicker
07-14 15:51:26.171 E/ACRA ( 566): at android.view.LayoutInflater.inflate(LayoutInflater.java:551)
07-14 15:51:26.171 E/ACRA ( 566): at android.view.LayoutInflater.inflate(LayoutInflater.java:429)
07-14 15:51:26.171 E/ACRA ( 566): at android.view.LayoutInflater.inflate(LayoutInflater.java:380)
07-14 15:51:26.171 E/ACRA ( 566): at com.android.internal.policy.PhoneWindow.setContentView(PhoneWindow.java:474)
07-14 15:51:26.171 E/ACRA ( 566): at android.app.Activity.setContentView(Activity.java:2387)
07-14 15:51:26.171 E/ACRA ( 566): at com.gamerscave.codingclicker.Game.onCreate(Game.java:62)
07-14 15:51:26.171 E/ACRA ( 566): at android.app.Activity.performCreate(Activity.java:6876)
07-14 15:51:26.171 E/ACRA ( 566): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1135)
07-14 15:51:26.171 E/ACRA ( 566): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3207)
07-14 15:51:26.171 E/ACRA ( 566): ... 9 more
07-14 15:51:26.171 E/ACRA ( 566): Caused by: android.view.InflateException: Binary XML file line #21: Error inflating class com.gamerscave.codingclicker.Clicker
07-14 15:51:26.171 E/ACRA ( 566): at android.view.LayoutInflater.createView(LayoutInflater.java:657)
07-14 15:51:26.171 E/ACRA ( 566): at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:776)
07-14 15:51:26.171 E/ACRA ( 566): at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:716)
07-14 15:51:26.171 E/ACRA ( 566): at android.view.LayoutInflater.rInflate(LayoutInflater.java:847)
07-14 15:51:26.171 E/ACRA ( 566): at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:810)
07-14 15:51:26.171 E/ACRA ( 566): at android.view.LayoutInflater.rInflate(LayoutInflater.java:855)
07-14 15:51:26.171 E/ACRA ( 566): at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:810)
07-14 15:51:26.171 E/ACRA ( 566): at android.view.LayoutInflater.inflate(LayoutInflater.java:527)
07-14 15:51:26.171 E/ACRA ( 566): ... 17 more
07-14 15:51:26.171 E/ACRA ( 566): Caused by: java.lang.reflect.InvocationTargetException
07-14 15:51:26.171 E/ACRA ( 566): at java.lang.reflect.Constructor.newInstance(Native Method)
07-14 15:51:26.171 E/ACRA ( 566): at android.view.LayoutInflater.createView(LayoutInflater.java:631)
07-14 15:51:26.171 E/ACRA ( 566): ... 24 more
07-14 15:51:26.171 E/ACRA ( 566): Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.TextView.setText(java.lang.CharSequence)' on a null object reference
07-14 15:51:26.171 E/ACRA ( 566): at com.gamerscave.codingclicker.Clicker.init(Clicker.java:241)
07-14 15:51:26.171 E/ACRA ( 566): at com.gamerscave.codingclicker.Clicker.<init>(Clicker.java:217)
07-14 15:51:26.171 E/ACRA ( 566): ... 26 more

PHONE_MODEL = SM-G925F
ANDROID_VERSION = 6.0.1


It is the exact same every time except for the time defined in front of each line.

For the problem: The exact same input every time, but it generates different ID's every time. I caused it 3 times and here are the ID's:

69b30e6cff3768ff3e3616d85a4b2ea8
e2c090b737a4e854106c373fa408779c
038e70fe97be4e23cac2d6bb42df751c


The stacktrace is not included the first two and last two lines of the .txt file.

How can I fix this? It is really annoying because it took ages to work out the code above and it doesn't work.

To clairify:

I want the output(ID) to be the same if the stacktrace is the same, but it doesn't appear to be working.

EDIT:

Thanks to Samurai8, I managed to get this:

$reworkedLog = $LOG;
$reworkedLog = str_replace('LOGCAT =', '', $reworkedLog);

$reworkedLog = preg_replace("'\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d*'", '', $reworkedLog);
$reworkedLog = preg_replace("'$\d*'", '', $reworkedLog);
$reworkedLog = preg_replace("'0x[0-9a-f]*'", '', $reworkedLog);
$reworkedLog = preg_replace("'(\d{5})'",'', $reworkedLog);
$reworkedLog = preg_replace("'... \d{1} more'", '', $reworkedLog);
$reworkedLog = preg_replace("'... \d{2} more'", '', $reworkedLog);

$issue_id = bicou_issue_id($reworkedLog, $package) . '<br>';


And this makes the error md5 to be the same every time

Answer

You should keep three things in mind:

  • md5 is a hash function. That means that many inputs have the same output. You are never guaranteed that two completely different stack traces produce a different id. This means that you cannot blindly ignore a stack trace, even if the id is already in the system.
  • A small difference in input will result in a different hash. Adding a single space to the stack trace, or having a single character changed, will cause a different hash to be generated, thus giving you a different id.
  • Stack traces often contain the date and time code was executed, memory locations of objects and instantiation numbers. In your case your logcat contains all of them:

    07-14 15:51:20.891
    

    and

    eglInitialize EGLDisplay = 0x7f97440178
    

    and

    at android.app.ActivityThread.access$1100
    

    The stack trace itself only contains the date and instantiation number, but is enough to create a different id.


What can you do? We can strip them from the stack trace that generates the id. We probably want to leave them in the actual stack trace as it gives some context. Just preg_replace(..) them with the following regexes:

  • \d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d* for dates and times
  • \$\d* for anonymous classes
  • 0x[0-9a-f]* for hexadecimal values, which might be a memory location that changes every run.

Of course this increases the chance that the id is not unique, but we already established that you had to log every error anyway. The chance that something is given the same id should be minimal, but make sure that wherever you log these bugs in can split the bug reports in case two different bugs get logged under the same id. Alternatively use a unique id for each crash report, and automatically suggest a duplicate based on the calculated id.

Comments