On 08/01/2019 21:57, Jonas Devlieghere wrote:
Before I got around to coding this up I realized you can't take the address of constructors in C++, so the function address won't work as an identifier.

You gave up way too easily. :P

I realized that constructors are going to be tricky, but I didn't want to dive into those details until I knew if you liked the general idea. The most important thing to realize here is that for the identifier thingy to work, you don't actually need to use the address of that method/constructor as the identifier. It is sufficient to have something that can be deterministically computed from the function. Then you can use the address of *that* as the identifier.

I've created a very simple prototype <https://godbolt.org/z/_xDt5r>, where I do just that. The way I handle constructors there is that I create a special class template (construct), whose instantiations are going to be unique for each constructor (I achieve that by making the class name and the constructor argument types the template parameters of that function). Then I can take the address of the static member function inside this class (&construct<class, arguments...>::doit), and use *that* as the ID.

As a nice side-effect, the "doit" method actually does invoke the constructor in question, so I can also use that in the replay code to treat constructors like any other method that returns an object.

I also do the same thing for (non-static) member functions via the "invoke" template, because even though it is possible to take the address of those, it is very hard to do anything else with the retrieved pointer. So the effect of this that in the rest of the code, I only have to work with free functions, as both constructors and member functions are converted into equivalent free functions. I haven't tried to handle destructors yet, but I don't think those should pose any problems that we haven't encountered already.

The example also show how you can use templates to automatically generate replay code for "simple" (i.e. those where you can (de)serialize each argument independently) functions, and then uses that to record/replay a very simple API.

You can see it in action like this:
$ g++ a.cc  # compile
$ ./a.out 2>/tmp/recording # generate the recording
SBFoo 47 42
Method 1 2
Static 10 11
$ cat /tmp/recording
0  # ID of the constructor
47 # constructor arg 1
42 # constructor arg 2
0x7ffd74d9a0f7 # constructor result
1  # id of SBFoo::Method
0x7ffd74d9a0f7 # this
1  # arg 1
2  # arg 2
2  # id of SBFoo::Static
10 # arg 1
11 # arg 2
$ ./a.out 1 < /tmp/recording # replay the recording
SBFoo 47 42
SBFoo 42 47
Method 1 2
Static 10 11

Note that when replaying the SBFoo constructor is called twice. This is because this code does not attempt to track the object instances in any way... it just creates a new one each time. This obviously needs to be fixed, but that's independent of the function ID issue.

hope you find that useful,
lldb-dev mailing list

Reply via email to