2010-11-02 6 views
10

Sto cercando un bug che potrebbe essere collegato all'ordine di inizializzazione dell'unità. C'è un modo per vedere quale sezione initialization è stata eseguita quando? Ho bisogno di sapere l'ordine. Questo è durante il debug, in modo da avere il pieno potere della Delphi, nel mio caso Delphi 2009.Posso determinare l'ordine in cui le mie unità sono state inizializzate?

ho potuto impostare i punti di interruzione, ma questo è piuttosto noiosa quando avere molte unità.

Avete qualche suggerimento?

+1

Correlato: Se si utilizza un'unità nella sezione di interfaccia, si sa che quell'unità sarà inizializzata PRIMA dell'unità che utilizza quell'unità. Quando si usano le unità nella sezione di implementazione questo non è il caso. Di solito, quando si utilizza un'unità con un singleton, creata nella sezione di inizializzazione, è necessario utilizzare quell'unità nella sezione di interfaccia per assicurarsi che sia inizializzata prima dell'uso. –

risposta

6

Per le unità nell'interfaccia utilizza lista, sezioni di inizializzazione delle unità utilizzate da un client vengono eseguiti in l'ordine in cui le unità appaiono in clausola uses del client.

vedere Guida in linea \ Programs and Units \ L'inizializzazione Sezione e questo articolo: Understanding Delphi Unit initialization order

ICARUS calcola l'ordine runtime di inizializzazione per la sua utilizzi Report:

questa sezione sono elencati l'ordine in cui le sezioni di inizializzazione vengono eseguite in fase di esecuzione.

+0

La parte che rende difficile è quando Unità1 usa diverse altre Unità che possono usare ancora più Unità. – Remko

+0

@Remko Esattamente. –

+0

@Heinrich: prova [ICARUS] (http://www.peganza.com/products_icarus.htm) per il calcolo dell'ordine di inizializzazione del runtime. – splash

0

ne dite di aggiungere

OutputDebugString('In MyUnit initialization'); 

alle sezioni di inizializzazione?

+0

Purtroppo non posso cambiare tutte le unità. Molti di loro non sono sotto il mio controllo. –

3

È possibile controllare l'unità Sistema e SysInit e cercare la procedura InitUnits. Qui vedi che ogni modulo compilato con Delphi ha una lista di inizializzazioni e finalizzatori di unità. L'uso di quelli più un file di mappa potrebbe darti l'esatto ordine di inizializzazione, ma ci vorrà qualche trucco con il puntatore.

+0

+1 e non dimenticare di abilitare debug dcu altrimenti non è possibile impostare BP in InitUnits. – Remko

+0

Btw sembra come InitContext.Module^.TypeInfo^.UnitNames contiene una matrice di stringhe con i nomi di unità. Se lo getto: PAnsiChar (InitContext.Module^.TypeInfo^.UnitNames) il risultato è (esempio): – Remko

+0

# 8 "Varianti" # 8'VarUtils '# 7'Windows' # 5'Types '# 7'SysInit 4'Math # 9'Character # 8'SysConst '# 6'System' # '# 8'SysUtils' '# 9'RTLConsts' '#' # 8'StrUtils 8'ImageHlp '# 8'MainUnit' # $ B' JwaWinNetWk '# $ A'JwaWinType' # 8'JwaWinNT '# $ E'JwaWinDLLNames' # $ B'JwaWinError '#' # 8'StdCtrls 6'Dwmapi '# 7'UxTheme' # '# 8'SyncObjs 7'Classes' 4'ZLib # 7'ActiveX '#' # 8'Messages 7'TypInfo '8'TimeSpan'List #' # '# 7'Contnrs 9'GraphUtil' # '#' # 9'ListActns 8'ExtCtrls '# 7' Finestre di dialogo e così via – Remko

0

È possibile impostare i punti di interruzione su tutte le sezioni di inizializzazione che non si rompono, ma scrivere un messaggio nel log debugger. Ti darà la stessa lista che aggiunge le chiamate OutputDebugString('...') ma senza dover modificare il codice sorgente di tutte le unità.

+0

Cerco di evitare questo come a mio avviso è soggetto a errori e un molto lavoro. Se ho forse centinaia di unità dipendenti dovrei impostare un punto di interruzione ovunque. E se avessi solo un dcu e nessun codice sorgente? E se dimenticassi qualche unità? E dopo il prossimo arresto di Delphi, tutti i punti di interruzione sono spariti. Sarebbe bello se ci fosse una soluzione più semplice. –

8

Ecco alcuni codice che ho appena testato in D2010, si noti che è necessario impostare un punto di interruzione System.InitUnits e ottenere l'indirizzo di InitContext var (@InitContext). Quindi modificare CtxPtr per avere questo indirizzo MENTRE ANCORA IN CORSO. (Forse qualcuno conosce un modo più intelligente per questo).

procedure TForm3.Button2Click(Sender: TObject); 
var 
    sl: TStringList; 
    ps: PShortString; 
    CtxPtr: PInitContext; 
begin 
    // Get the address by setting a BP in SysUtils.InitUnits (or map file?) 
    CtxPtr := PInitContext($4C3AE8); 

    sl := TStringList.Create; 
    try 
    ps := CtxPtr^.Module^.TypeInfo^.UnitNames; 

    for i := 0 to CtxPtr^.Module^.TypeInfo^.UnitCount - 1 do 
    begin 
     sl.Add(ps^); 
     // Move to next unit 
     DWORD(ps) := DWORD(ps) + Length(ps^) + 1; 
    end; 

    Memo1.Lines.Assign(sl); 
    finally 
    sl.Free; 
    end; 
end; 

/EDIT: e qui è una versione con JclDebug e un mapfile:

type 
    TForm3 = class(TForm) 
    ... 
    private 
    { Private declarations } 
    var 
     Segments: array of DWORD; 
    procedure PublicsByValue(Sender: TObject; const Address: TJclMapAddress; const Name: string); 
    procedure MapSegment(Sender: TObject; const Address: TJclMapAddress; Len: Integer; const GroupName, UnitName: string); 
    procedure MapClassTable(Sender: TObject; const Address: TJclMapAddress; Len: Integer; const SectionName, GroupName: string); 
    public 
    { Public declarations } 
    end; 

var 
    Form3: TForm3; 
    CtxPtr: PInitContext = nil; // Global var 

procedure TForm3.MapClassTable(Sender: TObject; const Address: TJclMapAddress; 
    Len: Integer; const SectionName, GroupName: string); 
begin 
    SetLength(Segments, Length(Segments) + 1); 
    SegMents[Address.Segment-1] := Address.Offset; 
end; 

procedure TForm3.PublicsByValue(Sender: TObject; const Address: TJclMapAddress; 
    const Name: string); 
const 
    InitContextStr = 'System.InitContext'; 
begin 
    if RightStr(Name, Length(InitContextStr)) = InitContextStr then 
    begin 
    CtxPtr := PInitContext(Segments[Address.Segment-1] + Address.Offset); 
    end; 
end; 

procedure TForm3.Button2Click(Sender: TObject); 
var 
    MapParser: TJclMapParser; 
    MapFile: String; 
    sl: TStringList; 
    ps: PShortString; 
    i: Integer; 
begin 
    MapFile := ChangeFileExt(Application.ExeName, '.map'); 

    MapParser := TJclMapParser.Create(MapFile); 
    try 
    MapParser.OnPublicsByValue := PublicsByValue; 
    MapParser.OnClassTable := MapClassTable; 
    MapParser.Parse; 
    finally 
    MapParser.Free; 
    end; 

    if CtxPtr = nil then 
    Exit; 

    sl := TStringList.Create; 
    try 
    ps := CtxPtr^.Module^.TypeInfo^.UnitNames; 

    for i := 0 to CtxPtr^.Module^.TypeInfo^.UnitCount - 1 do 
    begin 
     sl.Add(ps^); 
     // Move to next unit 
     DWORD(ps) := DWORD(ps) + Length(ps^) + 1; 
    end; 

    Memo1.Lines.Assign(sl); 
    finally 
    sl.Free; 
    end; 
end; 

uscita nel mio caso:

Variants 
VarUtils 
Windows 
Types 
SysInit 
System 
SysConst 
SysUtils 
Character 
RTLConsts 
Math 
StrUtils 
ImageHlp 
MainUnit 
JwaWinNetWk 
JwaWinType 
JwaWinNT 
JwaWinDLLNames 
JwaWinError 
StdCtrls 
Dwmapi 
UxTheme 
SyncObjs 
Classes 
ActiveX 
Messages 
TypInfo 
TimeSpan 
CommCtrl 
Themes 
Controls 
Forms 
StdActns 
ComCtrls 
CommDlg 
ShlObj 
StructuredQueryCondition 
PropSys 
ObjectArray 
UrlMon 
WinInet 
RegStr 
ShellAPI 
ComStrs 
Consts 
Printers 
Graphics 
Registry 
IniFiles 
IOUtils 
Masks 
DateUtils 
Wincodec 
WinSpool 
ActnList 
Menus 
ImgList 
Contnrs 
GraphUtil 
ZLib 
ListActns 
ExtCtrls 
Dialogs 
HelpIntfs 
MultiMon 
Dlgs 
WideStrUtils 
ToolWin 
RichEdit 
Clipbrd 
FlatSB 
Imm 
TpcShrd 

/EDIT2: Ed ecco una versione per D2009 (richiede JclDebug):

unit MainUnit; 

interface 

uses 
    Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, 
    Dialogs, StrUtils, JclDebug, StdCtrls; 

type 
    TForm1 = class(TForm) 
    Button1: TButton; 
    Memo1: TMemo; 
    procedure Button1Click(Sender: TObject); 
    private 
    { Private declarations } 
    var 
     Segments: array of DWORD; 
    procedure PublicsByValue(Sender: TObject; const Address: TJclMapAddress; const Name: string); 
    procedure MapClassTable(Sender: TObject; const Address: TJclMapAddress; Len: Integer; const SectionName, GroupName: string); 
    public 
    { Public declarations } 
    end; 

var 
    Form1: TForm1; 
    CtxPtr: PInitContext = nil; // Global var 
    Symbols: TStringList; 

implementation 

{$R *.dfm} 

procedure TForm1.Button1Click(Sender: TObject); 
var 
    MapParser: TJclMapParser; 
    MapFile: String; 
    sl: TStringList; 
    ps: PShortString; 
    i: Integer; 
    s: String; 
    Idx: Integer; 
begin 
    MapFile := ChangeFileExt(Application.ExeName, '.map'); 

    MapParser := TJclMapParser.Create(MapFile); 
    try 
    MapParser.OnPublicsByValue := PublicsByValue; 
    MapParser.OnClassTable := MapClassTable; 
    Memo1.Lines.BeginUpdate; 
    MapParser.Parse; 
    Memo1.Lines.EndUpdate; 

    finally 
    MapParser.Free; 
    end; 

    if CtxPtr = nil then 
    Exit; 

    sl := TStringList.Create; 
    try 

    for i := 0 to CtxPtr^.InitTable.UnitCount-1 do 
    begin 
     if Assigned(CtxPtr^.InitTable.UnitInfo^[i].Init) then 
     begin 
     s := Format('$%.8x', [DWORD(CtxPtr^.InitTable.UnitInfo^[i].Init)]); 
     Idx := Symbols.IndexOfObject(TObject(CtxPtr^.InitTable.UnitInfo^[i].Init)); 
     if Idx > -1 then 
     begin 
      Memo1.Lines.Add(Format('%.4d: %s', [i, Symbols[Idx]])); 
     end; 
     end; 
    end; 

    finally 
    sl.Free; 
    end; 
end; 

procedure TForm1.MapClassTable(Sender: TObject; const Address: TJclMapAddress; 
    Len: Integer; const SectionName, GroupName: string); 
begin 
    SetLength(Segments, Length(Segments) + 1); 
    SegMents[Address.Segment-1] := Address.Offset; 
end; 

procedure TForm1.PublicsByValue(Sender: TObject; const Address: TJclMapAddress; 
    const Name: string); 
const 
    InitContextStr = 'System.InitContext'; 
begin 
    if RightStr(Name, Length(InitContextStr)) = InitContextStr then 
    begin 
    CtxPtr := PInitContext(Segments[Address.Segment-1] + Address.Offset); 
    end 
    else begin 
    Symbols.AddObject(Name, TObject(Segments[Address.Segment-1] + Address.Offset)); 
    end; 
end; 

initialization 
    Symbols := TStringList.Create; 
    Symbols.Sorted := True; 
    Symbols.Duplicates := dupIgnore; 

finalization 
    FreeAndNil(Symbols); 

end. 

Ou tput sul mio sistema (Unitname.Unitname è in realtà Unitname.Inizializzazione):

0001: System.System 
0003: Windows.Windows 
0011: SysUtils.SysUtils 
0012: VarUtils.VarUtils 
0013: Variants.Variants 
0014: TypInfo.TypInfo 
0016: Classes.Classes 
0017: IniFiles.IniFiles 
0018: Registry.Registry 
0020: Graphics.Graphics 
0023: SyncObjs.SyncObjs 
0024: UxTheme.UxTheme 
0025: MultiMon.MultiMon 
0027: ActnList.ActnList 
0028: DwmApi.DwmApi 
0029: Controls.Controls 
0030: Themes.Themes 
0032: Menus.Menus 
0033: HelpIntfs.HelpIntfs 
0034: FlatSB.FlatSB 
0036: Printers.Printers 
0047: GraphUtil.GraphUtil 
0048: ExtCtrls.ExtCtrls 
0051: ComCtrls.ComCtrls 
0054: Dialogs.Dialogs 
0055: Clipbrd.Clipbrd 
0057: Forms.Forms 
0058: JclResources.JclResources 
0059: JclBase.JclBase 
0061: JclWin32.JclWin32 
0063: ComObj.ComObj 
0064: AnsiStrings.AnsiStrings 
0065: JclLogic.JclLogic 
0066: JclStringConversions.JclStringConversions 
0067: JclCharsets.JclCharsets 
0068: Jcl8087.Jcl8087 
0073: JclIniFiles.JclIniFiles 
0074: JclSysInfo.JclSysInfo 
0075: JclUnicode.JclUnicode 
0076: JclWideStrings.JclWideStrings 
0077: JclRegistry.JclRegistry 
0078: JclSynch.JclSynch 
0079: JclMath.JclMath 
0080: JclStreams.JclStreams 
0081: JclAnsiStrings.JclAnsiStrings 
0082: JclStrings.JclStrings 
0083: JclShell.JclShell 
0084: JclSecurity.JclSecurity 
0085: JclDateTime.JclDateTime 
0086: JclFileUtils.JclFileUtils 
0087: JclConsole.JclConsole 
0088: JclSysUtils.JclSysUtils 
0089: JclUnitVersioning.JclUnitVersioning 
0090: JclPeImage.JclPeImage 
0091: JclTD32.JclTD32 
0092: JclHookExcept.JclHookExcept 
0093: JclDebug.JclDebug 
0094: MainUnit.MainUnit 
+0

+1 È piuttosto impressionante, grazie per il tuo impegno! È interessante vedere l'uso di TJclMapParser. Ma sfortunatamente sono legato a Delphi 2009 e il facile RTTI non è disponibile qui: -/Ma per tutti quelli con D2010 questa potrebbe essere la strada da percorrere. –

+0

intendi InitUnits nell'unità di sistema ?, perché non esiste in SysUtils. –

+0

@Mohammed Nasman: Sì, l'ho corretto. (Se si utilizza la versione con JclDebug/Mapfile non è necessario impostare il punto di interruzione, l'indirizzo verrà letto dal mapfile) – Remko

Problemi correlati