Recent

Author Topic: Portable and non-deletable breakpoint  (Read 1785 times)

MarkMLl

  • Hero Member
  • *****
  • Posts: 8572
Portable and non-deletable breakpoint
« on: July 13, 2021, 10:03:59 am »
Is there an approved way of putting something in a program which will act as a breakpoint if run under a debugger but is otherwise ignored? I don't really want to use Assert(false) since that will always raise an exception.

The scenario is that I've got a fork of some old code I'm modernising gradually, it runs OK standalone but I want to mark the place I'm currently working on so that it's immediately apparent in the (Lazarus etc.) IDE. I don't want to use a standard breakpoint since this can obviously be cleared by the IDE, irrespective of any dire warnings in the source.

Yesterday evening I caused myself much headscratching when I put a breakpoint on a valid line but it didn't actually trigger, at which point control headed off into (what I should have remembered was) Terra Incognita.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Logitech, TopSpeed & FTL Modula-2 on bare metal (Z80, '286 protected mode).
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

440bx

  • Hero Member
  • *****
  • Posts: 6480
Re: Portable and non-deletable breakpoint
« Reply #1 on: July 13, 2021, 10:52:41 am »
From your posts, I suspect you want a cross platform solution.

First, I'll mention the Windows-only solution which is use IsDebuggerPresent() followed by an int 3.  Code like this:
Code: Pascal  [Select][+][-]
  1. if IsDebuggerPresent() then asm int 3 end;
Quite simple but, unfortunately Windows only.

I presume there has to be a way under other operating systems (commonly Linux) to determine if a program is being debugged, if so, just implement your own version of IsDebuggerPresent() for that platform and problem solved.

HTH.
FPC v3.2.2 and Lazarus v4.0rc3 on Windows 7 SP1 64bit.

MarkMLl

  • Hero Member
  • *****
  • Posts: 8572
Re: Portable and non-deletable breakpoint
« Reply #2 on: July 13, 2021, 10:59:53 am »
@440bx The int can be easily replaced by an Assert(false). I was pretty sure I'd seen this discussed (multiple times) before, and Google points me at https://forum.lazarus.freepascal.org/index.php?topic=46981.0 where Thaddy suggests a Linux hack via /proc... I'll experiment and report back but can't test behaviour on Mac etc.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Logitech, TopSpeed & FTL Modula-2 on bare metal (Z80, '286 protected mode).
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

MarkMLl

  • Hero Member
  • *****
  • Posts: 8572
Re: Portable and non-deletable breakpoint
« Reply #3 on: July 13, 2021, 11:59:31 am »
This works nicely under Linux:

Code: Pascal  [Select][+][-]
  1. {$if not declared(IsDebuggerPresent)    }
  2.  
  3. function grep(caseInsensitive: boolean; const pattern, fn: ansistring): ansistring;
  4.  
  5. var
  6.   scratch: TStringList;
  7.   i: integer;
  8.  
  9. begin
  10.   result := '';
  11.   if not FileExists(fn) then
  12.     exit;
  13.   scratch := TStringList.Create;
  14.   try
  15.     scratch.LoadFromFile(fn);
  16.     for i := 0 to scratch.Count - 1 do
  17.       if caseInsensitive then begin
  18.         if AnsiContainsText(scratch[i], pattern) then
  19.           exit(scratch[i])              (* Via finally                          *)
  20.       end else
  21.         if AnsiContainsStr(scratch[i], pattern) then
  22.           exit(scratch[i])              (* Via finally                          *)
  23.   finally
  24.     scratch.Free
  25.   end
  26. end { grep } ;
  27.  
  28.  
  29. function isDebuggerPresent(): boolean;
  30.  
  31. var
  32.   scratch: ansistring;
  33.  
  34. begin
  35.   scratch := grep(true, 'TracerPid', '/proc/' + IntToStr(GetProcessID()) + '/status');
  36.  
  37. (* TracerPid has existed since at least late Linux kernel 2.x, as of 2021 it's  *)
  38. (* unreasonable to expect Lazarus/FPC to be running on anything older and while *)
  39. (* the /proc tree might be supressed on embedded systems this is unlikely to be *)
  40. (* the case on a system that might have a debugger on it. Having no TracerPid   *)
  41. (* can reasonably be interpreted as there being no active debugger, but it is   *)
  42. (* still necessary to check that it is non-zero.                                *)
  43.  
  44.   if scratch = '' then
  45.     result := false
  46.   else begin
  47.     scratch := DelSpace(Tab2Space(scratch, 1));
  48.     result := Pos(':0', scratch) <> Length(scratch) - 1
  49.   end
  50. end { isDebuggerPresent } ;
  51.  
  52. {$endif not declared(IsDebuggerPresent) }
  53.  
  54. ...
  55.   if IsDebuggerPresent() then // Non-continuable forced breakpoint
  56.     Assert(false);
  57.  
  58.   if IsDebuggerPresent() then // Continuable forced breakpoint
  59.     try Assert(false, 'Terra Incognita (here be dragons!)') except end;
  60.  

Google suggests that Solaris/SunOS has something comparable but not in the same place. Can anybody speak for Macs and the BSDs?

MarkMLl
« Last Edit: July 13, 2021, 12:14:22 pm by MarkMLl »
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Logitech, TopSpeed & FTL Modula-2 on bare metal (Z80, '286 protected mode).
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

 

TinyPortal © 2005-2018