From ec57939f4d6b52b15c33bccbbee69e49ce3fdaf0 Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Thu, 30 Oct 2025 13:22:27 +0100 Subject: [PATCH] chore(developer): further debugging for assertion failure Adds breadcrumbs for debug memo to try and narrow down sequence of events leading to assertion failure with backspace. Relates-to: #11706 Test-bot: skip Cherry-pick-of: #15056 --- .../KeymanDeveloperDebuggerMemo.pas | 10 +++++++ developer/src/tike/child/UfrmDebug.pas | 28 +++++++++++++----- .../debug/Keyman.System.Debug.DebugEvent.pas | 29 +++++++++++++++++++ 3 files changed, 59 insertions(+), 8 deletions(-) diff --git a/developer/src/common/delphi/components/KeymanDeveloperDebuggerMemo.pas b/developer/src/common/delphi/components/KeymanDeveloperDebuggerMemo.pas index c6a703581b..54e2c87ee8 100644 --- a/developer/src/common/delphi/components/KeymanDeveloperDebuggerMemo.pas +++ b/developer/src/common/delphi/components/KeymanDeveloperDebuggerMemo.pas @@ -36,6 +36,7 @@ type TMemoSelection = record Start, Finish: Integer; Anchor: Integer; + function ToString: string; end; TKeymanDeveloperDebuggerMemo = class(TRichEdit41) @@ -107,6 +108,8 @@ end; function TKeymanDeveloperDebuggerMemo.GetSelection: TMemoSelection; begin + Result.Anchor := 0; + // EM_GETSEL doesn't tell us the anchor position, but we can figure // it out with this kludge. I am not aware of side effects from this // at this time. @@ -170,4 +173,11 @@ begin RegisterComponents('Keyman', [TKeymanDeveloperDebuggerMemo]); end; +{ TMemoSelection } + +function TMemoSelection.ToString: string; +begin + Result := Format('%d %d %d', [Start, Finish, Anchor]); +end; + end. diff --git a/developer/src/tike/child/UfrmDebug.pas b/developer/src/tike/child/UfrmDebug.pas index 15a5b858b7..6137cab712 100644 --- a/developer/src/tike/child/UfrmDebug.pas +++ b/developer/src/tike/child/UfrmDebug.pas @@ -109,7 +109,7 @@ type procedure ResetEvents; procedure ExecuteEvent(n: Integer); - procedure ExecuteEventAction(n: Integer); + procedure ExecuteEventAction(EventNumber: Integer); procedure ExecuteEventRule(n: Integer); procedure SetExecutionPointLine(ALine: Integer); procedure SetUIStatus(const Value: TDebugUIStatus); @@ -210,6 +210,7 @@ uses dmActionsMain, Glossary, Keyman.Developer.System.Project.ProjectLog, + Keyman.System.KeymanSentryClient, Keyman.UI.Debug.CharacterGridRenderer, KeyNames, kmxfile, @@ -462,6 +463,10 @@ begin modifier := modifier and not KM_CORE_MODIFIER_LCTRL; end; + TKeymanSentryClient.Instance.Breadcrumb('default', + Format('ProcessKeyEvent: vk=%x mod=%x scan=%x beforeText="%s" beforeSelection=%s',[vkey, modifier, scan, Copy(memo.GetTextCR, 1, 256), memo.Selection.ToString]), + 'debugger'); + if not SetKeyEventContext then Exit(False); @@ -598,8 +603,14 @@ end; procedure TfrmDebug.ExecuteEvent(n: Integer); begin + TKeymanSentryClient.Instance.Breadcrumb('default', + Format('ExecuteEvent(%d/%d): [%s]; b:%s; s:%s', + [n, FEvents.Count, FEvents[n].ToString, memo.Selection.ToString, FSavedSelection.ToString]), + 'debugger'); + memo.ReadOnly := False; memo.Selection := FSavedSelection; + if FEvents[n].EventType = etAction then ExecuteEventAction(n) else ExecuteEventRule(n); @@ -667,7 +678,7 @@ begin end; -procedure TfrmDebug.ExecuteEventAction(n: Integer); +procedure TfrmDebug.ExecuteEventAction(EventNumber: Integer); type TMemoSelectionState = record Selection: TMemoSelection; @@ -711,24 +722,25 @@ procedure TfrmDebug.ExecuteEventAction(n: Integer); procedure DoBackspace(BackspaceType: km_core_backspace_type; ExpectedValue: NativeUInt); var t: string; - m, n: Integer; + m, m1: Integer; dk: TDeadKeyInfo; state: TMemoSelectionState; function AssertionMessage: string; begin Result := 'Assertion failed. Extra data: '+ + 'EventNumber='+IntToStr(EventNumber)+'; '+ 'BackspaceType='+IntToStr(Ord(BackspaceType))+'; '+ 'ExpectedValue=U+'+IntToHex(ExpectedValue, 4)+'; '+ 'memo.SelStart='+IntToStr(memo.SelStart)+'; '+ 'memo.SelLength='+IntToStr(memo.SelLength)+'; '+ - 'memo.Text='+Copy(memo.GetTextCR, 1, 256); + 'memo.Text="'+Copy(memo.GetTextCR, 1, 256)+'"'; end; begin // Offset is zero-based, but string is 1-based. Beware! state := SaveMemoSelectionState; - n := memo.SelStart; - m := n; + m1 := memo.SelStart; + m := m1; if memo.SelLength > 0 then begin @@ -800,7 +812,7 @@ procedure TfrmDebug.ExecuteEventAction(n: Integer); memo.Lines.BeginUpdate; try - memo.Text := Copy(t, 1, m) + Copy(t, n+1, MaxInt); + memo.Text := Copy(t, 1, m) + Copy(t, m1+1, MaxInt); memo.SelStart := m; finally memo.Lines.EndUpdate; @@ -894,7 +906,7 @@ procedure TfrmDebug.ExecuteEventAction(n: Integer); begin DisableUI; frmDebugStatus.Elements.UpdateStores(nil); - with FEvents[n].Action do + with FEvents[EventNumber].Action do begin case ActionType of KM_CORE_IT_EMIT_KEYSTROKE: DoEmitKeystroke(dwData); diff --git a/developer/src/tike/debug/Keyman.System.Debug.DebugEvent.pas b/developer/src/tike/debug/Keyman.System.Debug.DebugEvent.pas index a5853faba0..74fbb2369e 100644 --- a/developer/src/tike/debug/Keyman.System.Debug.DebugEvent.pas +++ b/developer/src/tike/debug/Keyman.System.Debug.DebugEvent.pas @@ -62,6 +62,7 @@ type public constructor Create; destructor Destroy; override; + function ToString: string; override; property Action: TDebugEventActionData read FAction; property Rule: TDebugEventRuleData read FRule; property EventType: TDebugEventType read FEventType write SetEventType; @@ -94,6 +95,8 @@ type vk: uint16_t; modifier_state: uint16_t ): Boolean; overload; + + function ToString: string; override; end; implementation @@ -134,6 +137,23 @@ begin end; end; +function TDebugEvent.ToString: string; +begin + // Debug string + if FEventType = etAction then + begin + Result := Format('A %d %d %d "%s"', [Ord(FAction.ActionType), FAction.dwData, FAction.nExpectedValue, FAction.Text]); + end + else if FEventType = etRuleMatch then + begin + Result := Format('R %d %x %x %d "%s"', [FRule.ItemType, FRule.Key.VirtualKey, FRule.Key.Modifiers, FRule.Line, FRule.Context]); + end + else + begin + Result := '?'+IntToStr(Ord(FEventType)); + end; +end; + function TDebugEventList.AddActionItem(key: Word; action: pkm_core_action_item): Boolean; begin Result := True; @@ -330,6 +350,15 @@ begin end; end; +function TDebugEventList.ToString: string; +var + I: Integer; +begin + Result := ''; + for I := 0 to Count - 1 do + Result := Result + '['+IntToStr(I)+': '+Items[I].ToString + '] '; +end; + function TDebugEventList.AddStateItems( state: pkm_core_state; vk: uint16_t;