/* 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(); }