Well, FPC has a curious way of writing line info. Though I haven't seen that particular order myself. But it may depend on OS, Fpc version, settings, ...
Usually (for me) entering a function works fine. But leaving goes "end, begin, end" sometimes even "end, begin, begin, end".
One issues is, that some procedures have hidden, internal "finally" blocks. And while they are executed at the end, fpc puts the line number of the begin there.
And the stepping from begin to begin => the finally is a sub procedure (at least on some OS), but the debugger hides that (or you could never "step over F8" when there is code in a finally block. So in the "begin begin" case the debugger steppend to the finally... (Ok that bit could be avoided by the debugger, but it would still go to the begin line, because FPC said that is what should be done).
Breakpoints have the same reason as stepping. A single line of code can generate multiple blocks of asm (E.g. a specialized generic, that code may exist once for every different type for which it got specialized). A breakpoint set to such a line must catch it, never mind which asm block got entered by the CPU. If the compiler writes some of the "end" code with the begin line number, then the begin line number has several blocks => and the breakpoint catches for any of them.
I don't know an example for the end line containing "begin" code, but if it does then the same thing happens.
Then depending on the debugger ...
There is an issue where fpc writes line info incorrect and overlapping with code the end of the procedure before... This can also lead to strange stepping/breakpoints. For the last procedure in a unit, the code may jump to the first line in another unit.... And with gdb I have seen the debugged app crash, because the breakpoint went into the wrong place. Not sure if that issue still exists....