gdb and FpDebug lazarus / 12-Debugger ---------------------------------------------------------------------- Seenkao · 2022-01-28 08:52 Я попробовал Lazarus 2.2.0 и у меня такое ощущение что мне связали руки... При использовании gdb я получаю полную отладочную информацию, при использовании FpDebug я получаю кусок отладочной информации. Я не говорил, что мне нужна отладочная информация, только в точке останова. Я просматриваю её и вверх и вниз, без отладки. Это недоработка или где-то у меня проблема? (скрины последовательно для gdb и следом для FpDebug). google translate: I tried Lazarus 2.2.0 and I feel like my hands are tied... When using gdb I get full debug information, when using FpDebug I get a piece of debug information. I didn't say I needed debug information, only at the breakpoint. I look it up and down, without debugging. Is this a bug or is there a problem somewhere? (screenshots in sequence for gdb and next for FpDebug). Linux - Devuan 4.0 FPC 3.2.0 ---------------------------------------------------------------------- Seenkao · 2022-01-28 08:53 FpDebug ---------------------------------------------------------------------- zamtmn · 2022-01-28 09:01 You can switch debugger and continue using GDB ---------------------------------------------------------------------- Seenkao · 2022-01-28 09:07 Я об этом знаю. Я задавал другой вопрос! Не надо направлять тему в другое русло. google translate: I know this very well. I asked a different question! It is not necessary to direct the topic in another direction. ---------------------------------------------------------------------- Martin_fr · 2022-01-28 12:10 Quote from: Seenkao on January 28, 2022, 10:07:04 amЯ об этом знаю. Я задавал другой вопрос! Не надо направлять тему в другое русло. google translate: I know this very well. I asked a different question! It is not necessary to direct the topic in another direction. Yes it is a "bug" or a "known issue". The dis-assembler in FpDebug is not complete. It only shows partial asm. Other known Issues in FpDebug are: - no debugging in DLL (libraries) - watching "var a: variant;" only works with "DWARF2" / "DWARF with sets" - stacktraces in the kernel (or in some optimized code) are often "cut off" ---------------------------------------------------------------------- ccrause · 2022-01-28 12:54 Quote from: Martin_fr on January 28, 2022, 01:10:54 pmYes it is a "bug" or a "known issue". The dis-assembler in FpDebug is not complete. It only shows partial asm. To add to Martin's comment: Lazarus only requests disassembly of instructions within a certain range of the current instruction.  This requires very little storage.  Because the x86 instruction set has variable length encoding, reverse disassembly (disassembling from current address to lower address) is non-deterministic and not perfromed.  To improve this, one could search for the function containing the current instruction address and disassemble from the start of the current function. In contrast, gdb performs a linear sweep of all the memory in an executable section.  This gives better coverage, but requires processing time and storage. ---------------------------------------------------------------------- Seenkao · 2022-02-03 08:38 Благодарю за информацию! Будем надеяться что FPDebug догонит и перегонит GDB в этом направлении!  google translate: Thank you for the information! Let's hope that FPDebug will catch up and overtake GDB in this direction! ---------------------------------------------------------------------- 440bx · 2022-02-03 10:34 Quote from: Martin_fr on January 28, 2022, 01:10:54 pmThe dis- assembler in FpDebug is not complete. It only shows partial asm. Just curious... there are a number of open source dis-assemblers out there.  Is there some reason (other than most, if not all, are written in C/C++) for not using one of them until the currently used dis- assembler (presumably written in Pascal) is complete ? ---------------------------------------------------------------------- PascalDragon · 2022-02-03 12:48 Quote from: 440bx on February 03, 2022, 11:34:11 amQuote from: Martin_fr on January 28, 2022, 01:10:54 pmThe dis-assembler in FpDebug is not complete. It only shows partial asm. Just curious... there are a number of open source dis-assemblers out there.  Is there some reason (other than most, if not all, are written in C/C++) for not using one of them until the currently used dis- assembler (presumably written in Pascal) is complete ? The reason is not that the disassembler can't handle the code, but it's the one mentioned by ccrause. This problem would exist with a third party disassembler as well. ---------------------------------------------------------------------- Marc · 2022-02-03 13:22 One could iterate disassembling from a location n-1 and see if the disassemby ends on the current location. You may repeat that for a x amount of bytes before that location until you draw the conclusion that the previous instruction doesn't change anymore. One could also start to disassemble n+16 bytes before the current location and discard the disassembled statements of the first 16 bytes (16 here is the assumed max size of a single instruction) Marc ---------------------------------------------------------------------- Martin_fr · 2022-02-03 13:53 Quote from: 440bx on February 03, 2022, 11:34:11 amQuote from: Martin_fr on January 28, 2022, 01:10:54 pmThe dis-assembler in FpDebug is not complete. It only shows partial asm. Just curious... there are a number of open source dis-assemblers out there.  Is there some reason (other than most, if not all, are written in C/C++) for not using one of them until the currently used dis- assembler (presumably written in Pascal) is complete ? First of all, the issue of only disassembling small part (i.e. not backwards, no scroll) => that is not the actual disassemble code. That is the framework surrounding it. Even when using gdb to disassemble, the backward/scroll sub-address-range calculations are done by the IDE (actually our gdb wrapper code). Then again, there are other issues, that need fixing too (support for some missing mnemonics). As for the actual question, the short answer: There may be some reasons. (Not sure if there always had been, but in the meantime there may be...) Most of that dis-ass code was written long ago. I do not know the reasons, why (back then) it was done that way (I was not working on fpdebug then, maybe not even aware of the existence of Lazarus). If you go back on the git history, maybe the logs will tell some bits. But I wouldn't expect too much. After that, I guess it has just been kept as it is. Eventually however some changes have been made. And those I referred to above "There may be some reasons". There are cases, when the debugger makes decisions based on the next asm statement. E.g. when stepping, it needs to know if there is a call or ret. But also some other decisions (looking at various asm instructions). Originally, the code for single-stepping would take the disassembled asm as text, and parse the text. Now, the dis-assembler returns an enum, representing the statement. That saves the unnecessary creation of a string, and the subsequent parsing of that string. From where we are now (the above), both paths are open. But I am not sure, if switching to external would be more beneficial. For any comparison, we have to take out any functionality that goes on top of the actual translation. Translation here being to take a byte sequence "85 C0" and translate it into the text "test eax,eax". As for the question: "Maybe there is an existing dis-assembler that can do some of the other stuff needed?" Unlikely (but feel free to give examples, if you know of any). Why unlikely? Well, for any of this the dis-ass needs input from us => We need to read the memory of the target process (the external lib would not have access to the debugged process). We then hand over the byte-sequence. For other tasks, we would need to supply additional data (such as access to debug info, and what we want the dis-ass to do with that info) and that would return some of the burden to us. Also, we already have (pre-)parsed debug info (in our own set of classes and structures). The external code would have to parse it again => adding extra resource usage (cpu and mem). Debugging an app, is as it stands already a memory hungry operation. So, if we compare the remaining functionality (just translating bytes into mnemonics), then we get: Maybe reduce long time maintenance? Depends, how much work (e.g. C to pas) there will be for each update of the external code. Also relies on continuous existence of the external project. As for short term effects: Probably - None -(my guess only). The time to initially include the 3rd party code, would probably be at least what would be required to fix (most of) the missing bits in our own code. Depending on the above (first bullet): Less work for bug fixes. But if they will be slower or faster is in the hands of someone else. (We do have that with GDB, bug fixes can take for-ever / still waiting => Ok, bug fixes on our own list can have the same wait...) More work, adding our own features on top. We must adhere to whatever API the code uses (and convert any input data we have). We must also parse the result, if we want to add further data, decoration, functionality. If we add support for more CPU targets to FpDebug, the decision can be reviewed for each such target. If there is no pre-existing pascal code for dis-assembling, the cost/benefit analysis changes. Though, personally I think, if a Pascal version can be created at low cost (e.g. automatic translation for another language to Pascal) then that should probably be preferred. At least if the results of the pro/contra evaluation are otherwise a close call... ---------------------------------------------------------------------- Martin_fr · 2022-02-03 13:59 Quote from: Marc on February 03, 2022, 02:22:56 pmOne could iterate disassembling from a location n-1 and see if the disassemby ends on the current location. You may repeat that for a x amount of bytes before that location until you draw the conclusion that the previous instruction doesn't change anymore. IIRC, that is what we do (fallback) using gdb. Not sure though, it was at least considered. We also (if we have no start point) go back an extra large block of bytes, hoping that within this data any initially wrong "offset" will balance out. We then ignore the returned data for the "lead up" block. IIRC, where we can, we find the start of a function. However, that only works, if all functions have their own debug info entry. That is not always a given. And then, it becomes just a question of random luck... There are various other things that can be tried.... ---------------------------------------------------------------------- 440bx · 2022-02-03 17:54 @PascalDragon, @Marc and @Martin_fr, Thank you for the replies and special thanks to Martin for the detailed, comprehensive reply. I asked the question mostly because I got the impression that the current dis-assembler could not handle some instructions but, the real problem seems to be that in order to dis-assemble correctly, some analysis is required to determine where instructions start and end, as well as separating code from data (I am aware of other issues but, those two are usually the "biggies") and that's where the current implementation may need some "enhancements". I think my question got answered quite well ---------------------------------------------------------------------- Martin_fr · 2022-02-03 17:56 Quote from: 440bx on February 03, 2022, 06:54:17 pmI asked the question mostly because I got the impression that the current dis- assembler could not handle some instructions True. But that is a smaller problem (measured on the effort needed to fix it). ---------------------------------------------------------------------- ccrause · 2022-02-03 18:10 Quote from: Marc on February 03, 2022, 02:22:56 pmOne could iterate disassembling from a location n-1 and see if the disassemby ends on the current location. You may repeat that for a x amount of bytes before that location until you draw the conclusion that the previous instruction doesn't change anymore. One could also start to disassemble n+16 bytes before the current location and discard the disassembled statements of the first 16 bytes (16 here is the assumed max size of a single instruction) Marc Unfortunately the x86 variable length encoding makes this infeasible.  Below an example of valid x86 instructions for the byte sequence 66 c7 05 93 05 93 e7 b6 cc when scanned in reverse, starting at the last byte in the sequence:: Code: [Select]                        cc  int3                      b6 cc  mov  dh,0xcc                   e7 b6 cc  out  0xb6,eax;  int3                93 e7 b6 cc  xchg ebx,eax;  out 0xb6,eax; int3             05 93 e7 b6 cc  add  eax,0xccb6e793          93 05 93 e7 b6 cc  xchg ebx,eax;  add  eax,0xccb6e793       05 93 05 93 e7 b6 cc  add  eax,0xe7930593;  mov  dh,0xcc 66 c7 05 93 05 93 e7 b6 cc  mov  WORD PTR ds:0xe7930593,0xccb6 ---------------------------------------------------------------------- Marc · 2022-02-04 08:04 Quote from: ccrause on February 03, 2022, 07:10:31 pmUnfortunately the x86 variable length encoding makes this infeasible.  Below an example of valid x86 instructions for the byte sequence 66 c7 05 93 05 93 e7 b6 cc when scanned in reverse, starting at the last byte in the sequence:: Thats why I suggested to use at least max instruction length bytes  (or the double of that) Marc ---------------------------------------------------------------------- Marc · 2022-02-04 08:13 Quote from: Martin_fr on February 03, 2022, 02:53:58 pmMost of that dis-ass code was written long ago. I do not know the reasons, why (back then) it was done that way (I was not working on fpdebug then, maybe not even aware of the existence of Lazarus). If you go back on the git history, maybe the logs will tell some bits. But I wouldn't expect too much. After that, I guess it has just been kept as it is. I wrote the disassember as proof of concept, could it be done. (a much longer time ago I wrote a Z80 disassember in ZX Spectrum basic) The Intel instruction set is well defined when which bit means what, so in that case it is easy to create a string builder returning the disassembled text. After finishing it, it became clear that a string only isn't useful in all cases (Delphi allows to show the contents of the memory location). That said, adding missing instructions shouldn't be that difficult if they follow the same pattern. Marc ---------------------------------------------------------------------- Seenkao · 2022-02-04 09:29 Я считаю ни чего изобретать не надо. Достаточно найти точку входа в процедуру. Отладочную информацию выдавать только по данной процедуре до точки выхода. Дополнительно можно ещё показывать отладочную информацию из вызываемой функции. Но вероятнее всего её удерживать в памяти надо будет. Саму трассировку делать по выведенному отладочному коду. Ведь отладочная информация имеется уже? Но я этим не занимался, потому многое могу упускать из вида. Когда начинаешь подобным заниматься, много разного выползает наружу. Google translate: Я считаю ни чего изобретать не надо. Достаточно найти точку входа. Отладочную информацию выдавать только по данной процедуре до точки выхода. Дополнительно можно ещё отображать отладочную информацию из вызываемой функции. Вероятно, всего этого она будет удерживать в памяти. Саму трассировку делать по выведенному отладочному коду. Ведь отладочная информация уже имеется? Но я этим не стал, потому что многое могу упустить из вида. Когда начинаешь заниматься подобным, много разного выползает. ---------------------------------------------------------------------- 440bx · 2022-02-04 10:18 The post above proves that Google can translate Russian to Russian flawlessly.  They never stop improving their stuff ---------------------------------------------------------------------- ccrause · 2022-02-04 10:20 Quote from: Marc on February 04, 2022, 09:04:07 amQuote from: ccrause on February 03, 2022, 07:10:31 pmUnfortunately the x86 variable length encoding makes this infeasible.  Below an example of valid x86 instructions for the byte sequence 66 c7 05 93 05 93 e7 b6 cc when scanned in reverse, starting at the last byte in the sequence:: Thats why I suggested to use at least max instruction length bytes  (or the double of that) Marc I may be missing something, but the issue I see is that the longest possible instruction encoding that ends on the desired address is not guaranteed to be the correct interpretation. The reverse disassemble algorithm you describe (or at least something very similar) is already present in fpdebug, but it is not enabled for x86 because of the poor accuracy.  It is used for AVR with good success, but even with that simple instruction encoding scheme there is a 0.2% chance of incorrect disassembly. ---------------------------------------------------------------------- Marc · 2022-02-04 11:06 Quote from: ccrause on February 03, 2022, 07:10:31 pmI may be missing something, but the issue I see is that the longest possible instruction encoding that ends on the desired address is not guaranteed to be the correct interpretation. The reverse disassemble algorithm you describe (or at least something very similar) is already present in fpdebug, but it is not enabled for x86 because of the poor accuracy.  It is used for AVR with good success, but even with that simple instruction encoding scheme there is a 0.2% chance of incorrect disassembly. It's better than nothing. The Delphi debugger suffers from this too. When I see unexpected instructions, I scroll to the line before that until I see something understandable. Note that this is only needed when line info is missing otherwise you could start disassembling from the previous known location (fpdebug has this list). ---------------------------------------------------------------------- Seenkao · 2022-02-04 11:43 Quote from: 440bx on February 04, 2022, 11:18:42 amThe post above proves that Google can translate Russian to Russian flawlessly.  They never stop improving their stuff Quote from: Seenkao on February 04, 2022, 10:29:55 amGoogle translate: Я считаю ни чего изобретать не надо. Достаточно найти точку входа. Отладочную информацию выдавать только по данной процедуре до точки выхода. Дополнительно можно ещё отображать отладочную информацию из вызываемой функции. Вероятно, всего этого она будет удерживать в памяти. Саму трассировку делать по выведенному отладочному коду. Ведь отладочная информация уже имеется? Но я этим не стал, потому что многое могу упустить из вида. Когда начинаешь заниматься подобным, много разного выползает. I don't think it's necessary to invent anything. It is enough to find an entry point. Issue debugging information only for this procedure up to the exit point. Additionally, you can also display debugging information from the called function. She will probably keep all this in mind. Do the tracing itself according to the debug code. After all, debugging information is already available? But I didn't do it, because I can lose sight of a lot. When you start doing something like this, a lot of different stuff creeps out. ---------------------------------------------------------------------- Martin_fr · 2022-02-09 15:43 Updates.... (on the "main" branch in git) - Marc has started to add some of the missing instructions - FpDebug will now show (in most cases) assembler before the current line, if this is in a function (It will start at the begin of the function / It will not yet go further back)