fpc/compiler/dbgbase.pas

692 lines
22 KiB
ObjectPascal

{
Copyright (c) 2003-2006 by Peter Vreman and Florian Klaempfl
This units contains the base class for debug info generation
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
****************************************************************************
}
unit dbgbase;
{$i fpcdefs.inc}
interface
uses
cclasses,
systems,
parabase,
symconst,symbase,symdef,symtype,symsym,
fmodule,
aasmtai,aasmdata;
type
TDebugInfo=class
protected
{ definitions }
{ collect all defs in one list so we can reset them easily }
defnumberlist : TFPObjectList;
deftowritelist : TFPObjectList;
procedure appenddef(list:TAsmList;def:tdef);
procedure beforeappenddef(list:TAsmList;def:tdef);virtual;
procedure afterappenddef(list:TAsmList;def:tdef);virtual;
procedure appenddef_ord(list:TAsmList;def:torddef);virtual;
procedure appenddef_float(list:TAsmList;def:tfloatdef);virtual;
procedure appenddef_file(list:TAsmList;def:tfiledef);virtual;
procedure appenddef_enum(list:TAsmList;def:tenumdef);virtual;
procedure appenddef_array(list:TAsmList;def:tarraydef);virtual;
procedure appenddef_record(list:TAsmList;def:trecorddef);virtual;
procedure appenddef_object(list:TAsmList;def:tobjectdef);virtual;
procedure appenddef_classref(list:TAsmList;def: tclassrefdef);virtual;
procedure appenddef_pointer(list:TAsmList;def:tpointerdef);virtual;
procedure appenddef_string(list:TAsmList;def:tstringdef);virtual;
procedure appenddef_procvar(list:TAsmList;def:tprocvardef);virtual;
procedure appenddef_variant(list:TAsmList;def:tvariantdef);virtual;
procedure appenddef_set(list:TAsmList;def:tsetdef);virtual;
procedure appenddef_formal(list:TAsmList;def:tformaldef);virtual;
procedure appenddef_undefined(list:TAsmList;def: tundefineddef);virtual;
procedure appendprocdef(list:TAsmList;def:tprocdef);virtual;
procedure write_remaining_defs_to_write(list:TAsmList);
{ symbols }
procedure appendsym(list:TAsmList;sym:tsym);
procedure beforeappendsym(list:TAsmList;sym:tsym);virtual;
procedure afterappendsym(list:TAsmList;sym:tsym);virtual;
procedure appendsym_staticvar(list:TAsmList;sym:tstaticvarsym);virtual;
procedure appendsym_paravar(list:TAsmList;sym:tparavarsym);virtual;
procedure appendsym_localvar(list:TAsmList;sym:tlocalvarsym);virtual;
procedure appendsym_fieldvar(list:TAsmList;sym:tfieldvarsym);virtual;
procedure appendsym_unit(list:TAsmList;sym:tunitsym);virtual;
procedure appendsym_const(list:TAsmList;sym:tconstsym);virtual;
procedure appendsym_type(list:TAsmList;sym:ttypesym);virtual;
procedure appendsym_label(list:TAsmList;sym:tlabelsym);virtual;
procedure appendsym_absolute(list:TAsmList;sym:tabsolutevarsym);virtual;
procedure appendsym_property(list:TAsmList;sym:tpropertysym);virtual;
{ symtable }
procedure write_symtable_parasyms(list:TAsmList;paras: tparalist);
procedure write_symtable_syms(list:TAsmList;st:TSymtable);
procedure write_symtable_defs(list:TAsmList;st:TSymtable);
procedure write_symtable_procdefs(list:TAsmList;st:TSymtable);
procedure reset_unit_type_info;
procedure write_used_unit_type_info(list:TAsmList;hp:tmodule);
public
constructor Create;virtual;
procedure inserttypeinfo;virtual;
procedure insertmoduleinfo;virtual;
procedure insertlineinfo(list:TAsmList);virtual;
procedure referencesections(list:TAsmList);virtual;
end;
TDebugInfoClass=class of TDebugInfo;
var
CDebugInfo : array[tdbg] of TDebugInfoClass;
current_debuginfo : tdebuginfo;
procedure InitDebugInfo(hp:tmodule; restore_current_debuginfo : boolean);
procedure DoneDebugInfo(hp:tmodule;var current_debuginfo_reset : boolean);
procedure RegisterDebugInfo(const r:tdbginfo;c:TDebugInfoClass);
implementation
uses
cutils,
globals,
verbose,
cgbase;
constructor TDebugInfo.Create;
begin
end;
procedure TDebugInfo.insertmoduleinfo;
begin
end;
procedure TDebugInfo.inserttypeinfo;
begin
end;
procedure TDebugInfo.insertlineinfo(list:TAsmList);
begin
end;
procedure TDebugInfo.referencesections(list:TAsmList);
begin
end;
{**************************************
Definition
**************************************}
procedure TDebugInfo.appendprocdef(list:TAsmList;def:tprocdef);
begin
end;
procedure TDebugInfo.beforeappenddef(list:TAsmList;def:tdef);
begin
end;
procedure TDebugInfo.afterappenddef(list:TAsmList;def:tdef);
begin
end;
procedure TDebugInfo.appenddef_ord(list:TAsmList;def:torddef);
begin
end;
procedure TDebugInfo.appenddef_float(list:TAsmList;def:tfloatdef);
begin
end;
procedure TDebugInfo.appenddef_formal(list:TAsmList;def: tformaldef);
begin
end;
procedure TDebugInfo.appenddef_undefined(list:TAsmList;def: tundefineddef);
begin
end;
procedure TDebugInfo.appenddef_set(list:TAsmList;def: tsetdef);
begin
end;
procedure TDebugInfo.appenddef_object(list:TAsmList;def: tobjectdef);
begin
end;
procedure TDebugInfo.appenddef_classref(list:TAsmList;def: tclassrefdef);
begin
appenddef_pointer(list,tpointerdef(pvmttype));
end;
procedure TDebugInfo.appenddef_variant(list:TAsmList;def: tvariantdef);
begin
end;
procedure TDebugInfo.appenddef_enum(list:TAsmList;def:tenumdef);
begin
end;
procedure TDebugInfo.appenddef_file(list:TAsmList;def: tfiledef);
begin
end;
procedure TDebugInfo.appenddef_array(list:TAsmList;def:tarraydef);
begin
end;
procedure TDebugInfo.appenddef_record(list:TAsmList;def:trecorddef);
begin
end;
procedure TDebugInfo.appenddef_pointer(list:TAsmList;def:tpointerdef);
begin
end;
procedure TDebugInfo.appenddef_string(list:TAsmList;def:tstringdef);
begin
end;
procedure TDebugInfo.appenddef_procvar(list:TAsmList;def:tprocvardef);
begin
end;
procedure TDebugInfo.appenddef(list:TAsmList;def:tdef);
begin
if (def.dbg_state in [dbg_state_writing,dbg_state_written]) then
exit;
{ never write generic template defs }
if df_generic in def.defoptions then
begin
def.dbg_state:=dbg_state_written;
exit;
end;
{ to avoid infinite loops }
def.dbg_state := dbg_state_writing;
beforeappenddef(list,def);
{ queued defs have to be written later }
if (def.dbg_state=dbg_state_queued) then
exit;
case def.typ of
stringdef :
appenddef_string(list,tstringdef(def));
enumdef :
appenddef_enum(list,tenumdef(def));
orddef :
appenddef_ord(list,torddef(def));
pointerdef :
appenddef_pointer(list,tpointerdef(def));
floatdef :
appenddef_float(list,tfloatdef(def));
filedef :
appenddef_file(list,tfiledef(def));
recorddef :
appenddef_record(list,trecorddef(def));
variantdef :
appenddef_variant(list,tvariantdef(def));
classrefdef :
appenddef_classref(list,tclassrefdef(def));
setdef :
appenddef_set(list,tsetdef(def));
formaldef :
appenddef_formal(list,tformaldef(def));
arraydef :
appenddef_array(list,tarraydef(def));
procvardef :
appenddef_procvar(list,tprocvardef(def));
objectdef :
appenddef_object(list,tobjectdef(def));
undefineddef :
appenddef_undefined(list,tundefineddef(def));
procdef :
begin
{ procdefs are already written in a separate step. procdef
support in appenddef is only needed for beforeappenddef to
write all local type defs }
end;
else
internalerror(200601281);
end;
afterappenddef(list,def);
def.dbg_state := dbg_state_written;
end;
procedure TDebugInfo.write_remaining_defs_to_write(list:TAsmList);
var
n : integer;
looplist,
templist: TFPObjectList;
def : tdef;
begin
templist := TFPObjectList.Create(False);
looplist := deftowritelist;
while looplist.count > 0 do
begin
deftowritelist := templist;
for n := 0 to looplist.count - 1 do
begin
def := tdef(looplist[n]);
case def.dbg_state of
dbg_state_written:
continue;
dbg_state_writing:
internalerror(200610052);
dbg_state_unused:
internalerror(200610053);
dbg_state_used:
appenddef(list,def);
dbg_state_queued:
begin
{ can happen in case an objectdef was used from another
unit that was compiled without debug info, and we are
using Stabs (which means that parent types have to be
written before child types). In this case, the child
objectdef will be queued and never written, because its
definition is not inside the current unit and hence will
not be encountered }
if def.typ<>objectdef then
internalerror(2012072401);
if not assigned(tobjectdef(def).childof) or
(tobjectdef(def).childof.dbg_state=dbg_state_written) then
appenddef(list,def)
else if tobjectdef(def).childof.dbg_state=dbg_state_queued then
begin
{ ensure that the parent is indeed queued }
deftowritelist.add(tobjectdef(def).childof);
deftowritelist.add(def);
end
else if tobjectdef(def).childof.dbg_state=dbg_state_used then
{ comes somewhere after the current def in the looplist
and will be written at that point, so we will have to
wait until the next iteration }
deftowritelist.add(def)
else
internalerror(2012072402);
end;
end;
end;
looplist.clear;
templist := looplist;
looplist := deftowritelist;
end;
templist.free;
end;
{**************************************
Symbols
**************************************}
procedure TDebugInfo.beforeappendsym(list:TAsmList;sym:tsym);
begin
end;
procedure TDebugInfo.afterappendsym(list:TAsmList;sym:tsym);
begin
end;
procedure TDebugInfo.appendsym_staticvar(list:TAsmList;sym:tstaticvarsym);
begin
end;
procedure TDebugInfo.appendsym_paravar(list:TAsmList;sym: tparavarsym);
begin
end;
procedure TDebugInfo.appendsym_localvar(list:TAsmList;sym: tlocalvarsym);
begin
end;
procedure TDebugInfo.appendsym_fieldvar(list:TAsmList;sym: tfieldvarsym);
begin
end;
procedure TDebugInfo.appendsym_const(list:TAsmList;sym:tconstsym);
begin
end;
procedure TDebugInfo.appendsym_label(list:TAsmList;sym: tlabelsym);
begin
end;
procedure TDebugInfo.appendsym_property(list:TAsmList;sym: tpropertysym);
begin
end;
procedure TDebugInfo.appendsym_type(list:TAsmList;sym: ttypesym);
begin
end;
procedure TDebugInfo.appendsym_unit(list:TAsmList;sym: tunitsym);
begin
end;
procedure TDebugInfo.appendsym_absolute(list:TAsmList;sym:tabsolutevarsym);
begin
end;
procedure TDebugInfo.appendsym(list:TAsmList;sym:tsym);
begin
if sym.isdbgwritten then
exit;
beforeappendsym(list,sym);
case sym.typ of
staticvarsym :
if not assigned(tstaticvarsym(sym).fieldvarsym) or
not(df_generic in tdef(tstaticvarsym(sym).fieldvarsym.owner.defowner).defoptions) then
appendsym_staticvar(list,tstaticvarsym(sym));
unitsym:
appendsym_unit(list,tunitsym(sym));
labelsym :
appendsym_label(list,tlabelsym(sym));
localvarsym :
appendsym_localvar(list,tlocalvarsym(sym));
paravarsym :
if tparavarsym(sym).localloc.loc<>LOC_VOID then
appendsym_paravar(list,tparavarsym(sym));
constsym :
appendsym_const(list,tconstsym(sym));
typesym :
appendsym_type(list,ttypesym(sym));
enumsym :
{ ignore enum syms, they are written by the owner }
;
syssym :
{ ignore sys syms, they are only of internal use }
;
procsym :
{ ignore proc syms, they are written by procdefs }
;
absolutevarsym :
appendsym_absolute(list,tabsolutevarsym(sym));
propertysym :
appendsym_property(list,tpropertysym(sym));
namespacesym :
{ ignore namespace syms, they are only of internal use }
;
else
internalerror(200601242);
end;
afterappendsym(list,sym);
sym.isdbgwritten:=true;
end;
{**************************************
Symtables
**************************************}
procedure TDebugInfo.write_symtable_defs(list:TAsmList;st:TSymtable);
var
def : tdef;
i : longint;
nonewadded : boolean;
begin
case st.symtabletype of
staticsymtable :
list.concat(tai_comment.Create(strpnew('Defs - Begin Staticsymtable')));
globalsymtable :
list.concat(tai_comment.Create(strpnew('Defs - Begin unit '+st.name^+' has index '+tostr(st.moduleid))));
else
;
end;
repeat
nonewadded:=true;
for i:=0 to st.DefList.Count-1 do
begin
def:=tdef(st.DefList[i]);
if (def.dbg_state in [dbg_state_used,dbg_state_queued]) then
begin
appenddef(list,def);
nonewadded:=false;
end;
end;
until nonewadded;
case st.symtabletype of
staticsymtable :
list.concat(tai_comment.Create(strpnew('Defs - End Staticsymtable')));
globalsymtable :
list.concat(tai_comment.Create(strpnew('Defs - End unit '+st.name^+' has index '+tostr(st.moduleid))));
else
;
end;
end;
procedure TDebugInfo.write_symtable_parasyms(list:TAsmList;paras: tparalist);
var
i : longint;
sym : tsym;
begin
for i:=0 to paras.Count-1 do
begin
sym:=tsym(paras[i]);
if (sym.visibility<>vis_hidden) then
begin
appendsym(list,sym);
{ if we ever write this procdef again for some reason (this
can happen with DWARF), then we want to write all the
parasyms again as well. }
sym.isdbgwritten:=false;
end;
end;
end;
procedure TDebugInfo.write_symtable_syms(list:TAsmList;st:TSymtable);
var
i : longint;
sym : tsym;
begin
case st.symtabletype of
staticsymtable :
list.concat(tai_comment.Create(strpnew('Syms - Begin Staticsymtable')));
globalsymtable :
list.concat(tai_comment.Create(strpnew('Syms - Begin unit '+st.name^+' has index '+tostr(st.moduleid))));
else
;
end;
for i:=0 to st.SymList.Count-1 do
begin
sym:=tsym(st.SymList[i]);
if (sym.visibility<>vis_hidden) and
(not sym.isdbgwritten) and
{ avoid all generic symbols }
not (sp_generic_dummy in sym.symoptions) and
not ((sym.typ=typesym) and assigned(ttypesym(sym).typedef) and
(df_generic in ttypesym(sym).typedef.defoptions)) then
appendsym(list,sym);
end;
case st.symtabletype of
staticsymtable :
list.concat(tai_comment.Create(strpnew('Syms - End Staticsymtable')));
globalsymtable :
list.concat(tai_comment.Create(strpnew('Syms - End unit '+st.name^+' has index '+tostr(st.moduleid))));
else
;
end;
end;
procedure TDebugInfo.write_symtable_procdefs(list:TAsmList;st:TSymtable);
var
i : longint;
def : tdef;
begin
for i:=0 to st.DefList.Count-1 do
begin
def:=tdef(st.DefList[i]);
case def.typ of
procdef :
begin
appendprocdef(list,tprocdef(def));
if assigned(tprocdef(def).localst) then
write_symtable_procdefs(list,tprocdef(def).localst);
end;
objectdef,recorddef :
begin
write_symtable_procdefs(list,tabstractrecorddef(def).symtable);
end;
else
;
end;
end;
end;
procedure TDebugInfo.reset_unit_type_info;
var
hp : tmodule;
begin
hp:=tmodule(loaded_units.first);
while assigned(hp) do
begin
hp.is_dbginfo_written:=false;
hp:=tmodule(hp.next);
end;
end;
procedure TDebugInfo.write_used_unit_type_info(list:TAsmList;hp:tmodule);
var
pu : tused_unit;
begin
pu:=tused_unit(hp.used_units.first);
while assigned(pu) do
begin
if not pu.u.is_dbginfo_written and not assigned(pu.u.package) then
begin
{ prevent infinte loop for circular dependencies }
pu.u.is_dbginfo_written:=true;
{ write type info from used units, use a depth first
strategy to reduce the recursion in writing all
dependent stabs }
write_used_unit_type_info(list,pu.u);
if assigned(pu.u.globalsymtable) then
write_symtable_defs(list,pu.u.globalsymtable);
end;
pu:=tused_unit(pu.next);
end;
end;
{****************************************************************************
Init / Done
****************************************************************************}
procedure InitDebugInfo(hp:tmodule; restore_current_debuginfo : boolean);
begin
if not assigned(CDebugInfo[target_dbg.id]) then
begin
Comment(V_Fatal,'cg_f_debuginfo_output_not_supported');
exit;
end;
{$ifndef llvm}
hp.DebugInfo:=CDebugInfo[target_dbg.id].Create;
{$else}
{ we can't override the assignment of target_dbg with the LLVM class,
because we still need to know whether to tell LLVM to generate
DWARFv2/3/4/5/... }
hp.DebugInfo:=CDebugInfo[dbg_llvm].Create;
{$endif}
if restore_current_debuginfo then
begin
if current_debuginfo=nil then
current_debuginfo:=tdebuginfo(hp.DebugInfo)
else
internalerror(2012032101);
end;
end;
procedure DoneDebugInfo(hp:tmodule;var current_debuginfo_reset : boolean);
begin
current_debuginfo_reset:=false;
if assigned(hp.DebugInfo) then
begin
if hp.DebugInfo=current_debuginfo then
begin
current_debuginfo:=nil;
current_debuginfo_reset:=true;
end;
hp.DebugInfo.Free;
hp.DebugInfo:=nil;
end;
end;
procedure RegisterDebugInfo(const r:tdbginfo;c:TDebugInfoClass);
var
t : tdbg;
begin
t:=r.id;
if assigned(dbginfos[t]) then
writeln('Warning: DebugInfo is already registered!')
else
Getmem(dbginfos[t],sizeof(tdbginfo));
dbginfos[t]^:=r;
CDebugInfo[t]:=c;
end;
const
dbg_none_info : tdbginfo =
(
id : dbg_none;
idtxt : 'NONE';
);
initialization
RegisterDebugInfo(dbg_none_info,TDebugInfo);
end.