mirror of https://github.com/red-prig/fpPS4.git
330 lines
5.8 KiB
Plaintext
330 lines
5.8 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);
|
|
|
|
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);
|
|
begin
|
|
thread_suspend_all(p_host_ipc_td);
|
|
|
|
Writeln(StdErr,str);
|
|
p_host_ipc.error(str);
|
|
|
|
print_backtrace_td(StdErr);
|
|
end;
|
|
|
|
end.
|
|
|