2014-01-29 8 views
19

Ich habe festgestellt, dass eine meiner Anwendung auf dem Markt veröffentlicht seltsame Ergebnisse auf einigen Telefonen. Bei der Untersuchung stellt sich heraus, dass es ein Problem mit einer Funktion gibt, die den Abstand zwischen zwei GeoPoints berechnet - manchmal gibt es einen völlig falschen Wert zurück. Dieses Problem reproduziert nur auf Geräten mit MediaTek MT6589 SoC (aka MTK6589). Und AFAIK alle diese Geräte haben Android 4.2 installiert.Doppelte Genauigkeit Wert Rechenfehler auf MediaTek Prozessoren

aktualisiert war ich auch in der Lage, den Fehler auf Lenovo S6000 Tablet mit MediaTek MT8125/8389 Chip zu reproduzieren und auf Fly IQ444 Quattro mit MT6589 und mit Android 4.1 installiert.

Ich erstellte ein Testprojekt, das hilft, den Fehler zu reproduzieren. Es führt Berechnungen wiederholt für 1'000 oder 100'000 Iterationen durch. Um mögliche Threading-Probleme auszuschließen, wird die Berechnung auf dem UI-Thread ausgeführt (mit kleinen Pausen, um die Benutzeroberfläche zu erhalten). Im Testprojekt verwendete ich nur einen Teil aus der ursprünglichen Abstandsformel:

private double calcX() { 
    double t = 1.0; 
    double X = 0.5 + t/16384; 
    return X; 
} 

Wie Sie sich auf web2.0calc.com überprüfen kann der Wert von X sollte etwa sein: 0.50006103515625.
Allerdings wird auf den Geräten mit MT6589-Chip oft der falsche Wert berechnet: 2.0. Das Projekt ist available at Google Code (APK ist auch verfügbar). Die Quelle der Testklasse ist im folgenden dargestellt:

public class MtkTestActivity extends Activity { 

    static final double A = 0.5; 
    static final double B = 1; 
    static final double D = 16384; 

    static final double COMPUTED_CONST = A + B/D; 

    /* 
    * Main calculation where bug occurs 
    */ 
    public double calcX() { 
    double t = B; 
    double X = A + t/D; 
    return X; 
    } 

    class TestRunnable implements Runnable { 

    static final double EP = 0.00000000001; 

    static final double EXPECTED_LOW = COMPUTED_CONST - EP; 

    static final double EXPECTED_HIGH = COMPUTED_CONST + EP; 

    public void run() { 
     for (int i = 0; i < SMALL_ITERATION; i++) { 
     double A = calcX(); 

     if (A < EXPECTED_LOW || A > EXPECTED_HIGH) { 
      mFailedInCycle = true; 
      mFails++; 
      mEdit.getText().append("FAILED on " + mIteration + " iteration with: " + A + '\n'); 
     } 
     mIteration++; 
     } 

     if (mIteration % 5000 == 0) { 
     if (mFailedInCycle) { 
      mFailedInCycle = false; 
     } else { 
      mEdit.getText().append("passed " + mIteration + " iterations\n"); 
     } 
     } 

     if (mIteration < mIterationsCount) { 
     mHandler.postDelayed(new TestRunnable(), DELAY); 
     } else { 
     mEdit.getText().append("\nFinished test with " + mFails + " fails"); 
     } 
    } 

    } 

    public void onTestClick(View v) { 
    startTest(IT_10K); 
    } 

    public void onTestClick100(View v) { 
    startTest(IT_100K); 
    } 

    private void startTest(int iterationsCount) { 
    Editable text = mEdit.getText(); 
    text.clear(); 
    text.append("\nStarting " + iterationsCount + " iterations test..."); 
    text.append("\n\nExpected result " + COMPUTED_CONST + "\n\n"); 
    mIteration = 0; 
    mFails = 0; 
    mFailedInCycle = false; 
    mIterationsCount = iterationsCount; 
    mHandler.postDelayed(new TestRunnable(), 100); 
    } 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.activity_main); 
    mHandler = new Handler(getMainLooper()); 
    mEdit = (EditText) findViewById(R.id.edtText1); 
    } 

    private static final int IT_10K = 1000; 

    private static final int IT_100K = 100000; 

    private static final int SMALL_ITERATION = 50; 

    private static final int DELAY = 10; 

    private int mIteration; 

    private int mFails; 

    private boolean mFailedInCycle; 

    private Handler mHandler; 

    private int mIterationsCount; 

    private EditText mEdit; 

} 

Um das Problem zu beheben, es reicht nur alle Methode double zu float in calcX() zu ändern.

Weitere Untersuchungen
Turning JIT aus (von android:vmSafeMode="true" zum App-Manifest Hinzufügen) behebt auch Fehler.

Hat jemand diesen Bug schon mal gesehen? Vielleicht ist das ein bekanntes Problem?

ps.s .: wenn jemand in der Lage wäre, diesen Fehler auf dem Gerät mit anderen Chip zu reproduzieren, oder könnte es mit jedem MediaTek-Chip und Android> = 4.3 testen, werde ich es sehr zu schätzen wissen.

+1

https://code.google.com/p/android/issues/detail?id=63790 Details, was auf dem HTC One S Geräte wie genau dieses Problem sieht Android 4.1.1 läuft, sowie eine Reihe von anderen Geräten . In diesem Fehler sieht es anfangs wie eine schlechte Interaktion mit ProGuard aus, aber weitere Analyse hat es auf einen kleineren Dalvik Bytecode reduziert. Die Ursache wurde noch nicht ermittelt, und die einzige Problemumgehung ist die, die Sie entdeckt haben, um das JIT zu deaktivieren. –

+0

Ja, es scheint sehr ähnlich zu dem, was ich gepostet habe. Ein Link zu Google-Code enthält eine Test-App, die Sie ausprobieren können, und [hier] (http: // stackoverflow.com/questions/20573598/progard-cause-increase-calculations) ist eine detailliertere Beschreibung und Diskussion mit dem Entwickler von ProGuard. – Dmitry

+1

Ich kann diesen Fehler in einem MT6589-Telefon (Canvas HD) mit Android 4.2.1 reproduzieren. – HackToHell

Antwort

3

Dies war ein JIT-Bug, der in der JellyBean-Quelle von Ende 2012 bis Anfang 2013 aktiv war. Kurz gesagt, wenn zwei oder mehr Doppelpräzisionskonstanten in den hohen 32 Bits unterschiedlich waren, aber in den niedrigen 32 Bits identisch waren in dem gleichen Grundblock verwendet würden, würde die JIT denken, dass sie die gleichen wären, und einen von ihnen unangemessen optimieren.

stellte ich den Defekt in: https://android-review.googlesource.com/#/c/47280/

und fixiert es in: https://android-review.googlesource.com/#/c/57602/

Der Defekt in einem der letzten nicht Android baut erscheinen soll.

1

Das Problem, mit dem Sie konfrontiert sind, könnte möglicherweise mit der Prozessorhardware zusammenhängen. Es gibt einige notorische Beispiele in der Computergeschichte:
1994, einige Intel Pentium-Prozessoren hatten einen Fehler, der Gleitkommaberechnungsfehler (FDIV-Fehler) verursachte. Dies war nur ab der 4. Nachkommastelle. Intel hat schließlich ein Ersatzprogramm eingeführt, um die defekten CPUs gegen gute austauschen zu können. Die DEC VAX 11/785 (1984 eingeführt) hatte einen Konstruktionsfehler in ihrem (optionalen) Fließkomma-Coprozessor. Aufgrund einer Racebedingung in Hardware, manchmal der Floating-Point-Coprozessor einen beliebigen Wert anstelle des gewünschten Ergebnisses auf einigen Maschinen zurückgegeben. Die Digital Equipment Corporation hat ein Programm aufgelegt, das den (5 großen Leiterplatten) Koprozessor bei allen Kunden durch einen Hardwarewartungsvertrag ersetzen soll.

Ich würde vorschlagen, dass Sie versuchen könnten, weitere Tests auf einer breiteren Hardwarebasis durchzuführen, um das Problem besser zu verstehen. Wenn das Problem wirklich mit der Hardware zu tun haben würde, würde ich meinen besten Ansatz wäre, einen Weg zu finden, um das Problem zu umgehen & es für andere Entwickler zu dokumentieren.

1

Hat jemand diesen Bug schon mal gesehen? Vielleicht ist das ein bekanntes Problem?

Diese werden gelegentlich auf einigen der Android-Mailinglisten angezeigt.

Ich glaube, was Sie sehen, ist die Wirkung von (1) verschiedenen CPUs und deren Handhabung von Gleitkommawerten und (2) Speichergrößen Unterschiede, die zu verschiedenen Rundungen und Kürzungen führen.

Für (1) verwenden, etwa wie folgt in nativem Code verwendet wird:

  • _controlfp(_PC_24, _MCW_PC);
  • _controlfp(_RC_NEAR, _MCW_RC);

Für (2) verwenden, um die gemeinsame Speichergröße, die ein float . In der nativen Welt gibt es manchmal ein anderes verwandtes Problem: Ein Float wird an eine Funktion übergeben, aber der Wert an der Funktion ist immer 0.0f (und nicht der 0-Wert, der zum Aufrufen der Funktion verwendet wird). Sie können das mit -mfloat-abi=softfp löschen. Siehe Hard-float and JNI.

Leider sind Sie dem Hersteller bei der Verwendung ihres Android Java-Ports ausgeliefert. Genießen Sie ihre Tweaks, Versehen und Implementierungsfehler. Zumindest ist es nicht corrupting your VM.

1

verbrachte ich die letzte Woche dieses Problem untersucht und hier ist das, was ich gefunden habe:

  • Dieser Fehler vorher bemerkt wurde, von den Nutzern von MT6589-Geräten (zB here und here)
  • Gemeinsame Abhilfe die Deaktivierung von JIT (für spezifische App oder für das gesamte System)
  • Ich konnte dieses Problem auf dem Paar von Geräten mit MT6589 und MT8125/8389, reproduzieren es ist derzeit nicht auf den Geräten mit anderen Chips reproduziert worden als erwähnt siehe Update se ctions unter
  • Der Ausdruck, der den Fehler wiedergibt, wenn viel einfacher, als ich in der Frage gestellt, gerade:
    X = A + b/D
  • Die Verzögerung zwischen Berechnungen ist ein wichtiger Teil des Fehlers: ohne es Fehler erscheinen sporadisch, mit einem kleinen Schlaf nach der Berechnung reproduziert es immer (sobald der Code JITed wurde)
  • Ich habe ein Skript erstellt, das eine einfache JAR-Datei zusammensetzt und dalvikvm direkt ausführt, Parameter an es übergeben. Dies ermöglichte die Einstellung des JIT-Schwellwerts und den Empfang des von JIT
  • generierten Ausgabe-ARM-Codes. Die Übergabe von -Xjitdisableopt:1 an Dalvik behebt das Problem (dieser Parameter deaktiviert kLoadStoreElimination Optimierung).Man kann auch dalvik.vm.extra-opts=-Xjitdisableopt:1 zu build.prop Datei als schnelle Abhilfe hinzufügen, die JIT (Root-und Neustart erforderlich sind)
  • Obwohl dieses Problem ähnelt dem Fehler #63790 erwähnt von Scott Barta, ich denke, es ist der andere (auch Autor von erwähnt Fehler Dmitry haben bestätigt, dass diese „Mediatek“ Fehler nicht am Telefon von # 63790)
  • betroffen ist reproduziert UPDATE: ich habe libdvm.so geputtet (von Fly IQ4410 mit MT6589-Chip) auf dem Emulator und der Fehler reproduziert dort . Aber wenn ich libdvm.so verwende, kompiliert aus den Android 4.2 Quellen, verschwindet der Fehler. Es sieht aus wie es ein Problem mit JIT kompilierten Code durch eine spezielle Version von libdvm Bibliothek mit dem betroffenen Geräten
  • UPDATE versandt hergestellt ist: reproduzierten erfolgreich den Fehler auf Samsung Ace 2 Telefon (NovaThor U8500, Android 4.1.2) mit dem gleichen wie oben Technik - nahm libdvm.so von Fly IQ444 (MT6589, Android 4.1.2)

ich einen Fehlerbericht #65750 eingereicht haben.

Hier sind die Quelle und der JIT-Montage Ausgang des Tests, den Fehler zu reproduzieren verwendet:

public class Calc { 

    static final double A = 0.5; 
    static final double B = 1; 
    static final double D = 16384; 

    public double calcX() { 
    double t = B; 
    double X = A + t/D; 
    return X; 
    }  
} 

JIT-Ausgang für einen gewöhnlichen Lauf von Dalvik:

D/dalvikvm: Dumping LIR insns 
D/dalvikvm: installed code is at 0x45deb000 
D/dalvikvm: total size is 124 bytes 
D/dalvikvm: 0x45deb000 (0000): data 0xc278(49784) 
D/dalvikvm: 0x45deb002 (0002): data 0x457a(17786) 
D/dalvikvm: 0x45deb004 (0004): data 0x0044(68) 
D/dalvikvm: 0x45deb006 (0006): ldr  r0, [r15pc, -#8] 
D/dalvikvm: 0x45deb00a (000a): ldr  r1, [r0, #0] 
D/dalvikvm: 0x45deb00c (000c): adds r1, r1, #1 
D/dalvikvm: 0x45deb00e (000e): str  r1, [r0, #0] 
D/dalvikvm: -------- entry offset: 0x0000 
D/dalvikvm: L0x4579e28c: 
D/dalvikvm: -------- dalvik offset: 0x0000 @ const-wide/high16 v0, (#16368), (#0) 
D/dalvikvm: 0x45deb010 (0010): vldr d8, [r15, #96] 
D/dalvikvm: -------- dalvik offset: 0x0002 @ const-wide/high16 v2, (#16352), (#0) 
D/dalvikvm: 0x45deb014 (0014): vmov.f64 d9, d8 
D/dalvikvm: -------- dalvik offset: 0x0004 @ const-wide/high16 v4, (#16592), (#0) 
D/dalvikvm: 0x45deb018 (0018): vmov.f64 d10, d9 
D/dalvikvm: -------- dalvik offset: 0x0006 @ div-double/2addr v0, v4, (#0) 
D/dalvikvm: 0x45deb01c (001c): vdivd d8, d8, d10 
D/dalvikvm: -------- dalvik offset: 0x0007 @ add-double/2addr v0, v2, (#0) 
D/dalvikvm: 0x45deb020 (0020): vadd d8, d8, d9 
D/dalvikvm: -------- dalvik offset: 0x0008 @ return-wide v0, (#0), (#0) 
D/dalvikvm: 0x45deb024 (0024): vmov.f64 d11, d8 
D/dalvikvm: 0x45deb028 (0028): vstr d11, [r6, #16] 
D/dalvikvm: 0x45deb02c (002c): vstr d8, [r5, #0] 
D/dalvikvm: 0x45deb030 (0030): vstr d10, [r5, #16] 
D/dalvikvm: 0x45deb034 (0034): vstr d9, [r5, #8] 
D/dalvikvm: 0x45deb038 (0038): blx_1 0x45dea028 
D/dalvikvm: 0x45deb03a (003a): blx_2 see above 
D/dalvikvm: 0x45deb03c (003c): b  0x45deb040 (L0x4579f068) 
D/dalvikvm: 0x45deb03e (003e): undefined 
D/dalvikvm: L0x4579f068: 
D/dalvikvm: -------- reconstruct dalvik PC : 0x457b83f4 @ +0x0008 
D/dalvikvm: 0x45deb040 (0040): ldr  r0, [r15pc, #28] 
D/dalvikvm: Exception_Handling: 
D/dalvikvm: 0x45deb044 (0044): ldr  r1, [r6, #108] 
D/dalvikvm: 0x45deb046 (0046): blx  r1 
D/dalvikvm: -------- end of chaining cells (0x0048) 
D/dalvikvm: 0x45deb060 (0060): .word (0x457b83f4) 
D/dalvikvm: 0x45deb064 (0064): .word (0) 
D/dalvikvm: 0x45deb068 (0068): .word (0x40d00000) 
D/dalvikvm: 0x45deb06c (006c): .word (0) 
D/dalvikvm: 0x45deb070 (0070): .word (0x3fe00000) 
D/dalvikvm: 0x45deb074 (0074): .word (0) 
D/dalvikvm: 0x45deb078 (0078): .word (0x3ff00000) 
D/dalvikvm: End LCalc;calcX, 6 Dalvik instructions. 

Der interessanteste Teil ist :

vldr d8, [r15, #96] ; d8 := 1.0 
vmov.f64 d9, d8   ; d9 := d8 
vmov.f64 d10, d9  ; d10 := d9 // now d8, d9 and d10 contains 1.0 !!! 
vdivd d8, d8, d10  ; d8 := d8/d10 = 1.0 
vadd d8, d8, d9  ; d8 := d8 + d9 = 2.0 
vmov.f64 d11, d8 

Nun, der von JIT produzierte Code sieht völlig falsch aus. Statt drei nur eine Konstante ist 1,0, gelesen und als Ergebnis erhalten wir die Berechnung von X = 1.0 + 1.0/1.0, die überraschenderweise nicht auf 2,0

bewertet Und hier ist der JIT-Ausgang für Dalvik mit kLoadStoreElimination Optimierungslauf deaktiviert (die den Fehler behebt):

D/dalvikvm: Dumping LIR insns 
D/dalvikvm: installed code is at 0x45d64000 
D/dalvikvm: total size is 124 bytes 
D/dalvikvm: 0x45d64000 (0000): data 0x5260(21088) 
D/dalvikvm: 0x45d64002 (0002): data 0x4572(17778) 
D/dalvikvm: 0x45d64004 (0004): data 0x0044(68) 
D/dalvikvm: 0x45d64006 (0006): ldr  r0, [r15pc, -#8] 
D/dalvikvm: 0x45d6400a (000a): ldr  r1, [r0, #0] 
D/dalvikvm: 0x45d6400c (000c): adds r1, r1, #1 
D/dalvikvm: 0x45d6400e (000e): str  r1, [r0, #0] 
D/dalvikvm: -------- entry offset: 0x0000 
D/dalvikvm: L0x45717274: 
D/dalvikvm: -------- dalvik offset: 0x0000 @ const-wide/high16 v0, (#16368), (#0) 
D/dalvikvm: 0x45d64010 (0010): vldr d8, [r15, #96] 
D/dalvikvm: -------- dalvik offset: 0x0002 @ const-wide/high16 v2, (#16352), (#0) 
D/dalvikvm: 0x45d64014 (0014): vldr d10, [r15, #76] 
D/dalvikvm: 0x45d64018 (0018): vldr d9, [r15, #80] 
D/dalvikvm: 0x45d6401c (001c): vstr d9, [r5, #8] 
D/dalvikvm: -------- dalvik offset: 0x0004 @ const-wide/high16 v4, (#16592), (#0) 
D/dalvikvm: 0x45d64020 (0020): vstr d10, [r5, #16] 
D/dalvikvm: -------- dalvik offset: 0x0006 @ div-double/2addr v0, v4, (#0) 
D/dalvikvm: 0x45d64024 (0024): vdivd d8, d8, d10 
D/dalvikvm: -------- dalvik offset: 0x0007 @ add-double/2addr v0, v2, (#0) 
D/dalvikvm: 0x45d64028 (0028): vadd d8, d8, d9 
D/dalvikvm: 0x45d6402c (002c): vstr d8, [r5, #0] 
D/dalvikvm: -------- dalvik offset: 0x0008 @ return-wide v0, (#0), (#0) 
D/dalvikvm: 0x45d64030 (0030): vmov.f64 d11, d8 
D/dalvikvm: 0x45d64034 (0034): vstr d11, [r6, #16] 
D/dalvikvm: 0x45d64038 (0038): blx_1 0x45d63028 
D/dalvikvm: 0x45d6403a (003a): blx_2 see above 
D/dalvikvm: 0x45d6403c (003c): b  0x45d64040 (L0x45718050) 
D/dalvikvm: 0x45d6403e (003e): undefined 
D/dalvikvm: L0x45718050: 
D/dalvikvm: -------- reconstruct dalvik PC : 0x457313f4 @ +0x0008 
D/dalvikvm: 0x45d64040 (0040): ldr  r0, [r15pc, #28] 
D/dalvikvm: Exception_Handling: 
D/dalvikvm: 0x45d64044 (0044): ldr  r1, [r6, #108] 
D/dalvikvm: 0x45d64046 (0046): blx  r1 
D/dalvikvm: -------- end of chaining cells (0x0048) 
D/dalvikvm: 0x45d64060 (0060): .word (0x457313f4) 
D/dalvikvm: 0x45d64064 (0064): .word (0) 
D/dalvikvm: 0x45d64068 (0068): .word (0x40d00000) 
D/dalvikvm: 0x45d6406c (006c): .word (0) 
D/dalvikvm: 0x45d64070 (0070): .word (0x3fe00000) 
D/dalvikvm: 0x45d64074 (0074): .word (0) 
D/dalvikvm: 0x45d64078 (0078): .word (0x3ff00000) 
D/dalvikvm: End LCalc;calcX, 6 Dalvik instructions 

Alle drei Konstanten wie erwartet geladen und korrekte Auswertung durchgeführt.

Verwandte Themen