Recent

Author Topic: Break program and debug  (Read 1282 times)

eye

  • New Member
  • *
  • Posts: 11
Break program and debug
« on: May 13, 2025, 01:13:59 pm »
I have a program with random numbers in loops (sometimes loops run over a million of times) which is giving me the expected outcome. Although sometimes (something like 2-3% of times) when it runs it stucks and I have to terminate it. Is there anyway to break the program in order to trace it from that point in order to detect what causes the problem?

Handoko

  • Hero Member
  • *****
  • Posts: 5445
  • My goal: build my own game engine using Lazarus
Re: Break program and debug
« Reply #1 on: May 13, 2025, 01:59:56 pm »
How to debug is an interesting topic unfortunately it is rarely discussed.

In your Lazarus IDE, you can try:
Lazarus main menu > Run > Add Watch
and
Lazarus main menu > Run > Add Breakpoint

I personally prefer to debug code using my own ways:

Duplicate the project to a new folder. Remove all the unnecessary objects/variables/code. Test and analyze the code manually. Remove some part of the code, run and repeat until I get the line that causes the problem.

Because I write it, I am very good in debugging my own code. When I write code I know exactly what each line does, each block, each variable and its data type, the algorithm and so on. If I have doubt I always consult the documentation. By fully paying attention, it minimizes bugs and if something not working as I want it to be, I can locate the bug easily.

Instead of using breakpoints and checking the value of certain variable or object, I usually send the value to the form's Caption. Simple and easy. Sometimes I need to keep track a variable that keep changing, so I wrote myself a debug module that can log the variable to screen. I could improve the module to log to a file but so far I have not needed it.

To test if certain condition has ever reached, I usually put the code like this:
If condition_reached then Halt;

That is how I doing debug. And here, you may find something helpful:
https://www.youtube.com/@silvercoder70/search?query=debug

ccrause

  • Hero Member
  • *****
  • Posts: 1027
Re: Break program and debug
« Reply #2 on: May 13, 2025, 02:39:05 pm »
If you started the program inside the debugger (by pressing F9, or click on the green Run button, or from the menu Run | Run) then you can simply pause the program by clicking on Run | Pause from the main menu). This should pause the running program and display a bunch of extra information, such as call stack, source position (if the point of execution is inside your code) and allow you to inspect variables that are in scope.

If you started the program outside of Lazarus you can attach the debugger by clicking on Run | Attach to program (from the main menu in Lazarus), then proceed as above.

Note: Including debug information in the compiled program is optional but will definitely make debugging a lot easier.

440bx

  • Hero Member
  • *****
  • Posts: 5479
Re: Break program and debug
« Reply #3 on: May 13, 2025, 03:08:25 pm »
I have a program with random numbers in loops (sometimes loops run over a million of times) which is giving me the expected outcome. Although sometimes (something like 2-3% of times) when it runs it stucks and I have to terminate it. Is there anyway to break the program in order to trace it from that point in order to detect what causes the problem?
There are two problems:

1. You provide a description of the problem, which is good but, without seeing the code, it's difficult to make a recommendation that fits the code.

2. From the description you gave, it seems that you do expect the loop to run over a million times but, sometimes it seems to turn into an infinite loop.  In that case, if I were you, I would add a little bit of code that would test the iteration count and if greater than some number then the program would break into the debugger.

Another problem in your description is that you didn't mention what O/S you're using but, presuming you're using Windows, the code would look something like this:
Code: Pascal  [Select][+][-]
  1. if IsDebuggerPresent() and <some iteration counter > some number you feel is appropriate> then asm int3 end;
  2.  
That will cause the program to break into the debugger when the number of iterations exceeds whatever upper threshold you deemed appropriate.  I know the same thing can be achieved in Linux but, I don't know how, someone else will have provide the Linux equivalent.

Once you're in the debugger, execute instructions one at a time looking around at what's making the loop not end.

Lastly, if you post some code, it might be possible to offer a more efficient method of figuring out what the problem is. It might even be possible to figure out why there are cases where it does not work as expected.

HTH.
(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v4.0rc3) on Windows 7 SP1 64bit.

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 11361
  • Debugger - SynEdit - and more
    • wiki
Re: Break program and debug
« Reply #4 on: May 13, 2025, 03:16:41 pm »
Most of the answer was already given.

1) Make sure you are setup for debugging
https://wiki.lazarus.freepascal.org/Debugger_Setup

The global part should be correct out of the box.

But make sure in your project settings, that debug info is enabled.

2) Run your app (using "Run" / do not use "run without debugger")

3) When the problem happens, use the Pause button (or "Pause" from the run menu)
Ignore the assembler / you can close that

4) Open "Callstack" and "Threads" from menu "View" > "Debug Windows"

On Windows you will have to change the thread, on other platforms it may already be correct but you may have to check.

When going through the threads, watch the callstack.
For one of the threads (using the "current" button to select it), the callstack should show many lines, and within the top 10 lines it should show one of your source files.
Then select your source file in the callstack (if it is not the very top entry).  (Use "Current" from the popup up menu of the callstack)

Double click the callstack to see the source.

Now you can either
- if your source is at the top of the callstack => singlestep
Otherwise
- set a breakpoint on the next source line, and run => will pause at breakpoint, then you can single step
- Use "Step out" to get to your source file (may need several step outs)

After you have set the callstack with "current" you can also inspect variables (hover mouse or "add to watches")

MarkMLl

  • Hero Member
  • *****
  • Posts: 8437
Re: Break program and debug
« Reply #5 on: May 13, 2025, 04:37:48 pm »
Adding to what Martin said, use fpcdebug for i386 and amd64, otherwise gdb.

Also

Code: Pascal  [Select][+][-]
  1.   if IsDebuggerPresent() and <stuff> then
  2.      asm
  3.       int3
  4.     end;
  5.  

is again strictly x86, possibly subject to OS 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: 8437
Re: Break program and debug
« Reply #6 on: May 16, 2025, 02:35:15 pm »
Unfortunately this appears to have no effect under Linux (x86_64 Debian "Stable"). IsDebuggerPresent() is easy enough and should be reliable from kernel 2.x onwards, but int3 does nothing at least with the version of Lazarus I'm running (which serves well in all other regards).

Code: Pascal  [Select][+][-]
  1. (* Output the content of a file, iff there are multiple lines they will be
  2.   separated by the OS-specific line ending. Optionally, tabs may be converted
  3.   to single spaces and then multiple consecutive spaces collapsed to single.
  4. *)
  5. function cat(const fn: string; match: string= '';
  6.                                         collapseWhitespace: boolean= false): string;
  7.  
  8. var
  9.   sl: TStringList;
  10.   i: integer;
  11.  
  12. begin
  13.   result := '';
  14.   try
  15.     sl := TStringList.Create;
  16.     try
  17.       sl.LoadFromFile(fn);
  18.  
  19. (* The RTL documentation says that the strings will be separated by the EOL     *)
  20. (* marker, but this cannot be trusted: assume that there might be a terminator  *)
  21. (* which should be removed (and that also removing any trailing spaces is not   *)
  22. (* likely to cause problems to the caller).                                     *)
  23.  
  24.       if match = '' then
  25.         result := TrimRight(sl.Text)
  26.       else begin
  27.         for i := 0 to sl.Count - 1 do begin
  28.           if collapseWhitespace then
  29.             sl[i] := DelSpace1(Tab2Space(sl[i], 1));
  30.           if Pos(match, sl[i]) > 0 then begin
  31.             if result <> '' then
  32.               result += LineEnding;
  33.             result += sl[i]
  34.           end
  35.         end
  36.       end
  37.     finally
  38.       FreeAndNil(sl)
  39.     end
  40.   except                                (* Inability to open or read the file   *)
  41.   end                                   (* returns an empty string.             *)
  42. end { cat } ;
  43.  
  44.  
  45. (* Under Linux, return true if the program is running under a debugger (gdb or
  46.   fpcdebug). The rationale for this is that there might be problems catching
  47.   e.g. the HUP and USR1 signals while debugging, so it is desirable to add menu
  48.   entries or some sort of "easter egg" to the GUI to simulate them.
  49. *)
  50. function isDebuggerPresent(): boolean;
  51.  
  52. const
  53.   xIsDebuggerPresent: byte= 2;          (* Static variable                      *)
  54.  
  55. begin
  56.   if not (xIsDebuggerPresent in [0, 1]) then
  57.     xIsDebuggerPresent := Ord(cat('/proc/' + IntToStr(GetProcessID()) + '/status',
  58.                                         'TracerPid:', true) <> 'TracerPid: 0');
  59.   result := boolean(xIsDebuggerPresent)
  60. end { isDebuggerPresent } ;
  61.  
  62. ...
  63.  
  64. asm
  65.   int3
  66. end;
  67.  

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

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 11361
  • Debugger - SynEdit - and more
    • wiki
Re: Break program and debug
« Reply #7 on: May 16, 2025, 03:41:35 pm »
int3 must be enabled in the global debugger backend settings (in the property grid).

But if you just need to break on a condition, then you have 2 options.

1)
Set a breakpoint, and fill in the "condition" field in the breakpoints properties.

However, while the breakpoint will only stop if the condition is true, it will need to mini-pause each time it is passed and evaluate the condition. So this will slow down the app.

Conditional breakpoints are good, if you have a few dozen passes before you need the condition becomes true.

If you have thousand loops before you may need to stop, then that will be a long long wait.

2)
breakpoint on conditional code. Add some code, just for the debug run:
Code: Pascal  [Select][+][-]
  1. if myval = badval then
  2.   sleep(1); // breakpoint on this line
Make sure to compile with "optimization off" (not even level 1 / really off).

99.9% it works with O1 too. But better be safe.

You can put code like this into {$IFDEF WITH_DEBUG_FOO}
And then have a debug build mode in which you define WITH_DEBUG_FOO.
Then you don't need to remove the code when you compile for release.


3)
Use asserts.
Code: Pascal  [Select][+][-]
  1. assert(myval <> badval, 'myval is ok');'

You don't need a breakpoint. If it is wrong, then the debugger will stop for the exception.

You may have to go to the callstack window, and select the correct callstack. There is an issue were the debugger may show you the calling function, instead of the line with the assert.

MarkMLl

  • Hero Member
  • *****
  • Posts: 8437
Re: Break program and debug
« Reply #8 on: May 16, 2025, 05:11:32 pm »
Thanks Martin, I see it and confirm it works.

I'm aware of the other cases, and was exercising this mainly to check out (my implementation of) IsDebuggerPresent(). The reason I knocked that together was that I was debugging something that would have benefited from receiving a USR1 (to dump internal caches to a status file) but found that was blocked by the debugger (fpcdebug or gdb), hence thought that it would be useful to detect the case where a debugger was in play and add a couple more menu entries to the GUI.

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

eye

  • New Member
  • *
  • Posts: 11
Re: Break program and debug
« Reply #9 on: May 17, 2025, 01:18:40 pm »
Thanks for the answers

In my case I found myself a much simpler solution.

I used a counter inside the loops and when the counter was reaching a number i considered too high I used a breakpoint by checking the value of counter.

MarkMLl

  • Hero Member
  • *****
  • Posts: 8437
Re: Break program and debug
« Reply #10 on: May 17, 2025, 01:28:43 pm »
While not an FAQ it's definitely something that's been asked multiple times: as has "how can I tell if running under the IDE".

There's ways of circumventing those tests, but getting things in the same place "for the record" is definitely useful.

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

What I can do

  • Full Member
  • ***
  • Posts: 167
Re: Break program and debug
« Reply #11 on: May 22, 2025, 04:16:00 am »
I am just a hobbyist  so take it with a grain of salt but here is what I did in Lazarus 3.8
Code: Pascal  [Select][+][-]
  1. If DebugActive then ShowMSG('Break triggered',[mrOK,mrCancel],false) then asm int3 end;

Where DebugActive is a global switch. I can walk through my whole code in real time and choose if I want to break or not.
However since the install of Lazarus 4.0 I have not yet got the "int3" to get processed yet. so be warned if you try this.

Marc

  • Administrator
  • Hero Member
  • *
  • Posts: 2649
Re: Break program and debug
« Reply #12 on: May 28, 2025, 08:55:51 am »
The reason I knocked that together was that I was debugging something that would have benefited from receiving a USR1 (to dump internal caches to a status file) but found that was blocked by the debugger (fpcdebug or gdb), hence thought that it would be useful to detect the case where a debugger was in play and add a couple more menu entries to the GUI.

MarkMLl

I've been thinking about adding an option to let the debugger pass signals to the debugged app when I was trying to capture ctrl-c. Due to lack of time it never made it.
//--
{$I stdsig.inc}
//-I still can't read someones mind
//-Bugs reported here will be forgotten. Use the bug tracker

 

TinyPortal © 2005-2018