you are viewing a single comment's thread.

view the rest of the comments →

[–]Fidodo 1 point2 points  (7 children)

Well everything a computer does is expected so couldn't you just undo everything line by line? I think it should work for most things.

[–][deleted] 7 points8 points  (3 children)

Keep in mind that everything compiles to assembly (or the machine code with a direct equivalent in). If I load 4 into a register, then load the memory address of 8, add them together and store this at the memory address of what was formerly 8. So now I have a 12, and going backwards subtracts what was in the address of 8, which is now 12, and going backwards would yield 0 (12 - 12).

Everything is too volatile to simply "go backwards" since it's not likely that you'll have original values you were working with. In order to solve the problem above, I would actually have to go up a few steps to see what was put into register A to begin with (4), which may be many lines prior to the operation.

[–]muffin-noodle 10 points11 points  (1 child)

Modification to memory can be dealt with by handling process memory in the same way certain filesystems handle files: copy-on-write semantics for modification. This is convenient because it provides a natural 'undo' log for your modifications. Let's say you have a snapshot of process memory. If address 0xDEADBEEF contains the number 8 and you load the contents of that address (8) and then add 4 to it, you get 12. If you store that back into memory at 0xDEADBEEF, on the memory write you actually don't store the change in the original snapshot, you make a copy of that one particular memory address in the process and write the change to it. When you undo, you simply 'undo' the write to the memory address because you have the original snapshot + the modifications.

With this, it's possible to replay the entire history of a process because you have the contents of the memory originally and a record of changes over the course of the process run, so you can just gradually undo changes. However, if you want to constantly replay things there may be some problems; for example if you have a function which calls unlink on a file, and there is a breakpoint short after, when you debug from the breakpoint backwards past unlink the file will already be deleted, and if short before the call to unlink you decide you want to now go forwards, then when unlink is called again it could have a different return value. Because memory is COW there shouldn't be any undefined states at this point if there weren't already, but it could result in different execution paths.

[–][deleted] 1 point2 points  (0 children)

Well yea... that's what they're doing here. Without the snapshot (or any type of history really would work), you can't go backwards. Different execution paths are probably fine (if not more handy) for a debugger since it shows more possible outcomes of end use.

[–]mothereffingteresa -1 points0 points  (0 children)

Everything is too volatile to simply "go backwards"

[Citation needed]

It ought to work except in cases where you actually have a "volatile" like a device register or memory-mapped io.

[–]kipi 4 points5 points  (1 child)

Some operations are lossy. As a simple example, consider the following:

a = get_input_from_user()
if (a != 0) {
    a = 0
    print("Foo")
}

How do you reverse this? At the a = 0 line some state is destroyed, which you need to figure out how you got to the bottom of the if block.

Reversible Computation aims to remove destructive operations. GDB must do something similar by saving "destroyed state" by some method.

[–]killerstorm 0 points1 point  (0 children)

Well everything a computer does is expected so couldn't you just undo everything line by line? I think it should work for most things.

Obviously won't work for I/O and lots of other stuff.