싞영진 - Cuvix Information
Transcription
싞영진 - Cuvix Information
C++ 개발자들을 위한 Visual Studio 2012 세미나 Visual C++ 2012 마이크로소프트 순정 Agile QA 프레임워크 싞영진 jiniya.net 제가 컴퓨터 조판 시스템 문제를 해결하기 위해서 수년의 시간을 쏟아 부으면서 배운 것이 무엇이냐고요? 가장 중요한 교훈 중 하나는 아마도 “소프트웨어 개발은 어렵다”는 것입니다. - Knuth 버그가 발생하면 … 버그는 왜 생기는 걸까? No Update, No Bug 버그를 피하는 방법 애초에 버그 따위는 만들지 않는 최고의 프로그래머를 채용한다. 모니터 하나, 개발자 둘 짝 프로그래밍이 효과를 보려면… 모든 프로그래머에게 테스트 프로그래머를 배정해 준다. 코드 리뷰를 통과하지 못한 코드는 커밋조차 못하게 한다. 필요한 코드보다 테스트 코드를 먼저 작성한다. 컴파일러 경고 레벨을 올리고, 정적 분석기를 사용한다. 단 하나의 경고도 용납하지 않는다. 모든 릴리즈 코드는 일정 수준의 커버리지를 확보한다. 버그를 줄이는 컨벤션을 만들고 모두가 그 방식을 따르도록 한다. 젂문 테스터를 고용해서 무작위 사용 테스트를 진행한다. 구조화된 체크리스트를 만들고, 릴리즈 시에는 반드시 해당 목록을 점검한다. 개발팀 회식을 매주 진행한다. 약수 물을 떠놓고 기도한다. 내 코드에 버그가 없기를… 정적 단위 코드 약수 분석기 테스트 커버리지 물… 정적 분석기 1>c:\program files\microsoft sdks\windows\v6.0a\include\strsafe.h(3970) : warning C6387: 'argument 4' might be '0': this does not adhere to the specification for the function 'StringCopyWorkerW': Lines: 3927, 3928, 3929, 3931, 3937, 3939, 3940, 3941, 3943, 3945, 3947, 3951, 3968, 3970 1>c:\program files\microsoft sdks\windows\v6.0a\include\strsafe.h(3954) : warning C6011: Dereferencing NULL pointer 'pszSrc': Lines: 3927, 3928, 3929, 3931, 3937, 3939, 3940, 3941, 3943, 3945, 3947, 3951, 3954 class XVm { public: PVOID ptr_; SIZE_T size_; operator PVOID () { return ptr_; } SIZE_T GetSize() { return size_; } }; int InitCode() { XVm *mem = new XVm; // … ULONG protect = some_value; // … VirtualProtect(mem , mem->GetSize() , protect , NULL); // … return 0; } class XVm { public: PVOID ptr_; SIZE_T size_; operator PVOID () { return ptr_; } SIZE_T GetSize() { return size_; } }; int InitCode() { XVm *mem = new XVm; // … ULONG protect = some_value; // … ULONG old_protect; VirtualProtect(mem , mem->GetSize() , protect , &old_protect); // … return 0; } class XVm { public: PVOID ptr_; SIZE_T size_; operator PVOID () { return ptr_; } SIZE_T GetSize() { return size_; } }; int InitCode() { XVm *mem = new XVm; // … ULONG protect = some_value; // … ULONG old_protect; VirtualProtect(*mem , mem->GetSize() , protect , &old_protect); // … return 0; } class XVm { public: PVOID ptr_; SIZE_T size_; PVOID GetPtr() { return ptr_; } SIZE_T GetSize() { return size_; } }; int InitCode() { XVm *mem = new XVm; // … ULONG protect = some_value; // … ULONG old_protect; VirtualProtect(mem->GetPtr() , mem->GetSize() , protect , &old_protect); // … return 0; } BOOL InitLog(LPCWSTR dir, LPCWSTR name) { WCHAR path[MAX_PATH] = {0,}; StringCbCopyW(path, sizeof(path), dir); size_t len; StringCchLengthW(path, ARRAYSIZE(path), &len); if(len == 0) return FALSE; if(path[len - 1] == L'\\') path[len - 1] = L'\0'; StringCbCatW(path, sizeof(path), name); // ... return TRUE; } BOOL InitLog(LPCWSTR dir, LPCWSTR name) { WCHAR path[MAX_PATH] = {0,}; StringCbCopyW(path, sizeof(path), dir); size_t len; StringCchLengthW(path, ARRAYSIZE(path), &len); if(len == 0 || len > ARRAYSIZE(path)) return FALSE; if(path[len - 1] == L'\\') path[len - 1] = L'\0'; StringCbCatW(path, sizeof(path), name); // ... return TRUE; } SAL Standard Source Code Annotation Language _Must_inspect_result_ STRSAFEAPI StringCchLengthW( _In_reads_or_z_(cchMax) STRSAFE_PCNZWCH psz , _In_ _In_range_(1, STRSAFE_MAX_CCH) size_t cchMax , _Out_opt_ _Deref_out_range_(<, cchMax) _Deref_out_range_(<=, _String_length_(psz)) size_t* pcchLength ); #define STRSAFEAPI __inline HRESULT __stdcall typedef _Return_type_success_(return >= 0) long HRESULT; BOOL InitLog(LPCWSTR dir, LPCWSTR name) { WCHAR path[MAX_PATH] = {0,}; StringCbCopyW(path, sizeof(path), dir); size_t len; HRESULT r; r = StringCchLengthW(path, ARRAYSIZE(path), &len); if(FAILED(r)) return FALSE; if(len == 0) return FALSE; if(path[len - 1] == L'\\') path[len - 1] = L'\0'; StringCbCatW(path, sizeof(path), name); // ... return TRUE; } 단위 테스트 #include "stdafx.h" #include "CppUnitTest.h" #include <Windows.h> #include "notifier.hpp" using namespace Microsoft::VisualStudio::CppUnitTestFramework; namespace utesttest { TEST_CLASS(NotifyTester) { public: TEST_METHOD(BlackNotifyTest) { Notifier n; Assert::AreEqual(TRUE, n.Notify(Notifier::CODE_BLACK, L"BITEM")); Assert::IsFalse(n.Notify(Notifier::CODE_BLACK, L"BITEM")); } TEST_METHOD(SuspNotifyTest) { Notifier n; Assert::IsTrue(n.Notify(Notifier::CODE_SUSPICOUS, L"SITEM")); Assert::AreEqual(FALSE, n.Notify(Notifier::CODE_SUSPICOUS, L"SITEM")); } }; } Assert::AreEqual(Value, Expression) Assert::AreNotEqual(Value, Expression) Assert::IsNull(Expression) Assert::IsNotNull(Expression) Assert::IsTrue(Expression) Assert::IsFalse(Expression) Assert::AreSame(Object, Expression) Assert::AreNotSame(Object, Expression) 코드 커버리지 MorphCode(module, MCL_HARDCORE); if(IsAvastOn()) { MorphCode(module, MCL_LOW); MakeCodeCompatibleWithAvast(module); } else { MorphCode(module, MCL_HARDCORE); } std::wstring GetGrade(int score) { if(score > 90) return L"A"; if(score > 80) return L"B"; if(score > 70) return L"C"; if(score > 60) return L"D"; return L"F"; } TEST_METHOD(ATest) { Assert::AreEqual(std::wstring(L"A"), Assert::AreEqual(std::wstring(L"A"), Assert::AreEqual(std::wstring(L"A"), Assert::AreEqual(std::wstring(L"A"), } GetGrade(95)); GetGrade(94)); GetGrade(91)); GetGrade(99)); TEST_METHOD(BTest) { Assert::AreEqual(std::wstring(L"B"), Assert::AreEqual(std::wstring(L"C"), Assert::AreEqual(std::wstring(L"D"), Assert::AreEqual(std::wstring(L"F"), Assert::AreEqual(std::wstring(L"F"), } GetGrade(85)); GetGrade(74)); GetGrade(61)); GetGrade(59)); GetGrade(49)); 버그에 대처하는 우리의 자세 1. 무슨 스레드인지 모르는 어디선가에서 사운드 파일 핸들이 닫힘 2. ALAudio.dll!FALAudioStreamManager::DestroyStream 스레드가 생성됨 A. FScopeCriticalSection을 사용해서 크리티컬 섹션 획득 B. ALAudio.dll!FALAudioStreamOgg::ReadChunks 호출 i. vorbisfile.dll!ov_read 호출 1. 닫힌 핸들로 ReadFile 호출함. 닫힌 핸들이기 때문에 정상적이 값을 반환하지 못하고 실패함. 이로 인해서 ReadChunks 함수가 리턴하지 못하고 무한 루프를 돌게 됨. 3. 메인 스레드에서 Engine.dll!ALAudioSubsystem::Update 함수가 호출됨 A. DestroyStream이 획득하고 있는 크리티컬 섹션을 획득하려고 시도함. 해당 스레드가 무한 루프에 빠져 있기 때문에 같이 무한 루프에 빠짐. 게임 메인 스레드이기 때문에 다른 모듞 작업이 진행되지 않고 멈추게 됨. void CheckDebugger() { __try { CloseHandle((HANDLE) 0x1234); } __except(EXCEPTION_EXECUTE_HANDLER) { // Detect Debugger } } 인이라는 것은 활 쏘는 것과 같다. 활을 쏠 때는 자세를 바르게 한 후에 쏘는 법이다. 화살이 과녁에 맞지 않으면 자기를 이긴 자를 원망할 것이 아니라 (과녁에 맞지 않은 까닭을) 도리어 자기 자신에게서 찾는다. - 맹자 © 2012 Microsoft Corporation. All rights reserved. Microsoft, Windows, and other product names are or may be registered trademarks and/or trademarks in the U.S. and/or other countries. The information herein is for informational purposes only and represents the current view of Microsoft Corporation as of the date of this presentation. Because Microsoft must respond to changing market conditions, it should not be interpreted to be a commitment on the part of Microsoft, and Microsoft cannot guarantee the accuracy of any information provided after the date of this presentation. MICROSOFT MAKES NO WARRANTIES, EXPRESS, IMPLIED OR STATUTORY, AS TO THE INFORMATION IN THIS PRESENTATION.
Similar documents
MS by LATEX (http://jupiter.hallym.ac.kr) 2016 1 21
Windows 7 : alzip 응용프로그램을 다운로드하여 설치. ⇒ texlive.iso 파일에 마우스 오른쪽 클릭하여 관리자 권한으로
More information