我正在制作一个小型库,使从web中获取数据对于我的程序的其余部分来说很简单。
图书馆request.c
有6个功能,5个“公共”和1个“私有”。
我使用统一测试框架按照这个顺序测试所有“公共”函数。这会导致test_request_fetch
上的分段错误。
但是,如果我制作了一个小程序,以相同的顺序调用相同的函数,它就会工作得很好。
所以我的测试有什么问题?我刚开始使用C。我试过使用gdb并瞄准val差生,但即使有了这些工具,我也不知道如何调试它。
编辑:在request.c
文件中,request_set_headers
函数、注释掉curl_easy_setopt
和没有标题设置的测试将删除SEGFAULT,并且100%的测试通过。我不知道这是为什么。
请让我知道,如果我应该添加任何信息或澄清的东西。
编辑:我试着按照Andreas的建议做一个最小的可重复示例,但是有很多文件。我做了一个git来简化代码的运行:
项目文件夹设置
requestlib/
src/
request.c
request.h
tests/
includes/ (unity v2.5.2 is here)
test_request.c
try/
simpletest.c
compile-simpletest.sh
ex_bin/
compile.sh
test.sh
CMakeLists.txt
request.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <curl/curl.h>
#include "request.h"
MemoryStruct chunk = {
.memory = NULL,
.size = 0
};
CURL *curl_handle = NULL;
static size_t
WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp)
{
size_t realsize = size * nmemb;
MemoryStruct *mem = (MemoryStruct *)userp;
char *ptr = realloc(mem->memory, mem->size + realsize + 1);
if(!ptr) {
/* out of memory! */
printf("not enough memory (realloc returned NULL)\n");
return 0;
}
mem->memory = ptr;
memcpy(&(mem->memory[mem->size]), contents, realsize);
mem->size += realsize;
mem->memory[mem->size] = 0;
return realsize;
}
int request_fetch(MemoryStruct *putdatahere) {
if (!curl_handle) {
return 100;
}
int rc = 0;
putdatahere = &chunk;
if (chunk.memory != NULL) {
free(chunk.memory);
}
chunk.memory = malloc(1);
chunk.size = 0;
rc += curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&chunk);
rc += curl_easy_perform(curl_handle);
return rc;
}
int request_init(void) {
static int function_runs = 0;
if (function_runs != 0) {
return 100;
}
function_runs += 1;
int rc = 0;
rc += curl_global_init(CURL_GLOBAL_ALL);
curl_handle = curl_easy_init();
rc += curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1L);
rc += curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
// rc += curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&chunk);
rc += curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
return rc;
}
int request_set_headers(char **headers, int num) {
if (!curl_handle) {
return 100;
}
struct curl_slist *list = NULL;
int rc = 0;
char *s;
for (int a = 0; a < num; a = a + 1) {
s = headers[a];
// printf("header set: %s\n", s);
list = curl_slist_append(list, s);
}
/// REMOVING THIS LINE TAKES AWAY SEGFAULT ///
rc = curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, list);
curl_slist_free_all(list);
return rc;
}
int request_set_url(char *url) {
if (!curl_handle) {
return 100;
}
return curl_easy_setopt(curl_handle, CURLOPT_URL, url);
}
int request_cleanup(void) {
static int function_runs = 0;
if (function_runs != 0) {
return 100;
}
function_runs += 1;
if (chunk.memory != NULL) {
free(chunk.memory);
chunk.memory = NULL;
chunk.size = 0;
}
curl_easy_cleanup(curl_handle);
curl_handle = NULL;
curl_global_cleanup();
return 0;
}
request.h
#ifndef REQUEST_H
#define REQUEST_H
#include <stdlib.h>
#include <curl/curl.h>
typedef struct MemoryStruct {
char *memory;
size_t size;
} MemoryStruct;
extern MemoryStruct chunk;
extern CURL *curl_handle;
extern CURLcode res;
// static size_t
// WriteMemoryCallback(void *, size_t, size_t, void *);
int request_init(void);
int request_fetch(struct MemoryStruct *putdatahere);
int request_set_headers(char **, int);
int request_set_url(char *);
int request_cleanup(void);
#endif // REQUEST_H
test_request.c
#include <string.h>
#include <stdlib.h>
#include <curl/curl.h>
#include "../tests/includes/ex_unity/src/unity.h"
#include "../src/request.h"
void setUp(void) {
// we don't need any setup
}
void tearDown(void) {
// don't need any teardown
}
void test_request_init(void) {
char *failmsg = "request_init SHOULD return 0 AND create a curl_handle that is NOT NULL, but it didn't.";
int cond;
int rc = request_init();
cond = (rc == 0 && curl_handle != NULL);
TEST_ASSERT_MESSAGE(cond, failmsg);
failmsg = "On second call, request_init SHOULD return 100, but it didn't.";
rc = request_init();
cond = (rc == 100);
TEST_ASSERT_MESSAGE(cond, failmsg);
}
void test_request_set_headers(void) {
char *failmsg = "request_setopt SHOULD return 0, but did not.";
int cond;
char *headers[] = {"Content-Type: multipart/mixed"};
int rc = request_set_headers(headers, 1);
cond = (rc == 0);
TEST_ASSERT_MESSAGE(cond, failmsg);
// failmsg = "request_set_headers SHOULD return 100 when curl_handle is NULL, but did not.";
// CURL *tmpbak = curl_handle;
// curl_handle = NULL;
// rc = request_set_headers(headers, 1);
// curl_handle = tmpbak;
// cond = (rc == 100);
// TEST_ASSERT_MESSAGE(cond, failmsg);
}
void test_request_set_url(void) {
char *failmsg = "request_set_url SHOULD return 0, but did not.";
int cond;
char *url = "https://raw.githubusercontent.com/undoingtech/Sticky-Steps/main/README.md";
int rc = request_set_url(url);
cond = (rc == 0);
TEST_ASSERT_MESSAGE(cond, failmsg);
failmsg = "request_set_url SHOULD return 100 when curl_handle is NULL, but did not.";
CURL *tmpbak = curl_handle;
curl_handle = NULL;
rc = request_set_url(url);
curl_handle = tmpbak;
cond = (rc == 100);
TEST_ASSERT_MESSAGE(cond, failmsg);
}
void test_request_fetch(void) {
char *failmsg = "request_fetch SHOULD return 100 when curl_handle is NULL, but did not.";
CURL *tmpbak = curl_handle;
curl_handle = NULL;
MemoryStruct *putdatahere = NULL;
int rc = request_fetch(putdatahere);
curl_handle = tmpbak;
int cond = (rc == 100);
TEST_ASSERT_MESSAGE(cond, failmsg);
/// THIS TEST CAUSES THE SEGFAULT ///
failmsg = "request_fetch SHOULD return 0 AND chunk size SHOULD be greater than 0, but did not.";
rc = request_fetch(putdatahere);
cond = (rc == 0);
TEST_ASSERT_MESSAGE(cond, failmsg);
}
void test_request_cleanup(void) {
char *failmsg = "request_cleanup SHOULD return 0 AND set curl_handle to NULL, but it didn't.";
int cond;
int rc = request_cleanup();
cond = (rc == 0 && curl_handle == NULL);
TEST_ASSERT_MESSAGE(cond, failmsg);
failmsg = "On second call, request_cleanup SHOULD return 100, but it didn't.";
rc = request_cleanup();
cond = (rc == 100);
TEST_ASSERT_MESSAGE(cond, failmsg);
}
// expect 2 "still reachable" leaks due to libcurl
// it could be libcurl just leaks
// or it could be I set it up wrong
int main(void) {
UNITY_BEGIN();
RUN_TEST(test_request_init);
RUN_TEST(test_request_set_headers);
RUN_TEST(test_request_set_url);
RUN_TEST(test_request_fetch);
RUN_TEST(test_request_cleanup);
return UNITY_END();
}
CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(requestlib)
add_library(request STATIC src/request.c)
target_link_libraries(request curl)
include(CTest)
find_program( MEMORYCHECK_COMMAND valgrind )
set( MEMORYCHECK_COMMAND_OPTIONS "--trace-children=yes --leak-check=full --dsymutil=yes" )
set( MEMORYCHECK_SUPPRESSIONS_FILE "${PROJECT_SOURCE_DIR}/valgrind_suppress.txt" )
add_executable(test_request tests/test_request.c)
add_library(unity STATIC tests/includes/ex_unity/src/unity.c)
target_include_directories(test_request PUBLIC "tests/includes/ex_unity/src")
target_link_libraries(test_request unity)
target_link_libraries(test_request curl)
target_link_libraries(test_request request)
add_test(
NAME test_request
COMMAND $<TARGET_FILE:test_request>
)
if ( CMAKE_COMPILER_IS_GNUCC )
target_compile_options(test_request PRIVATE -Wall -Wextra)
endif()
if ( MSVC )
target_compile_options(test_request PRIVATE /W4)
endif()
compile.sh
#!/bin/bash
# get script's directory
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
project_path="$(dirname "$SCRIPT_DIR")"
build_path="$project_path/build-linux"
mkdir -p $build_path
cmake -S $project_path -B $build_path
cmake --build $build_path --config "Debug"
test.sh
#!/bin/bash
# get script's directory
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
project_path="$(dirname "$SCRIPT_DIR")"
build_path="$project_path/build-linux"
mkdir -p $build_path
(cd $build_path; ctest -D ExperimentalMemCheck -C "Debug" -V)
单机运行时的偏值输出
1 ==7559== Invalid read of size 8
2 ==7559== at 0x48C21C8: ??? (in /usr/lib/x86_64-linux-gnu/libcurl-gnutls.so.4.7.0)
3 ==7559== by 0x4892496: ??? (in /usr/lib/x86_64-linux-gnu/libcurl-gnutls.so.4.7.0)
4 ==7559== by 0x48A75FD: ??? (in /usr/lib/x86_64-linux-gnu/libcurl-gnutls.so.4.7.0)
5 ==7559== by 0x48A8465: curl_multi_perform (in /usr/lib/x86_64-linux-gnu/libcurl-gnutls.so.4.7.0)
6 ==7559== by 0x487F03A: curl_easy_perform (in /usr/lib/x86_64-linux-gnu/libcurl-gnutls.so.4.7.0)
7 ==7559== by 0x10C56D: request_fetch (in /media/sf_portal/mono/requestlib/so/build-linux/test_request)
8 ==7559== by 0x109523: test_request_fetch (in /media/sf_portal/mono/requestlib/so/build-linux/test_request)
9 ==7559== by 0x10C1BB: UnityDefaultTestRun (in /media/sf_portal/mono/requestlib/so/build-linux/test_request)
10 ==7559== by 0x10964A: main (in /media/sf_portal/mono/requestlib/so/build-linux/test_request)
11 ==7559== Address 0x55752c0 is 0 bytes inside an unallocated block of size 16 in arena "client"
12 ==7559==
13 ==7559== Invalid read of size 1
14 ==7559== at 0x48BD6E3: ??? (in /usr/lib/x86_64-linux-gnu/libcurl-gnutls.so.4.7.0)
15 ==7559== by 0x48C21D5: ??? (in /usr/lib/x86_64-linux-gnu/libcurl-gnutls.so.4.7.0)
16 ==7559== by 0x4892496: ??? (in /usr/lib/x86_64-linux-gnu/libcurl-gnutls.so.4.7.0)
17 ==7559== by 0x48A75FD: ??? (in /usr/lib/x86_64-linux-gnu/libcurl-gnutls.so.4.7.0)
18 ==7559== by 0x48A8465: curl_multi_perform (in /usr/lib/x86_64-linux-gnu/libcurl-gnutls.so.4.7.0)
19 ==7559== by 0x487F03A: curl_easy_perform (in /usr/lib/x86_64-linux-gnu/libcurl-gnutls.so.4.7.0)
20 ==7559== by 0x10C56D: request_fetch (in /media/sf_portal/mono/requestlib/so/build-linux/test_request)
21 ==7559== by 0x109523: test_request_fetch (in /media/sf_portal/mono/requestlib/so/build-linux/test_request)
22 ==7559== by 0x10C1BB: UnityDefaultTestRun (in /media/sf_portal/mono/requestlib/so/build-linux/test_request)
23 ==7559== by 0x10964A: main (in /media/sf_portal/mono/requestlib/so/build-linux/test_request)
24 ==7559== Address 0x0 is not stack'd, malloc'd or (recently) free'd
25 ==7559==
26 ==7559==
27 ==7559== Process terminating with default action of signal 11 (SIGSEGV)
28 ==7559== Access not within mapped region at address 0x0
29 ==7559== at 0x48BD6E3: ??? (in /usr/lib/x86_64-linux-gnu/libcurl-gnutls.so.4.7.0)
30 ==7559== by 0x48C21D5: ??? (in /usr/lib/x86_64-linux-gnu/libcurl-gnutls.so.4.7.0)
31 ==7559== by 0x4892496: ??? (in /usr/lib/x86_64-linux-gnu/libcurl-gnutls.so.4.7.0)
32 ==7559== by 0x48A75FD: ??? (in /usr/lib/x86_64-linux-gnu/libcurl-gnutls.so.4.7.0)
33 ==7559== by 0x48A8465: curl_multi_perform (in /usr/lib/x86_64-linux-gnu/libcurl-gnutls.so.4.7.0)
34 ==7559== by 0x487F03A: curl_easy_perform (in /usr/lib/x86_64-linux-gnu/libcurl-gnutls.so.4.7.0)
35 ==7559== by 0x10C56D: request_fetch (in /media/sf_portal/mono/requestlib/so/build-linux/test_request)
36 ==7559== by 0x109523: test_request_fetch (in /media/sf_portal/mono/requestlib/so/build-linux/test_request)
37 ==7559== by 0x10C1BB: UnityDefaultTestRun (in /media/sf_portal/mono/requestlib/so/build-linux/test_request)
38 ==7559== by 0x10964A: main (in /media/sf_portal/mono/requestlib/so/build-linux/test_request)
39 ==7559== If you believe this happened as a result of a stack
40 ==7559== overflow in your program's main thread (unlikely but
41 ==7559== possible), you can try to increase the size of the
42 ==7559== main thread stack using the --main-stacksize= flag.
43 ==7559== The main thread stack size used in this run was 8388608.
simpletest.c
#include <string.h>
#include <stdlib.h>
#include <curl/curl.h>
#include "../src/request.h"
void step1(void) {
request_init();
}
void step2(void) {
char *headers[] = {"Content-Type: multipart/mixed"};
request_set_headers(headers, 1);
}
void step3(void) {
request_set_url("https://raw.githubusercontent.com/undoingtech/Sticky-Steps/main/README.md");
}
void step4(void) {
MemoryStruct *putdatahere = NULL;
request_fetch(putdatahere);
}
void step5(void) {
request_cleanup();
}
int main(void) {
step1();
step2();
step3();
step4();
step5();
return 0;
}
编译-simpletest.sh(在try文件夹中运行)
#!/bin/bash
gcc -Wall -Wextra -g -o simpletest simpletest.c ../src/request.c -lcurl
发布于 2022-10-04 22:17:01
罪魁祸首在request.c
in request_set_headers()
。
/// REMOVING THIS LINE TAKES AWAY SEGFAULT ///
rc = curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, list);
curl_slist_free_all(list); // <-- curl_easy_perform SHOULD BE BEFORE THIS LINE!!
curl_easy_setopt
和保存头数据的curl_slist
设置头。curl_slist_free_all
设置标头的数据。request_fetch()
中调用curl_easy_perform
。根据这个libcurl例子的命令应该是:
curl_easy_perform
。curl_slist_free_all
。https://stackoverflow.com/questions/73950546
复制相似问题