자동화된 소스 분석, 처리, 검증을 통한 소스의 불필요한 #if - #endif...
Post on 28-Nov-2014
20.720 Views
Preview:
DESCRIPTION
TRANSCRIPT
자동화된 소스 분석, 처리, 검증을 통한 소스의 불필요한 #if - #endif 제거하기
김이선
veblush@[nexon|gmail]
BNB 프로그래머
카트라이더 리드 프로그래머
버블파이터 프로토타입
리드 프로그래머 에버플래닛
리드 프로그래머 던전엔파이터
테크니컬 디렉터 GTR
프로그래머
게임 프로그래밍 11년차
프로젝트 소스 코드의
유지보수성 (Maintainability) 관리가 중요!
[던전엔 파이터] 에서 했던
코드 관리 중 하나인
불필요한 #if - #endif 제거 작업
분석 처리 검증 결론 결과 도입 도입
오늘 알아볼 코드
[던전엔 파이터]
“퀘스트 시스템”
추가 작업
// 김철수: 퀘스트 시스템 구현
//#define _QUEST_SYSTEM
Flags.h
1단계
플래그 정의
#ifdef _QUEST_SYSTEM
ui.questWnd.setButton(…);
ui.questWnd.setEvent(…);
#else
ui.questWnd.setDisable();
#endif
*
2단계
기능 구현
// 김철수: 퀘스트 시스템 구현
#define _QUEST_SYSTEM
Flags.h
3단계
기능 플래그 켬
작업 중인 코드가
다른 작업자, 라이브 코드에
영향을 주지 않음
(장기간 작업, 빠른 롤백, 이벤트)
국가별 소스 코드 공유에 도움
(국가별 플래그, …)
간단한 작업 방식
(교육 비용이 낮음)
플래그 켬/끔이 Rebuild 를 초래
(Flags.h 파일을 모든 파일이 #include 하므로)
코드가 점차 읽기 어렵고 수정하기 어려워짐
(#ifdef - #endif 블록이 코드에 가득 차기 시작함)
코드가 점차 읽기 어렵고 수정하기 어려워짐
(#ifdef - #endif 블록이 코드에 가득 차기 시작함)
죽은 코드가 생겨남
(꺼진 플래그나 #else 에 묶엔 코드들)
그럼 플래그가 얼마나?
5400 개
+1500 /1yr
2015 년 10000 개
플래그 개수가 늘어남에 따라
발생하는 추가적인 문제!
#ifdef _EVENT_2006
ui.notice.setText(“월드컵 이벤트!”);
ui.notice.setVisible(1);
ui.notice.setEventHandler(…);
#endif
문제 1
죽은 코드
#ifdef _NEW_UI_COMPONENT
ui.notice.setText(“…”);
ui.notice.setVisible(1);
#else
set(UI_NOTICE, TEXT, “…”);
set(UI_NOTICE, VISIBLE, 1);
#endif
문제 2
중복 코드
플래그 작업 방식은 유지하되
불필요하게 늘어난 플래그 개수를 줄여보자
불필요한 플래그 제거!
ui.quest.setVisible(1);
ui.quest.setEventHandler(…);
플래그 켜서 제거
#ifdef _QUEST_SYSTEM
#endif
ui.quest.setVisible(1);
ui.quest.setEventHandler(…);
플래그 켜서 제거
#ifdef _EVENT_2006
ui.notice.setText(“월드컵 이벤트!”);
ui.notice.setVisible(1);
ui.notice.setEventHandler(…);
#endif
플래그 꺼서 제거
플래그 꺼서 제거
#define _NEW_QUEST
플래그 병합
제거
#ifdef _NEW_QUEST
# define _NEW_QUEST_FIX
#endif
#ifdef _NEW_QUEST
ui.event.setText(…);
# ifdef _NEW_QUEST_FIX
ui.event.setPos(…);
# endif
#endif
#define _NEW_QUEST
플래그 병합
제거 #ifdef _NEW_QUEST
ui.event.setText(…);
ui.event.setPos(…);
#endif
개운하다!
제거할 플래그 선택
소스에서 플래그 제거
테스트
수천개를 수작업으로?
Image: http://www.flickr.com/photos/15271532@N00/1172675049
리팩토링 딜레마! 실수하면?
누가하지?
자동화
Image: http://www.enggtechsolutions.com/?page_id=5
제거할 플래그 선택
소스에서 플래그 제거
테스트
선택 자동화
제거 자동화
검증 자동화
분석 처리 검증 도입 결론 결과
플래그에 대한 정보 얻기 (삭제할 플래그를 추리기 위해)
소스 파일
#define F | #undef F
#ifdef F | #ifndef F | #if defined(F)
국가별 Flags.h 파일에 정의된 플래그 추리기
#define 뒤에 있는 주석도 가져오기
Subversion Log
모든 커밋의 Rev#, 날짜, 작성자, 로그
변경한 파일에 포함된 플래그 목록
플래그 정보 _QUEST_SYSTEM
사용: 한국, 중국
날짜: 2008-06-21
파일: Interface/QuestWindow.h
Interface/QuestWindow.cpp System/Quest.h
System/Quest.cpp
SVN: #29110, ironwater
“[추가] 퀘스트 시스템 1차 작업”
주석: 김철수, 퀘스트!
판단을 내릴 정보 가공! (단순한 정보 나열을 구체화)
플래그 정보 쿼리 결과
DB 테이블 쿼리 수행
DB 를 통해 유연한 분석 가능
국가별 / 연도별 플래그 등장 표
DB: 큰 그림 분석
시간별 증감 추이
국가별 사용 현황
작업자별 플래그 추가 파일별 플래그 추이
플래그 영향력
DB: 오류 분석
정의만 되고 사용되지 않음 (버려짐?)
정의는 없고 사용만 있음 (오타?)
정의가 여러 곳에 있음 (응?)
정의가 여러 곳에 있으면서 값이 다름 (으악!)
모두 켜거나 끈 플래그 목록 추리기
모든 국가에서 켜고 끈
플래그 수집
작성자 및 상급자에게 삭제 리뷰
요청
피드백을 받아 진행
분석 처리 검증 도입 결론 결과
제거할 플래그를
소스에서 제거
ui.quest.appendMsg(“…”);
questDlg.show();
플래그 켜서
제거
#define _QUEST_SYSTEM // 퀘스트
#ifdef _QUEST_SYSTEM
#endif
ui.quest.appendMsg(“…”);
questDlg.show();
플래그 켜서
제거
ui.event.setVisible(0);
플래그 꺼서
제거
#ifdef _EVENT_2008 // 2008 설 이벤트
ui.event.setText(…);
ui.event.show();
#else
#endif
ui.event.setVisible(0);
플래그 꺼서
제거
#define _NEW_QUEST
플래그 병합
제거
#ifdef _NEW_QUEST
# define _NEW_QUEST_FIX
#endif
#ifdef _NEW_QUEST
ui.event.setText(…);
# ifdef _NEW_QUEST_FIX
ui.event.setPos(…);
# endif
#endif
#define _NEW_QUEST
플래그 병합
제거 #ifdef _NEW_QUEST
ui.event.setText(…);
ui.event.setPos(…);
#endif
http://dotat.at/prog/unifdef
Unifdef 가 기본적인 기능은 잘 해줌
다만 부분 평가는 해주지 않음
#if defined(A) && defined(B)
A=켬|끔 B=그냥둠
↓
#if 1|0 && defined(B)
병합 제거는 따로 구현
#ifdef A
# ifdef A_fix
…
# endif
#endif
#ifdef A
…
#endif
작업 흐름
unifdef 부분 평가 병합 제거
원시소스
제거 플래그 리스트
결과소스
분석 처리 검증 도입 결론 결과
처리 원시소스 결과소스
원시EXE 결과EXE =
동일한 소스를 두 번 빌드 후 EXE
빌드시간, 디버그 정보 등이 EXE 에 포함
동일한 소스를 두 번 빌드 후 OBJ
디버그 정보가 OBJ 에 포함
# name
#1 .drectve
#2 .debug$S
#3 .text
#4 .debug$S
#5 .rdata
#6 .text
#7 .debug$S
#8 .debug$T
OBJ 비교할 때
debug 섹션은 제외!
처리 원시소스 결과소스
원시OBJ 결과OBJ =
원시OBJ 결과OBJ ≠
DUMPBIN
처리 원시소스 비교 OK 같음
다름
다른 함수 찾기
원인 파악 툴 버그
소스 문제
플래그 켬/끔 오류
#include “show.h” //#include “Flags.h” void show() { #ifdef _QUEST_SYSTEM ui.quest.print(…); ui.questDlg.show(); #endif }
오류의 예
ASSERT 매크로 등에 있음!
검증 단계의 unifdef 는 행을 유지하도록.
__LINE__
__TIME__
작업 전에만 지웠다가 다시 살림.
분석 처리 검증 도입 결론 결과
제거 플래그 후보 선택
작업자의 리뷰
처리 / 검증 / 커밋
자동화 자동화
작업자 리뷰에 시간이 들기 때문에
리뷰 플래그 개수를 한번에 300~500 개로 유지
6 번의 플래그 제거 작업
총 2,107 개의 플래그 제거
2,309 파일에서
191,979 라인 제거
90%
10%
라인
코드 제거
작성한 툴
분석
처리
검증
Track
Uniform
CompareBin
소스 & SVN 에서 정보 수집
Unifdef + 추가 소스 처리
OBJ 파일 동일 검사
BatchRun 플래그 제거 및 검증을 일괄 실행
작성한 툴
분석
처리
검증
Track
Uniform
CompareBin
소스 & SVN 에서 정보 수집
Unifdef + 추가 소스 처리
OBJ 파일 동일 검사
BatchRun 플래그 제거 및 검증을 일괄 실행
총 LOC:
1500
분석 처리 검증 도입 결론 결과
불필요한 #if - #endif 제거는
소스 코드를 깔끔하게 유지하는데 도움!
소스의 유지보수성을 확보하는 작업을
자동화 시키고 신뢰성 있게 만드는 것이 중요!
자동화된 솔루션은 반복해서 사용할 수 있어
계속해서 도움을 받을 수 있음!
분석은 처리 뿐 아니라 대상을 바라보는
다른 관점을 제시하는 데에도 도움을 줌!
분석, 수행, 검증의 틀을
코드 / 데이터를 개선하는데 사용해보자!
문제되는 이름 추리기
소스에서 자동 변경
빌드 테스트
자동화된 Rename 리팩토링 ?
여러분들도!
감사합니다!
top related