FPPS4/sys/kern/subr_backtrace.pas

335 lines
5.9 KiB
Plaintext

unit subr_backtrace;
{$mode ObjFPC}{$H+}
{$CALLING SysV_ABI_CDecl}
interface
uses
mqueue,
kern_thr;
procedure print_frame(var f:text;frame:Pointer);
procedure print_backtrace(var f:text;rip,rbp:Pointer;skipframes:sizeint);
procedure print_backtrace_td(var f:text);
procedure print_error_td(const str:shortstring;resumable:Boolean=False);
implementation
uses
vmparam,
systm,
sys_bootparam,
kern_named_id,
subr_dynlib,
elf_nid_utils,
ps4libdoc;
function IS_TRAP_FUNC(rip:qword):Boolean; external;
function IS_JIT_FUNC (rip:qword):Boolean; external;
function CaptureBacktrace(td:p_kthread;rbp:PPointer;skipframes,count:sizeint;frames:PCodePointer):sizeint;
label
_next;
var
adr:Pointer;
procedure push; inline;
begin
if (skipframes<>0) then
begin
Dec(skipframes);
end else
if (count<>0) then
begin
frames[0]:=adr;
Dec(count);
Inc(frames);
Inc(Result);
end;
end;
begin
Result:=0;
while (rbp<>nil) and
(rbp<>Pointer(-1)) and
(count<>0) do
begin
adr:=fuptr(rbp[1]);
rbp:=fuptr(rbp[0]);
_next:
if (adr<>nil) then
begin
push;
end else
begin
Break;
end;
if (td<>nil) and
(IS_TRAP_FUNC(QWORD(adr)) or IS_JIT_FUNC(QWORD(adr))) then
begin
adr:=Pointer(td^.td_frame.tf_rip);
rbp:=Pointer(td^.td_frame.tf_rbp);
goto _next;
end;
end;
end;
type
TLQRec=record
Base :Pointer;
Addr :Pointer;
LastAdr:Pointer;
LastNid:QWORD;
end;
Function trav_proc(h_entry:p_sym_hash_entry;var r:TLQRec):Integer;
var
adr:Pointer;
begin
Result:=0;
adr:=r.Base+fuptr(h_entry^.sym.st_value);
if (adr<=r.Addr) then
if (adr>r.LastAdr) then
begin
r.LastAdr:=adr;
r.LastNid:=fuptr(h_entry^.nid);
Result:=1;
end;
end;
Function find_proc_lib_entry(lib_entry:p_Lib_Entry;var r:TLQRec):Integer;
var
h_entry:p_sym_hash_entry;
begin
Result:=0;
h_entry:=fuptr(lib_entry^.syms.tqh_first);
while (h_entry<>nil) do
begin
Result:=Result+trav_proc(h_entry,r);
h_entry:=fuptr(h_entry^.link.tqe_next);
end;
end;
Function find_proc_obj(obj:p_lib_info;var r:TLQRec):Integer;
var
lib_entry:p_Lib_Entry;
begin
Result:=0;
lib_entry:=fuptr(obj^.lib_table.tqh_first);
while (lib_entry<>nil) do
begin
Result:=Result+find_proc_lib_entry(lib_entry,r);
lib_entry:=fuptr(lib_entry^.link.tqe_next);
end;
end;
type
TDynlibLineInfo=record
func :shortstring;
source :t_id_name;
base_addr:ptruint;
func_addr:ptruint;
end;
function GetDynlibLineInfo(addr:ptruint;var info:TDynlibLineInfo):boolean;
var
obj:p_lib_info;
r:TLQRec;
adr:QWORD;
lock:Boolean;
begin
Result:=False;
lock:=False;
if not dynlibs_locked then
begin
dynlibs_lock;
lock:=True;
end;
obj:=find_obj_by_addr_safe(Pointer(addr));
if (obj<>nil) then
begin
r.LastAdr:=nil;
r.LastNid:=0;
r.Addr:=Pointer(addr);
r.Base:=fuptr(obj^.map_base);
info.base_addr:=QWORD(r.Base);
info.source:=Default(t_id_name);
copyin_nofault(@obj^.name,@info.source,SizeOf(t_id_name));
if (info.source='') then
begin
copyin_nofault(obj^.lib_path,@info.source,SizeOf(t_id_name));
end;
if (find_proc_obj(obj,r)<>0) then
begin
info.func:=ps4libdoc.GetFunctName(r.LastNid);
if (info.func='Unknow') then
begin
info.func:=EncodeValue64(r.LastNid);
end;
info.func_addr:=QWORD(r.LastAdr);
Result:=True;
end else
begin
info.func_addr:=0;
adr:=QWORD(obj^.init_proc_addr);
if (adr<=QWORD(r.Addr)) then
if (adr>info.func_addr) then
begin
info.func:='dtInit';
info.func_addr:=adr;
Result:=True;
end;
adr:=QWORD(obj^.fini_proc_addr);
if (adr<=QWORD(r.Addr)) then
if (adr>info.func_addr) then
begin
info.func:='dtFini';
info.func_addr:=adr;
Result:=True;
end;
adr:=QWORD(obj^.entry_addr);
if (adr<=QWORD(r.Addr)) then
if (adr>info.func_addr) then
begin
info.func:='Entry';
info.func_addr:=adr;
Result:=True;
end;
end;
end;
if lock then
begin
dynlibs_unlock;
end;
end;
function find_obj_by_handle(id:Integer):p_lib_info;
var
obj:p_lib_info;
begin
Result:=nil;
obj:=TAILQ_FIRST(@dynlibs_info.obj_list);
while (obj<>nil) do
begin
if (obj^.id=id) then
begin
Exit(obj);
end;
//
obj:=TAILQ_NEXT(obj,@obj^.link);
end;
end;
procedure print_frame(var f:text;frame:Pointer);
var
info:TDynlibLineInfo;
offset1:QWORD;
offset2:QWORD;
begin
if is_guest_addr(ptruint(frame)) then
begin
info:=Default(TDynlibLineInfo);
if GetDynlibLineInfo(ptruint(frame),info) then
begin
offset1:=QWORD(frame)-QWORD(info.base_addr);
offset2:=QWORD(frame)-QWORD(info.func_addr);
Writeln(f,' offset $',HexStr(offset1 shr 24,5),'|',HexStr(offset1,6),' ',info.source,':',info.func,'+$',HexStr(offset2,6));
end else
begin
if (info.base_addr<>0) then
begin
offset1:=QWORD(frame)-QWORD(info.base_addr);
Writeln(f,' offset $',HexStr(offset1 shr 24,5),'|',HexStr(offset1,6),' ',info.source);
end else
begin
Writeln(f,' $',HexStr(frame),' ',info.source);
end;
end;
end else
if (BackTraceStrFunc<>nil) then
begin
Writeln(f,BackTraceStrFunc(frame));
end else
begin
Writeln(f,' $',HexStr(frame));
end;
end;
procedure print_backtrace(var f:text;rip,rbp:Pointer;skipframes:sizeint);
var
i,count:sizeint;
frames:array [0..255] of codepointer;
begin
count:=max_frame_dump;
count:=30;
if (skipframes>0) then
begin
Dec(skipframes);
end else
begin
print_frame(f,rip);
end;
count:=CaptureBacktrace(curkthread,rbp,skipframes,count,@frames[0]);
if (count<>0) then
for i:=0 to count-1 do
begin
print_frame(f,frames[i]);
end;
end;
procedure print_backtrace_td(var f:text);
var
td:p_kthread;
begin
td:=curkthread;
if (td=nil) then Exit;
//
print_backtrace(stderr,Pointer(td^.td_frame.tf_rip),Pointer(td^.td_frame.tf_rbp),0);
end;
procedure print_error_td(const str:shortstring;resumable:Boolean=False);
begin
thread_suspend_all(p_host_ipc_td);
Writeln(StdErr,str);
print_backtrace_td(StdErr);
p_host_ipc.error(str);
if resumable then
begin
thread_resume_all(p_host_ipc_td);
end;
end;
end.