From 44e79eb0d4a135531f7a0c90a8215d1c6ab93719 Mon Sep 17 00:00:00 2001 From: Pascal Riekenberg <pascal@riekenberg.eu> Date: Tue, 19 Apr 2022 23:28:26 +0200 Subject: [PATCH 1/4] First attempt: Name threads for debugging --- components/fpdebug/fpdbgclasses.pp | 9 +++++++ components/fpdebug/fpdbgwinclasses.pas | 25 +++++++++++++++++++ .../lazdebuggerfp/fpdebugdebugger.pas | 8 +++--- 3 files changed, 38 insertions(+), 4 deletions(-) diff --git a/components/fpdebug/fpdbgclasses.pp b/components/fpdebug/fpdbgclasses.pp index bb1361ba48..9980386e4f 100644 --- a/components/fpdebug/fpdbgclasses.pp +++ b/components/fpdebug/fpdbgclasses.pp @@ -211,11 +211,13 @@ type FStoreStepStartAddr, FStoreStepEndAddr: TDBGPtr; FStoreStepSrcLineNo: integer; FStoreStepFuncAddr: TDBGPtr; + FThreadNum: Integer; procedure LoadRegisterValues; virtual; property Process: TDbgProcess read FProcess; function ResetInstructionPointerAfterBreakpoint: boolean; virtual; abstract; procedure DoBeforeBreakLocationMapChange; // A new location added / or a location removed => memory will change procedure ValidateRemovedBreakPointInfo; + function GetName: String; virtual; public constructor Create(const AProcess: TDbgProcess; const AID: Integer; const AHandle: THandle); virtual; procedure DoBeforeProcessLoop; @@ -260,6 +262,8 @@ type procedure StoreStepInfo(AnAddr: TDBGPtr = 0); property ID: Integer read FID; property Handle: THandle read FHandle; + property Name: String read GetName; + property ThreadNum: Integer read FThreadNum; property NextIsSingleStep: boolean read FNextIsSingleStep write FNextIsSingleStep; property RegisterValueList: TDbgRegisterValueList read GetRegisterValueList; property CallStackEntryList: TDbgCallstackEntryList read FCallStackEntryList; @@ -2898,6 +2902,11 @@ begin FPausedAtRemovedBreakPointState := rbUnknown; end; +function TDbgThread.GetName: String; +begin + Result := 'Thread ' + IntToStr(FID); +end; + constructor TDbgThread.Create(const AProcess: TDbgProcess; const AID: Integer; const AHandle: THandle); begin FID := AID; diff --git a/components/fpdebug/fpdbgwinclasses.pas b/components/fpdebug/fpdbgwinclasses.pas index 4b3359576d..229fa1bb5e 100644 --- a/components/fpdebug/fpdbgwinclasses.pas +++ b/components/fpdebug/fpdbgwinclasses.pas @@ -122,6 +122,8 @@ uses FpDbgCommon, FpdMemoryTools, FpErrorMessages; type + PPWSTR = ^PWSTR; + TGetThreadDescription = function(threadHandle: THandle; name: PPWSTR): HResult; stdcall; TFpWinCtxFlags = (cfSkip, cfControl, cfFull); TFpContextChangeFlag = (ccfControl, ccfInteger); @@ -143,6 +145,7 @@ type procedure LoadRegisterValues; override; function GetFpThreadContext(var AStorage: TFpContext; out ACtxPtr: PFpContext; ACtxFlags: TFpWinCtxFlags): Boolean; function SetFpThreadContext(ACtxPtr: PFpContext; ACtxFlags: TFpWinCtxFlags = cfSkip): Boolean; + function GetName: String; override; public procedure Suspend; procedure SuspendForStepOverBreakPoint; @@ -237,6 +240,8 @@ implementation var DBG_VERBOSE, DBG_WARNINGS, FPDBG_WINDOWS: PLazLoggerLogGroup; + KernelHandle : THandle; + GetThreadDescription: TGetThreadDescription; {$ifdef cpux86_64} const @@ -1594,6 +1599,22 @@ begin DebugLn(DBG_WARNINGS and (not Result), ['Unable to set Context for ', ID, ': ', GetLastErrorText]); end; +function TDbgWinThread.GetName: String; +var + n: PWSTR; +begin + if Assigned(GetThreadDescription) then begin + if Succeeded(GetThreadDescription(Handle, @n)) then begin + Result := WideCharToString(n); + LocalFree(HLOCAL(n)); + if Result = '' then + Result := inherited GetName; + end else + Result := inherited GetName; + end else + Result := inherited GetName; +end; + procedure TDbgWinThread.Suspend; var r: DWORD; @@ -1925,5 +1946,9 @@ initialization TX86AsmDecoder )); + KernelHandle := GetModuleHandle(KernelDLL); + if KernelHandle <> 0 then + GetThreadDescription := TGetThreadDescription(GetProcAddress(KernelHandle, 'GetThreadDescription')); + end. diff --git a/components/lazdebuggers/lazdebuggerfp/fpdebugdebugger.pas b/components/lazdebuggers/lazdebuggerfp/fpdebugdebugger.pas index 23ed1e79d5..400f3530f2 100644 --- a/components/lazdebuggers/lazdebuggerfp/fpdebugdebugger.pas +++ b/components/lazdebuggers/lazdebuggerfp/fpdebugdebugger.pas @@ -916,21 +916,21 @@ begin if Assigned(CallStack) and (CallStack.Count > 0) then begin c := CallStack.Items[0]; if t = nil then begin - n := Threads.CurrentThreads.CreateEntry(c.AnAddress, nil, c.FunctionName, c.SourceFile, '', c.Line, FpThr.ID, 'Thread ' + IntToStr(FpThr.ID), 'paused'); + n := Threads.CurrentThreads.CreateEntry(c.AnAddress, nil, c.FunctionName, c.SourceFile, '', c.Line, FpThr.ID, FpThr.Name, 'paused'); Threads.CurrentThreads.Add(n); n.Free; end else - t.Init(c.AnAddress, nil, c.FunctionName, c.SourceFile, '', c.Line, FpThr.ID, 'Thread ' + IntToStr(FpThr.ID), 'paused'); + t.Init(c.AnAddress, nil, c.FunctionName, c.SourceFile, '', c.Line, FpThr.ID, FpThr.Name, 'paused'); end else begin if t = nil then begin - n := Threads.CurrentThreads.CreateEntry(0, nil, '', '', '', 0, FpThr.ID, 'Thread ' + IntToStr(FpThr.ID), 'paused'); + n := Threads.CurrentThreads.CreateEntry(0, nil, '', '', '', 0, FpThr.ID, FpThr.Name, 'paused'); Threads.CurrentThreads.Add(n); n.Free; end else - t.Init(0, nil, '', '', '', 0, FpThr.ID, 'Thread ' + IntToStr(FpThr.ID), 'paused'); + t.Init(0, nil, '', '', '', 0, FpThr.ID, FpThr.Name, 'paused'); end; end; From a70f8d70931ed3a426b06910c138b5f31f326a1e Mon Sep 17 00:00:00 2001 From: Pascal Riekenberg <pascal@riekenberg.eu> Date: Tue, 19 Apr 2022 23:28:26 +0200 Subject: [PATCH 2/4] First attempt: Name threads for debugging --- components/fpdebug/fpdbgclasses.pp | 9 +++++++ components/fpdebug/fpdbgwinclasses.pas | 25 +++++++++++++++++++ .../lazdebuggerfp/fpdebugdebugger.pas | 8 +++--- 3 files changed, 38 insertions(+), 4 deletions(-) diff --git a/components/fpdebug/fpdbgclasses.pp b/components/fpdebug/fpdbgclasses.pp index 67323d190c..6b7a67eee1 100644 --- a/components/fpdebug/fpdbgclasses.pp +++ b/components/fpdebug/fpdbgclasses.pp @@ -211,11 +211,13 @@ type FStoreStepStartAddr, FStoreStepEndAddr: TDBGPtr; FStoreStepSrcLineNo: integer; FStoreStepFuncAddr: TDBGPtr; + FThreadNum: Integer; procedure LoadRegisterValues; virtual; property Process: TDbgProcess read FProcess; function ResetInstructionPointerAfterBreakpoint: boolean; virtual; abstract; procedure DoBeforeBreakLocationMapChange; // A new location added / or a location removed => memory will change procedure ValidateRemovedBreakPointInfo; + function GetName: String; virtual; public constructor Create(const AProcess: TDbgProcess; const AID: Integer; const AHandle: THandle); virtual; procedure DoBeforeProcessLoop; @@ -260,6 +262,8 @@ type procedure StoreStepInfo(AnAddr: TDBGPtr = 0); property ID: Integer read FID; property Handle: THandle read FHandle; + property Name: String read GetName; + property ThreadNum: Integer read FThreadNum; property NextIsSingleStep: boolean read FNextIsSingleStep write FNextIsSingleStep; property RegisterValueList: TDbgRegisterValueList read GetRegisterValueList; property CallStackEntryList: TDbgCallstackEntryList read FCallStackEntryList; @@ -2910,6 +2914,11 @@ begin FPausedAtRemovedBreakPointState := rbUnknown; end; +function TDbgThread.GetName: String; +begin + Result := 'Thread ' + IntToStr(FID); +end; + constructor TDbgThread.Create(const AProcess: TDbgProcess; const AID: Integer; const AHandle: THandle); begin FID := AID; diff --git a/components/fpdebug/fpdbgwinclasses.pas b/components/fpdebug/fpdbgwinclasses.pas index 4b3359576d..229fa1bb5e 100644 --- a/components/fpdebug/fpdbgwinclasses.pas +++ b/components/fpdebug/fpdbgwinclasses.pas @@ -122,6 +122,8 @@ uses FpDbgCommon, FpdMemoryTools, FpErrorMessages; type + PPWSTR = ^PWSTR; + TGetThreadDescription = function(threadHandle: THandle; name: PPWSTR): HResult; stdcall; TFpWinCtxFlags = (cfSkip, cfControl, cfFull); TFpContextChangeFlag = (ccfControl, ccfInteger); @@ -143,6 +145,7 @@ type procedure LoadRegisterValues; override; function GetFpThreadContext(var AStorage: TFpContext; out ACtxPtr: PFpContext; ACtxFlags: TFpWinCtxFlags): Boolean; function SetFpThreadContext(ACtxPtr: PFpContext; ACtxFlags: TFpWinCtxFlags = cfSkip): Boolean; + function GetName: String; override; public procedure Suspend; procedure SuspendForStepOverBreakPoint; @@ -237,6 +240,8 @@ implementation var DBG_VERBOSE, DBG_WARNINGS, FPDBG_WINDOWS: PLazLoggerLogGroup; + KernelHandle : THandle; + GetThreadDescription: TGetThreadDescription; {$ifdef cpux86_64} const @@ -1594,6 +1599,22 @@ begin DebugLn(DBG_WARNINGS and (not Result), ['Unable to set Context for ', ID, ': ', GetLastErrorText]); end; +function TDbgWinThread.GetName: String; +var + n: PWSTR; +begin + if Assigned(GetThreadDescription) then begin + if Succeeded(GetThreadDescription(Handle, @n)) then begin + Result := WideCharToString(n); + LocalFree(HLOCAL(n)); + if Result = '' then + Result := inherited GetName; + end else + Result := inherited GetName; + end else + Result := inherited GetName; +end; + procedure TDbgWinThread.Suspend; var r: DWORD; @@ -1925,5 +1946,9 @@ initialization TX86AsmDecoder )); + KernelHandle := GetModuleHandle(KernelDLL); + if KernelHandle <> 0 then + GetThreadDescription := TGetThreadDescription(GetProcAddress(KernelHandle, 'GetThreadDescription')); + end. diff --git a/components/lazdebuggers/lazdebuggerfp/fpdebugdebugger.pas b/components/lazdebuggers/lazdebuggerfp/fpdebugdebugger.pas index 23ed1e79d5..400f3530f2 100644 --- a/components/lazdebuggers/lazdebuggerfp/fpdebugdebugger.pas +++ b/components/lazdebuggers/lazdebuggerfp/fpdebugdebugger.pas @@ -916,21 +916,21 @@ begin if Assigned(CallStack) and (CallStack.Count > 0) then begin c := CallStack.Items[0]; if t = nil then begin - n := Threads.CurrentThreads.CreateEntry(c.AnAddress, nil, c.FunctionName, c.SourceFile, '', c.Line, FpThr.ID, 'Thread ' + IntToStr(FpThr.ID), 'paused'); + n := Threads.CurrentThreads.CreateEntry(c.AnAddress, nil, c.FunctionName, c.SourceFile, '', c.Line, FpThr.ID, FpThr.Name, 'paused'); Threads.CurrentThreads.Add(n); n.Free; end else - t.Init(c.AnAddress, nil, c.FunctionName, c.SourceFile, '', c.Line, FpThr.ID, 'Thread ' + IntToStr(FpThr.ID), 'paused'); + t.Init(c.AnAddress, nil, c.FunctionName, c.SourceFile, '', c.Line, FpThr.ID, FpThr.Name, 'paused'); end else begin if t = nil then begin - n := Threads.CurrentThreads.CreateEntry(0, nil, '', '', '', 0, FpThr.ID, 'Thread ' + IntToStr(FpThr.ID), 'paused'); + n := Threads.CurrentThreads.CreateEntry(0, nil, '', '', '', 0, FpThr.ID, FpThr.Name, 'paused'); Threads.CurrentThreads.Add(n); n.Free; end else - t.Init(0, nil, '', '', '', 0, FpThr.ID, 'Thread ' + IntToStr(FpThr.ID), 'paused'); + t.Init(0, nil, '', '', '', 0, FpThr.ID, FpThr.Name, 'paused'); end; end; From 5897bdfc8f2d54646f92cbdc1998584c08d62733 Mon Sep 17 00:00:00 2001 From: Pascal Riekenberg <pascal@riekenberg.eu> Date: Thu, 12 May 2022 09:57:04 +0200 Subject: [PATCH 3/4] adding support for setting name by debugger exception --- components/fpdebug/fpdbgwinclasses.pas | 42 ++++++++++++++++++-------- 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/components/fpdebug/fpdbgwinclasses.pas b/components/fpdebug/fpdbgwinclasses.pas index 229fa1bb5e..26b1d6b555 100644 --- a/components/fpdebug/fpdbgwinclasses.pas +++ b/components/fpdebug/fpdbgwinclasses.pas @@ -123,7 +123,6 @@ uses type PPWSTR = ^PWSTR; - TGetThreadDescription = function(threadHandle: THandle; name: PPWSTR): HResult; stdcall; TFpWinCtxFlags = (cfSkip, cfControl, cfFull); TFpContextChangeFlag = (ccfControl, ccfInteger); @@ -136,6 +135,8 @@ type FIsSuspended: Boolean; FIsSkippingBreakPoint: Boolean; FIsSkippingBreakPointAddress: TDBGPtr; + FDoNotPollName: Boolean; + FName: String; protected FThreadContextChanged: boolean; FThreadContextChangeFlags: TFpContextChangeFlags; @@ -240,8 +241,6 @@ implementation var DBG_VERBOSE, DBG_WARNINGS, FPDBG_WINDOWS: PLazLoggerLogGroup; - KernelHandle : THandle; - GetThreadDescription: TGetThreadDescription; {$ifdef cpux86_64} const @@ -316,6 +315,7 @@ var _Wow64GetThreadContext: function (hThread: THandle; var lpContext: WOW64_CONTEXT): BOOL; stdcall = nil; _Wow64SetThreadContext: function (hThread: THandle; const lpContext: WOW64_CONTEXT): BOOL; stdcall = nil; _DebugBreakProcess: function(Process:HANDLE): WINBOOL; stdcall = nil; + _GetThreadDescription: function(hThread: THandle; ppszThreadDescription: PPWSTR): HResult; stdcall = nil; procedure LoadKernelEntryPoints; var @@ -333,6 +333,7 @@ begin Pointer(_DebugActiveProcess) := GetProcAddress(hMod, 'DebugActiveProcess'); Pointer(_GetFinalPathNameByHandle) := GetProcAddress(hMod, 'GetFinalPathNameByHandleW'); Pointer(_DebugBreakProcess) := GetProcAddress(hMod, 'DebugBreakProcess'); + Pointer(_GetThreadDescription) := GetProcAddress(hMod, 'GetThreadDescription'); {$ifdef cpux86_64} Pointer(_IsWow64Process) := GetProcAddress(hMod, 'IsWow64Process'); Pointer(_Wow64GetThreadContext) := GetProcAddress(hMod, 'Wow64GetThreadContext'); @@ -346,6 +347,7 @@ begin DebugLn(DBG_WARNINGS and (_DebugActiveProcess = nil), ['WARNING: Failed to get DebugActiveProcess']); DebugLn(DBG_WARNINGS and (_GetFinalPathNameByHandle = nil), ['WARNING: Failed to get GetFinalPathNameByHandle']); DebugLn(DBG_WARNINGS and (_DebugBreakProcess = nil), ['WARNING: Failed to get DebugBreakProcess']); + DebugLn(DBG_WARNINGS and (_GetThreadDescription = nil), ['WARNING: Failed to get GetThreadDescription']); {$ifdef cpux86_64} DebugLn(DBG_WARNINGS and (_IsWow64Process = nil), ['WARNING: Failed to get IsWow64Process']); DebugLn(DBG_WARNINGS and (_Wow64GetThreadContext = nil), ['WARNING: Failed to get Wow64GetThreadContext']); @@ -1251,8 +1253,11 @@ function TDbgWinProcess.AnalyseDebugEvent(AThread: TDbgThread): TFPDEvent; OnDebugOutputEvent(Self, AEvent.dwProcessId, AEvent.dwThreadId, S); end; +const + EXCEPTION_SET_THREADNAME = $406D1388; var InterceptAtFirst: Boolean; + threadname: String; begin if AThread <> nil then TDbgWinThread(AThread).EndSingleStepOverBreakPoint; @@ -1279,6 +1284,17 @@ begin end; EXCEPTION_SINGLE_STEP, STATUS_WX86_SINGLE_STEP: begin result := deBreakpoint; + end; + EXCEPTION_SET_THREADNAME: begin + if AThread <> nil then begin + if not ReadString(TDbgPtr(MDebugEvent.Exception.ExceptionRecord.ExceptionInformation[1]), 200, threadname) then + threadname := 'error getting threadname'; + with TDbgWinThread(AThread) do begin + FName := threadname; + FDoNotPollName := True; + end; + end; + result := deInternalContinue; end else begin HandleException(MDebugEvent, InterceptAtFirst); @@ -1603,15 +1619,17 @@ function TDbgWinThread.GetName: String; var n: PWSTR; begin - if Assigned(GetThreadDescription) then begin - if Succeeded(GetThreadDescription(Handle, @n)) then begin - Result := WideCharToString(n); - LocalFree(HLOCAL(n)); - if Result = '' then - Result := inherited GetName; - end else - Result := inherited GetName; - end else + Result := ''; + if FDoNotPollName then begin + Result := FName; + end else begin + if _GetThreadDescription <> nil then + if Succeeded(_GetThreadDescription(Handle, @n)) then begin + Result := WideCharToString(n); + LocalFree(HLOCAL(n)); + end; + end; + if Result = '' then Result := inherited GetName; end; From d7eb52797dbbcf68415df422faebc85a966cf1a3 Mon Sep 17 00:00:00 2001 From: Pascal Riekenberg <pascal@riekenberg.eu> Date: Thu, 12 May 2022 10:22:10 +0200 Subject: [PATCH 4/4] missing changes from last commit --- components/fpdebug/fpdbgwinclasses.pas | 4 ---- 1 file changed, 4 deletions(-) diff --git a/components/fpdebug/fpdbgwinclasses.pas b/components/fpdebug/fpdbgwinclasses.pas index 26b1d6b555..5fec0ee2bf 100644 --- a/components/fpdebug/fpdbgwinclasses.pas +++ b/components/fpdebug/fpdbgwinclasses.pas @@ -1964,9 +1964,5 @@ initialization TX86AsmDecoder )); - KernelHandle := GetModuleHandle(KernelDLL); - if KernelHandle <> 0 then - GetThreadDescription := TGetThreadDescription(GetProcAddress(KernelHandle, 'GetThreadDescription')); - end.