Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

mmk_mock/mmk_when question #5

Open
kugel- opened this issue Mar 28, 2017 · 5 comments
Open

mmk_mock/mmk_when question #5

kugel- opened this issue Mar 28, 2017 · 5 comments

Comments

@kugel-
Copy link

kugel- commented Mar 28, 2017

Hello,

I know this is very experimental, but we're playing around with mimick in the need for a decent mocking framework.

I'm mainly asking for clarification. I found that the mock is installed by calling mmk_mock(). It doesn't seem to matter when mmk_when() is called, so long it's called after mmk_mock().

Is this intended like this? I think it makes the API a bit awkward, if you want to install the mock in the middle of a test. It would seem more natural to have the mmk_when() call at the beginning of a scope, and making it effective with a later mmk_mock() call.

Also, maybe I'm missing something, but as of now mmk_mock() could also be folded into mmk_when(), and then calling mmk_when() when the mock should be installed.

Don't think of this as a bug report, I just want to understand mimick better, because it seems very useful.

@kugel-
Copy link
Author

kugel- commented Mar 28, 2017

I have two more questions:

  1. Have you considered libffi for installing trampolines in a more portable manner (libffi supprts a vast number of architectures)?
  2. Can you install real functions for mocks instead of mmk_when()-sugar? The ability to return expressions and to modify arbitrary global variables seems limited inside mmk_when().

@Snaipe
Copy link
Owner

Snaipe commented Mar 28, 2017

The thing is that mmk_when is supposed to be called multiple times -- it just setups an input-output binding for an existing mock. Think of it as it inserting an if (actual_inputs == when_inputs) return when_output in the mock's code.

It might be a bit awkward when you have a really simple behaviour to describe, but when you want to make a mock that does something a bit more complicated depending on inputs, putting that complex handling in one big mmk_when makes things hard, when you could split that into multiple mmk_when.

As for your questions:

  1. I'll take a more in-depth look at libffi. The problem with mimick is that it has to both do this low-level plumbing while not being able to trust anyone (which is why mimick is redefining a few libc functions itself because it cannot assume that they aren't going to be mocked themselves). If libffi is using internally some functions that are probably going to be mocked, this is not going to work out.

  2. You could either just install a stub with mmk_stub_create using your function, or use the .then_call parameter in mmk_when. I'd recommend the first one if you don't want to specify additional behaviour with mmk_when, or don't care about verifying relationships with mmk_verify.

@kugel-
Copy link
Author

kugel- commented Mar 28, 2017

ffi is really low-level too. I'm not aware that it calls libc functions besides mmap() and mprotect() to get writable, then executable sections. It goes great lengths for many architectures to install trampoline functions with any number of parameters and even variadic functions (the trampoline functions can call back into user code which can then introspect the parameters). It can also be linked statically.

Just saying, it would be help to get rid of assembler code and support more architectures.

@Snaipe
Copy link
Owner

Snaipe commented Mar 28, 2017

I'll send an email to their mailing list to get some more info. libffi seems to heavily rely on the assumption that callers know exactly the types & calling conventions of their functions are, which is sadly not the case for mimick -- we just forward a call to another function, preserving registers along the way as they are. I hope there's a way for me to use libffi for just that, but I don't know enough of the library yet.

@Snaipe
Copy link
Owner

Snaipe commented Jul 13, 2017

So I investigated some more on how I could "convince" libffi (or dyncall, for that matter), but I don't think this is going anywhere.

Fundamentally, the problem is very delicate for Mimick:

  • Mimick has to make sure that the user isn't mocking one of the functions it's using -- so I have to be extra careful when writing trampoline code.
  • The trampoline implementation must not call the mock function. It tries to be as invisible as possible, by not touching at all the registers that should have been passed to the real function. It even has to jmp, to avoid messing with the stack. In contrast, libffi/dyncall provide an interface to call foreign functions, which is not what we want.
  • The trampoline has no idea what the parameters are. We just know someone called us, and we have a bunch of registers that may or may have some significance as parameters. So, all in all, I can't build a function call with libffi, since they need those informations.
  • Mimick's trampoline must prepare the mock context before handing control to the mock function. Even if I tried to integrate libffi to do my bidding, I would still end up in square one with the problem of preparing the libffi procedure to call, which is even more intrusive for the trampoline.

All in all, libffi & dyncall both address the problem of calling foreign functions, but that's not the problem we are trying to solve with trampolines in Mimick. It's a shame because that means that I'll have to maintain & test a bunch of different architectures, but I don't know any other project that does this.

ziyao233 pushed a commit to ziyao233/Mimick that referenced this issue Feb 9, 2024
* Drop conflicting type aliasing.
* Force C linkage if compiling for C++.
* Cast explicitly and deal with C99 features.
* Handle array arguments properly.
* Avoid brace initializers for scalars.
* Force C++11.

Signed-off-by: Michel Hidalgo <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants