Autor Thema: Code zum Verzweifeln  (Gelesen 7480 mal)

FlashBurn

  • Beiträge: 844
    • Profil anzeigen
Gespeichert
« am: 18. December 2011, 14:38 »
Bin heute beim Debugging mal wieder auf ein Problem zum Verzweifeln gestoßen.

Im Prinzip geht es darum das eine Variable auf den Wert 1024 überprüft wird und dann muss halt anderer Code ausgeführt werden. Mit dem originalen Code funktioniert das (aus irgendeinem mir nicht bekannten Grund) nicht und mit Code der nur eine Ausgabe macht (genau zw. dem ändern der Variable und dem Vergleich des neuen Wertes), funktioniert es.

Also hier mal die entsprechende Stelle des Code´s welcher nicht funktioniert:
movl %esi, %ebp
movl _ZL14g_sStack4kbPtr(,%esi,4), %edi
sall $12, %ebp
sall %cl, %edx
movl %ebx, -1069547520(%ebp,%edi,4)
leal 1(%edi), %ebx
movl %ebx, _ZL14g_sStack4kbPtr(,%esi,4)
cmpl $1024, %ebx
je .L25

Und hier der Code der funktioniert:
movl %edi, %ecx
movl _ZL14g_sStack4kbPtr(,%edi,4), %edx
sall $12, %ecx
movl %eax, -1069547520(%ecx,%edx,4)
leal 1(%edx), %eax
movl %eax, _ZL14g_sStack4kbPtr(,%edi,4)
movl %eax, 4(%esp)
movl $.LC0, (%esp)
call _ZN5Video6printfEPKcz
movl %ebp, %ecx
sall %cl, %esi
cmpl $1024, _ZL14g_sStack4kbPtr(,%edi,4)
je .L25

Der einzige Unterschied der mir aufgefallen ist, ist der das einmal direkt der Inhalt des Registers überprüft wird und einmal der Inhalt der Speicheradresse. Fällt euch noch was auf oder ein warum der erste Code nicht funktioniert?

Für alle die die ganze Funktion haben wollen, einmal der nicht funktionierende Code:
_ZN3Pmm10dealloc4kbEPv:
pushl %ebp
pushl %edi
pushl %esi
pushl %ebx
movl 20(%esp), %ebx
testl $4095, %ebx
jne .L24
movl %ebx, %esi
movl %ebx, %eax
shrl $22, %esi
movl $1, %edx
shrl $27, %eax
movl %esi, %ecx
andl $31, %ecx
#APP
# 39 "../../src/include/spinlock.hpp" 1
lock xaddl %edx,_ZL10g_sPmmLock
cmpl %edx,_ZL10g_sPmmLock+4
je 2f
1: pause
cmpl %edx,_ZL10g_sPmmLock+4
jne 1b
2:

# 0 "" 2
#NO_APP
movl %esi, %ebp
movl _ZL14g_sStack4kbPtr(,%esi,4), %edi
sall $12, %ebp
sall %cl, %edx
movl %ebx, -1069547520(%ebp,%edi,4)
leal 1(%edi), %ebx
movl %ebx, _ZL14g_sStack4kbPtr(,%esi,4)
cmpl $1024, %ebx
je .L25
orl %edx, _ZL15g_sBitmap4mb4kb(,%eax,4)
movl _ZL10g_sFree4kb, %eax
incl %eax
movl %eax, _ZL10g_sFree4kb
.L23:
movl _ZL15g_sFreeTotal4kb, %eax
incl %eax
movl %eax, _ZL15g_sFreeTotal4kb
movl _ZL10g_sPmmLock+4, %eax
incl %eax
movl %eax, _ZL10g_sPmmLock+4
movb $1, %al
.L21:
popl %ebx
popl %esi
popl %edi
popl %ebp
ret
.L24:
xorl %eax, %eax
jmp .L21
.L25:
orl %edx, _ZL12g_sBitmap4mb(,%eax,4)
xorl %edx, _ZL15g_sBitmap4mb4kb(,%eax,4)
subl $1023, _ZL10g_sFree4kb
incl _ZL10g_sFree4mb
jmp .L23

Und einmal die vollständige funktionierende Funktion:
_ZN3Pmm10dealloc4kbEPv:
pushl %ebp
pushl %edi
pushl %esi
pushl %ebx
subl $28, %esp
movl 48(%esp), %eax
testl $4095, %eax
jne .L24
movl %eax, %edi
movl %eax, %ebx
shrl $22, %edi
movl $1, %esi
movl %edi, %ebp
movl %esi, %edx
shrl $27, %ebx
andl $31, %ebp
#APP
# 39 "../../src/include/spinlock.hpp" 1
lock xaddl %edx,_ZL10g_sPmmLock
cmpl %edx,_ZL10g_sPmmLock+4
je 2f
1: pause
cmpl %edx,_ZL10g_sPmmLock+4
jne 1b
2:

# 0 "" 2
#NO_APP
movl %edi, %ecx
movl _ZL14g_sStack4kbPtr(,%edi,4), %edx
sall $12, %ecx
movl %eax, -1069547520(%ecx,%edx,4)
leal 1(%edx), %eax
movl %eax, _ZL14g_sStack4kbPtr(,%edi,4)
movl %eax, 4(%esp)
movl $.LC0, (%esp)
call _ZN5Video6printfEPKcz
movl %ebp, %ecx
sall %cl, %esi
cmpl $1024, _ZL14g_sStack4kbPtr(,%edi,4)
je .L25
movl _ZL10g_sFree4kb, %eax
orl %esi, _ZL15g_sBitmap4mb4kb(,%ebx,4)
incl %eax
movl %eax, _ZL10g_sFree4kb
.L23:
movl _ZL15g_sFreeTotal4kb, %eax
incl %eax
movl %eax, _ZL15g_sFreeTotal4kb
movl _ZL10g_sPmmLock+4, %eax
incl %eax
movl %eax, _ZL10g_sPmmLock+4
movb $1, %al
.L21:
addl $28, %esp
popl %ebx
popl %esi
popl %edi
popl %ebp
ret
.L24:
xorl %eax, %eax
jmp .L21
.L25:
orl %esi, _ZL12g_sBitmap4mb(,%ebx,4)
xorl %esi, _ZL15g_sBitmap4mb4kb(,%ebx,4)
subl $1023, _ZL10g_sFree4kb
incl _ZL10g_sFree4mb
jmp .L23

Mal ganz davon abgesehen, dass GCC da mMn ein wenig komischen Code für -O2 erzeugt.

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #1 am: 18. December 2011, 16:08 »
Was genau heißt "funktioniert" bzw. "funktioniert nicht"?

Zeig bitte mal den C++-Code. Ohne den nützt der Assemblercode nicht viel. Letztendlich muss ja die Korrektur in dem C++-Code erfolgen.
Dieser Text wird unter jedem Beitrag angezeigt.

FlashBurn

  • Beiträge: 844
    • Profil anzeigen
Gespeichert
« Antwort #2 am: 18. December 2011, 16:23 »
Zitat von: jidder
Was genau heißt "funktioniert" bzw. "funktioniert nicht"?
Zitat von: flashburn
Im Prinzip geht es darum das eine Variable auf den Wert 1024 überprüft wird und dann muss halt anderer Code ausgeführt werden. Mit dem originalen Code funktioniert das (aus irgendeinem mir nicht bekannten Grund) nicht und mit Code der nur eine Ausgabe macht (genau zw. dem ändern der Variable und dem Vergleich des neuen Wertes), funktioniert es.

Also gut erstmal die ganze Funktion:
#define PMM_4KB_PER_4MB 1024

static uint32        g_sBitmap4mb[PMM_BITMAP_4MB_SLOTS];
static uint32        g_sBitmap4mb4kb[PMM_BITMAP_4MB_SLOTS];
static const uint32* g_spStack4kb= (uint32*)PMM_STACK_BASE;
static uint32        g_sStack4kbPtr[PMM_MAX_4MB_PAGES];
static Spinlock      g_sPmmLock;
static uint32        g_sFreeTotal4kb;
static uint32        g_sFree4kb;
static uint32        g_sFree4mb;

bool
Pmm::dealloc4kb(void* base)
{
    uint32  slot;
    uint32  bit;
    uint32  stackNum;
    uint32* stack4kb;
   
    if(unlikely((uint32)base & 0xFFF))
        return false;
    //27 because 22 = 4.194.304 (size of one 4mb page) and 5 = 32 (bits in one slot)
    slot= (uint32)base >> 27;
    bit= ((uint32)base >> 22) & 31;
    stackNum= (uint32)base >> 22;
    stack4kb= (uint32*)((uint32)g_spStack4kb + (sizeof(uint32) * 1024 * stackNum));
   
    g_sPmmLock.acquire();
   
    stack4kb[g_sStack4kbPtr[stackNum]++]= (uint32)base;
   
    Video::printf("stackPtr: %d\n",g_sStack4kbPtr[stackNum]);
    //genau hier liegt das Problem, mit obiger Anweisung funktioniert die Abfrage, ohne sie nicht
    if(unlikely(g_sStack4kbPtr[stackNum] == PMM_4KB_PER_4MB)) {
        g_sBitmap4mb[slot]|= (1 << bit);
        g_sBitmap4mb4kb[slot]^= (1 << bit);
       
        g_sFree4kb-= 1023;
        g_sFree4mb++;
    } else {
        g_sBitmap4mb4kb[slot]|= (1 << bit);
       
        g_sFree4kb++;
    }
   
    g_sFreeTotal4kb++;
   
    g_sPmmLock.release();
   
    return true;
}

Und jetzt mal das Problemstück:
    stack4kb[g_sStack4kbPtr[stackNum]++]= (uint32)base;
   
    Video::printf("stackPtr: %d\n",g_sStack4kbPtr[stackNum]);
    //genau hier liegt das Problem, mit obiger Anweisung funktioniert die Abfrage, ohne sie nicht
    if(unlikely(g_sStack4kbPtr[stackNum] == PMM_4KB_PER_4MB)) {

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #3 am: 18. December 2011, 16:56 »
Zitat von: jidder
Was genau heißt "funktioniert" bzw. "funktioniert nicht"?
Zitat von: flashburn
Im Prinzip geht es darum das eine Variable auf den Wert 1024 überprüft wird und dann muss halt anderer Code ausgeführt werden. Mit dem originalen Code funktioniert das (aus irgendeinem mir nicht bekannten Grund) nicht und mit Code der nur eine Ausgabe macht (genau zw. dem ändern der Variable und dem Vergleich des neuen Wertes), funktioniert es.
Das habe ich gelesen, ich hatte gehofft, dass du das weiter ausführen könntest. Diese Problembeschreibung hilft ja offensichtlich nicht weiter, wie du durch den Ausdruck deiner Verzweiflung selbst festgestellt hast. Da zwischen Bildschirmausgabe und der Ausführung von if-Abfragen kein offensichtlicher Zusammenhang besteht, wäre es sinnvoll zumindest das gewünschte Verhalten und das tatsächliche Verhalten zu beschreiben. Bei "funktioniert"/"funktioniert nicht" zu bleiben ist für das Finden einer Lösung nicht geeignet.

Mit dem C++-Code kann ich meine Frage nochmal konkretisieren: Wenn du die Ausgabe machst, werden die Variablen g_sBitmap4mb, g_sBitmap4mb4kb, g_sFree4kb, g_sFree4mb so verändert, wie es im C++-Code steht? Wie sehen die Eingaben dafür aus? Was ist der einfachste Testfall, den du dafür erzeugen kannst? (Ich nehme an, dass eine Folge von Aufrufen der Funktion dafür reichen sollte.)

Den einzigen Ansatz, den ich vorschlagen kann ohne die weiteren Rahmenbedingungen zu kennen, wäre zu überprüfen, ob PMM_STACK_BASE tatsächlich vernünftig im Speicher platziert ist, sowie ob die statischen Variablen korrekt funktionieren (Linkerskript, BSS-Initialisierung). Die weiteren üblichen Kandidaten sind fehlplatzierte Stacks und unzureichende Segment Limits. Um diese Probleme von vornherein auszuschließen, würde ich empfehlen den Code als User Mode Programm testen.
« Letzte Änderung: 18. December 2011, 16:58 von Jidder »
Dieser Text wird unter jedem Beitrag angezeigt.

MNemo

  • Beiträge: 547
    • Profil anzeigen
Gespeichert
« Antwort #4 am: 18. December 2011, 16:57 »
hmm… habe ich dich richtig verstanden, dass nur die printf() den unterschied macht ?

dann ist es wirklich merwürdig. wobei mir dein inline assemembler (spinnlock) etwas merkwüridg vorkommt.


je 2f

jne 1b

gcc sollte dier da noch keine lables durch sprungweiten ersetzen, und selbst wenn, dann sehen die recht fragwürdig aus.

„Wichtig ist nicht, besser zu sein als alle anderen. Wichtig ist, besser zu sein als du gestern warst!“

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #5 am: 18. December 2011, 17:01 »
@MNemo: In GAS kann man Labels mit Zahlen als Namen verwenden, die dann bei der Verwendung in Makros oder Inline Assembler recht praktisch sind: jmp 1f bedeutet "Springe zum nächsten Label, das 1 heißt" und jmp 2b bedeutet "Springe zum vorangegangenen Label, das 2 heißt". Also f=forward und b=back. Im Code (in der Assemblerausgabe etwas seltsam eingerückt) stehen auch die dazugehörigen Labels "1:" und "2:".
Dieser Text wird unter jedem Beitrag angezeigt.

MNemo

  • Beiträge: 547
    • Profil anzeigen
Gespeichert
« Antwort #6 am: 18. December 2011, 17:04 »
ah… ich habe sie als 0x1f und 0x2b interpretiert :-)
„Wichtig ist nicht, besser zu sein als alle anderen. Wichtig ist, besser zu sein als du gestern warst!“

FlashBurn

  • Beiträge: 844
    • Profil anzeigen
Gespeichert
« Antwort #7 am: 18. December 2011, 17:06 »
Zitat von: jidder
Mit dem C++-Code kann ich meine Frage nochmal konkretisieren: Wenn du die Ausgabe machst, werden die Variablen g_sBitmap4mb, g_sBitmap4mb4kb, g_sFree4kb, g_sFree4mb so verändert, wie es im C++-Code steht?
Du hast mich damit auf etwas gebracht, auf das hätte ich selbst kommen sollen. Also nein, weder wo der Code nicht funktioniert, noch wo er "funktioniert" geht die if-Abfrage true aus.

Jetzt bin ich sogar noch verwirrter als vorher, aber ich habe nen neuen Ansatzpunkt wo ich suchen kann. Also schonmal danke!

Zitat von: jidder
Den einzigen Ansatz, den ich ohne weitere Rahmenbedingungen zu kennen, vorschlagen kann, wäre zu überprüfen, ob PMM_STACK_BASE tatsächlich vernünftig im Speicher platziert ist, sowie ob die statischen Variablen korrekt funktionieren. Die weiteren üblichen Kandidaten sind fehlplatzierte Stacks und unzureichende Segment Limits. Um diese Probleme von vornherein auszuschließen, würde ich empfehlen den Code als User Mode Programm testen.
Ich werde mal gucken ob ich das vernünftig hinbekomme. Allerdings werde ich als erstes, einfach Bochs und den Debug-Port nutzen.

Zitat von: jidder
Im Code (in der Assemblerausgabe etwas seltsam eingerückt) stehen auch die dazugehörigen Labels "1:" und "2:".
Das sieht so komisch aus, weil mir so wäre als wenn Labels nicht alleine stehen dürfen, sondern dahinter immer eine Instruktion folgen muss?!

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #8 am: 18. December 2011, 17:20 »
Zitat von: jidder
Den einzigen Ansatz, den ich ohne weitere Rahmenbedingungen zu kennen, vorschlagen kann, wäre zu überprüfen, ob PMM_STACK_BASE tatsächlich vernünftig im Speicher platziert ist, sowie ob die statischen Variablen korrekt funktionieren. Die weiteren üblichen Kandidaten sind fehlplatzierte Stacks und unzureichende Segment Limits. Um diese Probleme von vornherein auszuschließen, würde ich empfehlen den Code als User Mode Programm testen.
Ich werde mal gucken ob ich das vernünftig hinbekomme. Allerdings werde ich als erstes, einfach Bochs und den Debug-Port nutzen.
Fürs produktive Debugging ist das natürlich nicht besonders gut. Deinen Code hab ich auf die Schnelle mit diesem Programm getestet:

#include <cstdarg>
#include <cstdio>
#include <iostream>

using namespace std;

class Pmm{
public:
bool dealloc4kb(void* base);
};

class Video{
public:
static int printf(const char *fmt, ...) {
va_list args;
int ret;
va_start(args, fmt);
ret = vprintf(fmt, args);
va_end(args);
return ret;
}
};

#define unlikely(x) x

typedef unsigned int uint32;

#define PMM_BITMAP_4MB_SLOTS 1024
#define PMM_MAX_4MB_PAGES 1024

#define PMM_4KB_PER_4MB 1024

static uint32 PMM_STACK_BASE[1024*1024];

// hier den code aus deinem post einfügen (ohne spinlock)

int main() { /*testfall*/ }

Das sieht so komisch aus, weil mir so wäre als wenn Labels nicht alleine stehen dürfen, sondern dahinter immer eine Instruktion folgen muss?!
Ich glaube nicht, dass das stimmt. Im Prinzip ist das ja auch egal, weil es keine Preise dafür gibt, wenn die Assemblerausgabe vom GCC hübsch aussieht.  Worauf es ankommt ist der Quellcode. Ich hab das nur erwähnt, weil MNemo das vermutlich nicht auf den ersten Blick gesehen hat. (Aber wenn du willst, dass die Ausgabe einheitlich aussieht, musst du eigentlich nur die \t's im Inline-Assembler an der richtigen Stelle platzieren. Keine Tabs vor Labels oder so.)
Dieser Text wird unter jedem Beitrag angezeigt.

FlashBurn

  • Beiträge: 844
    • Profil anzeigen
Gespeichert
« Antwort #9 am: 18. December 2011, 17:30 »
Zitat von: jidder
Fürs produktive Debugging ist das natürlich nicht besonders gut.
Also erstmal danke für die Mühe, aber Problem gefunden. Zumal ich das mit dem Bochs Debugging-Port ziemlich praktisch finde. Das eigentliche Problem ist ja das man auf dem Bildschirm nur begrenzt Informationen darstellen kann, aber über den Debugging-Port kann ich mir alle Ausgaben ansehen, auch "alte" (die von neueren auf dem Bildschirm verdrängt wurden).

So bin ich dann nämlich auch schnell drauf gekommen, dass das Problem gar nicht an meiner dealloc4kb, sondern an meiner dealloc4mb lag. Diese habe ich wohl nicht am selben Tag geschrieben wie den restlichen Code, denn da fehlte etwas entscheidendes (nämlich das der "Stack" richtig aufgebaut wird). Ich möchte auch gar nicht mehr wissen, warum es mit der Ausgabe trotzdem funktioniert hat. Stack ist genug da und eigentlich hätte auch der Code fehlschlagen müssen.

Zitat von: jidder
(Aber wenn du willst, dass die Ausgabe einheitlich aussieht, musst du eigentlich nur die \t's im Inline-Assembler an der richtigen Stelle platzieren.)
Ich habe nochmal nachgesehen und nach dem ich die \t´s an einer vernünftigeren Stelle platziert habe, sieht der Output auch gleich viel besser aus. Also nochmals Danke!

 

Einloggen