Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

>> Why can't I debug a function without restarting my program?

> Because you use C or one of its derivatives instead of Lisp or one of its derivatives. In Common Lisp you can not only debug a function without restarting your program, you can redefine classes without restarting your program. It is truly awesome. You should try it some time.

There is no technical reason why it shouldn't be possible in C, if you are willing to do without many optimizations. One approach is to make a function stub that simply jumps into the currently loaded implementation. A more efficient but more convoluted way is to patch the address of the currently loaded implementation at all call sites.

The problem is that in general you can't simply replace a function without either restarting the process or risking to crash it. In general functions have some implementation-dependent context that is accumulated in the running process, and a different implementation does the accumulation in a different way. I'm not a lisper, but there is no way this is different in LISP. (And it is not because in C you often use global data. "Global" vs "local" is only a syntactic distinction anyway).

If you're willing to risk crashing your process that's okay. It's often a fine choice. And you can do it in C. The easiest way to implement approach #1 is to load DLLs / shared objects.



There is no technical limitation why it is impossible in C, however the raw details of how C is implemented make it entirely impractical.

A super generalized description of a C function might check if a register contains a positive value. If so, it will then jump to address 0x42, which is a memory offset, which is the beginning of another function. Its near impossible to "swap" out what lies at 0x42 since that was defined at compile time and is included in a monolithic executable.

Looking at more dynamic languages, like C#, Java or LISP, they run on a virtual or abstracted machine instead of raw registers. This means that a similarly defined function will instead jump to whatever matches the requirement. That means that we could have a lookup table that says we should jump to a symbol of S42, and based on what we have loaded in memory, S42 resides at 0x42. Essentially, all functions are function pointers, and we can change the value the resides in that memory address in order to swap out implementations of whatever function maintains the same signature as the intended function. This is why one can make trivial changes to C# in visual studio while stopped at a breakpoint and have those changes applied to the running program. Instead of jumping to 0x42, we jump to 0x84 by "hotswapping" the value of the pointer we're about to jump into.

Obviously this isn't entirely the truth, there are a lot more nuances and it's a fair bit more complicated than this, but the idea should hold water.


Your point is moot. C doesn't "run on raw registers". That's just common practice but has nothing to do with C per se. You can easily run it on the C#'s virtual machine.

Furthermore, it doesn't matter whether you are running on a virtual machine or on bare metal. What matters is if you have turned on optimizations (such as hardcoding of function addresses, or even code inlining) that remove flexibility (such as hot swapping of code). Visual Studio can hot-edit C code as well.

And as I stated, it is pretty easy and common practice to hot swap object code through DLLs or shared objects even on less high-tech platforms. It's easily done with function pointers (as you described) and a simple API (GetProcAddress() on Windows, dlsym() on Linux). Why shouldn't it be possible in C?

Virtual Machines bring portable executables and nothing more, I think. Well, maybe a slightly nicer platform for co-existence and interoperability of multiple programming languages (but then again, there is considerable lock-in to the platform).


> There is no technical limitation why it is impossible in C,

Yes, there is. You can't trace the stack in portable C, so you can't build a proper garbage collector.


Debugging a function and GC are independent things. Also, you can easily add tracing information since you will know how the C code is being run. There is no good reason not to.

There are easy ways to build a GC in portable C as well, of course, if less performant.

As you will know, in portable C you cannot even implement a system call. So what?


> Debugging a function and GC are independent things.

Yes, but redefining a function and GC are not.

> There are easy ways to build a GC in portable C as well, of course, if less performant.

No, because you can't walk the stack. Also pointer aliasing.

[UPDATE] I just realized that I was wrong about redefining functions in C. Not only is it possible, you can actually do it with currently available technology using dynamically linked libraries. But I have never seen anyone actually do this (which is why it didn't occur to me).


> But I have never seen anyone actually do this (which is why it didn't occur to me).

I wrote about this in my two earlier comments. This is very old technology. And very commonly used. I think most plugin systems wrap dynamically linked libraries.

This is also an easy way to redefine functions without needing GC. Under the hood, it is implemented in the loader's way: virtual memory pages mapped as read-only and executable (see mmap() or mprotect() for example).




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: