0

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.my.package
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.my.package
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.my.package-2/base.apk / 1.0 running in com.my.package rsrc of package com.my.package
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.my.package-2/lib/arm64
07-14 15:51:22.791 I/ACRA    (  566): ACRA is enabled for com.my.package, initializing...
07-14 15:51:22.851 D/InjectionManager(  566): InjectionManager
07-14 15:51:22.851 D/InjectionManager(  566): fillFeatureStoreMap com.my.package
07-14 15:51:22.851 I/InjectionManager(  566): Constructor com.my.package, 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.my.package: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.my.package-2/base.apk / 1.0 running in com.my.package rsrc of package com.my.package
07-14 15:51:22.861 W/ResourcesManager(  566): getTopLevelResources: /data/app/com.my.package-2/base.apk / 1.0 running in com.my.package rsrc of package com.my.package
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.my.package-2/base.apk / 1.0 running in com.my.package rsrc of package com.my.package
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.my.package-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.my.package.Splash isFragment :false
07-14 15:51:22.941 D/InjectionManager(  613): InjectionManager
07-14 15:51:22.941 D/InjectionManager(  613): fillFeatureStoreMap com.my.package
07-14 15:51:22.941 I/InjectionManager(  613): Constructor com.my.package, 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.my.package time:414449072
07-14 15:51:26.021 W/ResourcesManager(  566): getTopLevelResources: /data/app/com.my.package-2/base.apk / 1.0 running in com.my.package rsrc of package com.my.package
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.my.package
07-14 15:51:26.171 E/ACRA    (  566): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.my.package/com.my.package.Game}: android.view.InflateException: Binary XML file line #21: Binary XML file line #21: Error inflating class com.my.package.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.my.package.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.my.package.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.my.package.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.my.package.Clicker.init(Clicker.java:241)
07-14 15:51:26.171 E/ACRA    (  566):   at com.my.package.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

  • md5 only outputs the same value for the exact same input. I can't view pastebin from here, but does I am assuming it contains either dates/times, memory positions or unique identifiers for the system, which cause the issue id to be not unique. Keep in mind that while two stack traces might generate the same id, the stack traces do not have to be the same. Maybe just send the stack trace to the server, then compare it on the server, and send back an appropriate id? – Sumurai8 Jul 15 '16 at 17:09
  • I was not aware of that. The problem as far as I see as to solving this is to identify similar issues and convert it to an ID that makes it easy to identify that one issue is the same as a different issue. Removed pastebin link and added it to the question –  Jul 15 '16 at 17:13

1 Answers1

2

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.

Sumurai8
  • 20,333
  • 11
  • 66
  • 100
  • I get this error when I run it: Warning: preg_replace(): Delimiter must not be alphanumeric or backslash –  Jul 15 '16 at 20:26
  • 1
    See http://stackoverflow.com/questions/7660545/delimiter-must-not-be-alphanumeric-or-backslash-and-preg-match . Keep in mind that you also have to use the `g` modifier. Also fixed an unescaped `$` in the second regex, which should match the literal `$` character, not the end of the string. – Sumurai8 Jul 15 '16 at 20:35
  • I tried it, finally got no exceptions and it removed all the changing params. The issue ID still changes –  Jul 17 '16 at 16:00