[NDC2015] 언제 어디서나 프로파일링 가능한 코드네임 JYP 작성기 - 라이브...

Preview:

Citation preview

언제 어디서나 프로파일링 !JYP, 개발 중이나 배포 후에도

NEXON 인프라기술팀 하재승

프로파일을 여러 상황에서 자주 수행하기 위해범용 프로파일러를 작성한 경험의 공유 구현에 초점

개발 알파 버전 Windows 용 온라인 게임으로 제한 기초적인 수준의 결과 분석

발표 목표

프로파일링 ?

성능 문제를 확인하기 위한 동적 분석 도구

프로그램 실행 중의 메모리 사용량 , 수행 시간 / 횟수 등을 수집

프로파일러

프로파일링 방식 (CPU)

샘플링 주기적으로 실행 정보를 수집코드 삽입 측정할 부분에 코드를 삽입하여 수집그 외

JYP 에선 샘플링 방식을 이용해 구현

Self, Deep, Total count

샘플링으로 콜스택을 수집호출 순서 : A → B → C → D

D 는 스스로가 실행 중에 샘플링됨 → Self ++

A, B, C 가 콜스택에 존재하는 상태 → A, B, C 의 Deep ++

모두의 Total ++

참고자료

최적화와 프로파일링NDC 2008, 송창규님 사례를 통해 살펴보는 프로파일링과 최적화NDC 2013, 김이선님프로파일러를 이용한 게임 클라이언트

최적화 KGC 2014, 김종원님

한계

지속적으로 성능 문제를 확인하며 개선해야 함

특정 환경 , 특정 시점에서만 재현되는 경우 비개발자 머신에서 발생했을 때 이미 유저에게 배포한 이후

재현된 컴퓨터에 프로파일러 설치하고 attach? (*VerySleepy)

라이브 프로파일러 JYP

언제든 프로파일링을 할 수 있게

간단한 준비제공하는 Library 를 사용하거나코드 변경 없이 외부에서 Injection

수집된 내용이 서버로 전송되어 분석 분석 시점에 PDB 파일이 필요 ( 서버 )

배포 후에도 된다더니 ? (1)

배포 시 보안프로그램이 적용 nProtect, HackShield, XignCode 등 외부 툴 사용이 제한됨

디버거 , ReadProcessMemory, CreateRemoteThread, …

배포 후에도 된다더니 ? (2)

프로그램 내부에서 수집은 가능→ Library 통해 JYP 적용시 잘 동작→ 렉걸리는 집 컴퓨터에서도 바로 성능 테스트→ ??!?!?!→ PROFIT!!!

개발 관련 또는 개인 정보 유출 가능성

수집되는 정보 실행 중인 프로그램의 스택 값 읽어들인 모듈 정보= minidump 를 통해 수집되는 정보와 동일

PDB 는 노출되지 않음분석 작업은 서버에서만 이루어짐

대상 프로그램

JYP Client JYP Controller

Binary

User

Web

JYP Server

컨트롤러 프로그램으로 시작 / 중지

수집한 내용을 서버로 전송 후 분석

웹 인터페이스를 통해 결과 확인

컨트롤러 프로그램으로 시작 / 중지

수집한 내용을 서버로 전송 후 분석

웹 인터페이스를 통해 결과 확인

시연

테스트 클라이언트

http://www.minetest.net/

C++ 로 구현된 마인크래프트형 게임Irrlicht 엔진으로 구현 ( 이을리흐트 )

엔진과 게임을 직접 빌드하여pdb 를 JYP 에 추가해둠

캡쳐

컨트롤러로 등록 후 시작 / 멈춤

분석결과 보기

웹페이지에서 결과 조회하는 내용

숫자들

샘플링 시간 34 초분석 7 분샘플 개수 2472 개 72 개 / 초

게임 플레이에 거의 영향 없음

JYP 내부 구현

기본 아이디어

쓰레드별 스택을 수집하여 저장

저장된 데이터를 덤프인 것처럼 분석 루틴에 넘겨주자

분석된 콜스택으로부터 Call count, Call graph 등을 추출

대상 프로그램

JYP Client JYP Controller

Binary

User

Web

JYP Server

대상 프로그램

JYP Client JYP Controller

Binary

User

Web

JYP Server

JYP 컨트롤러

JYP 서버를 통해 인증

게임 내부의 JYP 클라이언트와 통신

서버 주소를 클라이언트에 전송함

대상 프로그램

JYP Client JYP Controller

Binary

User

Web

JYP Server

JYP 클라이언트 (1)

스택 수집 및 전송 – 부하는 적으면서 자주

SuspendThread → 수집 → ResumeThread→ 전송

수집은 어떻게 ?

스택

스택이 자라는 방향

ESP / RSP(Stack Pointer)

스택의 시작어떻게 알아낼까 ?

Thread Information Block (TIB)

FS:[0x04] Stack Base / Bottom of stackFS:[0x08] Stack Limit / Ceiling of stackFrom http://en.wikipedia.org/wiki/Win32_Thread_Information_Block

x86 에선 FS, x64 에선 GS 에 저장

NtQueryInformationThread 함수로TIB 주소를 얻어올 수 있음

THREAD_BASIC_INFORMATION typedef enum _THREADINFOCLASS { ThreadBasicInformation = 0,} THREADINFOCLASS;

typedef LONG KPRIORITY;

typedef struct _CLIENT_ID { HANDLE UniqueProcess; HANDLE UniqueThread;} CLIENT_ID;

typedef struct _THREAD_BASIC_INFORMATION{NTSTATUS ExitStatus;PVOID TebBaseAddress;CLIENT_ID ClientId;KAFFINITY AffinityMask;KPRIORITY Priority;KPRIORITY BasePriority;} THREAD_BASIC_INFORMATION, *PTHREAD_BASIC_INFORMATION;

ntdll.dll

NtQueryInformationThread(thread, ThreadBasicInformation, &basicInfo, sizeof(basicInfo), NULL);

tib = basicInfo.TebBaseAddresstib->StackBase

JYP 클라이언트 (2)

모든 쓰레드에 대해 ,

Esp (Rsp) 부터 StackBase 까지 복사

snappy 로 스택 내용을 압축 수십 kb → 수 kb 수준

대상 프로그램

JYP Client JYP Controller

Binary

User

Web

JYP Server

JYP 서버 - 수집

asio 서버로 수집컨트롤러로 인증을 하면 모듈 등록을 시작한다 .

컨트롤러를 통해 한 번의 시작 / 멈춤을 기준으로 분석 작업을 생성

JYP 서버 - 분석

수집된 내용을 분석하는 자식 프로세스 생성

DbgHelp.dll 의 함수들을 이용하여 분석

Process Id 를 임의로 주면 실제 프로세스 없이 메모리 정보만 가지고 분석할 수 있다 .

SymLoadModule64 DbgHelp 에 분석하려는 덤프에 있는 모듈의 이름 , 메모리 상 위치 , 크기를 알려줌

SymGetSymFromAddr64 SymGetLineFromAddr64 StackWalk64 의 결과로 부터 함수 이름과 줄 위치를 얻어옴

StackWalk64 주어진 메모리 정보와 레지스터들이 저장된 CONTEXT 로 부터 콜스택을 분석한다 .

ReadMemoryRoutine 을 커스텀 버전으로 대체

StackWalk64 분석과정에서 코드 영역을 읽어 콜스택 분석에 사용하기 때문에 각 모듈의 이미지 정보도 필요하다 .

JYP 서버 – 결과 조회

Crow + AngularJS

Deep, Self sample 개수 순 정렬Call graph 보여주기

모듈 , 쓰레드 필터링

JYP 서버 – 결과 조회

JYP 서버 – 결과 조회

Crow

http://github.com/ipkn/crow

내일 Crow 세션이 준비되어있습니다 !

간단히 소개하자면 …

Crow inside JYP

crow::App<crow::CookieParser> app;

app.route_dynamic("/")([&](){ … })

app.route_dynamic("/api/list")app.route_dynamic("/api/detail").methods(crow::HTTPMethod::POST)

crow::App<crow::CookieParser> app;crow::mustache::set_base(".");

auto tpl_index = crow::mustache::load("index.html");

app.route_dynamic("/")([&]{

return tpl_index.render();});

crow::json::wvalue val;

for(auto& m : result.modules_){ val["ms"][idx][0] = m.moduleName; val["ms"][idx][1] = m.baseAddr; val["ms"][idx][2] = m.baseSize; idx++;}

...

AngularJS

https://angularjs.org/

구현 상의 삽질 (1)

매번 미니 덤프 생성하는 방식 시도 느려서 교체 ( 초당 10 회 수준 )

스택 시작 주소를 얻기 위해 VirtualQueryEx 로 ESP 부터 올라가며 읽기 가능한 페이지 인지 확인했었다 실제 스택보다 더 크게 계산

구현 상의 삽질 (2)

스택의 binary diff 만 전송 시도 diff 크기가 압축에 비해 훨씬 작다 binary diff 생성이 심각하게 느림 snappy 압축만으로 충분히 작아서 해결

가끔 데드락 발생하던 문제 수정SuspendThread + 메모리 할당

마무리

결론 ?

여러 도구들을 결합해서 프로파일러를 만듬

앞으로 ..

서버 , 모바일로 확장 리눅스 - ELF, DWARF 포맷 지원 유니티

모니터링 기능 추가이벤트 기반 자동 기록

그리고 더 앞으로 ...

넥슨 라이브인프라 실의실시간 모니터링 시스템을 통한 연계

상황별 정보 ( 현재 게임 상태 , FPS 등 )

특정 퀘스트나 맵에서 렉 유발→ 유저 불만 발생 → 개발팀 인식→ 서버에 상황 등록 → 재현시 자동으로 성능 정보 수집 모님의 우주정복 계획 (?)

QnA

라이브 프로파일러 JYP

게임 클라이언트에 JYP 심어두기컨트롤러 프로그램으로 시작 / 중지수집한 내용을 서버로 전송 후 분석웹 인터페이스를 통해 결과 확인

Recommended