/* Copyright (c) 2008-2010, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
// This file is part of ThreadSanitizer, a dynamic data race detector.
// Author: Konstantin Serebryany.
// This file contains tests for various parts of ThreadSanitizer.
#include <gtest/gtest.h>
#include "ts_heap_info.h"
#include "ts_simple_cache.h"
#include "dense_multimap.h"
// Testing the HeapMap.
struct TestHeapInfo {
uintptr_t ptr;
uintptr_t size;
int val;
TestHeapInfo() : ptr(0), size(0), val(0) { }
TestHeapInfo(uintptr_t p, uintptr_t s, uintptr_t v) :
ptr(p), size(s), val(v) { }
};
TEST(ThreadSanitizer, HeapInfoTest) {
HeapMap<TestHeapInfo> map;
TestHeapInfo *info;
EXPECT_EQ(0U, map.size());
EXPECT_EQ(NULL, map.GetInfo(12345));
// Insert range [1000, 1000+100) with value 1.
map.InsertInfo(1000, TestHeapInfo(1000, 100, 1));
EXPECT_EQ(1U, map.size());
info = map.GetInfo(1000);
EXPECT_TRUE(info);
EXPECT_EQ(1000U, info->ptr);
EXPECT_EQ(100U, info->size);
EXPECT_EQ(1, info->val);
EXPECT_TRUE(map.GetInfo(1000));
EXPECT_EQ(1, info->val);
EXPECT_TRUE(map.GetInfo(1050));
EXPECT_EQ(1, info->val);
EXPECT_TRUE(map.GetInfo(1099));
EXPECT_EQ(1, info->val);
EXPECT_FALSE(map.GetInfo(1100));
EXPECT_FALSE(map.GetInfo(2000));
EXPECT_EQ(NULL, map.GetInfo(2000));
EXPECT_EQ(NULL, map.GetInfo(3000));
// Insert range [2000, 2000+200) with value 2.
map.InsertInfo(2000, TestHeapInfo(2000, 200, 2));
EXPECT_EQ(2U, map.size());
info = map.GetInfo(1000);
EXPECT_TRUE(info);
EXPECT_EQ(1, info->val);
info = map.GetInfo(2000);
EXPECT_TRUE(info);
EXPECT_EQ(2, info->val);
info = map.GetInfo(1000);
EXPECT_TRUE(info);
EXPECT_EQ(1, info->val);
EXPECT_TRUE((info = map.GetInfo(1050)));
EXPECT_EQ(1, info->val);
EXPECT_TRUE((info = map.GetInfo(1099)));
EXPECT_EQ(1, info->val);
EXPECT_FALSE(map.GetInfo(1100));
EXPECT_TRUE((info = map.GetInfo(2000)));
EXPECT_EQ(2, info->val);
EXPECT_TRUE((info = map.GetInfo(2199)));
EXPECT_EQ(2, info->val);
EXPECT_FALSE(map.GetInfo(2200));
EXPECT_FALSE(map.GetInfo(3000));
// Insert range [3000, 3000+300) with value 3.
map.InsertInfo(3000, TestHeapInfo(3000, 300, 3));
EXPECT_EQ(3U, map.size());
EXPECT_TRUE((info = map.GetInfo(1000)));
EXPECT_EQ(1, info->val);
EXPECT_TRUE((info = map.GetInfo(2000)));
EXPECT_EQ(2, info->val);
EXPECT_TRUE((info = map.GetInfo(3000)));
EXPECT_EQ(3, info->val);
EXPECT_TRUE((info = map.GetInfo(1050)));
EXPECT_EQ(1, info->val);
EXPECT_TRUE((info = map.GetInfo(2100)));
EXPECT_EQ(2, info->val);
EXPECT_TRUE((info = map.GetInfo(3200)));
EXPECT_EQ(3, info->val);
// Remove range [2000,2000+200)
map.EraseInfo(2000);
EXPECT_EQ(2U, map.size());
EXPECT_TRUE((info = map.GetInfo(1050)));
EXPECT_EQ(1, info->val);
EXPECT_FALSE(map.GetInfo(2100));
EXPECT_TRUE((info = map.GetInfo(3200)));
EXPECT_EQ(3, info->val);
}
TEST(ThreadSanitizer, PtrToBoolCacheTest) {
PtrToBoolCache<256> c;
bool val = false;
EXPECT_FALSE(c.Lookup(123, &val));
c.Insert(0, false);
c.Insert(1, true);
c.Insert(2, false);
c.Insert(3, true);
EXPECT_TRUE(c.Lookup(0, &val));
EXPECT_EQ(false, val);
EXPECT_TRUE(c.Lookup(1, &val));
EXPECT_EQ(true, val);
EXPECT_TRUE(c.Lookup(2, &val));
EXPECT_EQ(false, val);
EXPECT_TRUE(c.Lookup(3, &val));
EXPECT_EQ(true, val);
EXPECT_FALSE(c.Lookup(256, &val));
EXPECT_FALSE(c.Lookup(257, &val));
EXPECT_FALSE(c.Lookup(258, &val));
EXPECT_FALSE(c.Lookup(259, &val));
c.Insert(0, true);
c.Insert(1, false);
EXPECT_TRUE(c.Lookup(0, &val));
EXPECT_EQ(true, val);
EXPECT_TRUE(c.Lookup(1, &val));
EXPECT_EQ(false, val);
EXPECT_TRUE(c.Lookup(2, &val));
EXPECT_EQ(false, val);
EXPECT_TRUE(c.Lookup(3, &val));
EXPECT_EQ(true, val);
c.Insert(256, false);
c.Insert(257, false);
EXPECT_FALSE(c.Lookup(0, &val));
EXPECT_FALSE(c.Lookup(1, &val));
EXPECT_TRUE(c.Lookup(2, &val));
EXPECT_EQ(false, val);
EXPECT_TRUE(c.Lookup(3, &val));
EXPECT_EQ(true, val);
EXPECT_TRUE(c.Lookup(256, &val));
EXPECT_EQ(false, val);
EXPECT_TRUE(c.Lookup(257, &val));
EXPECT_EQ(false, val);
}
TEST(ThreadSanitizer, IntPairToBoolCacheTest) {
IntPairToBoolCache<257> c;
bool val = false;
map<pair<int,int>, bool> m;
for (int i = 0; i < 1000000; i++) {
int a = (rand() % 1024) + 1;
int b = (rand() % 1024) + 1;
if (c.Lookup(a, b, &val)) {
EXPECT_EQ(1U, m.count(make_pair(a,b)));
EXPECT_EQ(val, m[make_pair(a,b)]);
}
val = (rand() % 2) == 1;
c.Insert(a, b, val);
m[make_pair(a,b)] = val;
}
}
TEST(ThreadSanitizer, DenseMultimapTest) {
typedef DenseMultimap<int, 3> Map;
Map m1(1, 2);
EXPECT_EQ(m1[0], 1);
EXPECT_EQ(m1[1], 2);
EXPECT_EQ(m1.size(), 2U);
Map m2(3, 2);
EXPECT_EQ(m2[0], 2);
EXPECT_EQ(m2[1], 3);
EXPECT_EQ(m2.size(), 2U);
Map m3(m1, 0);
EXPECT_EQ(m3.size(), 3U);
EXPECT_EQ(m3[0], 0);
EXPECT_EQ(m3[1], 1);
EXPECT_EQ(m3[2], 2);
Map m4(m3, 1);
EXPECT_EQ(m4.size(), 4U);
EXPECT_EQ(m4[0], 0);
EXPECT_EQ(m4[1], 1);
EXPECT_EQ(m4[2], 1);
EXPECT_EQ(m4[3], 2);
Map m5(m4, 5);
Map m6(m5, -2);
Map m7(m6, 2);
EXPECT_EQ(m7.size(), 7U);
EXPECT_TRUE(m7.has(-2));
EXPECT_TRUE(m7.has(0));
EXPECT_TRUE(m7.has(1));
EXPECT_TRUE(m7.has(2));
EXPECT_TRUE(m7.has(5));
EXPECT_FALSE(m7.has(3));
EXPECT_FALSE(m7.has(-1));
EXPECT_FALSE(m7.has(4));
Map m8(m7, Map::REMOVE, 1);
EXPECT_EQ(m8.size(), 6U);
EXPECT_TRUE(m8.has(1));
Map m9(m8, Map::REMOVE, 1);
EXPECT_EQ(m9.size(), 5U);
EXPECT_FALSE(m9.has(1));
}
TEST(ThreadSanitizer, NormalizeFunctionNameNotChangingTest) {
const char *samples[] = {
// These functions should not be changed by NormalizeFunctionName():
// C functions
"main",
"pthread_mutex_unlock",
"pthread_create@@GLIBC_2.2.5",
"pthread_create@*"
// Valgrind can give us this, we should keep it.
"(below main)",
// C++ operators
"operator new[]",
"operator delete[]",
// PIN on Windows handles non-templated C++ code well
"my_namespace::ClassName::Method",
"PositiveTests_HarmfulRaceInDtor::A::~A",
"PositiveTests_HarmfulRaceInDtor::B::`scalar deleting destructor'",
// Objective-C on Mac
"+[NSNavFBENode _virtualNodeOfType:]",
"-[NSObject(NSObject) autorelease]",
"-[NSObject(NSKeyValueCoding) setValue:forKeyPath:]",
"-[NSCell(NSPrivate_CellMouseTracking) _setMouseTrackingInRect:ofView:]",
// TODO(glider): other interesting cases from Objective-C?
// Should we "s/:.*\]/\]/" ?
};
for (size_t i = 0; i < sizeof(samples) / sizeof(samples[0]); i += 2) {
EXPECT_STREQ(samples[i], NormalizeFunctionName(samples[i]).c_str());
}
}
TEST(ThreadSanitizer, NormalizeFunctionNameChangingTest) {
const char *samples[] = {
// These functions should be changed by removing <.*> and (.*) while
// correctly handling the "function returns a [template] function pointer"
// case.
// This is a list of (full demangled name, short name) pairs.
"SuppressionTests::Foo(int*)", "SuppressionTests::Foo",
"logging::LogMessage::Init(char const*, int)", "logging::LogMessage::Init",
"void DispatchToMethod<net::SpdySession, void (net::SpdySession::*)(int), int>(net::SpdySession*, void (net::SpdySession::*)(int), Tuple1<int> const&)",
"DispatchToMethod",
"MessageLoop::DeferOrRunPendingTask(MessageLoop::PendingTask const&)",
"MessageLoop::DeferOrRunPendingTask",
"spdy::SpdyFramer::ProcessInput(char const*, unsigned long)",
"spdy::SpdyFramer::ProcessInput",
"base::RefCountedThreadSafe<history::HistoryBackend, base::DefaultRefCountedThreadSafeTraits<history::HistoryBackend> >::Release() const",
"base::RefCountedThreadSafe::Release",
"net::X509Certificate::Verify(std::string const&, int, net::CertVerifyResult*) const",
"net::X509Certificate::Verify",
"(anonymous namespace)::ExtentToStringSet(ExtensionExtent const&, std::set<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::less<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > >*)",
"::ExtentToStringSet",
"scoped_ptr<(anonymous namespace)::ImportEndedObserver>::operator->() const",
"scoped_ptr::operator->",
"int (anonymous namespace)::ValueCompare<long>(long, long)",
"::ValueCompare",
"std::basic_string<unsigned short, base::string16_char_traits, std::allocator<unsigned short> > const& std::__median<std::basic_string<unsigned short, base::string16_char_traits, std::allocator<unsigned short> >, (anonymous namespace)::CompareQuality>(std::basic_string<unsigned short, base::string16_char_traits, std::allocator<unsigned short> > const&, std::basic_string<unsigned short, base::string16_char_traits, std::allocator<unsigned short> > const&, std::basic_string<unsigned short, base::string16_char_traits, std::allocator<unsigned short> > const&, (anonymous namespace)::CompareQuality)",
"std::__median",
"std::basic_ostream<char, std::char_traits<char> >& std::operator<< <char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, std::_Setprecision)",
"std::operator<<",
"net::(anonymous namespace)::CookieSignature::operator<(net::(anonymous namespace)::CookieSignature const&) const",
"net::::CookieSignature::operator<",
"v8::Handle<v8::Value> (*v8::ToCData<v8::Handle<v8::Value> (*)(v8::Arguments const&)>(v8::internal::Object*))(v8::Arguments const&)",
"v8::ToCData",
"v8::internal::Handle<v8::internal::Object> v8::FromCData<v8::Handle<v8::Value> (*)(v8::Local<v8::String>, v8::AccessorInfo const&)>(v8::Handle<v8::Value> (*)(v8::Local<v8::String>, v8::AccessorInfo const&))",
"v8::FromCData",
"WebCore::operator<<(WebCore::TextStream&, WebCore::LineCap)",
"WebCore::operator<<",
"__gnu_cxx::__normal_iterator<void (**)(), std::vector<void (*)(), std::allocator<void (*)()> > >::base() const",
"__gnu_cxx::__normal_iterator::base",
"__gnu_cxx::__normal_iterator<device_orientation::DataFetcher* (* const*)(), std::vector<device_orientation::DataFetcher* (*)(), std::allocator<device_orientation::DataFetcher* (*)()> > >::operator++()",
"__gnu_cxx::__normal_iterator::operator++",
"__gnu_cxx::__normal_iterator<std::pair<int, std::basic_string<unsigned short, base::string16_char_traits, std::allocator<unsigned short> > >*, std::vector<std::pair<int, std::basic_string<unsigned short, base::string16_char_traits, std::allocator<unsigned short> > >, std::allocator<std::pair<int, std::basic_string<unsigned short, base::string16_char_traits, std::allocator<unsigned short> > > > > >::operator->() const",
"__gnu_cxx::__normal_iterator::operator->",
"std::less<CancelableRequestConsumerTSimple<PageUsageData*>::PendingRequest>::operator()(CancelableRequestConsumerTSimple<PageUsageData*>::PendingRequest const&, CancelableRequestConsumerTSimple<PageUsageData*>::PendingRequest const&) const",
"std::less::operator()",
"SuppressionTests::MyClass<int>::Fooz(int*) const",
"SuppressionTests::MyClass::Fooz",
// Templates and functions returning function pointers
"void (*SuppressionTests::TemplateFunction1<void (*)(int*)>(void (*)(int*)))(int*)",
"SuppressionTests::TemplateFunction1", // Valgrind, Linux
"void SuppressionTests::TemplateFunction2<void>()",
"SuppressionTests::TemplateFunction2", // OMG, return type in template
"void (**&SuppressionTests::TemplateFunction3<void (*)(int)>())",
"SuppressionTests::TemplateFunction3", // Valgrind, Linux
"SuppressionTests::TemplateFunction1<void (__cdecl*)(int *)>",
"SuppressionTests::TemplateFunction1", // PIN, Windows
"SuppressionTests::MyClass<int>::Fooz",
"SuppressionTests::MyClass::Fooz",
"std::operator<<char,std::char_traits<char>,std::allocator<char> >",
"(malformed frame)", // Should be "std::operator<"? Really?
"std::_Ranit<stdext::_Hash<stdext::_Hmap_traits<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,int,stdext::hash_compare<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::less<std::basic_string<char,std::char_traits<char>,std::allocator<char> > > >,std::allocator<std::pair<std::basic_string<char,std::char_traits<char>,std::allocator<char> > const ,int> >,0> >::_List_position,int,stdext::_Hash<stdext::_Hmap_traits<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,int,stdext::hash_compare<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::less<std::basic_string<char,std::char_traits<char>,std::allocator<char> > > >,std::allocator<std::pair<std::basic_string<char,std::char_traits<char>,std::allocator<char> > const ,int> >,0> >::_List_position const *,stdext::_Hash<stdext::_Hmap_traits<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,int,stdext::hash_compare<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::less<std::basic_string<char,std::char_traits<char>,std::allocator<char> > > >,std::allocator<std::pair<std::basic_string<char,std::char_traits<char>,std::allocator<char> > const ,int> >,0> >::_List_position const &>::_Ranit<stdext::_Hash<stdext::_Hmap_traits<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,int,stdext::hash_compare<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::less<std::basic_string<char,std::char_traits<char>,std::allocator<char> > > >,std::allocator<std::pair<std::basic_string<char,std::char_traits<char>,std::allocator<char> > const ,int> >,0> >::_List_position,int,stdext::_Hash<stdext::_Hmap_traits<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,int,stdext::hash_compare<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::less<std::basic_string<char,std::char_traits<char>,std::allocator<char> > > >,std::allocator<std::pair<std::basic",
"(malformed frame)",
"std::_Tree_val<std::_Tmap_traits<net::`anonymous namespace'::CookieSignature,std::set<std::_Tree<std::_Tmap_traits<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,net::CookieMonster::CanonicalCookie *,std::less<std::basic_string<char,std::char_traits<char>,std::allocator<char> > >,std::allocator<std::pair<std::basic_string<char,std::char_traits<char>,std::allocator<char> > const ,net::CookieMonster::CanonicalCookie *> >,1> >::iterator,net::`anonymous namespace'::OrderByCreationTimeDesc,std::allocator<std::_Tree<std::_Tmap_traits<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,net::CookieMonster::CanonicalCookie *,std::less<std::basic_string<char,std::char_traits<char>,std::allocator<char> > >,std::allocator<std::pair<std::basic_string<char,std::char_traits<char>,std::allocator<char> > const ,net::CookieMonster::CanonicalCookie *> >,1> >::iterator> >,std::less<net::`anonymous namespace'::CookieSignature>,std::allocator<std::pair<net::`anonymous namespace'::CookieSignature const ,std::set<std::_Tree<std::_Tmap_traits<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,net::CookieMonster::CanonicalCookie *,std::less<std::basic_string<char,std::char_traits<char>,std::allocator<char> > >,std::allocator<std::pair<std::basic_string<char,std::char_traits<char>,std::allocator<char> > const ,net::CookieMonster::CanonicalCookie *> >,1> >::iterator,net::`anonymous namespace'::OrderByCreationTimeDesc,std::allocator<std::_Tree<std::_Tmap_traits<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,net::CookieMonster::CanonicalCookie *,std::less<std::basic_string<char,std::char_traits<char>,std::allocator<char> > >,std::allocator<std::pair<std::basic_string<char,std::char_traits<char>,std::allocator<char> > const ,net::CookieMonster::CanonicalCookie *> >,1> >::iterator> > > >,0> >::~_Tree_val<std::_Tmap_traits<net::`anonymous namespace'::CookieSignature,std::set<std::_Tree<std::_Tmap_traits<std::basic_string<",
"(malformed frame)",
"__gnu_cxx::new_allocator<char>::allocate(unsigned long, void const*)",
"__gnu_cxx::new_allocator::allocate",
"PositiveTests_HarmfulRaceInDtor::A::~A()", // Valgrind, Linux
"PositiveTests_HarmfulRaceInDtor::A::~A",
"X::foo(int*) const()", // GCC, Linux
"X::foo",
"X::foo(int*) const volatile",
"X::foo",
"base::(anonymous namespace)::ThreadFunc(void*)",
"base::::ThreadFunc", // TODO(timurrrr): keep "anonymous namespace"?
"operator new[](unsigned long)", "operator new[]", // Valgrind, Linux
};
for (size_t i = 0; i < sizeof(samples) / sizeof(samples[0]); i += 2) {
EXPECT_STREQ(samples[i+1], NormalizeFunctionName(samples[i]).c_str());
}
}
int main(int argc, char **argv) {
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}