Coder Social home page Coder Social logo

coolxv / cpp-stub Goto Github PK

View Code? Open in Web Editor NEW
249.0 20.0 78.0 11.05 MB

C++ unit test stub(not mock) and awesome.Surpported ISA x86,x86-64,arm64,arm32,arm thumb,mips64,riscv,loongarch64.

License: MIT License

C++ 98.23% Makefile 0.10% C 1.68%
hook stub mock fake dummy spy unit-testing cpp cpp11 inline-hook

cpp-stub's People

Contributors

coolxv avatar hujianzhong-uos avatar idings avatar jiridanek avatar qdkevinkou avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cpp-stub's Issues

使用lambda表达式作为桩函数的一种思路

核心**就是为lambda表达式生成独一无二的类和执行函数。

#include <cassert>
#include <utility>

#include "cpp_stub/stub.h"

/***
 * FakeFunction 可以封装一个函数或lambda表达式,用于替换目标函数。
 * 需要注意的是模板参数中的N和S,对于同一个测试用例中的FakeFunction,一定不能有相同的N、S组合。
 * 如果测试用例中只有一个FakeFunction或传入的函数声明都不相同时,可以省略N或S。
 * 如果想要通过某个类型区分FakeFunction,请设置模板参数S。
 *
 * 使用示例:
 *      auto fun = []() { prstd::size_tf("hehe\n"); };
 *      FakeFunction<void(void), 1> fl(fun);
 *
 *      decltype(fl)::Exec 即为lambda表达式的函数指针,可以用于Stub::set函数。
 */
template <class T, std::size_t N = 0, class S = void>
class FakeFunction;

template <class Res, class... Args, std::size_t N, class S>
class FakeFunction<Res(Args...), N, S> {
public:
    template <class Function>
    explicit FakeFunction(Function f)
        : m_Call(Call<Function>), m_Release(Release<Function>), m_Function(new Function(f)) {
        assert(m_Instance == nullptr);
        m_Instance = this;
    }

    template <class Method, class Function>
    explicit FakeFunction(Stub* stub, Method m, Function f) : FakeFunction(f) {
        stub->set(m, Exec);
    }

    template <class Method, class Function, class ExitFunction>
    explicit FakeFunction(Stub* stub, Method m, Function f, ExitFunction e) : FakeFunction(stub, m, f) {
        m_CallWhenExit = CallWhenExit<ExitFunction>;
        m_FunctionCallWhenExit = new ExitFunction(e);
    }

    ~FakeFunction() {
        if (m_CallWhenExit != nullptr) {
            m_CallWhenExit(this);
            m_CallWhenExit = nullptr;
        }

        m_Release(this);
        m_Release = nullptr;
        m_Instance = nullptr;
    }

    FakeFunction(const FakeFunction&) = delete;
    FakeFunction& operator=(const FakeFunction&) = delete;

    static Res Exec(Args... args) { return m_Instance->m_Call(m_Instance, std::forward<Args>(args)...); }

private:
    template <class Function>
    static Res Call(FakeFunction* that, Args... args) {
        ++(that->CallTimes);
        Function* f = static_cast<Function*>(that->m_Function);
        return (*f)(std::forward<Args>(args)...);
    }

    template <class Function>
    static void Release(FakeFunction* that) {
        delete static_cast<Function*>(that->m_Function);
    }

    template <class Function>
    static void CallWhenExit(FakeFunction* that) {
        (*static_cast<Function*>(that->m_FunctionCallWhenExit))(that->CallTimes);
        delete static_cast<Function*>(that->m_FunctionCallWhenExit);
    }

public:
    int CallTimes = 0;

private:
    static FakeFunction* m_Instance;
    Res (*m_Call)(FakeFunction*, Args...) = nullptr;
    void (*m_Release)(FakeFunction*) = nullptr;
    void (*m_CallWhenExit)(FakeFunction*) = nullptr;
    void* m_Function = nullptr;
    void* m_FunctionCallWhenExit = nullptr;
};

template <class Res, class... Args, std::size_t N, class S>
FakeFunction<Res(Args...), N, S>* FakeFunction<Res(Args...), N, S>::m_Instance = nullptr;

arm64执行错误

老师,您好:
因为我的待测代码是纯C语言的,所以我只用了stub.h这个头文件,但是用aarch64位编译器编译完成后在飞腾CPU的平台上执行会随机出现两种种不同结果:打桩失败,打桩成功后reset后段错误。
image

交叉编译arm32无法正确运行程序

前两天在使用 stub 进行插桩时,遇到了在 arm32 上执行段错误的问题,通过对源代码的反汇编分析发现,使用 arm-linux-g++ 编译器编译出来的地址和程序中获取函数的地址有差距,后来发现需要在编译的时候添加 -marm 参数。这样就能得到正确的地址了。
11f6541a1ddc6849375b98c057e77f2

Compilation failure on Aarch64 Fedora Linux because of missing `#include <cstdint>`

https://download.copr.fedorainfracloud.org/results/jdanek/skupper-router/fedora-rawhide-aarch64/03820885-skupper-router/builder-live.log.gz

/builddir/build/BUILD/skupper-router/tests/cpp-stub/cpp_stub.h:47:11: error: 'uint32_t' was not declared in this scope
   47 |         ((uint32_t*)fn)[0] = 0x58000040 | 9;\
      |           ^~~~~~~~
/builddir/build/BUILD/skupper-router/tests/cpp-stub/cpp_stub.h:248:13: note: in expansion of macro 'REPLACE_FAR'
  248 |             REPLACE_FAR(this, fn, fn_stub);
      |             ^~~~~~~~~~~
/builddir/build/BUILD/skupper-router/tests/cpp-stub/cpp_stub.h:26:1: note: 'uint32_t' is defined in header '<cstdint>'; did you forget to '#include <cstdint>'?
   25 | #include <map>
  +++ |+#include <cstdint>

The above is my copy of the files. The corresponding lines in this project are at

cpp-stub/src/stub.h

Lines 40 to 41 in 691b625

((uint32_t*)fn)[0] = 0x58000040 | 9;\
((uint32_t*)fn)[1] = 0xd61f0120 | (9 << 5);\

Thread Sanitizer crashes when running `REPLCE_NEAR`

I am using void Stub::set to install my stub. The program crashes when compiled in GCC with Thread Sanitizer.

ThreadSanitizer:DEADLYSIGNAL
==4759==ERROR: ThreadSanitizer: SEGV on unknown address 0x000000001297 (pc 0x7f5971a10868 bp 0x1000010d4a00 sp 0x7ffff03b4e90 T4759)
==4759==The signal is caused by a WRITE memory access.
    #0 __tsan_write1 <null> (libtsan.so.0+0x91868)
    #1 void Stub::set<int (*)(char*, unsigned long, char const*, __va_list_tag*), int (*)(char*, unsigned long, char const*, ...)>(int (*)(char*, unsigned long, char const*, __va_list_tag*), int (*)(char*, unsigned long, char const*, ...)) /home/runner/work/qpid-dispatch/qpid-dispatch/qpid-dispatch/tests/cpp-stub/cpp_stub.h:247 (c_unittests+0x490ea4)
    #2 _DOCTEST_ANON_FUNC_2 /home/runner/work/qpid-dispatch/qpid-dispatch/qpid-dispatch/tests/c_unittests/test_terminus.cpp:88 (c_unittests+0x490ea4)
    #3 doctest::Context::run() /home/runner/work/qpid-dispatch/qpid-dispatch/qpid-dispatch/tests/c_unittests/doctest.h:6486 (c_unittests+0x458747)
    #4 main /home/runner/work/qpid-dispatch/qpid-dispatch/qpid-dispatch/tests/c_unittests/doctest.h:6571 (c_unittests+0x43b762)
    #5 __libc_start_call_main <null> (libc.so.6+0x2d55f)
    #6 __libc_start_main_impl <null> (libc.so.6+0x2d60b)
    #7 _start <null> (c_unittests+0x43c8e4)

My guess is that the address is not instrumented correctly by TSan and that therefore it is correct to resolve this by disabling TSan instrumentation for Stub::set. The Address Sanitizer tool is not complaining, given the exactly same code.

Licensing terms?

First, thanks for this tool. It's a hidden gem.

Can you add a license file to the repository?

I think we can make use of this tool in a project, but won't be able to unless it is available under an open-source license like MIT, GPL2, or GPL3.

can i mock C functin a.lib or a.so

hello,
my project is very complex.I just want ut some function in project, whats build to lib or so.
In samples, include *.cc

你好,我有个C的嵌入式工程,应该说比较复杂,我想只对部分函数做测试,在工程内是被编译为动态库、静态库的。
我看了示例都是将源码引入到测试文件内,不知道是否可以直接对库内函数进行MOCK。

Store to misaligned address in `REPLACE_NEAR` macro

I am using amd64 architecture. I enabled AddressSanitizer and UndefinedBehavior sanitizer in GCC. My tests produce the following undefined behavior error:

cpp_stub.h:244:13: runtime error: store to misaligned address 0x0000004bc2e1 for type 'int', which requires 4 byte alignment
0x0000004bc2e1: note: pointer points here
 fd ff ff  e9 25 8a 3e 69 00 68 2b  00 00 00 e9 30 fd ff ff  ff 25 82 3e 69 00 68 2c  00 00 00 e9 20
              ^ 
pc_0x9534d7###func_void Stub::set<int (*)(char*, unsigned long, char const*, __va_list_tag*), int (*)(char*, unsigned long, char const*, ...)>(int (*)(char*, unsigned long, char const*, __va_list_tag*), int (*)(char*, unsigned long, char const*, ...))###file_/home/jdanek/repos/qpid/qpid-dispatch/tests/cpp-stub/cpp_stub.h###line_244###obj_(c_unittests+0x9534d7)
pc_0x94cb4b###func__DOCTEST_ANON_FUNC_2###file_/home/jdanek/repos/qpid/qpid-dispatch/tests/c_unittests/test_terminus.cpp###line_88###obj_(c_unittests+0x94cb4b)
pc_0x7fc0ce###func_doctest::Context::run()###file_/home/jdanek/repos/qpid/qpid-dispatch/tests/c_unittests/doctest.h###line_6486###obj_(c_unittests+0x7fc0ce)
pc_0x7ff7c0###func_main###file_/home/jdanek/repos/qpid/qpid-dispatch/tests/c_unittests/doctest.h###line_6571###obj_(c_unittests+0x7ff7c0)
pc_0x7f11e90db55f###func___libc_start_call_main###file_<null>###line_0###obj_(libc.so.6+0x2d55f)
pc_0x7f11e90db60b###func___libc_start_main_impl###file_<null>###line_0###obj_(libc.so.6+0x2d60b)
pc_0x4bec64###func__start###file_<null>###line_0###obj_(c_unittests+0x4bec64)

The stub.cpp code is

REPLACE_NEAR(this, fn, fn_stub);

The macro is

    //5 byte(jmp rel32)
    #define REPLACE_NEAR(t, fn, fn_stub)\
        *fn = 0xE9;\
        *(int *)(fn + 1) = (int)(fn_stub - fn - CODESIZE_MIN);\
        //CACHEFLUSH((char *)fn, CODESIZE);

Looking into code, there is more statements suffering from this, e.g. *(long long *)(fn + 2) = (long long)fn_stub;\ in the same file.

For a solution, I am thinking that std::copy or memcpy should solve this, but it feels more clumsy than the current code and it may need helper variable.

请教一下Inline Hook之后有办法在stub函数中调用原函数执行吗?

您好,

感谢您提供那么好用的开源库,我有个问题想请教一下您,希望您能解答一二。

我现在有个场景需要在Stub函数中调用原函数(例如下面的例子,malloc函数已经被替换成malloc_hook函数malloc_hook函数会额外维护一些关于分配的内存地址和内存大小等信息,并调用原函数“malloc”分配内存)。以下代码这种方式hook了malloc函数后,所以malloc_hook内部的malloc函数也会被hook,实际变成了递归函数,与预期不符。

有没有办法在执行了stub.set(malloc, malloc_hook);的情况下,仍有一种方式可以调用原来的malloc函数?

void* malloc_hook(size_t size) {
    printf("Malloc tries to allocate %ld size of memory\n", size);
    //stub.reset(malloc); // 1. 性能影响; 2.多线程有锁和信号量等同步机制时会发生时序问题
    void* = malloc(size);
    //stub.set(malloc, malloc_stub); 
    return ret;
}

int main( ) {
    Stub stub;
    stub.set(malloc, malloc_hook);

    A();
   
    stub.reset(A);
    return 0;

通过mprotect去修改内存页权限的疑问

在stub.h代码中,会通过 mprotect(pageof(pstub->fn), m_pagesize * 2, PROT_READ | PROT_WRITE | PROT_EXEC)去修改函数指针所在的内存页的读写权限,在这里我有个疑惑,请教下您,函数指针所在的页应该属于代码段,属于不可修改的部分,为什么这里会修改成功呢?

overloaded template functions?

Given these static template functions...

template
void foo(int) {}

template<typename X, typename Y>
void foo(int, int, int) {}

Is it possible to stub this? The examples show a class template function, but not a standalone template function.

Incorrect far jump on Linux x86_64.

The following code breaks the calling convention on Linux x86_64 when the callee is a variadic function.

cpp-stub/src/stub.h

Lines 145 to 150 in 540c48c

//12 byte
*(unsigned char*)fn = 0x48;
*((unsigned char*)fn + 1) = 0xb8;
*(unsigned long long *)((unsigned char *)fn + 2) = (unsigned long long)fn_stub;
*(unsigned char *)((unsigned char *)fn + 10) = 0x50;
*(unsigned char *)((unsigned char *)fn + 11) = 0xc3;

It changes the RAX register. However the least 8 bits of the register are used for the number of floating point arguments passed to a variadic function. (See here)

ARM32 run Segment fault.

compile command: arm-linux-gnueabi-g++ test_object_member_function_linux.cpp -std=c++11 -I ../src -o test_object_member_funciton_linux
then move to arm32 run it .

[root@DMD3A ~/bin]#./test_object_member_funciton_linux 
Segmentation fault

A segmentation fault occurs when it compiles the UT on the arm64 platform.

root@centos:/home/xxx/cpp-stub/ut_examples/gtest/ut/src# ll
total 12
drwxr-xr-x. 4 root root 87 Feb 4 07:57 ./
drwxr-xr-x. 5 root root 43 Feb 3 10:12 ../
-rw-r--r--. 1 root root 588 Feb 4 07:31 Makefile
drwxr-xr-x. 2 root root 25 Feb 4 07:57 fixtures/
-rw-r--r--. 1 root root 220 Feb 3 09:56 main.cpp
drwxr-xr-x. 2 root root 26 Feb 4 07:57 mocks/
-rw-r--r--. 1 root root 3233 Feb 4 07:27 test_obj.cpp
root@centos:/home/xxx/cpp-stub/ut_examples/gtest/ut/src# make
g++ -std=c++11 -g -no-pie -fno-stack-protector -Wall -Wno-unused-function -Wno-unused-variable -Wno-pmf-conversions -I../include -Imocks -Ifixtures -I../../src -I../../../../src -c -MMD test_obj.cpp -o test_obj.o
g++ -std=c++11 -g -no-pie -fno-stack-protector -Wall -Wno-unused-function -Wno-unused-variable -Wno-pmf-conversions -I../include -Imocks -Ifixtures -I../../src -I../../../../src -c -MMD main.cpp -o main.o
g++ -std=c++11 -g -no-pie -fno-stack-protector -Wall -Wno-unused-function -Wno-unused-variable -Wno-pmf-conversions ./test_obj.o ./main.o -L../lib -lgtest -lgmock -lm -lpthread -o ut
root@centos69203:/home/xxx/cpp-stub/ut_examples/gtest/ut/src# ./ut --gtest_filter=get_number_test.returns_correct_value
Note: Google Test filter = get_number_test.returns_correct_value
[==========] Running 1 test from 1 test case.
[----------] Global test environment set-up.
[----------] 1 test from get_number_test
[ RUN ] get_number_test.returns_correct_value
Segmentation fault (core dumped)
root@centos:/home/xxx/cpp-stub/ut_examples/gtest/ut/src# ll
total 4340
drwxr-xr-x. 4 root root 179 Feb 4 07:58 ./
drwxr-xr-x. 5 root root 43 Feb 3 10:12 ../
-rw-r--r--. 1 root root 588 Feb 4 07:31 Makefile
-rw-------. 1 root root 2031616 Feb 4 07:58 core.90826
drwxr-xr-x. 2 root root 25 Feb 4 07:57 fixtures/
-rw-r--r--. 1 root root 220 Feb 3 09:56 main.cpp
-rw-r--r--. 1 root root 1700 Feb 4 07:58 main.d
-rw-r--r--. 1 root root 116440 Feb 4 07:58 main.o
drwxr-xr-x. 2 root root 26 Feb 4 07:57 mocks/
-rw-r--r--. 1 root root 3233 Feb 4 07:27 test_obj.cpp
-rw-r--r--. 1 root root 1850 Feb 4 07:58 test_obj.d
-rw-r--r--. 1 root root 1479800 Feb 4 07:58 test_obj.o
-rwxr-xr-x. 1 root root 844208 Feb 4 07:58 ut*
root@centos:/home/xxx/cpp-stub/ut_examples/gtest/ut/src# gdb ut core.90826
GNU gdb (Ubuntu 8.1.1-0ubuntu1) 8.1.1
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later http://gnu.org/licenses/gpl.html
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "aarch64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
http://www.gnu.org/software/gdb/bugs/.
Find the GDB manual and other documentation resources online at:
http://www.gnu.org/software/gdb/documentation/.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ut...done.
[New LWP 90826]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/aarch64-linux-gnu/libthread_db.so.1".
Core was generated by `./ut --gtest_filter=get_number_test.returns_correct_value'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0 0x000000000040cf88 in Stub::set<int (Obj::)(), int ()(void*)> (this=0xffffdb3e9b10, addr=&virtual Obj::get_number(), addr_stub=0x40a17c <get_number_stub(void*)>) at ../../../../src/stub.h:305

warning: Source file is more recent than executable.
305 std::memcpy(pstub->code_buf, fn, CODESIZE_MIN);
(gdb) bt
#0 0x000000000040cf88 in Stub::set<int (Obj::)(), int ()(void*)> (this=0xffffdb3e9b10, addr=&virtual Obj::get_number(), addr_stub=0x40a17c <get_number_stub(void*)>) at ../../../../src/stub.h:305
#1 0x000000000040a1cc in get_number_test_returns_correct_value_Test::TestBody (this=0x1b103450) at test_obj.cpp:75
#2 0x0000ffff7f434548 in void testing::internal::HandleSehExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::)(), char const) () from /usr/local/gtest/lib/libgtest.so
#3 0x0000ffff7f42dbd8 in void testing::internal::HandleExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::)(), char const) () from /usr/local/gtest/lib/libgtest.so
#4 0x0000ffff7f40c050 in testing::Test::Run() () from /usr/local/gtest/lib/libgtest.so
#5 0x0000ffff7f40c93c in testing::TestInfo::Run() () from /usr/local/gtest/lib/libgtest.so
#6 0x0000ffff7f40cff0 in testing::TestCase::Run() () from /usr/local/gtest/lib/libgtest.so
#7 0x0000ffff7f417160 in testing::internal::UnitTestImpl::RunAllTests() () from /usr/local/gtest/lib/libgtest.so
#8 0x0000ffff7f4357cc in bool testing::internal::HandleSehExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::)(), char const) () from /usr/local/gtest/lib/libgtest.so
#9 0x0000ffff7f42eb10 in bool testing::internal::HandleExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::)(), char const) () from /usr/local/gtest/lib/libgtest.so
#10 0x0000ffff7f415ce0 in testing::UnitTest::Run() () from /usr/local/gtest/lib/libgtest.so
#11 0x00000000004182ac in RUN_ALL_TESTS () at ../include/gtest/gtest.h:2341
#12 0x0000000000418218 in main (argc=1, argv=0xffffdb3ea008) at main.cpp:12
(gdb) q
root@centos:/home/xxx/cpp-stub/ut_examples/gtest/ut/src# readelf -h ut
ELF Header:
Magic: 7f 45 4c 46 02 01 01 03 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - GNU
ABI Version: 0
Type: EXEC (Executable file)
Machine: AArch64
Version: 0x1
Entry point address: 0x409e70
Start of program headers: 64 (bytes into file)
Start of section headers: 841840 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 9
Size of section headers: 64 (bytes)
Number of section headers: 37
Section header string table index: 36

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.