Einzelnen Beitrag anzeigen

Bernd1982

Registriert seit: 22. Dez 2009
11 Beiträge
 
#9

AW: FastMM4 und Delphi 7

  Alt 2. Jan 2018, 17:26
Ich habe das Problem auf eine sehr spezielle Art gelöst. Es bleibt dabei, bei meinen über hundert Units wird FastMM vom Compiler nicht an der frühen Stelle initialisiert, sondern erst nachdem andere Units schon Speicher vom alten MemoryManager angefordert haben. FastMM4 tritt daher so seinen Dienst nicht an. Die Reihenfolge wird vom Compiler festgelegt, so dass man zunächst keine Möglichkeit hat einzugreifen. Aber es geht doch

Ich habe nämlich 2 Änderungen gemacht:
  1. Ich habe die INITILAIZATION in FastMM4.pas mit ASM NOP NOP NOP NOP END; als Kennung anfangen lassen.
  2. Ich habe die system.pas geändert und neu kompiliert. Bei InitUnits wird jetzt zuerst die FastMM4 gesucht und früher initialisiert als vom Compiler vorgegeben.

Zu 1.: Die FastMM4.pas sieht am Ende jetzt also so aus:
Delphi-Quellcode:
initialization
  asm
  NOP
  NOP
  NOP
  NOP
  end;
  RunInitializationCode;
finalization
{$ifndef PatchBCBTerminate}
  FinalizeMemoryManager;
{$endif}
end.
Die 4 NOPs ergeben 4 x $90 also ein DWORD $90909090 im Code. Daran kann man diese Unit in der Liste erkennen.

Zu 2.: Der Compiler legt im Quellcode eine Liste an, die aus je 2 Pointern auf Prozeduren für jede Unit besteht: Init für die Initialisierung und FInit für die Finalisierung. Bevor beim Programmstart nun alle Units nacheinander in InitUnits in system.pas initialisiert werden, suche ich zuerst nach meinem Eintrag mit $90909090. Die Nummer in der Liste merke ich mir dann in der Variablen iFastMM4. Beim Initialisieren wird die Liste vorwärts abgearbeitet, beim Finalisieren rückwärts. Wenn die 5. Unit dran wäre, initialisiere ich dann immer erst die FastMM4. Wenn die FastMM4 regulär dran wäre ignoriere ich sie. Beim Finalisieren, wie gesagt, genau anders herum:
Delphi-Quellcode:
VAR iFastMM4:LongInt=-1; {Falls die Unit FastMM4 früher behandelt werden muss}

procedure FinalizeUnits;
var
  i,Count: Integer;
  Table: PUnitEntryTable;
  P: Pointer;
begin
  if InitContext.InitTable = nil then
    exit;
  Count := InitContext.InitCount;
  Table := InitContext.InitTable^.UnitInfo;
  try
    FOR i:=Count-1 DOWNTO 0 DO BEGIN
      InitContext.InitCount:=i;
      IF i=iFastMM4 THEN Continue;
      P:=Table^[i].FInit;
      IF Assigned(P) THEN TProc(P)();
      IF (i=5) AND (iFastMM4>-1) THEN BEGIN
        InitContext.InitCount:=iFastMM4+1;
        TProc(Table^[iFastMM4].FInit)() {erst die 5, dann FastMM4}
      END;
    END;
  except
    FinalizeUnits; { try to finalize the others }
    raise;
  end;
end;

const
  errCaption: array[0..5] of Char = 'Error'#0;

{***********************************************************}

procedure InitUnits;
var
  Count,i: Integer;
  Table: PUnitEntryTable;
  P,P1: Pointer;
begin
  if InitContext.InitTable = nil then
    exit;
  Count := InitContext.InitTable^.UnitCount;
  Table := InitContext.InitTable^.UnitInfo;
{$IFDEF LINUX}
  Inc(Cardinal(Table), InitContext.Module^.GOT);
{$ENDIF}
  try
    FOR i:=0 TO Count-1 DO BEGIN {Unit FastMM4 suchen}
      P:=Table^[i].Init;
      IF Assigned(P) THEN BEGIN
        P1:=P;
        Inc(DWord(P1),9);
        IF DWord(P1^)=DWord($90909090) THEN BEGIN {Initialisierung der Unit FastMM4 gefunden! (muss dort mit ASM NOP NOP NOP NOP END anfangen! NOP=$90)}
          IF I<=7 THEN Break;
          iFastMM4:=I;
          Break;
        END;
      END;
    END;

    FOR i:=0 TO Count-1 DO BEGIN
      InitContext.InitCount:=i+1;
      IF i=iFastMM4 THEN Continue;
      IF (i=5) AND (iFastMM4>-1) THEN BEGIN
        InitContext.InitCount:=iFastMM4+1;
        TProc(Table^[iFastMM4].Init)() {erst FastMM4, dann die 5}
      END;
      P:=Table^[i].Init;
      IF Assigned(P) THEN TProc(P)();
    END;
  except
    FinalizeUnits;
    raise;
  end;
end;
Die system.pas muss man manuell im CMD-Fenster mit dem Kommandozeilencompiler DCC32 kompilieren:

"C:\Program Files (X86)\Borland\Delphi7\Bin\DCC32.EXE" -$D+ -Y SYSTEM

und ohne Debug-Info:

"C:\Program Files (X86)\Borland\Delphi7\Bin\DCC32.EXE" -$D- -Y SYSTEM

Die geänderte system.pas kommt zurück nach "C:\Program Files (x86)\Borland\Delphi7\Source\Rtl\Sys", die entstandene system.dcu mit Debug-Info nach "C:\Program Files (x86)\Borland\Delphi7\Lib\Debug" und die ohne nach "C:\Program Files (x86)\Borland\Delphi7\Lib". Ggf. muss man zum Kopieren Admin-Rechte haben. Es ist nicht verkehrt, vorher alles zu sichern!

Wenn man in system.pas debuggen will, muss man in den Projekt-Optionen bei Compiler die Option [ ] mit Debug-DCUs aktivieren.

Das ist wirklich eine harte Nuss gewesen! Bestimmt hätte es auch eine einfachere Lösung gegeben, habe ich aber nicht gefunden. Es ist mir unbegreiflich, warum die FastMM.pas so spät initialisiert wird, obwohl sie oben im *.dpr als erstes stand. Egal - danke für Eure Hilfe.
  Mit Zitat antworten Zitat