The least annoying thing about writing and working with Linux debuggers is that all of the communication layers (kernel<->debugger, debugger<->C library, debugger<->loader, debugger<->compiler [1]) is basically undocumented. I mean, it's annoying trying to track down where in the source code these things exist, but if you have experience working in large codebases, this kind of task is needed sufficiently frequently that it's not difficult.
The real issue with debuggers is that ptrace is a pretty broken API. Supporting things like spawning threads, forking processes, fork+exec, etc. is difficult, and full of race conditions that are difficult to code correctly. Attaching to running multithreaded processes is another challenge. Writing a debugger that can correctly handle multithreaded applications is challenging, the documentation gives you zero insight into what the potential pitfalls are, and almost all examples are similarly uninformative, being too complex for their use case.
[1] Yes, there's DWARF. But if you're dealing with GNU extensions to DWARF, the documentation ceases...
The real issue with debuggers is that ptrace is a pretty broken API. Supporting things like spawning threads, forking processes, fork+exec, etc. is difficult, and full of race conditions that are difficult to code correctly. Attaching to running multithreaded processes is another challenge. Writing a debugger that can correctly handle multithreaded applications is challenging, the documentation gives you zero insight into what the potential pitfalls are, and almost all examples are similarly uninformative, being too complex for their use case.
[1] Yes, there's DWARF. But if you're dealing with GNU extensions to DWARF, the documentation ceases...