35

I am trying to test the dll which I wrote with GoogleTest and when I call one of the tests It throws me this error:

enter image description here

I have come to the conclusion that the problem is in assigning memory to vectors but I don't know how to resolve this as I am fairly new to C++ programming. The code is as follows:

#ArraysCPP11.h
#ifdef ARRAYSCP11_EXPORTS
#define ARRAYSCP11_API __declspec(dllexport)
#else
#define ARRAYSCP11_API __declspec(dllimport)
#endif

__declspec(dllexport) void removeWhiteSpaces(std::vector<std::string> v, std::vector<std::string> &output);
#ArraysCPP11.cpp
void removeWhiteSpaces(std::vector<std::string> v, std::vector<std::string> &output) { //odstranjevanje presledkov iz vector-ja (vsak drugi element je bil presledek)
    for (std::vector<std::string>::iterator it = v.begin(); it != v.end(); it++) {
        std::string buffer = *it;
        if (isdigit(buffer[0])){;
            output.push_back(*it);
        }
    }
}
#TestTemp.h

template<class T> 
class TestTemp
{
public:
   TestTemp();
   void SetValue(T obj_i);
   T GetValue();
   bool alwaysTrue();
   bool TestTemp<T>::formattingTest(std::string input, std::vector<std::string> realVector, std::vector<std::string> formattedInput);
private:
   T m_Obj;
};

template<class T>
inline bool TestTemp<T>::formattingTest(std::string input, std::vector<std::string> realVector, std::vector<std::string> formattedVector) {
std::string input2 = input;
//  std::vector<std::string> fResult;
std::string first;
std::string second;
bool endResult = true;
std::vector<std::string> end;
//std::vector<std::string> result = split(input2, ' ');
removeWhiteSpaces(formattedVector,end);
std::vector<std::string>::iterator yt = realVector.begin();
for (std::vector<std::string>::iterator it = end.begin(); it != end.end(); it++, yt++) {
    first = *it;
    second = *yt;
    if (first.compare(second) != 0) {
        endResult = false;
        break;
    }
}
return endResult;
}
   #ArraysCPP11-UnitTest.cpp
struct formattingTesting{
   //   formattingTesting* test;
   std::string start;
   std::vector<std::string> endResult;
   formattingTesting() {
   }
   explicit formattingTesting(const std::string start, const std::vector<std::string> endResult)
    : start{start}, endResult{endResult} 
   {
   }
};

struct fTest : testing::Test {
   formattingTesting* test;
   fTest() {
      test = new formattingTesting;
   }
   ~fTest() {
      delete test;
   }
};

struct format {
   std::string start;
   std::vector<std::string> end;
};

struct formTest : fTest, testing::WithParamInterface<format> {
   formTest() {
      test->start = GetParam().start;
      test->endResult = GetParam().end;
   }
};

TEST_P(formTest, test1) {
   bool endResult = true;
   TestTemp<int> TempObj;
   std::string first;
   std::string second;
   //std::string start ("1  2 3 4 5 6 7 8 9 10");
   //std::vector<std::string> end = { "1","2","3","4","5","6","7","8","9","10" };
   std::vector<std::string> start2 = { "1","","2","3","4","5","6","7","8","9","10" };
   std::string start = GetParam().start;
   std::vector<std::string> end = GetParam().end;
   bool result = TempObj.formattingTest(start,end,start2);      
   EXPECT_TRUE(result);
}

INSTANTIATE_TEST_CASE_P(Default, formTest, testing::Values(
   format{ "1", {"1"} },
   format{ " ", {} },
   format{ "1 2 3 4 5",{"1","2","3","4","5"} },
   format{ "1  2 3 4 5  6", {"1","2","3","4","5","6"} }
));


int main(int argc, char** argv)
{
   testing::InitGoogleTest(&argc, argv);
   RUN_ALL_TESTS();
   return 0;
}
Bo Persson
  • 90,663
  • 31
  • 146
  • 203
Rok
  • 476
  • 1
  • 5
  • 12
  • 2
    You have *undefined behavior* in your code. You have an empty string in the `start2` vector, that you ultimately pass to the `removeWhiteSpace` function, where you access the first character of the strings in the vector. If a string is empty, it doesn't have a first character, so you are indexing out of bounds. Also the name `removeWhiteSpace` is not very good, as the function doesn't actually remove white-space, it just checks if the first character of a string is a digit or not (wrongly as I just noted). – Some programmer dude Feb 10 '16 at 08:42
  • @JoachimPilebog the vector `start2` is primarily used for debugging the comparison part of the test (the part of the code that's after the call of `removeWhiteSpaces`) and will be removed after I fix the issue. Also the function `removeWhiteSpaces` does actually remove the white-space as it pushes only the elements that are digits into a new vector – Rok Feb 10 '16 at 09:05
  • Btw. why do you copy the vectors all over instead of passing them by const reference (for example in `removeWhiteSpaces(std::vector v, ...)` where you only look at the vector v so it doesn't need to be non-const). – EmDroid Feb 10 '16 at 09:50
  • 1
    @axalis Well to comment on your example from what I understand (I am very much a complete beginner in C++) I made the function work so the vector `v` is an input and the function pushes the values that are digits into the vector `output`. I do know that there's probably a better way to deal with that but I haven't really found one so any advice would be greatly appreciated – Rok Feb 10 '16 at 09:56
  • The point is you can use `void removeWhiteSpaces(const std::vector & v, std::vector &output);` to not copy the first vector (and use `std::vector::const_iterator` instead of `std::vector::iterator` inside). Similar for some other functions with input parameters which do not need to be modified inside. – EmDroid Feb 10 '16 at 10:54

6 Answers6

62

As this is a DLL, the problem might lie in different heaps used for allocation and deallocation (try to build the library statically and check if that will work).

The problem is, that DLLs and templates do not agree together very well. In general, depending on the linkage of the MSVC runtime, it might be problem if the memory is allocated in the executable and deallocated in the DLL and vice versa (because they might have different heaps). And that can happen with templates very easily, for example: you push_back() to the vector inside the removeWhiteSpaces() in the DLL, so the vector memory is allocated inside the DLL. Then you use the output vector in the executable and once it gets out of scope, it is deallocated, but inside the executable whose heap doesn't know anything about the heap it has been allocated from. Bang, you're dead.

This can be worked-around if both DLL and the executable use the same heap. To ensure this, both the DLL and the executable must use the dynamic MSVC runtime - so make sure, that both link to the runtime dynamically, not statically. In particular, the exe should be compiled and linked with /MD[d] and the library with /LD[d] or /MD[d] as well, neither one with /MT[d]. Note that afterwards the computer which will be running the app will need the MSVC runtime library to run (for example, by installing "Visual C++ Redistributable" for the particular MSVC version).

You could get that work even with /MT, but that is more difficult - you would need to provide some interface which will allow the objects allocated in the DLL to be deallocated there as well. For example something like:

__declspec(dllexport) void deallocVector(std::vector<std::string> &x);

void deallocVector(std::vector<std::string> &x) {
    std::vector<std::string> tmp;
    v.swap(tmp);
}

(however this does not work very well in all cases, as this needs to be called explicitly so it will not be called e.g. in case of exception - to solve this properly, you would need to provide some interface from the DLL, which will cover the vector under the hood and will take care about the proper RAII)


EDIT: the final solution was actually was to have all of the projects (the exe, dll and the entire googleTest project) built in Multi-threaded Debug DLL (/MDd) (the GoogleTest projects are built in Multi-threaded debug(/MTd) by default)

EmDroid
  • 5,918
  • 18
  • 18
  • 1
    I am going to try that but sadly in the end I need to find a way to make it work as a DLL as my supervisor wants it like that – Rok Feb 10 '16 at 10:01
  • 1
    Updated the answer, you basically need to link against the dynamic runtime to avoid this kind of problems. – EmDroid Feb 10 '16 at 10:03
  • 1
    so if i'm understanding correctly I need to set the runtime library of the exe to /MD and the one of DLL to /LD right? If so, I can't really do that for the DLL because the /LD option is not given to me – Rok Feb 10 '16 at 10:13
  • Hum ... I think you can use /MD for the library as well, the /LD just provides some additional automatic macro definitions AFAIK (maybe it was removed in the newer versions, I don't see it in MSVC 2013 either) – EmDroid Feb 10 '16 at 10:25
  • Well building the exe in /MD results in me getting a bunch of linker errors (I have been building it in /MT until now and it was bullt correctly) – Rok Feb 10 '16 at 10:29
  • Try to fully clean and rebuild, and if still in error, add comment with the error description or update the question and show the errors there. If the exe was built with /MT, that was most likely the cause of the problem because then the exe and the DLL indeed have different heaps. – EmDroid Feb 10 '16 at 10:34
  • Also, if you are linking the exe with some other static libraries, those must be built with /MD as well (otherwise the objects imported from them will not match). – EmDroid Feb 10 '16 at 10:52
  • when i cleaned and rebuilt the error it still throws me those errors the errors were all linker errors and they're shown in the screenshot below (these are just a few): [errors](http://prntscr.com/a1dr7o) – Rok Feb 10 '16 at 11:26
  • These are coming from libcpmtd.lib, that seems like some libs are still using /MT[d]. I guess it might be the gtestd.lib (GoogleTest library) which is built with /MTd. You will probably need to rebuild gtest with /MDd to be able to link against it. – EmDroid Feb 10 '16 at 11:45
  • I have rebuilt the gtest to MDd and tried rebuilding my exe and it still reports a few errors: [errors](http://prntscr.com/a1dzt6) – Rok Feb 10 '16 at 11:51
  • then try to also rebuild app and dll with MDd (MD and MDd runtimes can still conflict - MDd is normally used for debug builds and MD for release builds, they should not be mixed either) – EmDroid Feb 10 '16 at 11:54
  • I'm incredibly happy to report that the issue has been fixed. Thank you very much for the assistance – Rok Feb 10 '16 at 11:57
  • I had a similar problem and it turned out that my unittest project was set to a different code generation runtime library - so by setting it to the same as the DLL project, then no heap exception -- It would be great if the exception thrown could show your answer instead :-) – serup Apr 09 '18 at 07:59
7

I had the same issue with Microsoft visual studio 2019, simply fixed by changing the "Runtime library" to " Multi-Threaded Debug DLL (/MDd)"

right click on solution on "Solution Explorer" -> "properties" -> "C/C++" -> "Code Generation" , and change "Runtime library" to " Multi-Threaded Debug DLL (/MDd)"

4

I had a similar problem and it turned out that my unittest project was set to a different code generation runtime library - so by setting it to the same as the DLL project, then no heap exception

serup
  • 3,676
  • 2
  • 30
  • 34
4

That verification was implemented by Microsoft software developers a long time ago in 1992 - 1993 and it is No Longer valid since in case of Heterogeneous or MPI programming a new memory Could Be allocated Not from a Local Heap.

When an application gets a memory using OpenCL or CUDA APIs a GPU driver does all memory allocations and, of course, it doesn't use the Local Heap of the application. However, the application should release the memory before it exits. At that time Microsoft's Memory Leaks Detection API detects it and that assert is displayed.

Please take a look at a Video Technical Report regarding origins of that verification:

Origins of MS Visual Studio 2015 Assert __acrt_first_block == header ( VTR-010 ) https://www.youtube.com/watch?v=NJeA_YkLzxc

Note: A weblink to the youtube video updated since I've uploaded a video with some corrections.

4

I encountered the same error and I found a way to get more information about the cause of the problem: It is possible to set with visual studio a breakpoint condition on the line that raise this error (so that the debugger breaks before the error message).

It is necessary to open the corresponding file (debug_heap.cpp,somewhere in "C:\Program Files (x86)\Windows Kits\10\Source") and to write the following condition:

enter image description here

Then we can continue debugging and when the breakpoint is hit, we can observe the address of the block that raise the error (the "block" argument of the "free_dbg_nolock" function that contains the breakpoint).

From there you can observe the memory content of the block by copying the address into the memory window (Debug->Windows->Memory). If you are lucky it may be a string or an easily recognizable variable.

You can then identify the variable that causes the bug and try to fix it.

In my case it was a variable that was created in one dll and deleted in another. To correct it I replaced all my objects by pointers to these objects in one dll.

Malick
  • 6,252
  • 2
  • 46
  • 59
2

I was seeing this error too and in my case I had all the memory model settings lined up correctly. However having recently upgraded the projects from vs2013 to vs2015 I had stale references between the .exe and .dll, so in fact I was using the old DLL built with 2013. I had to remove the reference between the .exe and .dll and re-add it to update the name of the .lib that the exe was linking against. (Right click on the "References" child item of the .exe project and "Add", while confusingly also allows you to remove a reference).

fret
  • 1,542
  • 21
  • 36