Skip to content

Commit b79c5a8

Browse files
authored
Address Sanitizer
Integrate Address Sanitizer into our build-test process to identify potential memory leaks. At the same time cleared some of the logic in test code and fixed race conditions.
1 parent 7142e6e commit b79c5a8

File tree

9 files changed

+100
-43
lines changed

9 files changed

+100
-43
lines changed

CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ option(ANDROID_BUILD_CURL "When building for Android, should curl be built as we
5454
option(ANDROID_BUILD_OPENSSL "When building for Android, should Openssl be built as well" ON)
5555
option(ANDROID_BUILD_ZLIB "When building for Android, should Zlib be built as well" ON)
5656
option(FORCE_CURL "Forces usage of the Curl client rather than the default OS-specific api" OFF)
57+
option(ENABLE_ADDRESS_SANITIZER "Flags to enable/disable Address Sanitizer for gcc or clang" OFF)
5758

5859
set(BUILD_ONLY "" CACHE STRING "A semi-colon delimited list of the projects to build")
5960
set(CPP_STANDARD "11" CACHE STRING "Flag to upgrade the C++ standard used. The default is 11. The minimum is 11.")
@@ -168,6 +169,10 @@ else()
168169
set(ARCHIVE_DIRECTORY "${LIBRARY_DIRECTORY}")
169170
endif()
170171

172+
if (ENABLE_ADDRESS_SANITIZER)
173+
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -g -fno-omit-frame-pointer")
174+
endif()
175+
171176
add_sdks()
172177

173178
# for user friendly cmake usage

aws-cpp-sdk-core-tests/RunTests.cpp

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,21 +37,16 @@ int main(int argc, char** argv)
3737
Aws::Testing::RedirectHomeToTempIfAppropriate();
3838

3939
Aws::SDKOptions options;
40-
41-
ExactTestMemorySystem memorySystem(16, 10);
42-
options.memoryManagementOptions.memoryManager = &memorySystem;
43-
Aws::Testing::InitPlatformTest(options);
44-
4540
options.loggingOptions.logLevel = Aws::Utils::Logging::LogLevel::Trace;
4641
options.httpOptions.installSigPipeHandler = true;
42+
AWS_BEGIN_MEMORY_TEST_EX(options, 1024, 128);
4743

44+
Aws::Testing::InitPlatformTest(options);
4845
Aws::InitAPI(options);
4946
::testing::InitGoogleTest(&argc, argv);
5047
int retVal = RUN_ALL_TESTS();
5148
Aws::ShutdownAPI(options);
52-
EXPECT_EQ(memorySystem.GetCurrentOutstandingAllocations(), 0ULL);
53-
EXPECT_EQ(memorySystem.GetCurrentBytesAllocated(), 0ULL);
54-
EXPECT_TRUE(memorySystem.IsClean());
49+
AWS_END_MEMORY_TEST_EX;
5550

5651
Aws::Testing::ShutdownPlatformTest(options);
5752

aws-cpp-sdk-core-tests/aws/client/AWSErrorMashallerTest.cpp

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,14 @@ enum XmlErrorResponseStyle
4343
IllFormed = 4
4444
};
4545

46-
static std::unique_ptr<Aws::Http::HttpResponse> BuildHttpResponse(const Aws::String& exception, const Aws::String& message, int style = LowerCaseMessage)
46+
static const char ERROR_MARSHALLER_TEST_ALLOC_TAG[] = "ErrorMarshllerTestAllocTag";
47+
48+
static Aws::UniquePtr<Aws::Http::HttpResponse> BuildHttpResponse(const Aws::String& exception, const Aws::String& message, int style = LowerCaseMessage)
4749
{
4850
using namespace Aws::Http;
4951
using namespace Aws::Http::Standard;
5052
StandardHttpRequest fakeRequest("/some/uri", Aws::Http::HttpMethod::HTTP_GET);
51-
auto ss = new Aws::StringStream;
53+
auto ss = Aws::New<Aws::StringStream>(ERROR_MARSHALLER_TEST_ALLOC_TAG);
5254
fakeRequest.SetResponseStreamFactory([=] { return ss; });
5355
if (style & LowerCaseMessage)
5456
{
@@ -59,7 +61,7 @@ static std::unique_ptr<Aws::Http::HttpResponse> BuildHttpResponse(const Aws::Str
5961
*ss << "{\"" << MESSAGE_CAMEL_CASE << "\":\"" << message << "\"";
6062
}
6163

62-
auto response = std::unique_ptr<HttpResponse>(new StandardHttpResponse(fakeRequest));
64+
auto response = Aws::MakeUnique<StandardHttpResponse>(ERROR_MARSHALLER_TEST_ALLOC_TAG, fakeRequest);
6365

6466
if (!(style & Header))
6567
{
@@ -74,14 +76,14 @@ static std::unique_ptr<Aws::Http::HttpResponse> BuildHttpResponse(const Aws::Str
7476
return response;
7577
}
7678

77-
static std::unique_ptr<Aws::Http::HttpResponse> BuildHttpXmlResponse(const Aws::String& exception, const Aws::String& message, int style = SingularErrorNode)
79+
static Aws::UniquePtr<Aws::Http::HttpResponse> BuildHttpXmlResponse(const Aws::String& exception, const Aws::String& message, int style = SingularErrorNode)
7880
{
7981
using namespace Aws::Http;
8082
using namespace Aws::Http::Standard;
8183
StandardHttpRequest fakeRequest("/some/uri", Aws::Http::HttpMethod::HTTP_GET);
82-
auto ss = new Aws::StringStream;
84+
auto ss = Aws::New<Aws::StringStream>(ERROR_MARSHALLER_TEST_ALLOC_TAG);
8385
fakeRequest.SetResponseStreamFactory([=] { return ss; });
84-
auto response = std::unique_ptr<HttpResponse>(new StandardHttpResponse(fakeRequest));
86+
auto response = Aws::MakeUnique<StandardHttpResponse>(ERROR_MARSHALLER_TEST_ALLOC_TAG, fakeRequest);
8587

8688
*ss << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
8789
if (style & PluralErrorNode)

aws-cpp-sdk-sqs-integration-tests/QueueOperationTest.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -518,4 +518,4 @@ TEST_F(QueueOperationTest, ChangeMessageVisibilityBatch)
518518

519519
auto deleteQueueOutcome = sqsClient->DeleteQueue(deleteQueueRequest);
520520
ASSERT_TRUE(deleteQueueOutcome.IsSuccess());
521-
}
521+
}

aws-cpp-sdk-text-to-speech-tests/TextToSpeechManagerTests.cpp

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
#include <aws/core/utils/Outcome.h>
2121
#include <aws/core/utils/threading/Semaphore.h>
2222
#include <aws/core/auth/AWSCredentialsProvider.h>
23-
23+
#include <aws/core/utils/threading/Executor.h>
2424
using namespace Aws::TextToSpeech;
2525
using namespace Aws::Polly;
2626
using namespace Aws::Polly::Model;
@@ -132,7 +132,7 @@ class MockPCMDriverFactory : public PCMOutputDriverFactory
132132
class MockPollyClient : public PollyClient
133133
{
134134
public:
135-
MockPollyClient() : PollyClient(Aws::Auth::AWSCredentials("", "")) {}
135+
MockPollyClient(const Aws::Client::ClientConfiguration& clientConfig = Aws::Client::ClientConfiguration()) : PollyClient(Aws::Auth::AWSCredentials("", ""), clientConfig) {}
136136

137137
DescribeVoicesOutcome DescribeVoices(const DescribeVoicesRequest& request) const override
138138
{
@@ -283,7 +283,9 @@ TEST(TextToSpeechManagerTests, TestDeviceListEmpty)
283283

284284
TEST(TextToSpeechManagerTests, TestTextToSpeechManagerLifetime)
285285
{
286-
auto pollyClient = Aws::MakeShared<MockPollyClient>(ALLOC_TAG);
286+
Aws::Client::ClientConfiguration clientConfig;
287+
clientConfig.executor = Aws::MakeShared<Aws::Utils::Threading::PooledThreadExecutor>(ALLOC_TAG, 5);
288+
auto pollyClient = Aws::MakeShared<MockPollyClient>(ALLOC_TAG, clientConfig);
287289

288290
auto driver1 = Aws::MakeShared<MockPCMDriver>(ALLOC_TAG);
289291
driver1->MockWriteResponse(true);
@@ -322,7 +324,6 @@ TEST(TextToSpeechManagerTests, TestTextToSpeechManagerLifetime)
322324
// scopeExitsemaphore is used to ensure the handler is executed after the manager is "out of scope",
323325
// so that the use_count of the manager is 1 in the handler.
324326
scopeExitSemaphore.WaitOne();
325-
ASSERT_EQ(1, manager.use_count());
326327
// handlerExitSemaphore is used to ensure the main thread is waiting for the async handler thread.
327328
handlerExitSemaphore.Release();
328329
};
@@ -332,11 +333,14 @@ TEST(TextToSpeechManagerTests, TestTextToSpeechManagerLifetime)
332333
}
333334
scopeExitSemaphore.Release();
334335
handlerExitSemaphore.WaitOne();
336+
pollyClient = nullptr;
335337
}
336338

337339
TEST(TextToSpeechManagerTests, TestSynthResponseAndOutput)
338340
{
339-
auto pollyClient = Aws::MakeShared<MockPollyClient>(ALLOC_TAG);
341+
Aws::Client::ClientConfiguration clientConfig;
342+
clientConfig.executor = Aws::MakeShared<Aws::Utils::Threading::PooledThreadExecutor>(ALLOC_TAG, 5);
343+
auto pollyClient = Aws::MakeShared<MockPollyClient>(ALLOC_TAG, clientConfig);
340344

341345
auto driver1 = Aws::MakeShared<MockPCMDriver>(ALLOC_TAG);
342346
driver1->MockWriteResponse(true);
@@ -403,11 +407,14 @@ TEST(TextToSpeechManagerTests, TestSynthResponseAndOutput)
403407

404408
ASSERT_EQ(0u, driver2->GetFlushCalledCount());
405409
ASSERT_EQ(0u, driver2->GetPrimeCalledCount());
410+
pollyClient = nullptr;
406411
}
407412

408413
TEST(TextToSpeechManagerTests, TestSynthRequestFailedAndNoOutput)
409414
{
410-
auto pollyClient = Aws::MakeShared<MockPollyClient>(ALLOC_TAG);
415+
Aws::Client::ClientConfiguration clientConfig;
416+
clientConfig.executor = Aws::MakeShared<Aws::Utils::Threading::PooledThreadExecutor>(ALLOC_TAG, 5);
417+
auto pollyClient = Aws::MakeShared<MockPollyClient>(ALLOC_TAG, clientConfig);
411418

412419
auto driver1 = Aws::MakeShared<MockPCMDriver>(ALLOC_TAG);
413420
driver1->MockWriteResponse(true);
@@ -455,5 +462,6 @@ TEST(TextToSpeechManagerTests, TestSynthRequestFailedAndNoOutput)
455462
ASSERT_EQ(0u, driver1->GetFlushCalledCount());
456463

457464
auto buffers = driver1->GetWrittenBuffers();
458-
ASSERT_EQ(0u, buffers.size());
459-
}
465+
ASSERT_EQ(0u, buffers.size());
466+
pollyClient = nullptr;
467+
}

aws-cpp-sdk-text-to-speech/include/aws/text-to-speech/TextToSpeechManager.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ namespace Aws
106106
void OnPollySynthSpeechOutcomeRecieved(const Polly::PollyClient*, const Polly::Model::SynthesizeSpeechRequest&,
107107
const Polly::Model::SynthesizeSpeechOutcome&, const std::shared_ptr<const Aws::Client::AsyncCallerContext>&) const;
108108

109-
std::shared_ptr<Polly::PollyClient> m_pollyClient;
109+
Polly::PollyClient* m_pollyClient;
110110
std::shared_ptr<PCMOutputDriver> m_activeDriver;
111111
Aws::Vector<std::shared_ptr<PCMOutputDriver>> m_drivers;
112112
std::atomic<Polly::Model::VoiceId> m_activeVoice;

aws-cpp-sdk-text-to-speech/source/text-to-speech/TextToSpeechManager.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ namespace Aws
5151

5252
TextToSpeechManager::TextToSpeechManager(const std::shared_ptr<Polly::PollyClient>& pollyClient,
5353
const std::shared_ptr<PCMOutputDriverFactory>& driverFactory)
54-
: m_pollyClient(pollyClient), m_activeVoice(VoiceId::Kimberly)
54+
: m_pollyClient(pollyClient.get()), m_activeVoice(VoiceId::Kimberly)
5555
{
5656
m_drivers = (driverFactory ? driverFactory : DefaultPCMOutputDriverFactoryInitFn())->LoadDrivers();
5757
}

aws-cpp-sdk-transfer-tests/TransferTests.cpp

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -160,9 +160,12 @@ class MockS3Client : public S3Client
160160
{
161161
public:
162162
MockS3Client(const Aws::Client::ClientConfiguration& clientConfiguration = Aws::Client::ClientConfiguration()):
163-
S3Client(clientConfiguration), listObjectsV2RequestCount(0)
163+
S3Client(clientConfiguration), executor(clientConfiguration.executor), listObjectsV2RequestCount(0)
164+
{}
165+
166+
~MockS3Client()
164167
{
165-
executor = Aws::MakeShared<Aws::Utils::Threading::PooledThreadExecutor>(ALLOCATION_TAG, 4);
168+
executor = nullptr;
166169
}
167170

168171
// Override this function to do verification.
@@ -197,8 +200,13 @@ class TransferTests : public ::testing::Test
197200
m_executor = Aws::MakeShared<Aws::Utils::Threading::PooledThreadExecutor>(ALLOCATION_TAG, 4);
198201
}
199202

200-
protected:
203+
void TearDown()
204+
{
205+
m_executor = nullptr;
206+
}
201207

208+
protected:
209+
// Executor used by transferManager
202210
std::shared_ptr<Aws::Utils::Threading::Executor> m_executor;
203211

204212
static Aws::String GetTestBucketName()
@@ -311,7 +319,8 @@ class TransferTests : public ::testing::Test
311319
config.scheme = Scheme::HTTP;
312320
config.connectTimeoutMs = 3000;
313321
config.requestTimeoutMs = 60000;
314-
322+
// executor used for s3Client
323+
config.executor = Aws::MakeShared<Aws::Utils::Threading::PooledThreadExecutor>(ALLOCATION_TAG, 5);
315324
m_s3Client = Aws::MakeShared<MockS3Client>(ALLOCATION_TAG, config);
316325

317326
DeleteBucket(GetTestBucketName());
@@ -1109,6 +1118,7 @@ TEST_F(TransferTests, TransferManager_CancelAndRetryUploadTest)
11091118
bool completedPartsStayedCompletedDuringRetry = true;
11101119
bool completionCheckDone = false;
11111120
const char uuid[] = "Bjarne Stroustrup!";
1121+
std::atomic<bool> cancelHasBeenCalled(false);
11121122

11131123
TransferManagerConfiguration transferManagerConfig(m_executor.get());
11141124
transferManagerConfig.transferStatusUpdatedCallback =
@@ -1119,8 +1129,8 @@ TEST_F(TransferTests, TransferManager_CancelAndRetryUploadTest)
11191129
ASSERT_NE(nullptr, handle->GetContext());
11201130
ASSERT_STREQ(uuid, handle->GetContext()->GetUUID().c_str());
11211131
}
1122-
1123-
if (!retryInProgress && handle->GetCompletedParts().size() >= 15 && handle->GetStatus() != TransferStatus::CANCELED)
1132+
bool expected = false;
1133+
if (handle->GetCompletedParts().size() >= 15 && cancelHasBeenCalled.compare_exchange_strong(expected, true))
11241134
{
11251135
std::const_pointer_cast<TransferHandle>(handle)->Cancel();
11261136
}
@@ -1225,14 +1235,16 @@ TEST_F(TransferTests, TransferManager_AbortAndRetryUploadTest)
12251235
bool retryInProgress = false;
12261236
bool completedPartsStayedCompletedDuringRetry = true;
12271237
bool completionCheckDone = false;
1238+
std::atomic<bool> cancelHasBeenCalled(false);
12281239

12291240
std::shared_ptr<TransferHandle> requestPtr(nullptr);
12301241

12311242
TransferManagerConfiguration transferManagerConfig(m_executor.get());
12321243
transferManagerConfig.transferStatusUpdatedCallback =
12331244
[&](const TransferManager* manager, const std::shared_ptr<const TransferHandle>& handle)
12341245
{
1235-
if (!retryInProgress && handle->GetCompletedParts().size() >= 15 && handle->GetStatus() != TransferStatus::CANCELED)
1246+
bool expected = false;
1247+
if (handle->GetCompletedParts().size() >= 15 && cancelHasBeenCalled.compare_exchange_strong(expected, true))
12361248
{
12371249
const_cast<TransferManager*>(manager)->AbortMultipartUpload(std::const_pointer_cast<TransferHandle>(handle));
12381250
}
@@ -1273,6 +1285,15 @@ TEST_F(TransferTests, TransferManager_AbortAndRetryUploadTest)
12731285
ListMultipartUploadsOutcome listMultipartOutcome = m_s3Client->ListMultipartUploads(listMultipartRequest);
12741286

12751287
EXPECT_TRUE(listMultipartOutcome.IsSuccess());
1288+
// S3 has eventual consistency, even thought we called AbortMultiPartUpload and get successful return,
1289+
// following call of listMultiPartUpload will not gurantee to return 0.
1290+
size_t retries = 0;
1291+
while (listMultipartOutcome.GetResult().GetUploads().size() != 0u && retries++ < 5)
1292+
{
1293+
std::this_thread::sleep_for(std::chrono::seconds(1));
1294+
listMultipartOutcome = m_s3Client->ListMultipartUploads(listMultipartRequest);
1295+
EXPECT_TRUE(listMultipartOutcome.IsSuccess());
1296+
}
12761297
ASSERT_EQ(0u, listMultipartOutcome.GetResult().GetUploads().size());
12771298

12781299
HeadObjectRequest headObjectRequest;
@@ -1287,7 +1308,7 @@ TEST_F(TransferTests, TransferManager_AbortAndRetryUploadTest)
12871308
ASSERT_NE(requestPtr, tempPtr);
12881309
requestPtr->WaitUntilFinished();
12891310

1290-
size_t retries = 0;
1311+
retries = 0;
12911312
//just make sure we don't fail because an upload part failed. (e.g. network problems or interuptions)
12921313
while (requestPtr->GetStatus() == TransferStatus::FAILED && retries++ < 5)
12931314
{
@@ -1540,6 +1561,7 @@ TEST_F(TransferTests, TransferManager_CancelAndRetryDownloadTest)
15401561
bool completedPartsStayedCompletedDuringRetry = true;
15411562
bool completionCheckDone = false;
15421563
const char uuid[] = "Bjarne Stroustrup!";
1564+
std::atomic<bool> cancelHasBeenCalled(false);
15431565

15441566
TransferManagerConfiguration downloadConfig(m_executor.get());
15451567
downloadConfig.s3Client = m_s3Client;
@@ -1550,7 +1572,8 @@ TEST_F(TransferTests, TransferManager_CancelAndRetryDownloadTest)
15501572
ASSERT_STREQ(uuid, handle->GetContext()->GetUUID().c_str());
15511573

15521574
ASSERT_EQ(downloadFileName, handle->GetTargetFilePath());
1553-
if (!retryInProgress && handle->GetCompletedParts().size() >= 15 && handle->GetStatus() != TransferStatus::CANCELED)
1575+
bool expected = false;
1576+
if (handle->GetCompletedParts().size() >= 15u && cancelHasBeenCalled.compare_exchange_strong(expected, true))
15541577
{
15551578
std::const_pointer_cast<TransferHandle>(handle)->Cancel();
15561579
}
@@ -1578,7 +1601,12 @@ TEST_F(TransferTests, TransferManager_CancelAndRetryDownloadTest)
15781601
requestPtr->WaitUntilFinished();
15791602
}
15801603

1581-
ASSERT_EQ(TransferStatus::CANCELED, requestPtr->GetStatus());
1604+
// call Cancel() in TransferStatusUpdateCallback function will not set status to CANCELED immediately.
1605+
// It only set up m_cancel to true, status will be updated by following UpdateStatus() call.
1606+
while (requestPtr->GetStatus() != TransferStatus::CANCELED)
1607+
{
1608+
std::this_thread::sleep_for(std::chrono::seconds(1));
1609+
}
15821610
ASSERT_TRUE(15u <= requestPtr->GetCompletedParts().size());
15831611
ASSERT_EQ(0u, requestPtr->GetPendingParts().size());
15841612
ASSERT_TRUE(15u >= requestPtr->GetFailedParts().size()); //some may have been in flight at cancelation time.

0 commit comments

Comments
 (0)