mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-08-17 19:59:17 +02:00
fpdebug: Handling of multiple threads (Linux)
git-svn-id: trunk@58095 -
This commit is contained in:
parent
20b7bb3936
commit
50cce1a623
@ -210,6 +210,7 @@ const
|
|||||||
EFL = 14;
|
EFL = 14;
|
||||||
UESP = 15;
|
UESP = 15;
|
||||||
XSS = 16;
|
XSS = 16;
|
||||||
|
__WALL = $40000000;
|
||||||
|
|
||||||
NT_PRSTATUS = 1;
|
NT_PRSTATUS = 1;
|
||||||
NT_PRFPREG = 2;
|
NT_PRFPREG = 2;
|
||||||
@ -250,6 +251,7 @@ type
|
|||||||
FIsTerminating: boolean;
|
FIsTerminating: boolean;
|
||||||
FExceptionSignal: PtrUInt;
|
FExceptionSignal: PtrUInt;
|
||||||
FMasterPtyFd: cint;
|
FMasterPtyFd: cint;
|
||||||
|
FCurrentThreadId: THandle;
|
||||||
{$ifndef VER2_6}
|
{$ifndef VER2_6}
|
||||||
procedure OnForkEvent(Sender : TObject);
|
procedure OnForkEvent(Sender : TObject);
|
||||||
{$endif}
|
{$endif}
|
||||||
@ -308,6 +310,9 @@ procedure OnForkEvent;
|
|||||||
var
|
var
|
||||||
ConsoleTtyFd: cint;
|
ConsoleTtyFd: cint;
|
||||||
begin
|
begin
|
||||||
|
if fpPTrace(PTRACE_TRACEME, 0, nil, nil) <> 0 then
|
||||||
|
writeln('Failed to start trace of process. Errcode: '+inttostr(fpgeterrno));
|
||||||
|
|
||||||
if GConsoleTty<>'' then
|
if GConsoleTty<>'' then
|
||||||
begin
|
begin
|
||||||
ConsoleTtyFd:=FpOpen(GConsoleTty,O_RDWR+O_NOCTTY);
|
ConsoleTtyFd:=FpOpen(GConsoleTty,O_RDWR+O_NOCTTY);
|
||||||
@ -333,8 +338,6 @@ begin
|
|||||||
writeln('Failed to login to tty. Errcode: '+inttostr(fpgeterrno)+' - '+inttostr(GSlavePTyFd));
|
writeln('Failed to login to tty. Errcode: '+inttostr(fpgeterrno)+' - '+inttostr(GSlavePTyFd));
|
||||||
end;
|
end;
|
||||||
|
|
||||||
if fpPTrace(PTRACE_TRACEME, 0, nil, nil) <> 0 then
|
|
||||||
writeln('Failed to start trace of process. Errcode: '+inttostr(fpgeterrno));
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TDbgLinuxThread.GetDebugRegOffset(ind: byte): pointer;
|
function TDbgLinuxThread.GetDebugRegOffset(ind: byte): pointer;
|
||||||
@ -359,7 +362,7 @@ var
|
|||||||
e: integer;
|
e: integer;
|
||||||
begin
|
begin
|
||||||
fpseterrno(0);
|
fpseterrno(0);
|
||||||
AVal := PtrUInt(fpPTrace(PTRACE_PEEKUSR, Process.ProcessID, GetDebugRegOffset(ind), nil));
|
AVal := PtrUInt(fpPTrace(PTRACE_PEEKUSR, ID, GetDebugRegOffset(ind), nil));
|
||||||
e := fpgeterrno;
|
e := fpgeterrno;
|
||||||
if e <> 0 then
|
if e <> 0 then
|
||||||
begin
|
begin
|
||||||
@ -372,7 +375,7 @@ end;
|
|||||||
|
|
||||||
function TDbgLinuxThread.WriteDebugReg(ind: byte; AVal: PtrUInt): boolean;
|
function TDbgLinuxThread.WriteDebugReg(ind: byte; AVal: PtrUInt): boolean;
|
||||||
begin
|
begin
|
||||||
if fpPTrace(PTRACE_POKEUSR, Process.ProcessID, GetDebugRegOffset(ind), pointer(AVal)) = -1 then
|
if fpPTrace(PTRACE_POKEUSR, ID, GetDebugRegOffset(ind), pointer(AVal)) = -1 then
|
||||||
begin
|
begin
|
||||||
log('Failed to write dr'+inttostr(ind)+'-debug register. Errcode: '+inttostr(fpgeterrno));
|
log('Failed to write dr'+inttostr(ind)+'-debug register. Errcode: '+inttostr(fpgeterrno));
|
||||||
result := false;
|
result := false;
|
||||||
@ -389,9 +392,9 @@ begin
|
|||||||
result := true;
|
result := true;
|
||||||
io.iov_base:=@(FUserRegs.regs32[0]);
|
io.iov_base:=@(FUserRegs.regs32[0]);
|
||||||
io.iov_len:= sizeof(FUserRegs);
|
io.iov_len:= sizeof(FUserRegs);
|
||||||
if fpPTrace(PTRACE_GETREGSET, Process.ProcessID, pointer(PtrUInt(NT_PRSTATUS)), @io) <> 0 then
|
if fpPTrace(PTRACE_GETREGSET, ID, pointer(PtrUInt(NT_PRSTATUS)), @io) <> 0 then
|
||||||
begin
|
begin
|
||||||
log('Failed to read thread registers from processid '+inttostr(Process.ProcessID)+'. Errcode: '+inttostr(fpgeterrno));
|
log('Failed to read thread registers from threadid '+inttostr(ID)+'. Errcode: '+inttostr(fpgeterrno));
|
||||||
result := false;
|
result := false;
|
||||||
end;
|
end;
|
||||||
FUserRegsChanged:=false;
|
FUserRegsChanged:=false;
|
||||||
@ -494,7 +497,7 @@ begin
|
|||||||
io.iov_base:=@(FUserRegs.regs64[0]);
|
io.iov_base:=@(FUserRegs.regs64[0]);
|
||||||
io.iov_len:= sizeof(FUserRegs);
|
io.iov_len:= sizeof(FUserRegs);
|
||||||
|
|
||||||
if fpPTrace(PTRACE_SETREGSET, Process.ProcessID, pointer(PtrUInt(NT_PRSTATUS)), @io) <> 0 then
|
if fpPTrace(PTRACE_SETREGSET, ID, pointer(PtrUInt(NT_PRSTATUS)), @io) <> 0 then
|
||||||
begin
|
begin
|
||||||
log('Failed to set thread registers. Errcode: '+inttostr(fpgeterrno));
|
log('Failed to set thread registers. Errcode: '+inttostr(fpgeterrno));
|
||||||
end;
|
end;
|
||||||
@ -563,9 +566,12 @@ end;
|
|||||||
|
|
||||||
function TDbgLinuxProcess.CreateThread(AthreadIdentifier: THandle; out IsMainThread: boolean): TDbgThread;
|
function TDbgLinuxProcess.CreateThread(AthreadIdentifier: THandle; out IsMainThread: boolean): TDbgThread;
|
||||||
begin
|
begin
|
||||||
IsMainThread:=true;
|
IsMainThread:=False;
|
||||||
if AthreadIdentifier>-1 then
|
if AthreadIdentifier>-1 then
|
||||||
|
begin
|
||||||
|
IsMainThread := AthreadIdentifier=ProcessID;
|
||||||
result := TDbgLinuxThread.Create(Self, AthreadIdentifier, AthreadIdentifier)
|
result := TDbgLinuxThread.Create(Self, AthreadIdentifier, AthreadIdentifier)
|
||||||
|
end
|
||||||
else
|
else
|
||||||
result := nil;
|
result := nil;
|
||||||
end;
|
end;
|
||||||
@ -661,11 +667,11 @@ var
|
|||||||
e: integer;
|
e: integer;
|
||||||
begin
|
begin
|
||||||
errno := 0;
|
errno := 0;
|
||||||
AVal := TDbgPtr(fpPTrace(PTRACE_PEEKDATA, Process.ProcessID, pointer(Adr), nil));
|
AVal := TDbgPtr(fpPTrace(PTRACE_PEEKDATA, FCurrentThreadId, pointer(Adr), nil));
|
||||||
e := fpgeterrno;
|
e := fpgeterrno;
|
||||||
if e <> 0 then
|
if e <> 0 then
|
||||||
begin
|
begin
|
||||||
log('Failed to read data at address '+FormatAddress(Adr)+' from processid '+inttostr(Process.ProcessID)+'. Errcode: '+inttostr(e));
|
log('Failed to read data at address '+FormatAddress(Adr)+' from processid '+inttostr(FCurrentThreadId)+'. Errcode: '+inttostr(e));
|
||||||
result := false;
|
result := false;
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
@ -738,7 +744,7 @@ begin
|
|||||||
if ASize<WordSize then
|
if ASize<WordSize then
|
||||||
begin
|
begin
|
||||||
fpseterrno(0);
|
fpseterrno(0);
|
||||||
pi := TDbgPtr(fpPTrace(PTRACE_PEEKDATA, Process.ProcessID, pointer(AAdress), nil));
|
pi := TDbgPtr(fpPTrace(PTRACE_PEEKDATA, FCurrentThreadId, pointer(AAdress), nil));
|
||||||
e := fpgeterrno;
|
e := fpgeterrno;
|
||||||
if e <> 0 then
|
if e <> 0 then
|
||||||
begin
|
begin
|
||||||
@ -749,7 +755,7 @@ begin
|
|||||||
end;
|
end;
|
||||||
move(AData, pi, ASize);
|
move(AData, pi, ASize);
|
||||||
|
|
||||||
fpPTrace(PTRACE_POKEDATA, Process.ProcessID, pointer(AAdress), pointer(pi));
|
fpPTrace(PTRACE_POKEDATA, FCurrentThreadId, pointer(AAdress), pointer(pi));
|
||||||
e := fpgeterrno;
|
e := fpgeterrno;
|
||||||
if e <> 0 then
|
if e <> 0 then
|
||||||
begin
|
begin
|
||||||
@ -848,11 +854,11 @@ begin
|
|||||||
AThread.NextIsSingleStep:=SingleStep;
|
AThread.NextIsSingleStep:=SingleStep;
|
||||||
AThread.BeforeContinue;
|
AThread.BeforeContinue;
|
||||||
if SingleStep or assigned(FCurrentBreakpoint) then
|
if SingleStep or assigned(FCurrentBreakpoint) then
|
||||||
fpPTrace(PTRACE_SINGLESTEP, ProcessID, pointer(1), pointer(FExceptionSignal))
|
fpPTrace(PTRACE_SINGLESTEP, AThread.ID, pointer(1), pointer(FExceptionSignal))
|
||||||
else if FIsTerminating then
|
else if FIsTerminating then
|
||||||
fpPTrace(PTRACE_KILL, ProcessID, pointer(1), nil)
|
fpPTrace(PTRACE_KILL, AThread.ID, pointer(1), nil)
|
||||||
else
|
else
|
||||||
fpPTrace(PTRACE_CONT, ProcessID, pointer(1), pointer(FExceptionSignal));
|
fpPTrace(PTRACE_CONT, AThread.ID, pointer(1), pointer(FExceptionSignal));
|
||||||
e := fpgeterrno;
|
e := fpgeterrno;
|
||||||
if e <> 0 then
|
if e <> 0 then
|
||||||
begin
|
begin
|
||||||
@ -864,36 +870,75 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
function TDbgLinuxProcess.WaitForDebugEvent(out ProcessIdentifier, ThreadIdentifier: THandle): boolean;
|
function TDbgLinuxProcess.WaitForDebugEvent(out ProcessIdentifier, ThreadIdentifier: THandle): boolean;
|
||||||
|
var
|
||||||
|
PID: THandle;
|
||||||
begin
|
begin
|
||||||
ThreadIdentifier:=1;
|
ThreadIdentifier:=-1;
|
||||||
|
ProcessIdentifier:=-1;
|
||||||
|
|
||||||
ProcessIdentifier:=FpWaitPid(-1, FStatus, 0);
|
PID:=FpWaitPid(-1, FStatus, __WALL);
|
||||||
|
|
||||||
result := ProcessIdentifier<>-1;
|
result := PID<>-1;
|
||||||
if not result then
|
if not result then
|
||||||
Log('Failed to wait for debug event. Errcode: %d', [fpgeterrno]);
|
Log('Failed to wait for debug event. Errcode: %d', [fpgeterrno], dllError)
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
ThreadIdentifier := PID;
|
||||||
|
FCurrentThreadId := PID;
|
||||||
|
|
||||||
|
if not FProcessStarted and (PID <> ProcessID) then
|
||||||
|
Log('ThreadID of main thread does not match the ProcessID', dllDebug);
|
||||||
|
|
||||||
|
ProcessIdentifier := ProcessID;
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TDbgLinuxProcess.AnalyseDebugEvent(AThread: TDbgThread): TFPDEvent;
|
function TDbgLinuxProcess.AnalyseDebugEvent(AThread: TDbgThread): TFPDEvent;
|
||||||
|
|
||||||
|
//var
|
||||||
|
// NewThreadID: culong;
|
||||||
begin
|
begin
|
||||||
FExceptionSignal:=0;
|
FExceptionSignal:=0;
|
||||||
if wifexited(FStatus) or wifsignaled(FStatus) then
|
if wifexited(FStatus) or wifsignaled(FStatus) then
|
||||||
begin
|
begin
|
||||||
SetExitCode(wexitStatus(FStatus));
|
if AThread.ID=ProcessID then
|
||||||
|
begin
|
||||||
result := deExitProcess
|
// Main thread stop -> application exited
|
||||||
|
SetExitCode(wexitStatus(FStatus));
|
||||||
|
result := deExitProcess
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
// Thread stopped, just continue
|
||||||
|
// ToDo: Remove thread from administration
|
||||||
|
result := deInternalContinue;
|
||||||
|
end;
|
||||||
end
|
end
|
||||||
else if WIFSTOPPED(FStatus) then
|
else if WIFSTOPPED(FStatus) then
|
||||||
begin
|
begin
|
||||||
//log('Stopped ',FStatus, ' signal: ',wstopsig(FStatus));
|
//log('Stopped ',FStatus, ' signal: ',wstopsig(FStatus));
|
||||||
TDbgLinuxThread(AThread).ReadThreadState;
|
TDbgLinuxThread(AThread).ReadThreadState;
|
||||||
|
|
||||||
|
if (FStatus >> 8) = (SIGTRAP or (PTRACE_EVENT_CLONE << 8)) then
|
||||||
|
begin
|
||||||
|
// New thread started (stopped in 'parent' thread)
|
||||||
|
Result := deInternalContinue;
|
||||||
|
|
||||||
|
// Usefull in case of debugging:
|
||||||
|
//if fpPTrace(PTRACE_GETEVENTMSG, AThread.ID, nil, @NewThreadID) = -1 then
|
||||||
|
// Log('Failed to retrieve ThreadId of new thread. Errcode: %d', [fpgeterrno], dllInfo);
|
||||||
|
Exit;
|
||||||
|
end;
|
||||||
|
|
||||||
case wstopsig(FStatus) of
|
case wstopsig(FStatus) of
|
||||||
SIGTRAP:
|
SIGTRAP:
|
||||||
begin if not FProcessStarted then
|
begin
|
||||||
|
if not FProcessStarted then
|
||||||
begin
|
begin
|
||||||
result := deCreateProcess;
|
result := deCreateProcess;
|
||||||
FProcessStarted:=true;
|
FProcessStarted:=true;
|
||||||
|
if fpPTrace(PTRACE_SETOPTIONS, ProcessID, nil, Pointer( PTRACE_O_TRACEFORK or PTRACE_O_TRACEVFORK or PTRACE_O_TRACECLONE) ) <> 0 then
|
||||||
|
writeln('Failed to set set trace options. Errcode: '+inttostr(fpgeterrno));
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
result := deBreakpoint;
|
result := deBreakpoint;
|
||||||
@ -932,6 +977,11 @@ begin
|
|||||||
result := deException;
|
result := deException;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
SIGSTOP:
|
||||||
|
begin
|
||||||
|
// New thread (stopped within the new thread)
|
||||||
|
result := deInternalContinue;
|
||||||
|
end
|
||||||
else
|
else
|
||||||
begin
|
begin
|
||||||
ExceptionClass:='Unknown exception code '+inttostr(wstopsig(FStatus));
|
ExceptionClass:='Unknown exception code '+inttostr(wstopsig(FStatus));
|
||||||
|
@ -25,8 +25,23 @@ const
|
|||||||
PTRACE_SETREGS = 13;
|
PTRACE_SETREGS = 13;
|
||||||
PTRACE_GETFPREGS = 14;
|
PTRACE_GETFPREGS = 14;
|
||||||
PTRACE_SETFPREGS = 15;
|
PTRACE_SETFPREGS = 15;
|
||||||
|
PTRACE_SETOPTIONS = $4200;
|
||||||
|
PTRACE_GETEVENTMSG = $4201;
|
||||||
PTRACE_GETREGSET = $4204;
|
PTRACE_GETREGSET = $4204;
|
||||||
PTRACE_SETREGSET = $4205;
|
PTRACE_SETREGSET = $4205;
|
||||||
|
|
||||||
|
PTRACE_EVENT_FORK = 1;
|
||||||
|
PTRACE_EVENT_VFORK = 2;
|
||||||
|
PTRACE_EVENT_CLONE = 3;
|
||||||
|
PTRACE_EVENT_EXEC = 4;
|
||||||
|
PTRACE_EVENT_VFORK_DONE = 5;
|
||||||
|
PTRACE_EVENT_EXIT = 6;
|
||||||
|
PTRACE_EVENT_SECCOMP = 7;
|
||||||
|
PTRACE_EVENT_STOP = 128;
|
||||||
|
|
||||||
|
PTRACE_O_TRACEFORK = 1 << PTRACE_EVENT_FORK;
|
||||||
|
PTRACE_O_TRACEVFORK = 1 << PTRACE_EVENT_VFORK;
|
||||||
|
PTRACE_O_TRACECLONE = 1 << PTRACE_EVENT_CLONE;
|
||||||
{$endif linux}
|
{$endif linux}
|
||||||
PTRACE_ATTACH = 16;
|
PTRACE_ATTACH = 16;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user