Break program and debug lazarus / 12-Debugger ---------------------------------------------------------------------- eye · 2025-05-13 11:13 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 · 2025-05-13 11:59 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 · 2025-05-13 12:39 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 · 2025-05-13 13:08 Quote from: eye on May 13, 2025, 01:13:59 pmI 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][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i if IsDebuggerPresent() and some number you feel is appropriate> then asm int3 end; 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. ---------------------------------------------------------------------- Martin_fr · 2025-05-13 13:16 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 · 2025-05-13 14:37 Adding to what Martin said, use fpcdebug for i386 and amd64, otherwise gdb. Also Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i   if IsDebuggerPresent() and then     asm      int3    end;  is again strictly x86, possibly subject to OS etc. MarkMLl ---------------------------------------------------------------------- MarkMLl · 2025-05-16 12:35 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][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i (* Output the content of a file, iff there are multiple lines they will be  separated by the OS-specific line ending. Optionally, tabs may be converted  to single spaces and then multiple consecutive spaces collapsed to single.*)function cat(const fn: string; match: string= '';                                        collapseWhitespace: boolean= false): string; var  sl: TStringList;  i: integer; begin  result := '';  try    sl := TStringList.Create;    try      sl.LoadFromFile(fn); (* The RTL documentation says that the strings will be separated by the EOL     *)(* marker, but this cannot be trusted: assume that there might be a terminator  *)(* which should be removed (and that also removing any trailing spaces is not   *)(* likely to cause problems to the caller).                                   *)       if match = '' then        result := TrimRight(sl.Text)      else begin        for i := 0 to sl.Count - 1 do begin          if collapseWhitespace then            sl[i] := DelSpace1(Tab2Space(sl[i], 1));          if Pos(match, sl[i]) > 0 then begin            if result <> '' then              result += LineEnding;            result += sl[i]          end        end      end    finally      FreeAndNil(sl)    end  except                                (* Inability to open or read the file   *)  end                                   (* returns an empty string.             *)end { cat } ;  (* Under Linux, return true if the program is running under a debugger (gdb or  fpcdebug). The rationale for this is that there might be problems catching  e.g. the HUP and USR1 signals while debugging, so it is desirable to add menu  entries or some sort of "easter egg" to the GUI to simulate them.*)function isDebuggerPresent(): boolean; const  xIsDebuggerPresent: byte= 2;          (* Static variable                    *) begin  if not (xIsDebuggerPresent in [0, 1]) then    xIsDebuggerPresent := Ord(cat('/proc/' + IntToStr(GetProcessID()) + '/status',                                        'TracerPid:', true) <> 'TracerPid: 0');  result := boolean(xIsDebuggerPresent)end { isDebuggerPresent } ; ... asm  int3end;  MarkMLl ---------------------------------------------------------------------- Martin_fr · 2025-05-16 13:41 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][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i if myval = badval then   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][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i 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 · 2025-05-16 15:11 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 ---------------------------------------------------------------------- eye · 2025-05-17 11:18 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 · 2025-05-17 11:28 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 ---------------------------------------------------------------------- What I can do · 2025-05-22 02:16 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][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i 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 · 2025-05-28 06:55 Quote from: MarkMLl on May 16, 2025, 05:11:32 pmThe 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.