sinojelly / mockcpp

Two C/C++ testing tools, mockcpp and testngpp.
Apache License 2.0
67 stars 42 forks source link

MOCKER(write) 报 Segmentation fault (core dumped) 错误 #47

Open ejoful opened 1 year ago

ejoful commented 1 year ago

操作系统:Ubuntu 22.10 Linux ubuntu22-xxx 5.19.0-35-generic #36-Ubuntu SMP PREEMPT_DYNAMIC Fri Feb 3 18:36:56 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux

[root@xxx ~]# cat /etc/os-release PRETTY_NAME="Ubuntu 22.10" NAME="Ubuntu" VERSION_ID="22.10" VERSION="22.10 (Kinetic Kudu)" VERSION_CODENAME=kinetic ID=ubuntu ID_LIKE=debian HOME_URL="https://www.ubuntu.com/" SUPPORT_URL="https://help.ubuntu.com/" BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/" PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy" UBUNTU_CODENAME=kinetic LOGO=ubuntu-logo

编译器:gcc 12.2 [root@xxx ~]# gcc -v Using built-in specs. COLLECT_GCC=gcc COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/12/lto-wrapper OFFLOAD_TARGET_NAMES=nvptx-none:amdgcn-amdhsa OFFLOAD_TARGET_DEFAULT=1 Target: x86_64-linux-gnu Configured with: ../src/configure -v --with-pkgversion='Ubuntu 12.2.0-3ubuntu1' --with-bugurl=file:///usr/share/doc/gcc-12/README.Bugs --enable-languages=c,ada,c++,go,d,fortran,objc,obj-c++,m2 --prefix=/usr --with-gcc-major-version-only --program-suffix=-12 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-plugin --enable-default-pie --with-system-zlib --enable-libphobos-checking=release --with-target-system-zlib=auto --enable-objc-gc=auto --enable-multiarch --disable-werror --enable-cet --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-offload-targets=nvptx-none=/build/gcc-12-U8K4Qv/gcc-12-12.2.0/debian/tmp-nvptx/usr,amdgcn-amdhsa=/build/gcc-12-U8K4Qv/gcc-12-12.2.0/debian/tmp-gcn/usr --enable-offload-defaulted --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu Thread model: posix Supported LTO compression algorithms: zlib zstd gcc version 12.2.0 (Ubuntu 12.2.0-3ubuntu1)

mockcpp 版本:2.7 gtest 版本:1.13.0 下载地址: https://github.com/sinojelly/mockcpp/archive/refs/tags/v2.7.zip https://github.com/google/googletest/archive/refs/tags/v1.13.0.zip 编译运行步骤:

  1. 在 /tmp 目录下创建文件夹 cd /tmp mkdir gtest_mockcpp cd gtest_mockcpp mkdir 3rd mkdir 3rd/gtest mkdir 3rd/mockcpp
  2. 下载 mockcpp-2.7.zip、googletest-1.13.0.zip并解压 unzip mockcpp-2.7.zip unzip googletest-1.13.0.zip
  3. 编译、安装 gtest cd /tmp/gtest_mockcpp/googletest-1.13.0 修改 CMakeLists.txt文件添加 : set(CMAKE_INSTALL_PREFIX ${CMAKE_CURRENT_SOURCE_DIR}/../3rd/gtest) set(CMAKE_CXX_FLAGS "-std=c++14 -g -Wall -fPIE -pie -O0") 编译安装 mkdir cmake && cd cmake cmake .. make make install
  4. 编译、安装 mockcpp cd /tmp/gtest_mockcpp/mockcpp-2.7 修改 build_install.sh: set -e 改为 set -e CURRENT_DIR=$(cd `dirname $0`; pwd) INSTALL_PATH=~/test_tools/mockcpp_install 改为 INSTALL_PATH=${CURRENT_DIR}/../3rd/mockcpp XUNIT_NAME=testngpp 改为 #XUNIT_NAME=testngpp

    XUNIT_NAME=gtest 改为 XUNIT_NAME=gtest

    XUNIT_HOME=/usr/local 改为 XUNIT_HOME=${CURRENT_DIR}/../3rd/gtest

    修改 CMakeLists.txt 加上 set(CMAKE_CXX_STANDARD 14) 编译选项 执行sh build_install.sh

  5. 准备测试用例 cd /tmp/gtest_mockcpp mkdir code && cd code vim CMakeLists.txt vim test_main.cpp

测试用例: CMakeLists.txt

cmake_minimum_required(VERSION 2.8)
PROJECT(mockcpp_demo)
set(targetname mockcpp_demo)

set(CMAKE_CXX_FLAGS "-std=c++14 -g -Wall -fPIE -pie  -O0")
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

set(CURRENT_DIR ${CMAKE_CURRENT_SOURCE_DIR})
#******************************** DTFramework_C 配置 ************************
# 指定对应的头文件目录、静态库目录
if (NOT DEFINED DT_C_DIR)
    set(DT_C_DIR "${CURRENT_DIR}/../3rd")
endif ()

LINK_DIRECTORIES(${DT_C_DIR}/mockcpp/lib)
INCLUDE_DIRECTORIES(${DT_C_DIR}/mockcpp/include)

LINK_DIRECTORIES(${DT_C_DIR}/gtest/lib64)
INCLUDE_DIRECTORIES(${DT_C_DIR}/gtest/include)

add_executable(${targetname} test_main.cpp)
target_link_libraries(${targetname}  -pie -Wl,--start-group   pthread dl gtest gtest_main mockcpp -Wl,--end-group )

install(TARGETS ${targetname}
        RUNTIME  DESTINATION ${CMAKE_SOURCE_DIR}/output)

test_main.cpp

#include "gtest/gtest.h"
#include "mockcpp/mockcpp.hpp"
#include <stdio.h>
#include <iostream>
#include <fstream>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <cstdio>
#include <unistd.h>
#include <stdlib.h>

using namespace std;

ssize_t writeTest(int fd, const void *buf, size_t count) {
    cout << "line "<<__LINE__ << endl;
    cout << "3======" << endl;
    string absFileName = "./file.txt";
    fd = open(absFileName.c_str(), O_CREAT | O_RDWR | O_TRUNC, S_IREAD | S_IWRITE);
    string cipherKey = "asdfghjkl";
    int ret = write(fd, cipherKey.c_str(), cipherKey.length());
    close(fd);
    cout << "4======" << endl;
    cout << "line " <<__LINE__<< endl;
    return 3;
}

class Test_Demo_Suit : public testing::Test {
public:
    static void SetUpTestCase()
    {
        /*SetUpTestCase该函数在每个用例套执行前会执行*/
    }

    static void TearDownTestCase()
    {
        /* 该函数在每个用例套执行后会执行 */
    }

    virtual void SetUp()
    {
    }

    virtual void TearDown()
    {
        /* 该函数在每个测试用例执行完成后会执行 */
        GlobalMockObject::verify();
    }
};

TEST(UnitTest, writeToOpen) {
    cout << "line "<<__LINE__ << endl;
    MOCKER(write).defaults().will(invoke(writeTest));
    cout << "line "<<__LINE__ << endl;
}

int main(int argc, char* argv[]) {
    printf("\n hello,world");
    testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}

报错信息: [root@xxx cmake]# mockcpp_demo

hello,world[==========] Running 1 test from 1 test suite. [----------] Global test environment set-up. [----------] 1 test from UnitTest [ RUN ] UnitTest.writeToOpen line 56 Segmentation fault

ejoful commented 1 year ago

在 gcc 10.3.1 下面也有这个报错,麻烦大佬看看

ff12323 commented 1 year ago

你要mock系统调用可还行。。。 这个不是mock框架的事。 建议查看如下回答: https://stackoverflow.com/questions/71088486/how-to-mock-system-call-in-c-unit-testing-during-runtime-using-googlemock https://stackoverflow.com/questions/28392277/mocking-free-function

ejoful commented 1 year ago

mock是把被mock函数换成另外的函数了,mockcpp也是这样。mock系统函数,很可能出现问题。 建议不要mock系统函数。实在需要mock write函数,可以把它封装成另外一个 my_write 这样的函数,mock my_write。自己的被测代码里面不要直接调用write,而是调用 my_write。

你要mock系统调用可还行。。。 这个不是mock框架的事。 建议查看如下回答: https://stackoverflow.com/questions/71088486/how-to-mock-system-call-in-c-unit-testing-during-runtime-using-googlemock https://stackoverflow.com/questions/28392277/mocking-free-function

你发的是使用 googlemock 来 mock 系统函数,麻烦给贴个mockcpp的使用例子,多谢

ff12323 commented 1 year ago

这个mockcpp的实现方式,叫做 inline hook。大概是这样的: 1、给定函数的地址,其在内存中属于代码段 .text;属于只读 =====> 需要通过系统调用: mprotect,将其设置为可写,而修改汇编代码; 2、x86-64位机器实现:通过修改text段,函数代码的开头14个字节。变为 ===> jmp mock_func( 14字节 == jmp指令(6字节) + 函数地址(8字节))

一般函数修改代码段还好,能够起到mock的效果。 你这样会乱修改关键地方、或者动态库的,框架不支持。出现core,自己分析定位。