Live data from Hacker News

Writing a debugger from scratch: Breakpoints

timdbg.com

41–50 of 56 posts

Re: Writing a debugger from scratch: Breakpoints

#41

I’ll share this anecdote told by a friend of mine. He was on a team building a Modula-2 compiler for OS/2, and his group was working on the debugger. At some point a debugger becomes feature complete enough that you can use the debugger to ... debug the debugger. But this was OS/2 which has true multiple processes (unlike it’s contemporary Windows 3.1). So you could, naturally, run the debugger in one process and att…

Focus is still an issue today. We had a version of Firebug that would let you debug Firebug. It was great! A bit buggy though, so you can see where this is going…

That said, even today when debugging Chrome DevTools with Chrome DevTools, window placement is key!!! Ideally, different screens. That keeps the mind clear.

Re: Writing a debugger from scratch: Breakpoints

#43
post #9
post #6

Earlier quoted context omitted.

You use software breakpoints. Basically you overwrite the instruction you want to break at with a breakpoint instruction (e.g. int 3 on x86). This will cause the process to trap and the OS will then let the debugger process know about out somehow, e.g. via the SIGTRAP signal on Unix. The debugger then replaces the int 3 opcode (which is a single byte conveniently) with the first byte of the original instruction so th…

If you revert the int 3 to the original instruction’s byte, when do you put it back? The breakpoint could still be active. In a trivial example, the breaking instruction could be a jump to itself, which you’d expect to immediately break into the debugger again. I thought the debugger had to emulate the instruction instead, but it’s not like I’ve ever implemented one…

As i_don_t_know stated, if the CPU has the ability to single step an instruction, you use that. Otherwise:

* Restore the original instruction byte.

* Find the next instruction, and set a temporary software breakpoint there.

* Resume the one instruction

* Restore the original instruction byte at the temporary software breakpoint.

* Set the software breakpoint in the original instruction

* Resume running

The other thing to keep in mind is dealing with JMP, CALL and conditional branch instructions. It can get pretty messy pretty quick, which is why I find low level debuggers on old 8-bit CPUs a marvel as they had to deal with only software breakpoints.

Re: Writing a debugger from scratch: Breakpoints

#44

I’ll share this anecdote told by a friend of mine. He was on a team building a Modula-2 compiler for OS/2, and his group was working on the debugger. At some point a debugger becomes feature complete enough that you can use the debugger to ... debug the debugger. But this was OS/2 which has true multiple processes (unlike it’s contemporary Windows 3.1). So you could, naturally, run the debugger in one process and att…

It's bugs all the way down.

I want to hear stories about debugger-on-debugger heisenbugs.

Re: Writing a debugger from scratch: Breakpoints

#45
post #42

This is amazing! I've been thinking about writing a debugger (for learning how they work etc.). This series is going to be a massive help!

Honestly it's a great exercise for learning how low level stuff works in general. Happy to answer any questions you have!

Re: Writing a debugger from scratch: Breakpoints

#46
Working at Microsoft back in the early 00s I spent a lot of unfriendly hours with windbg. On one particular project we hunted for a terrible crash for months until it was uncovered that we were compiling against the single thread CRT when using threads extensively...whoops

Re: Writing a debugger from scratch: Breakpoints

#47
I've done something similar in Python: A Python debugger from scratch in Python

- https://github.com/parttimenerd/python-dbg/ - Part 1: https://mostlynerdless.de/blog/2023/09/20/lets-create-a-pyth... - Part 2: https://mostlynerdless.de/?p=1102&preview=1&_ppp=a17cda3e36

Re: Writing a debugger from scratch: Breakpoints

#48

I’ll share this anecdote told by a friend of mine. He was on a team building a Modula-2 compiler for OS/2, and his group was working on the debugger. At some point a debugger becomes feature complete enough that you can use the debugger to ... debug the debugger. But this was OS/2 which has true multiple processes (unlike it’s contemporary Windows 3.1). So you could, naturally, run the debugger in one process and att…

I think my record when I was on the WinDbg team was 5 debuggers deep. I honestly think one of the best parts of writing a debugger is being your own recursive customer. I think that's something you only get to do for a few things. Debuggers, languages/compilers, and operating systems. And probably a few others.

Not 5 levels, but I once wrote debug visualizers for a compiler using funceval (the visualizer uses the debugger to run code in the target process).

I think I once had to debug the debugger debugging the compiler compiling itself which felt like another really weird kind of recursion.

Re: Writing a debugger from scratch: Breakpoints

#49
post #36
post #27

Earlier quoted context omitted.

Because tree-sitter lexes as it parses, you may have to use an external scanner in order to deal with this kind of stuff. Where are you stuck trying to deal with forward declarations?

It's a simple parser that was originally made to be used through Atom that I would like to repurpose elsewhere https://github.com/edmundito/tree-sitter-ags-script/issues/1 If this could be solved, we could port this AGS Script parser to the AGS Editor. Today, the parser Adventure Game Studio uses for the needs like auto-complete and it's very simple refactor like things uses a custom handmade parser built in C#. I th…

It looks like a bug in the grammar. I’ll bookmark this and see if I can make time for it later. Probably won’t be able to. I recently build a grammar from scratch so I’m okay at tree sitter

Re: Writing a debugger from scratch: Breakpoints

#50
post #9

Earlier quoted context omitted.

If you revert the int 3 to the original instruction’s byte, when do you put it back? The breakpoint could still be active. In a trivial example, the breaking instruction could be a jump to itself, which you’d expect to immediately break into the debugger again. I thought the debugger had to emulate the instruction instead, but it’s not like I’ve ever implemented one…

I believe when you resume the debugger, you can tell the process/thread to single-step over one instruction. So it's something like this: 1. Overwrite instruction with int 3. 2. When you hit the breakpoint, restore the original instruction. 3. Single-step over the original instruction by changing the thread's EFlags (Intel). 4. Restore the breakpoint with int 3. 5. Resume normally.

Wouldn’t that race against any other thread in the process? I guess you could stop all threads when you hit the breakpoint and start them again after you restore the breakpoint, but the synchronisation of that would be really tricky too.
Post reply on HN