SlideShare a Scribd company logo
박한범
Kosslab
눈발자국
CONTENTS
1. Tracing 소개, Printf is nothing or everything.
2. Tracing 에 도움이 되는 요소들
3. Tracing 오픈소스 접근성을 올려주는 요소
4. Tracing 에 배경지식과 개발자 메시지 입히기
5. 눈발자국
Tracing 소개
Printf is nothing, or everything
프로그래밍은 쉬운 적이 없죠
코드의 시각화를 위한 Printf 문의 발전
Printf(“function entry : %d %dn”, a, b);
Static bool debug = true;
if (debug)
Printf(“function entry : %d %dn”, a,
b);#ifdef DEBUG
#define DBGPR(a, args...) printf("%s(%s:%d) " a, __func__,__FI
__LINE__, ##args)
#else
#define DBGPR(a, args...)
#endif
Printf is nothing, or everything
개발자가 Printf 를 활용하는 방식 = Tracing
Tracing, “프로그램의 실행 동안의 변화를 다각도로 기록하고 이를 검토하는 일”
기록을 위한 장치 = 계측기 = Printf = instrument
Operating System
Printf 보다 고도화 된 Instrument의
필요성
Application Programming Interface
Platform
Application
Library
계측기=Printf
필요
Printf, Printf, Printf, Printf 를 가능케
하라
Linking
Table
Kernal
Library
Function
Function
Function
Uftrace
kernel record
Binary
API
funcA() 호출 hook
getpid() 호출 hook
syscall
sys_getpid()
int funcA(int arg) {
return getpid() % arg;
}
User space Kernel space
API record
Function record
함수 호출 기록
API 호출 기록
Ftrace
- 함수 호출 기록
- 커널 이벤트 기록
커널 함수 호출
커널 이벤트 기록
Printf 안되면 되게 하라!
Uftrace
<Dynamic_Entry>
push %rbp
mov %rsp,%rbp
sub $0x10,%rsp
mov %edi,-0x4(%rbp)
push %rbp
mov %rsp,%rbp
mov $0x186a0,%edi
Jmp to origin
Jmp to origin
callq *0x2008b0(%rip) # 200fe8 < Dynamic_Entry >
callq *0x2008b0(%rip) # 200fe8 <Dynamic_Entry>
callq 5d0 <getpid@plt>
cltd
idivl -0x4(%rbp)
mov %edx,%eax
leaveq
retq
callq 77a <funcA>
pop %rbp
retq
<funcA>:
<main>:
Original Instructions
Original Instructions
모든 영역에서 Printf-ing 하다
$ sudo ./uftrace –L. –k –a test
Operating System
Application Programming Interface
Platform
Library
Application
# DURATION TID FUNCTION
[ 1598] | main() {
[ 1598] | funcA(100000) {
[ 1598] | getpid() {
0.269 us [ 1598] | sys_getpid();
1.375 us [ 1598] | } = 1598; /*
getpid */
1.375 us [ 1598] | } = 1598; /* funcA
*/
2.355 us [ 1598] | } = 1598; /* main */
기록했으니 검토를 시작해야죠…
Tracing, “프로그램의 실행 동안의 변화를 다각도로 기록하고 이를 검토하는 일”
 함수 호출
 API 호출
 커널 함수 호출
 함수 호출 인자
 함수 반환 인자
호출 기록 검토
시각화
Tracing
도움이 되는 요소들
Gcc에서 만난 문제를 추적했던 경우
$ ./compile_by_gcc
Segmentation fault (core dumped)
$ ./compile_by_clang
Hello world
static inline __attribute__((always_inline)) char*
get_msg() {
char str[64] = "Hello worldn";
return (char*)str;
}
int main() {
char* p;
p = get_msg();
puts(p);
return 0;
static inline __attribute__((always_inline)) char* get_msg()
{
const char* str = "Hello worldn";
char* p = malloc(strlen(str));
strcpy(p, str);
return p;
Mitigation도 좋지만 해결책을 찾아볼까?
static inline __attribute__((always_inline)) char* get_msg()
{
char* str = "Hello worldn";
return str;
}
Mitigation 방안 #1 - 문자를 수정
못함
Mitigation 방안 #2 - 메모리 해제가 강제됨
개발자가 남긴 #1 Warning Message
$ gcc inline.c
inline.c: In function ‘get_msg:
inline.c:6:9: warning: function returns address of local variable
[-Wreturn-local-addr]
return (char *)str;
if (DECL_P(inner) && !DECL_EXTERNAL(inner) && !TREE_STATIC(inner) &&
DECL_CONTEXT(inner) == current_function_decl) {
if (TREE_CODE(inner) == LABEL_DECL)
warning_at(loc, OPT_Wreturn_local_addr,
"function returns address of label");
else {
warning_at(loc, OPT_Wreturn_local_addr,
"function returns address of local variable");
tree zero = build_zero_cst(TREE_TYPE(res));
t = build2(COMPOUND_EXPR, TREE_TYPE(res), t, zero);
}
}
Warning Message 로 찾은 문제지점
if (DECL_P (inner)
&& !DECL_EXTERNAL (inner)
&& !TREE_STATIC (inner)
&& !DECL_DECLARED_INLINE_P(current_function_decl)
&& DECL_CONTEXT (inner) == current_function_decl)
return 될 tree가 속한 함수가
inline 으로 선언되어 있으면
NULL 로 바꾸지 마세요!
declared_inline_flag = 1
static_flag = 1
Current_function_decl.function_decl
;; Function get_msg (get_msg, funcdef_no=0, decl_uid=1792,
cgraph_uid=0, symbol_order=0)
Deleted dead store: str = "Hello worldn";
__attribute__((always_inline)) get_msg ()
{
char str[64];
<bb 2> [0.00%]:
str ={v} {CLOBBER};
return &str;
}
개발자가 남긴 #2 Debug 용 로그
inline.c.040t.dse1
Deleted dead store: str = "Hello worldn";
$ gcc –O1 –fdump-tree-all-details inline.c
if (DECL_P(inner) && !DECL_EXTERNAL(inner) && !TREE_STATIC(inner)
&&
DECL_CONTEXT(inner) == current_function_decl) {
if (TREE_CODE(inner) == LABEL_DECL)
warning_at(loc, OPT_Wreturn_local_addr,
"function returns address of label");
else {
warning_at(loc, OPT_Wreturn_local_addr,
"function returns address of local variable");
tree zero = build_zero_cst(TREE_TYPE(res));
t = build2(COMPOUND_EXPR, TREE_TYPE(res), t, zero);
}
}
메모리 최적화 때문일까?
else if
(DECL_DECLARED_INLINE_P(current_function_decl))
inner->base.volatile_flag = true;
메모리 관련 최적화
Disable
if (DECL_P (inner) && !DECL_EXTERNAL (inner)
&& !TREE_STATIC (inner)
&& !DECL_DECLARED_INLINE_P(current_function_decl)
&& DECL_CONTEXT (inner) == current_function_decl)
개발자가 남긴 메시지의 중요성
Dump of assembler code for function main:
<+4>: movdqa 0x1b4(%rip),%xmm0
<+12>: mov %rsp,%rdi
<+15>: movaps %xmm0,(%rsp)
<+19>: pxor %xmm0,%xmm0
<+23>: movaps %xmm0,0x10(%rsp)
<+28>: movaps %xmm0,0x20(%rsp)
<+33>: movaps %xmm0,0x30(%rsp)
<+38>: callq 0x400400 <puts@plt>
Dump of assembler code for function main:
<+4>: movabs $0x6f77206f6c6c6548,%rax
<+14>: mov $0xa646c72,%edx
<+19>: mov %rax,(%rsp)
<+23>: mov %rdx,0x8(%rsp)
<+28>: movq $0x0,0x10(%rsp)
<+37>: movq $0x0,0x18(%rsp)
<+46>: movq $0x0,0x20(%rsp)
<+55>: movq $0x0,0x28(%rsp)
<+64>: movq $0x0,0x30(%rsp)
<+73>: movq $0x0,0x38(%rsp)
<+82>: mov %rsp,%rdi
<+85>: callq 0x400400 <puts@plt> O1
$ gcc –O1 inline.c
$ ./a.out
Hello world
O2
$ gcc –O2 inline.c
$ ./a.out
Hello world
특정 오픈소스를 분석해야 하는 경우
public class Main {
public static void main(String[] args) {
System.out.println("Hello World!");
}
}
$ javac Main.java
$ dx --dex --output=classes.dex Main.class
$ zip Main.apk classes.dex
$uftracerecord-a`which dalvikvm64`–cp Main.apk Main
Tracing 에 도움이 되는 요소들
거대 오픈소스를 분석할 때는 관련 배경지식이 매우 중요합니다
VM
Interprete
r
Bytecode
동작 방식에
대한 이해
DEX 파일
포맷에
대한 지식
Bytecode에
대한 지식
Tracing 에 도움이 되는 요소들
Tracing 시각화의 유용성
Tracing 에 도움이 되는 요소들
Tracing 시각화의 유용성
art::ClassLinker::FindClass
└ art::ClassLinker::DefineClass
└ art::ClassLinker::LinkClass
└ art::ClassLinker::LinkMethods
└ art::ClassLinker::LinkInterfaceMethods
Class Path
Tracing
오픈소스 접근성을 올려주는 요소
개발자가 남긴 메시지는 매우 중요!!
;; Function get_msg (get_msg, funcdef_no=0, decl_uid=1792, cgraph_uid=0, symbol_order=0)
Deleted dead store: str = "Hello worldn";
__attribute__((always_inline)) get_msg ()
{
char str[64];
<bb 2> [0.00%]:
str ={v} {CLOBBER};
return &str;
}
코드는 접근성이 매우 떨어집니다.
;; Function get_msg (get_msg, funcdef_no=0, decl_uid=1792, cgraph_uid=0, symbol_order=0)
Deleted dead store: str = "Hello worldn";
__attribute__((always_inline)) get_msg ()
{
char str[64];
<bb 2> [0.00%]:
str ={v} {CLOBBER};
return &str;
}
(gdb) p *gsi->ptr
$4 = {code = GIMPLE_ASSIGN, no_warning = 0, visited = 0,
nontemporal_move = 0, plf = 1, modified = 0, has_volatile_ops =
0, pad = 0, subcode = 32, uid = 0,
location = 2147483655, num_ops = 2, bb = 0x7ffff6ede478,
next = 0x7ffff702d190, prev = 0x7ffff702d140}
VM
Interpreter
Tracing에는 배경지식이 매우 중요합니다.
Bytecode
동작 방식에
대한 이해
DEX 파일
포맷에
대한 지식
Bytecode에
대한 지식
INTERPRETER
CLASSLOADER
DalvikVM
HEAP DATA
Tables
Global Area
Class
Loader
Class
Loader
Loaded
Classes
Class
Loader
FindClass
From
LoadedClasses
1
vtable
ClassObject
static field
iftrable
directMethods
virtualMethods
Method Area
Method
Method
Dex Header
String_ids
Type_ids
Proto_ids
Field_ids
Method_ids
Class_defs
Data
DexFile Format
Dex
OpenDex
2
DexParse
3
LoadClass
4
Add ClassHash
To
Loaded Classes
5
ParseClassDef
LoadData
4-1
4-2
Method
Instructions
부족한 배경지식은 강제로 배우게
되니까요
else if
(DECL_DECLARED_INLINE_P(current_function_decl))
inner->base.volatile_flag = true;
GIMPLE RTL
TREE
Abstract IR
Front-End Back-End
Front-end Optimize Pass
DSE
Volatile
CLOBBER
CONSTRUTOR
누군가 알려줬다면 훨씬 쉬웠을텐데…
Open-source tracing data
API
디버깅 정보
배경 지식
관련 지식
Tracing
배경지식과 개발자 메시지 입히기
(feat, NodeJS/Pinpoint/v8)
NodeJS 개발자들이 남긴 메시지
function find_me_a() {
console.log("hellworld");
}
$ nodejs --trace hellworld.js
1: ~+0(this=0x355aaee01521 <JSGlobal Object>) {
2: ~find_me_a+0(this=0x355aaee01521 <JSGlobal Object>) {
hellworld
2: } -> 0x1aa1629004d1 <undefined>
1: } -> 0x1aa1629004d1 <undefined>
또 다른 메시지 —print-bytecode
--trace 옵션 enable 시 생성되는 bytecode
--trace 옵션 disable 시 생성되는 bytecode
[generated bytecode for function: find_me_a]
Parameter count 1
Register count 3
Frame size 24
18 E> 0x1ce45b01eb1f @ 5 : a5 StackCheck
27 S> 0x1ce45b01eb20 @ 6 : 13 00 00 LdaGlobal [0], [0]
0x1ce45b01eb23 @ 9 : 26 fa Star r1
35 E> 0x1ce45b01eb25 @ 11 : 28 fa 01 02 LdaNamedProperty r1, [1], [2]
0x1ce45b01eb29 @ 15 : 26 fb Star r0
0x1ce45b01eb2b @ 17 : 12 02 LdaConstant [2]
0x1ce45b01eb2d @ 19 : 26 f9 Star r2
35 E> 0x1ce45b01eb2f @ 21 : 59 fb fa f9 04 CallProperty1 r0, r1, r2, [4]
0x1ce45b01eb34 @ 26 : 0d LdaUndefined
0x1ce45b01eb35 @ 27 : 26 fb Star r0
0x1ce45b01eb37 @ 29 : 61 a8 01 fb 01 CallRuntime [TraceExit], r0-r0
53 S> 0x1ce45b01eb3c @ 34 : a9 Return
[generated bytecode for function: find_me_a]
Parameter count 1
Register count 3
Frame size 24
0x1ce45b01eb1a @ 0 : 61 a7 01 fb 00 CallRuntime [TraceEnter], r0-r0
18 E> 0x1ce45b01eb1f @ 5 : a5 StackCheck
27 S> 0x1ce45b01eb20 @ 6 : 13 00 00 LdaGlobal [0], [0]
0x1ce45b01eb23 @ 9 : 26 fa Star r1
35 E> 0x1ce45b01eb25 @ 11 : 28 fa 01 02 LdaNamedProperty r1, [1], [2]
0x1ce45b01eb29 @ 15 : 26 fb Star r0
0x1ce45b01eb2b @ 17 : 12 02 LdaConstant [2]
0x1ce45b01eb2d @ 19 : 26 f9 Star r2
35 E> 0x1ce45b01eb2f @ 21 : 59 fb fa f9 04 CallProperty1 r0, r1, r2, [4]
0x1ce45b01eb34 @ 26 : 0d LdaUndefined
0x1ce45b01eb35 @ 27 : 26 fb Star r0
0x1ce45b01eb37 @ 29 : 61 a8 01 fb 01 CallRuntime [TraceExit], r0-r0
53 S> 0x1ce45b01eb3c @ 34 : a9 Return
CallRuntime [TraceEnter], r0-r0
CallRuntime [TraceExit], r0-r0
TraceEnter/Exit 가 추가되는 것을 확인
function find_me_a() {
console.log("hellworld")
;
}
function find_me_a() {
TraceEnter();
console.log("hellworld");
TraceExit();
}
TraceEnter 의도된 작업
function find_me_a() {
TraceEnter();
console.log("hellworld");
TraceExit();
}
find_me_a caller
find_me_a
Javascript Stackframe
READ
WRITE
2: ~find_me_a+0(this=0x355aaee01521 <JSGlobal Object>) { stdout
TraceEnter Enter
Calling Convention – 인자 값 전달
방식
RUNTIME_FUNCTION
(Runtime_TraceEnter)
JavaScriptFrame::PrintTop
R1 레지스터에 인자#1 Set
R2 레지스터에 인자#2 Set
R1 레지스터에서 인자#1 Read
R2 레지스터에서 인자#2 Read
Call PrintTop
정해진 레지스터로 인자 Set 후 호출
정해진 레지스터에서 인자 읽기
Uftrace에서 활용하는 방식
RUNTIME_FUNCTION
(Runtime_TraceEnter)
JavaScriptFrame::PrintTop
R1 레지스터에 인자#1 Set
R2 레지스터에 인자#2 Set
R1 레지스터에서 인자#1 Read
R2 레지스터에서 인자#2 Read
Call PrintTop
정해진 레지스터로 인자 Set 후 호출
정해진 레지스터에서 인자 읽기
instrument
Uftrace
레지스터에서 인자들을 읽어들이기
Uftrace로 출력 Stream 가로채기
function find_me_a() {
TraceEnter();
console.log("hellworld");
TraceExit();
}
find_me_a caller
find_me_a
Javascript Stackframe
READ
WRITE
2: ~find_me_a+0(this=0x355aaee01521 <JSGlobal Object>) {
UFTRACE
Stream
TraceEnter Enter
Uftrace
Tracing Data에 기록한 결과
[ 30053] | v8::internal::Runtime_CompileLazy() {
[ 30053] | v8::internal::StackLimitCheck::JsHasOverflowed() {
0.145 us [ 30053] | v8::internal::GetCurrentStackPosition();
1.453 us [ 30053] | } /* v8::internal::StackLimitCheck::JsHasOverflowed */
[ 30053] | v8::internal::Compiler::Compile() {
[ 30053] | v8::internal::Compiler::Compile() {
[ 30053] | ~find_me_a+0(this=0x2c3bb9284de9 <JSGlobal Object>, 0x081d3615ead1 …)
JavaScript 호출
JavaScript 호출
처리 동작
Pinpoint 의 instrument
Pinpoint APM
Support GRPC since 1.9.0
Simple NodeJS APM
find_me_a
find_me_b
find_me_c
Uftrace
plugin
Collector
Database
WebUI
Webserver
http://server/find_me_a
GRPC
request
too much information
Javascript 함수의 Parsing
Declarations
function find_me_a
function find_me_b
function find_me_c
Statement
Call find_me_a
Parsing
Func 0
function find_me_a()
{
return
find_me_b();
}
function find_me_b()
{
return
find_me_c();
}
Javascript 코드의 실행 과정
START
v8::internal::_GLOBAL__N_1::CompileToplevel()
Enter Func 0
Call find_me_a()
v8::internal::Execution::Call
v8::internal::Execution::(anonymouse
namespace)::Invoke
v8::internal::Runtime_CompileLazy
v8::internal:__RT_imple_Runtime_CompileLazy
v8::internal::Compiler::Compile
Enter find_me_a
Call find_me_b()
v8::internal::Execution::Call
v8::internal::Execution::(anonymouse
namespace)::Invoke
v8::internal::Runtime_CompileLazy
v8::internal:__RT_imple_Runtime_CompileLazy
v8::internal::Compiler::Compile
Enter find_me_b
Call find_me_c()
v8::internal::Execution::Call
v8::internal::Execution::(anonymouse
namespace)::Invoke
v8::internal::Runtime_CompileLazy
v8::internal:__RT_imple_Runtime_Com
v8::internal::Compiler::Compile
TraceEnter 삽입 지점
START
v8::internal::_GLOBAL__N_1::CompileToplevel()
Enter Func 0
Call find_me_a()
v8::internal::Execution::Call
v8::internal::Execution::(anonymouse
namespace)::Invoke
v8::internal::Runtime_CompileLazy
v8::internal:__RT_imple_Runtime_CompileLazy
v8::internal::Compiler::Compile
Enter find_me_a
Call find_me_b()
v8::internal::Execution::Call
v8::internal::Execution::(anonymouse
namespace)::Invoke
v8::internal::Runtime_CompileLazy
v8::internal:__RT_imple_Runtime_CompileLazy
v8::internal::Compiler::Compile
Enter find_me_b
Call find_me_c()
v8::internal::Execution::Call
v8::internal::Execution::(anonymouse
namespace)::Invoke
v8::internal::Runtime_CompileLazy
v8::internal:__RT_imple_Runtime_Com
v8::internal::Compiler::Compile
TraceEnter 삽입 TraceEnter 삽입 TraceEnter 삽입
FLAG_trace
NodeJS에 대한 배경지식
v8::Local<v8::Script> script = v8::Script::Compile(context, source);
v8::Local<v8::Value> result = script->Run(context);
Javascript
OPERATING SYSTEM
Application Programming Interface
NodeJS
JavaScript
Engine
NodeJS와 v8의 상호작용
Native Component
Libuv (I/O, Http, …)
Parser
Abstract Syntax
Tree
Compiler
Generate Bytecode
Generate Opcode
Run
Result
Javascript Compile 함수
v8::internal::Compiler::Compile(Handle<JSFunction> function, ClearExceptionFlag, IsCompiledScope*)
v8::internal::JSFunction
Internal
Local
v8::Function
v8::Function *func = reinterpret_cast<v8::Function*>(ARG1(regs));
v8::String::Utf8Value name(func->CreationContext()->GetIsolate(), func->GetDebugName());
배경지식 입히기
특정 JavaScript Function만 기록되게 만드는 로직
v8 Uftrace Plugin
v8::internal::Compiler::Compile 시작 처리 Function 이름 읽기
v8::internal::FLAG_trace
on
Runtime_TraceEnter
Bytecode 삽입
v8::internal::FLAG_trace
off
v8::internal::Compiler::Compile 종료 처리
필요 함수만을 선별 기록
눈발자국
뒷사람의 이정표가 된다
치환
Tracing
Data
Plugin 제작을 통한 소기 목적 달성
v8::internal::Compiler::Compile
v8::internal:__RT_imple_Runtime_CompileLazy
v8::internal::Runtime_CompileLazy
Javascript 함수를
컴파일하는 CallTree
12,353,295 => 9,969,632 line <Function name> Lazy Compile
Javascript 함수가
컴파일된다는 문맥
Uftrace plugin
Tracing
Data
오픈소스 접근성 높히기
오픈소스
Tracing
Data
문맥 #1
문맥 #2
문맥 #3
문맥 #4
문맥 #5
문맥 #6
디버깅메시지 추가정보 배경지식
오픈소스에 문맥 정보를 부여
Q & A
Thank You

More Related Content

What's hot

사례를 통해 살펴보는 프로파일링과 최적화 NDC2013
사례를 통해 살펴보는 프로파일링과 최적화 NDC2013사례를 통해 살펴보는 프로파일링과 최적화 NDC2013
사례를 통해 살펴보는 프로파일링과 최적화 NDC2013
Esun Kim
 
[KGC 2011]Boost 라이브러리와 C++11
[KGC 2011]Boost 라이브러리와 C++11[KGC 2011]Boost 라이브러리와 C++11
[KGC 2011]Boost 라이브러리와 C++11
흥배 최
 
자동화된 소스 분석, 처리, 검증을 통한 소스의 불필요한 #if - #endif 제거하기 NDC2012
자동화된 소스 분석, 처리, 검증을 통한 소스의 불필요한 #if - #endif 제거하기 NDC2012자동화된 소스 분석, 처리, 검증을 통한 소스의 불필요한 #if - #endif 제거하기 NDC2012
자동화된 소스 분석, 처리, 검증을 통한 소스의 불필요한 #if - #endif 제거하기 NDC2012
Esun Kim
 
Modern C++ 프로그래머를 위한 CPP11/14 핵심
Modern C++ 프로그래머를 위한 CPP11/14 핵심Modern C++ 프로그래머를 위한 CPP11/14 핵심
Modern C++ 프로그래머를 위한 CPP11/14 핵심
흥배 최
 

What's hot (20)

사례를 통해 살펴보는 프로파일링과 최적화 NDC2013
사례를 통해 살펴보는 프로파일링과 최적화 NDC2013사례를 통해 살펴보는 프로파일링과 최적화 NDC2013
사례를 통해 살펴보는 프로파일링과 최적화 NDC2013
 
C++ 11 에 대해서 쉽게 알아봅시다 1부
C++ 11 에 대해서 쉽게 알아봅시다 1부C++ 11 에 대해서 쉽게 알아봅시다 1부
C++ 11 에 대해서 쉽게 알아봅시다 1부
 
GKAC 2015 Apr. - Android Looper
GKAC 2015 Apr. - Android LooperGKAC 2015 Apr. - Android Looper
GKAC 2015 Apr. - Android Looper
 
20150212 c++11 features used in crow
20150212 c++11 features used in crow20150212 c++11 features used in crow
20150212 c++11 features used in crow
 
[KGC 2011]Boost 라이브러리와 C++11
[KGC 2011]Boost 라이브러리와 C++11[KGC 2011]Boost 라이브러리와 C++11
[KGC 2011]Boost 라이브러리와 C++11
 
Javascript 조금 더 잘 알기
Javascript 조금 더 잘 알기Javascript 조금 더 잘 알기
Javascript 조금 더 잘 알기
 
자동화된 소스 분석, 처리, 검증을 통한 소스의 불필요한 #if - #endif 제거하기 NDC2012
자동화된 소스 분석, 처리, 검증을 통한 소스의 불필요한 #if - #endif 제거하기 NDC2012자동화된 소스 분석, 처리, 검증을 통한 소스의 불필요한 #if - #endif 제거하기 NDC2012
자동화된 소스 분석, 처리, 검증을 통한 소스의 불필요한 #if - #endif 제거하기 NDC2012
 
[KGC 2012]Boost.asio를 이용한 네트웍 프로그래밍
[KGC 2012]Boost.asio를 이용한 네트웍 프로그래밍[KGC 2012]Boost.asio를 이용한 네트웍 프로그래밍
[KGC 2012]Boost.asio를 이용한 네트웍 프로그래밍
 
포트폴리오에서 사용한 모던 C++
포트폴리오에서 사용한 모던 C++포트폴리오에서 사용한 모던 C++
포트폴리오에서 사용한 모던 C++
 
6 function
6 function6 function
6 function
 
GKAC 2015 Apr. - Battery, 안드로이드를 위한 쉬운 웹 API 호출
GKAC 2015 Apr. - Battery, 안드로이드를 위한 쉬운 웹 API 호출GKAC 2015 Apr. - Battery, 안드로이드를 위한 쉬운 웹 API 호출
GKAC 2015 Apr. - Battery, 안드로이드를 위한 쉬운 웹 API 호출
 
GKAC 2014 Nov. - 안드로이드 스튜디오로 생산성 올리기
GKAC 2014 Nov. - 안드로이드 스튜디오로 생산성 올리기GKAC 2014 Nov. - 안드로이드 스튜디오로 생산성 올리기
GKAC 2014 Nov. - 안드로이드 스튜디오로 생산성 올리기
 
불어오는 변화의 바람, From c++98 to c++11, 14
불어오는 변화의 바람, From c++98 to c++11, 14 불어오는 변화의 바람, From c++98 to c++11, 14
불어오는 변화의 바람, From c++98 to c++11, 14
 
Startup JavaScript 5 - 객체(Date, RegExp, Object, Global)
Startup JavaScript 5 - 객체(Date, RegExp, Object, Global)Startup JavaScript 5 - 객체(Date, RegExp, Object, Global)
Startup JavaScript 5 - 객체(Date, RegExp, Object, Global)
 
Startup JavaScript 8 - NPM, Express.JS
Startup JavaScript 8 - NPM, Express.JSStartup JavaScript 8 - NPM, Express.JS
Startup JavaScript 8 - NPM, Express.JS
 
Modern C++ 프로그래머를 위한 CPP11/14 핵심
Modern C++ 프로그래머를 위한 CPP11/14 핵심Modern C++ 프로그래머를 위한 CPP11/14 핵심
Modern C++ 프로그래머를 위한 CPP11/14 핵심
 
스위프트 성능 이해하기
스위프트 성능 이해하기스위프트 성능 이해하기
스위프트 성능 이해하기
 
[D2 오픈세미나]5.robolectric 안드로이드 테스팅
[D2 오픈세미나]5.robolectric 안드로이드 테스팅[D2 오픈세미나]5.robolectric 안드로이드 테스팅
[D2 오픈세미나]5.robolectric 안드로이드 테스팅
 
Startup JavaScript 6 - 함수, 스코프, 클로저
Startup JavaScript 6 - 함수, 스코프, 클로저Startup JavaScript 6 - 함수, 스코프, 클로저
Startup JavaScript 6 - 함수, 스코프, 클로저
 
Es2015 Simple Overview
Es2015 Simple OverviewEs2015 Simple Overview
Es2015 Simple Overview
 

Similar to Deview 2019 눈발자국

[0312 조진현] good bye dx9
[0312 조진현] good bye dx9[0312 조진현] good bye dx9
[0312 조진현] good bye dx9
진현 조
 
Cpp 0x kimRyungee
Cpp 0x kimRyungeeCpp 0x kimRyungee
Cpp 0x kimRyungee
scor7910
 
Remote-debugging-based-on-notrace32-20130619-1900
Remote-debugging-based-on-notrace32-20130619-1900Remote-debugging-based-on-notrace32-20130619-1900
Remote-debugging-based-on-notrace32-20130619-1900
Samsung Electronics
 
이권일 Sse 를 이용한 최적화와 실제 사용 예
이권일 Sse 를 이용한 최적화와 실제 사용 예이권일 Sse 를 이용한 최적화와 실제 사용 예
이권일 Sse 를 이용한 최적화와 실제 사용 예
zupet
 
MapReduce 실행 샘플 (K-mer Counting, K-means Clustering)
MapReduce 실행 샘플 (K-mer Counting, K-means Clustering)MapReduce 실행 샘플 (K-mer Counting, K-means Clustering)
MapReduce 실행 샘플 (K-mer Counting, K-means Clustering)
주영 송
 
Boost 라이브리와 C++11
Boost 라이브리와 C++11Boost 라이브리와 C++11
Boost 라이브리와 C++11
OnGameServer
 
Api design for c++ 6장
Api design for c++ 6장Api design for c++ 6장
Api design for c++ 6장
Ji Hun Kim
 

Similar to Deview 2019 눈발자국 (20)

DEVIEW-FULL-감독판.pptx
DEVIEW-FULL-감독판.pptxDEVIEW-FULL-감독판.pptx
DEVIEW-FULL-감독판.pptx
 
ffmpeg optimization using CUDA
ffmpeg optimization using CUDAffmpeg optimization using CUDA
ffmpeg optimization using CUDA
 
C++17 Key Features Summary - Ver 2
C++17 Key Features Summary - Ver 2C++17 Key Features Summary - Ver 2
C++17 Key Features Summary - Ver 2
 
[2012 CodeEngn Conference 07] nesk - Defcon 20th : 본선 CTF 문제풀이
[2012 CodeEngn Conference 07] nesk - Defcon 20th : 본선 CTF 문제풀이[2012 CodeEngn Conference 07] nesk - Defcon 20th : 본선 CTF 문제풀이
[2012 CodeEngn Conference 07] nesk - Defcon 20th : 본선 CTF 문제풀이
 
[0312 조진현] good bye dx9
[0312 조진현] good bye dx9[0312 조진현] good bye dx9
[0312 조진현] good bye dx9
 
[NDC2016] TERA 서버의 Modern C++ 활용기
[NDC2016] TERA 서버의 Modern C++ 활용기[NDC2016] TERA 서버의 Modern C++ 활용기
[NDC2016] TERA 서버의 Modern C++ 활용기
 
Cpp 0x kimRyungee
Cpp 0x kimRyungeeCpp 0x kimRyungee
Cpp 0x kimRyungee
 
온라인 게임에서 사례로 살펴보는 디버깅 in NDC2010
온라인 게임에서 사례로 살펴보는 디버깅 in NDC2010온라인 게임에서 사례로 살펴보는 디버깅 in NDC2010
온라인 게임에서 사례로 살펴보는 디버깅 in NDC2010
 
온라인 게임에서 사례로 살펴보는 디버깅 in NDC10
온라인 게임에서 사례로 살펴보는 디버깅 in NDC10온라인 게임에서 사례로 살펴보는 디버깅 in NDC10
온라인 게임에서 사례로 살펴보는 디버깅 in NDC10
 
NDC11_김성익_슈퍼클래스
NDC11_김성익_슈퍼클래스NDC11_김성익_슈퍼클래스
NDC11_김성익_슈퍼클래스
 
Remote-debugging-based-on-notrace32-20130619-1900
Remote-debugging-based-on-notrace32-20130619-1900Remote-debugging-based-on-notrace32-20130619-1900
Remote-debugging-based-on-notrace32-20130619-1900
 
7가지 동시성 모델 - 데이터 병렬성
7가지 동시성 모델 - 데이터 병렬성7가지 동시성 모델 - 데이터 병렬성
7가지 동시성 모델 - 데이터 병렬성
 
이권일 Sse 를 이용한 최적화와 실제 사용 예
이권일 Sse 를 이용한 최적화와 실제 사용 예이권일 Sse 를 이용한 최적화와 실제 사용 예
이권일 Sse 를 이용한 최적화와 실제 사용 예
 
MapReduce 실행 샘플 (K-mer Counting, K-means Clustering)
MapReduce 실행 샘플 (K-mer Counting, K-means Clustering)MapReduce 실행 샘플 (K-mer Counting, K-means Clustering)
MapReduce 실행 샘플 (K-mer Counting, K-means Clustering)
 
파이썬 스터디 2주차
파이썬 스터디 2주차파이썬 스터디 2주차
파이썬 스터디 2주차
 
IPython
IPythonIPython
IPython
 
ecdevday8 웹개발자의 약한고리 SQL 뛰어넘기
ecdevday8 웹개발자의 약한고리 SQL 뛰어넘기ecdevday8 웹개발자의 약한고리 SQL 뛰어넘기
ecdevday8 웹개발자의 약한고리 SQL 뛰어넘기
 
Boost라이브러리의내부구조 20151111 서진택
Boost라이브러리의내부구조 20151111 서진택Boost라이브러리의내부구조 20151111 서진택
Boost라이브러리의내부구조 20151111 서진택
 
Boost 라이브리와 C++11
Boost 라이브리와 C++11Boost 라이브리와 C++11
Boost 라이브리와 C++11
 
Api design for c++ 6장
Api design for c++ 6장Api design for c++ 6장
Api design for c++ 6장
 

Deview 2019 눈발자국

  • 2. CONTENTS 1. Tracing 소개, Printf is nothing or everything. 2. Tracing 에 도움이 되는 요소들 3. Tracing 오픈소스 접근성을 올려주는 요소 4. Tracing 에 배경지식과 개발자 메시지 입히기 5. 눈발자국
  • 3. Tracing 소개 Printf is nothing, or everything
  • 5. 코드의 시각화를 위한 Printf 문의 발전 Printf(“function entry : %d %dn”, a, b); Static bool debug = true; if (debug) Printf(“function entry : %d %dn”, a, b);#ifdef DEBUG #define DBGPR(a, args...) printf("%s(%s:%d) " a, __func__,__FI __LINE__, ##args) #else #define DBGPR(a, args...) #endif
  • 6. Printf is nothing, or everything 개발자가 Printf 를 활용하는 방식 = Tracing Tracing, “프로그램의 실행 동안의 변화를 다각도로 기록하고 이를 검토하는 일” 기록을 위한 장치 = 계측기 = Printf = instrument
  • 7. Operating System Printf 보다 고도화 된 Instrument의 필요성 Application Programming Interface Platform Application Library 계측기=Printf 필요
  • 8. Printf, Printf, Printf, Printf 를 가능케 하라 Linking Table Kernal Library Function Function Function Uftrace kernel record Binary API funcA() 호출 hook getpid() 호출 hook syscall sys_getpid() int funcA(int arg) { return getpid() % arg; } User space Kernel space API record Function record 함수 호출 기록 API 호출 기록 Ftrace - 함수 호출 기록 - 커널 이벤트 기록 커널 함수 호출 커널 이벤트 기록
  • 9. Printf 안되면 되게 하라! Uftrace <Dynamic_Entry> push %rbp mov %rsp,%rbp sub $0x10,%rsp mov %edi,-0x4(%rbp) push %rbp mov %rsp,%rbp mov $0x186a0,%edi Jmp to origin Jmp to origin callq *0x2008b0(%rip) # 200fe8 < Dynamic_Entry > callq *0x2008b0(%rip) # 200fe8 <Dynamic_Entry> callq 5d0 <getpid@plt> cltd idivl -0x4(%rbp) mov %edx,%eax leaveq retq callq 77a <funcA> pop %rbp retq <funcA>: <main>: Original Instructions Original Instructions
  • 10. 모든 영역에서 Printf-ing 하다 $ sudo ./uftrace –L. –k –a test Operating System Application Programming Interface Platform Library Application # DURATION TID FUNCTION [ 1598] | main() { [ 1598] | funcA(100000) { [ 1598] | getpid() { 0.269 us [ 1598] | sys_getpid(); 1.375 us [ 1598] | } = 1598; /* getpid */ 1.375 us [ 1598] | } = 1598; /* funcA */ 2.355 us [ 1598] | } = 1598; /* main */
  • 11. 기록했으니 검토를 시작해야죠… Tracing, “프로그램의 실행 동안의 변화를 다각도로 기록하고 이를 검토하는 일”  함수 호출  API 호출  커널 함수 호출  함수 호출 인자  함수 반환 인자 호출 기록 검토 시각화
  • 13. Gcc에서 만난 문제를 추적했던 경우 $ ./compile_by_gcc Segmentation fault (core dumped) $ ./compile_by_clang Hello world static inline __attribute__((always_inline)) char* get_msg() { char str[64] = "Hello worldn"; return (char*)str; } int main() { char* p; p = get_msg(); puts(p); return 0;
  • 14. static inline __attribute__((always_inline)) char* get_msg() { const char* str = "Hello worldn"; char* p = malloc(strlen(str)); strcpy(p, str); return p; Mitigation도 좋지만 해결책을 찾아볼까? static inline __attribute__((always_inline)) char* get_msg() { char* str = "Hello worldn"; return str; } Mitigation 방안 #1 - 문자를 수정 못함 Mitigation 방안 #2 - 메모리 해제가 강제됨
  • 15. 개발자가 남긴 #1 Warning Message $ gcc inline.c inline.c: In function ‘get_msg: inline.c:6:9: warning: function returns address of local variable [-Wreturn-local-addr] return (char *)str;
  • 16. if (DECL_P(inner) && !DECL_EXTERNAL(inner) && !TREE_STATIC(inner) && DECL_CONTEXT(inner) == current_function_decl) { if (TREE_CODE(inner) == LABEL_DECL) warning_at(loc, OPT_Wreturn_local_addr, "function returns address of label"); else { warning_at(loc, OPT_Wreturn_local_addr, "function returns address of local variable"); tree zero = build_zero_cst(TREE_TYPE(res)); t = build2(COMPOUND_EXPR, TREE_TYPE(res), t, zero); } } Warning Message 로 찾은 문제지점 if (DECL_P (inner) && !DECL_EXTERNAL (inner) && !TREE_STATIC (inner) && !DECL_DECLARED_INLINE_P(current_function_decl) && DECL_CONTEXT (inner) == current_function_decl) return 될 tree가 속한 함수가 inline 으로 선언되어 있으면 NULL 로 바꾸지 마세요! declared_inline_flag = 1 static_flag = 1 Current_function_decl.function_decl
  • 17. ;; Function get_msg (get_msg, funcdef_no=0, decl_uid=1792, cgraph_uid=0, symbol_order=0) Deleted dead store: str = "Hello worldn"; __attribute__((always_inline)) get_msg () { char str[64]; <bb 2> [0.00%]: str ={v} {CLOBBER}; return &str; } 개발자가 남긴 #2 Debug 용 로그 inline.c.040t.dse1 Deleted dead store: str = "Hello worldn"; $ gcc –O1 –fdump-tree-all-details inline.c
  • 18. if (DECL_P(inner) && !DECL_EXTERNAL(inner) && !TREE_STATIC(inner) && DECL_CONTEXT(inner) == current_function_decl) { if (TREE_CODE(inner) == LABEL_DECL) warning_at(loc, OPT_Wreturn_local_addr, "function returns address of label"); else { warning_at(loc, OPT_Wreturn_local_addr, "function returns address of local variable"); tree zero = build_zero_cst(TREE_TYPE(res)); t = build2(COMPOUND_EXPR, TREE_TYPE(res), t, zero); } } 메모리 최적화 때문일까? else if (DECL_DECLARED_INLINE_P(current_function_decl)) inner->base.volatile_flag = true; 메모리 관련 최적화 Disable if (DECL_P (inner) && !DECL_EXTERNAL (inner) && !TREE_STATIC (inner) && !DECL_DECLARED_INLINE_P(current_function_decl) && DECL_CONTEXT (inner) == current_function_decl)
  • 19. 개발자가 남긴 메시지의 중요성 Dump of assembler code for function main: <+4>: movdqa 0x1b4(%rip),%xmm0 <+12>: mov %rsp,%rdi <+15>: movaps %xmm0,(%rsp) <+19>: pxor %xmm0,%xmm0 <+23>: movaps %xmm0,0x10(%rsp) <+28>: movaps %xmm0,0x20(%rsp) <+33>: movaps %xmm0,0x30(%rsp) <+38>: callq 0x400400 <puts@plt> Dump of assembler code for function main: <+4>: movabs $0x6f77206f6c6c6548,%rax <+14>: mov $0xa646c72,%edx <+19>: mov %rax,(%rsp) <+23>: mov %rdx,0x8(%rsp) <+28>: movq $0x0,0x10(%rsp) <+37>: movq $0x0,0x18(%rsp) <+46>: movq $0x0,0x20(%rsp) <+55>: movq $0x0,0x28(%rsp) <+64>: movq $0x0,0x30(%rsp) <+73>: movq $0x0,0x38(%rsp) <+82>: mov %rsp,%rdi <+85>: callq 0x400400 <puts@plt> O1 $ gcc –O1 inline.c $ ./a.out Hello world O2 $ gcc –O2 inline.c $ ./a.out Hello world
  • 20. 특정 오픈소스를 분석해야 하는 경우 public class Main { public static void main(String[] args) { System.out.println("Hello World!"); } } $ javac Main.java $ dx --dex --output=classes.dex Main.class $ zip Main.apk classes.dex $uftracerecord-a`which dalvikvm64`–cp Main.apk Main
  • 21. Tracing 에 도움이 되는 요소들 거대 오픈소스를 분석할 때는 관련 배경지식이 매우 중요합니다 VM Interprete r Bytecode 동작 방식에 대한 이해 DEX 파일 포맷에 대한 지식 Bytecode에 대한 지식
  • 22. Tracing 에 도움이 되는 요소들 Tracing 시각화의 유용성
  • 23. Tracing 에 도움이 되는 요소들 Tracing 시각화의 유용성 art::ClassLinker::FindClass └ art::ClassLinker::DefineClass └ art::ClassLinker::LinkClass └ art::ClassLinker::LinkMethods └ art::ClassLinker::LinkInterfaceMethods Class Path
  • 25. 개발자가 남긴 메시지는 매우 중요!! ;; Function get_msg (get_msg, funcdef_no=0, decl_uid=1792, cgraph_uid=0, symbol_order=0) Deleted dead store: str = "Hello worldn"; __attribute__((always_inline)) get_msg () { char str[64]; <bb 2> [0.00%]: str ={v} {CLOBBER}; return &str; }
  • 26. 코드는 접근성이 매우 떨어집니다. ;; Function get_msg (get_msg, funcdef_no=0, decl_uid=1792, cgraph_uid=0, symbol_order=0) Deleted dead store: str = "Hello worldn"; __attribute__((always_inline)) get_msg () { char str[64]; <bb 2> [0.00%]: str ={v} {CLOBBER}; return &str; } (gdb) p *gsi->ptr $4 = {code = GIMPLE_ASSIGN, no_warning = 0, visited = 0, nontemporal_move = 0, plf = 1, modified = 0, has_volatile_ops = 0, pad = 0, subcode = 32, uid = 0, location = 2147483655, num_ops = 2, bb = 0x7ffff6ede478, next = 0x7ffff702d190, prev = 0x7ffff702d140}
  • 27. VM Interpreter Tracing에는 배경지식이 매우 중요합니다. Bytecode 동작 방식에 대한 이해 DEX 파일 포맷에 대한 지식 Bytecode에 대한 지식 INTERPRETER CLASSLOADER DalvikVM HEAP DATA Tables Global Area Class Loader Class Loader Loaded Classes Class Loader FindClass From LoadedClasses 1 vtable ClassObject static field iftrable directMethods virtualMethods Method Area Method Method Dex Header String_ids Type_ids Proto_ids Field_ids Method_ids Class_defs Data DexFile Format Dex OpenDex 2 DexParse 3 LoadClass 4 Add ClassHash To Loaded Classes 5 ParseClassDef LoadData 4-1 4-2 Method Instructions
  • 28. 부족한 배경지식은 강제로 배우게 되니까요 else if (DECL_DECLARED_INLINE_P(current_function_decl)) inner->base.volatile_flag = true; GIMPLE RTL TREE Abstract IR Front-End Back-End Front-end Optimize Pass DSE Volatile CLOBBER CONSTRUTOR
  • 29. 누군가 알려줬다면 훨씬 쉬웠을텐데… Open-source tracing data API 디버깅 정보 배경 지식 관련 지식
  • 30. Tracing 배경지식과 개발자 메시지 입히기 (feat, NodeJS/Pinpoint/v8)
  • 31. NodeJS 개발자들이 남긴 메시지 function find_me_a() { console.log("hellworld"); } $ nodejs --trace hellworld.js 1: ~+0(this=0x355aaee01521 <JSGlobal Object>) { 2: ~find_me_a+0(this=0x355aaee01521 <JSGlobal Object>) { hellworld 2: } -> 0x1aa1629004d1 <undefined> 1: } -> 0x1aa1629004d1 <undefined>
  • 32. 또 다른 메시지 —print-bytecode --trace 옵션 enable 시 생성되는 bytecode --trace 옵션 disable 시 생성되는 bytecode [generated bytecode for function: find_me_a] Parameter count 1 Register count 3 Frame size 24 18 E> 0x1ce45b01eb1f @ 5 : a5 StackCheck 27 S> 0x1ce45b01eb20 @ 6 : 13 00 00 LdaGlobal [0], [0] 0x1ce45b01eb23 @ 9 : 26 fa Star r1 35 E> 0x1ce45b01eb25 @ 11 : 28 fa 01 02 LdaNamedProperty r1, [1], [2] 0x1ce45b01eb29 @ 15 : 26 fb Star r0 0x1ce45b01eb2b @ 17 : 12 02 LdaConstant [2] 0x1ce45b01eb2d @ 19 : 26 f9 Star r2 35 E> 0x1ce45b01eb2f @ 21 : 59 fb fa f9 04 CallProperty1 r0, r1, r2, [4] 0x1ce45b01eb34 @ 26 : 0d LdaUndefined 0x1ce45b01eb35 @ 27 : 26 fb Star r0 0x1ce45b01eb37 @ 29 : 61 a8 01 fb 01 CallRuntime [TraceExit], r0-r0 53 S> 0x1ce45b01eb3c @ 34 : a9 Return [generated bytecode for function: find_me_a] Parameter count 1 Register count 3 Frame size 24 0x1ce45b01eb1a @ 0 : 61 a7 01 fb 00 CallRuntime [TraceEnter], r0-r0 18 E> 0x1ce45b01eb1f @ 5 : a5 StackCheck 27 S> 0x1ce45b01eb20 @ 6 : 13 00 00 LdaGlobal [0], [0] 0x1ce45b01eb23 @ 9 : 26 fa Star r1 35 E> 0x1ce45b01eb25 @ 11 : 28 fa 01 02 LdaNamedProperty r1, [1], [2] 0x1ce45b01eb29 @ 15 : 26 fb Star r0 0x1ce45b01eb2b @ 17 : 12 02 LdaConstant [2] 0x1ce45b01eb2d @ 19 : 26 f9 Star r2 35 E> 0x1ce45b01eb2f @ 21 : 59 fb fa f9 04 CallProperty1 r0, r1, r2, [4] 0x1ce45b01eb34 @ 26 : 0d LdaUndefined 0x1ce45b01eb35 @ 27 : 26 fb Star r0 0x1ce45b01eb37 @ 29 : 61 a8 01 fb 01 CallRuntime [TraceExit], r0-r0 53 S> 0x1ce45b01eb3c @ 34 : a9 Return CallRuntime [TraceEnter], r0-r0 CallRuntime [TraceExit], r0-r0
  • 33. TraceEnter/Exit 가 추가되는 것을 확인 function find_me_a() { console.log("hellworld") ; } function find_me_a() { TraceEnter(); console.log("hellworld"); TraceExit(); }
  • 34. TraceEnter 의도된 작업 function find_me_a() { TraceEnter(); console.log("hellworld"); TraceExit(); } find_me_a caller find_me_a Javascript Stackframe READ WRITE 2: ~find_me_a+0(this=0x355aaee01521 <JSGlobal Object>) { stdout TraceEnter Enter
  • 35. Calling Convention – 인자 값 전달 방식 RUNTIME_FUNCTION (Runtime_TraceEnter) JavaScriptFrame::PrintTop R1 레지스터에 인자#1 Set R2 레지스터에 인자#2 Set R1 레지스터에서 인자#1 Read R2 레지스터에서 인자#2 Read Call PrintTop 정해진 레지스터로 인자 Set 후 호출 정해진 레지스터에서 인자 읽기
  • 36. Uftrace에서 활용하는 방식 RUNTIME_FUNCTION (Runtime_TraceEnter) JavaScriptFrame::PrintTop R1 레지스터에 인자#1 Set R2 레지스터에 인자#2 Set R1 레지스터에서 인자#1 Read R2 레지스터에서 인자#2 Read Call PrintTop 정해진 레지스터로 인자 Set 후 호출 정해진 레지스터에서 인자 읽기 instrument Uftrace 레지스터에서 인자들을 읽어들이기
  • 37. Uftrace로 출력 Stream 가로채기 function find_me_a() { TraceEnter(); console.log("hellworld"); TraceExit(); } find_me_a caller find_me_a Javascript Stackframe READ WRITE 2: ~find_me_a+0(this=0x355aaee01521 <JSGlobal Object>) { UFTRACE Stream TraceEnter Enter Uftrace
  • 38. Tracing Data에 기록한 결과 [ 30053] | v8::internal::Runtime_CompileLazy() { [ 30053] | v8::internal::StackLimitCheck::JsHasOverflowed() { 0.145 us [ 30053] | v8::internal::GetCurrentStackPosition(); 1.453 us [ 30053] | } /* v8::internal::StackLimitCheck::JsHasOverflowed */ [ 30053] | v8::internal::Compiler::Compile() { [ 30053] | v8::internal::Compiler::Compile() { [ 30053] | ~find_me_a+0(this=0x2c3bb9284de9 <JSGlobal Object>, 0x081d3615ead1 …) JavaScript 호출 JavaScript 호출 처리 동작
  • 43. Javascript 함수의 Parsing Declarations function find_me_a function find_me_b function find_me_c Statement Call find_me_a Parsing Func 0 function find_me_a() { return find_me_b(); } function find_me_b() { return find_me_c(); }
  • 44. Javascript 코드의 실행 과정 START v8::internal::_GLOBAL__N_1::CompileToplevel() Enter Func 0 Call find_me_a() v8::internal::Execution::Call v8::internal::Execution::(anonymouse namespace)::Invoke v8::internal::Runtime_CompileLazy v8::internal:__RT_imple_Runtime_CompileLazy v8::internal::Compiler::Compile Enter find_me_a Call find_me_b() v8::internal::Execution::Call v8::internal::Execution::(anonymouse namespace)::Invoke v8::internal::Runtime_CompileLazy v8::internal:__RT_imple_Runtime_CompileLazy v8::internal::Compiler::Compile Enter find_me_b Call find_me_c() v8::internal::Execution::Call v8::internal::Execution::(anonymouse namespace)::Invoke v8::internal::Runtime_CompileLazy v8::internal:__RT_imple_Runtime_Com v8::internal::Compiler::Compile
  • 45. TraceEnter 삽입 지점 START v8::internal::_GLOBAL__N_1::CompileToplevel() Enter Func 0 Call find_me_a() v8::internal::Execution::Call v8::internal::Execution::(anonymouse namespace)::Invoke v8::internal::Runtime_CompileLazy v8::internal:__RT_imple_Runtime_CompileLazy v8::internal::Compiler::Compile Enter find_me_a Call find_me_b() v8::internal::Execution::Call v8::internal::Execution::(anonymouse namespace)::Invoke v8::internal::Runtime_CompileLazy v8::internal:__RT_imple_Runtime_CompileLazy v8::internal::Compiler::Compile Enter find_me_b Call find_me_c() v8::internal::Execution::Call v8::internal::Execution::(anonymouse namespace)::Invoke v8::internal::Runtime_CompileLazy v8::internal:__RT_imple_Runtime_Com v8::internal::Compiler::Compile TraceEnter 삽입 TraceEnter 삽입 TraceEnter 삽입 FLAG_trace
  • 46. NodeJS에 대한 배경지식 v8::Local<v8::Script> script = v8::Script::Compile(context, source); v8::Local<v8::Value> result = script->Run(context); Javascript OPERATING SYSTEM Application Programming Interface NodeJS JavaScript Engine NodeJS와 v8의 상호작용 Native Component Libuv (I/O, Http, …) Parser Abstract Syntax Tree Compiler Generate Bytecode Generate Opcode Run Result
  • 47. Javascript Compile 함수 v8::internal::Compiler::Compile(Handle<JSFunction> function, ClearExceptionFlag, IsCompiledScope*) v8::internal::JSFunction Internal Local v8::Function v8::Function *func = reinterpret_cast<v8::Function*>(ARG1(regs)); v8::String::Utf8Value name(func->CreationContext()->GetIsolate(), func->GetDebugName());
  • 48. 배경지식 입히기 특정 JavaScript Function만 기록되게 만드는 로직 v8 Uftrace Plugin v8::internal::Compiler::Compile 시작 처리 Function 이름 읽기 v8::internal::FLAG_trace on Runtime_TraceEnter Bytecode 삽입 v8::internal::FLAG_trace off v8::internal::Compiler::Compile 종료 처리
  • 51.
  • 52. 치환 Tracing Data Plugin 제작을 통한 소기 목적 달성 v8::internal::Compiler::Compile v8::internal:__RT_imple_Runtime_CompileLazy v8::internal::Runtime_CompileLazy Javascript 함수를 컴파일하는 CallTree 12,353,295 => 9,969,632 line <Function name> Lazy Compile Javascript 함수가 컴파일된다는 문맥
  • 53. Uftrace plugin Tracing Data 오픈소스 접근성 높히기 오픈소스 Tracing Data 문맥 #1 문맥 #2 문맥 #3 문맥 #4 문맥 #5 문맥 #6 디버깅메시지 추가정보 배경지식 오픈소스에 문맥 정보를 부여
  • 54. Q & A

Editor's Notes

  1. 눈발자국 제목으로 발표하게 된 박한범입니다.
  2. 먼저 Tracing이라는 용어가 생소할 수 있기 때문에 이에 대한 소개와, 제가 개발에 참여하고 있는 Tracing tool 에 대한 설명을 드리고자 합니다. 그리고 Gcc와 Android 일부에 Tracing을 했던 경험을 통해 Tracing에 어떤 요소들이 도움이 되는지, 그리고 그와 같은 요소들이 오픈소스에 대한 접근성을 올려주는지에 대해 검증해고 그 결론을 눈발자국이라는 발표 타이틀로 대신하려고 합니다.
  3. Tracing이라는 용어는 아마 대부분의 분들에게는 낯서실겁니다.
  4. 바뀌는 패러다임, 언어, 프레임워크, 프로그래밍은 시간이 지나고 경력이 쌓여도 매번 새롭고 늘 어렵습니다.
  5. 프로그램이 동작하는 모습이 시각적으로 보이지 않기 때문인 것도 프로그래밍이 어렵게 느껴지는 큰 이유입니다. 때문에 프로그래밍의 세계에서 살아남기 위해서는 개발자들은 Printf 같은 함수로 실행기록을 시각화하고 로그와 디버깅 메시지를 남기는 생존 기술을 배우게 됩니다.
  6. 이처럼 Printf 같은 함수를 활용하여 프로그램의 실행 동안의 변화를 기록하고 검토하는 작업을 tracing이라고 합니다. 용어의 생소함에 비해 개발자 분들은 자주 해오시던 작업입니다. Tracing에서는 Printf와 같이 실행 기록 등을 확인하기 위한 수단을 계측기, instrument라고 부릅니다. Tracing은 Instrument를 통해 프로그램의 실행 동안의 변화를 기록하고 이를 검토하는 작업입니다.
  7. 경력과 실력이 쌓이고 더 깊은 범위까지 책임져야 하는 위치에 오르게 되면, 더 다양하고 넓은 범위에 추적기술이 필요해지게 됩니다. 필요하고 원하는 곳에 Printf를 찍어보던 전과 다르게, 내 필요로 Printf를 찍어 볼 수 없는 상황에 처하게 됩니다. 이럴 때는 블랙박스처럼 라이브러리, 플랫폼 등에서 출력해주는 로그 메시지, 디버그 메시지에 의존할 수 밖에 없게 되죠. 내 프로그램부터 내가 프로그램이 기반하는 전 영역에서 Printf 를 쓸 수 있으면 어떨까요?
  8. 일반적인 실행프로그램의 구조는 대충 이렇습니다. C로 짜여진 함수 하나를 가지고 예를 들어보겠습니다. funcA 라는 함수는 integer형 arg를 인자로 받습니다. 그리고 getpid()라는 API를 호출하고 그 결과를 arg로 나눈 결과를 반환하죠. 이 과정에 전부 Printf를 찍는 상상을 해봅시다. - 가장 먼저FuncA가 호출될 때 FunA호출됐고 인자 arg 받았다고 찍고 - 다음에는 getpid()가 호출된 걸 찍고 - getpid() 내부에서 sys_getpid()를 호출하는 시스템콜을 날린걸 찍고 - 마지막으로 funcA가 getpid()의 값에 arg를 나눈 나머지를 반환하는걸 찍는 말로 설명하면 긴 과정입니다.
  9. 제가 참여하고 있는 Tracing 툴인 UFTRACE는 함수 시작부의 일부 instruction을 패치하여 instrument를 설치하여 함수 호출을 기록하게 됩니다.
  10. 이런 노력으로 Uftrace를 활용하면 프로그램이 실행되는 모든 경로가 출력되서 나오게 됩니다. 화면은 Uftrace로 instrument 하여 호출된 함수들을 출력한 결과입니다.
  11. Uftrace라는 툴을 통해 개발자가 본인의 영역을 넘는 범위의 다양한 데이터를 모을 수 있고, 이를 검토하는데 도움을 받을 수 있습니다. 물론 검토하는 것은 개발자인 여러분이고, 전적으로 여러분의 능력에 달렸습니다.
  12. 제가 오픈소스 분석에 Tracing을 활용하면서 겪은 시행착오를 공유해보려고 합니다.
  13. Gcc에서 특정한 문법이 버그를 Segmentation fault를 발생하는 상황이 있었습니다. Gcc로 컴파일하면 segment fault 가 발생하는데 Clang 같은 다른 컴파일러를 사용하면 문제가 발생하지 않았죠.
  14. 보통 이런 문제가 생기면 버그가 발생하지 않는 방향으로 코드를 변경하기 마련이지만, Tracing 툴 개발에 참여하는 입장이고, 마침 이때 Tracing에 대해 상당히 고취되어 있었기에 gcc에 도전해보게 됐습니다. 결과적으로 문제의 원인을 찾고 해결까지는 했지만, 그 과정에서 Tracing적 요소가 별로 없었습니다.
  15. 가장 먼저 Gcc에서 출력하는 경고 메시지를 단서로 찾기 시작했습니다.
  16. 경고 메시지를 보고 찾은 문제 발생 지점이고, 문제가 발생한 원인을 파악했습니다. 그리고 1차 수정을 했죠.
  17. 하지만 여전히 최적화 과정에서 문제가 발생했습니다. 심지어 이번에는 warning 메시지도 안 보여줘서 버그가 발생하는 경우와 아닌 경우의 Tracing 기록을 만들어서 비교해봤는데 너무 많은 차이점이 발생해서 파악이 힘들더군요. 이런저런 방법을 찾다 gcc에서 –fdump-tree-all-details 라는 옵션을 찾았는데 이 옵션을 추가하면 GCC의 Front-end에서 각 패스가 실행될 때마다 변화되는 내용을 TEXT로 해주게 되고, 여기서 의심스러운 문구를 찾을 수 있게 됩니다.
  18. 이 메시지는 메모리 최적화 관련 내용이므로 최적화를 막으면 될까? 하는 생각에 추가 수정을 해봤습니다.
  19. 원하는 결과가 나오고 동작하는 것을 확인할 수 있었습니다. 이처럼 개발자들이 남긴 경고, 에러, 디버깅 덤프는 문제 해결에 큰 도움이 됩니다.
  20. 때로는 오픈소스를 분석하는 것 자체가 업무가 될 경우도 있는데요, 저 또한 Android에 어떤 Hack을 할 방법을 찾기 위해 분석을 해야 했었습니다. 이 때는 Tracing될 데이터를 최소화 할 방법을 먼저 강구하는게 좋습니다.
  21. 오픈소스를 tracing할 때는 그와 관련된 배경지식들이 굉장히 많이 도움이 됩니다. 왜냐면 Tracing으로 기록되는 함수 이름들이 대부분 그런 배경지식과 관련된 키워들이 많이 포함되기 때문입니다.
  22. Uftrace로 기록된 함수 호출 기록을 FlameGraph 형태로 시각화해서 보면, 그와 관련된 배경 지식이 있다면 의미를 파악하기 쉽습니다. Android 가상머신의 경우 ‘Class를 로드하고 호출된 Method를 실행’ 하는 것이 주 된 기능이고, 이와 관련된 이름들을 찾아보면 금새 찾을 수 있을 정도로 파악이 쉽습니다. 특히 Android는 이와 관련된 기능 수행을 반복적으로 수행하고 있었고, 전체 기록 중에 Class 로드 관련 함수 호출이 36%, Method 실행 관련 함수 호출이 56% 이여서 더욱 확인이 쉬웠습니다.
  23. 또한 시각화를 통해 원하는 부분을 확대/축소해서 함수 호출의 calltree도 쉽게 확인해볼 수 있죠. 여기서 하나 말씀드리고 싶은게, FindClass라는 함수 이름은 쉽게 그 기능을 추측할 수 있으실 겁니다. 근데 여기서 LinkClass라는 함수의 동작은 어떤 기능일지 유추가 되시는지 궁금합니다. 배경지식이 없어 그 기능을 유추할 수 없는 경우, 소스코드의 분석을 할 수 밖에 없습니다. 앞에서 말씀드린 Tracing의 정의 중에 “검토하는 일"이 이와 관련된 내용입니다. 모든 Tracing은 결국은 미지의 영역에 도달하고 그와 관련된 정보와 지식을 축적하게 되는 일종의 과정인 셈 입니다.
  24. Tracing을 경험하면서 몇 가지 요소가 트레이싱에 도움이 된다는 생각을 하게 됐는데요
  25. Gcc에서는 개발자가 남긴 메시지가 매우 도움이 되었습니다.
  26. 만약에 개발자가 메시지를 남겨놓지 않았다면, 그 메시지가 구현된 구조체인 Gimple이라는 구조체를 분석해야 했을 겁니다.
  27. Tracing에는 아무리 사소한 것이라도 배경지식이 큰 도움이 됩니다. 이런 배경지식들은 인터넷 검색을 통해 생각보다 쉽게 찾을 수 있어요.
  28. 그런데 배경지식이 없다고 크게 걱정하지 않으셔도 되는게 트레이싱 과정에서 강제로 배우게 됩니다. 모르면 분석을 못하더라구요.
  29. 모든 Tracing 과정은 미지의 영역에 도달하게 됩니다. 왜냐면 아는 범위 내에서 생기는 문제 같은건 Tracing 할 필요가 없으니까요. 결국 Tracing은 미지의 영역을 개척하는 과정에서 관련된 정보와 지식을 축적하게 되는 일종의 과정인 셈 입니다. 근데 그게 Tracing을 하는 사람에게는 미지의 영역이지만 그 분야의 전문가에게는 상식같은거죠.
  30. 그래서 이미 많이 사용되는 오픈소스의 배경지식과 개발자 메시지를 Tracing과 접목시키면, 오픈소스에 대한 접근성을 향상시킬 수 있지 않을까? 하는 생각을 하게 됐고, NodeJS를 대상으로 검증해봤습니다.
  31. 먼저 NodeJS 개발자들이 남겨놓은 메시지를 찾아봅시다. Nodejs는 javaScript Engine으로 v8을 사용하는데 V8에는 Javascript 함수 호출을 출력해주는 --trace 옵션이 있습니다.
  32. 또한 Bytecode를 출력하는 옵션을 통해 –trace옵션을 줬을 때 어떤 변화가 생겼는지 확인해보면 TraceEnter와 TraceExit를 호출하는 코드가 함수의 시작과 종료 시점에 추가됨을 알 수 있습니다.
  33. 이는 마치 함수의 시작과 끝에 TraceEnter 와 TraceExit를 호출하는 코드를 추가한 것과 비슷한거죠.
  34. TraceEnter가 호출되면 JavaScript StackFrame에는 TraceEnter가 종료되고 복귀할 때 사용하기 위해 호출한 함수를 보관하게 됩니다. 즉, TraceEnter 내부에서 Javascript StackFrame을 보면 최상단에는 현재 호출된 함수가 들어있게 되므로, 이를 읽어서 stdout 스트림으로 출력하는 방식으로 Javascript 함수 호출을 출력해주게 됩니다.
  35. 여기서 잠깐 알고 넘어가야 할 게 있습니다. 함수를 호출할 때는 인자를 넘겨줘야 하고, 함수가 호출됐을 때는 인자를 넘겨 받아야 하죠, 이를 위해서 함수 호출자와 함수 피호출자 사이에는 인자를 주고 받는 규칙이 있어야 하며, 이 규칙을 Calling Convention 함수 호출 규약이라고 부릅니다.
  36. Uftrace는 함수의 최 상단에 instrument하여 Calling Convention에 따라 CPU 레지스터들에서 정해진 순서대로 인자를 읽어들일 수 있습니다.
  37. 일반적으로 Uftrace는 인자값을 저장하기 위해 함수호출규약을 활용하지만, 이번에는 인자값을 변경하는데 활용했습니다. TraceEnter 내부에서는 stdout을 인자값으로 넘겨줘 화면에 Javascript 함수 이름을 출력하지만, 이를 Uftrace용 스트림으로 변경하여 uftrace가 Tracing 기록에 함께 저장하도록 만든겁니다.
  38. Uftrace 통해 Tracing 데이터에 Javascript 함수 호출도 함께 기록할 수 있게 됐습니다. 화면은 Uftrace로 기록된 Tracing Data를 출력한 결과입니다. 이를 통해, JavaScript 함수 호출도 함께 Tracing 할 수 있을 뿐 아니라, JavaScript 함수 호출 후 어떤 동작이 이뤄지는지도 확인할 수 있게 되었습니다.
  39. Naver의 오픈소스 Pinpoint은 Java기반의 APM으로 인기 좋은 오픈소스입니다. Pinpoint는 Java에 instrument하여 함수 호출을 기록하는데 그 방식이 앞에서 제가 설명 드린 구조와 유사합니다. 그래서 pinpoint의 모델을 답습하여 NodeJS 용 APM Plugin을 만들면 좋지 않을까? 하는 생각이 들었습니다.
  40. Pinpoint는 1.9.0 버전부터 GRPC를 통해 외부 프로그램과의 통신도 지원한다고 하는데, 아직 자세한 내용은 발표되지 않았습니다. 하지만 Pinpoint는 APM으로 훌륭한 데이터-수집-보관-그리고 시각화 해줄 WebUI를 가지고 있기 때문에 소스에 포함된 프로토콜 스펙을 가져와 활용했습니다.
  41. 간단한 NodeJS APM으로 NodeJS에서 Javascript 함수가 호출되면 Uftrace Plugin을 통해 이를 읽어 Pinpoint의 수집기에 GRPC를 통해 기록하는 구조를 갖고 있습니다.
  42. 그 결과가 화면과 같습니다. Javascript 함수 호출이 2천번이상 기록이 됐는데 이게 find_me_a라는 함수 하나를 실행하기 까지의 기록입니다. 대부분 Nodejs나 express에 포함되어 있는 Javascript 함수 호출입니다. 너무 많은 함수 호출이 기록될 뿐 아니라, 이를 필터링 하더라도 매번 함수가 호출될 때마다 필터링 로직이 수행되어야 하므로 전체 성능에 영향을 미치게 됩니다.
  43. V8을 좀더 트레이싱 해보면, Javascript 소스코드가 어떻게 실행되는지 알 수 있습니다. 먼저, javascript 파일을 실행할 때, v8은 전체 함수를 파싱하거나 컴파일하지 않습니다. 단지 어떤 함수들이 선언되어 있는지만 파악해놓습니다.
  44. 그리고 함수가 호출되는 시점에 호출된 함수가 컴파일 되어 있는지 확인하고 아니라면 컴파일을 시작하죠. 이게 바로 Lazy 컴파일입니다. 아마 많이들 들어보셨을 것 같습니다.
  45. Lazy 컴파일이 될 때, --trace 옵션이 주어졌으면 FLAG_trace 라는 전역 변수가 활성화 되어 함수의 시작과 끝에 TraceEnter/Exit를 삽입해주게 되는 겁니다.
  46. NodeJS의 구조는 Javascript의 실행 엔진인 v8과 Javascript의 Native를 담당하는 node로 구성되어 있습니다. Node는 script를 입력 받고 이를 v8을 통해 컴파일하고 실행한 결과를 반환받아 추가 작업을 수행하고, V8은 node가 전달해준 실행하기 위해 여러가지 동작을 수행하게 됩니다. 하단에 보실 수 있는 두 줄의 소스코드가 Javascript를 v8에서 동작시키는 샘플코드 입니다. 이를 눈 여겨 봐주시길 바랍니다. v8::Local 로 선언되어 있는 변수들이 v8에서 v8과 상호작용 할 수 있도록 export 해주는 클래스들 입니다. V8에서 Export 해주는 이 클래스들은 Include 폴더만 include 하면 외부에서 쉽게 사용할 수 있습니다.
  47. 다시 컴파일로 돌아가보면, v8::internal::Compiler::Compile 함수는 v8::internal::JSFunction 클래스를 인자로 받는데 이는 앞에서 v8::Local 클래스들처럼 v8에서 외부로 export 해주는 타입인 v8::Function으로 변환 가능한 타입입니다. 그러므로 v8::internal::Compiler::Compile 함수에 Instrument한 Uftrace는 첫번째 인자를 전달하는 CPU 레지스터를 읽어들이고, 이를 v8::Function으로 변환하면 v8에서 export 해주는 클래스로 자유롭게 사용할 수 있습니다. 즉, 여기서 컴파일되는 함수의 이름을 읽을 수 있게 된 겁니다.
  48. 이에 따라 만든 특정 Javascript에만 TraceEnter가 삽입되는 로직을 그려봤습니다. V8::internal::Compiler::compile이 호출되면 Uftrace가 설치한 instrumen로 가로채지고, 여기서 Plugin을 호출하게 구조를 만듭니다. Plugin에서는 첫 번째 인자인 Function의 이름을 읽고, APM에 필요한 함수일 경우 FLAG_trace를 활성화하여 Runtime_TraceEnter가 삽입되도록 유도합니다. 그리고 v8::internal::Compiler::Compile가 종료될 때 FLAG_trace를 비활성화하여 다른 함수에 Runtime_TraceEnter가 삽입되는 것을 방지합니다. 이런 과정을 통해 특정 함수만 기록하도록 만들 수 있습니다.
  49. 필요한 함수만을 선별적으로 기록할 수 있게 되었습니다. 이에 따라 불필요한 함수를 골라내는데 필요한 성능부하를 없앨 수 있었습니다.
  50. NodeJS용 APM 기능을 만드는 과정을 통해 제가 말씀드리고 싶은 내용을 정리해보고자 합니다.
  51. 눈 발자국은 뒤따르는 사람에게는 이정표가 됩니다.
  52. Nodejs로 아주 간단한 Javascript 코드만 실행해도 1200만 라인의 기록이 생성됩니다. NodeJS APM을 개발하는 과정에서 Compile 과정을 분석하고 이를 어떤 함수가 컴파일 되는지 한 줄로 치환하면 200만 라인의 기록을 줄이고, 그 기록을 어떤 함수가 컴파일 된다는 문맥 정보로 전달할 수 있습니다.
  53. 또한 이런 식으로 Tracing 기록을 치환하고 가공하여 문맥 정보를 변경할 수 있는 Plugin 형태로 개발하여 다른 여러 오픈소스에도 적용하거나 같은 오픈소스에도 다른 문맥 정보를 보여줄 수 있습니다. 이런 문맥정보를 통해 마치 눈 발자국을 남기듯 오픈소스에 접근성을 높힐 수 있다는게 제 결론입니다. 그리고 NodeJS의 APM을 만드는게 가능했 듯이, 더 많은 분야와 범위에 Performance monitoring 등의 목적으로 Plugin을 개발하고자 합니다.
  54. 이상으로 발표를 마치겠습니다. 경청해주셔서 감사드립니다. <<<질문은 ???>>>