SlideShare a Scribd company logo
1 of 64
박한범
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__,__FILE__,
__LINE__, ##args)
#else
#define DBGPR(a, args...)
#endif
DBGPR("%d %dn", a, b);
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;
문제의 원인을 추적
push %rbp
mov %rsp,%rbp
sub $0x60,%rsp
lea -0x40(%rbp),%rax
movl $0x0,-0x44(%rbp)
mov %rax,%rcx
movaps 0x103(%rip),%xmm0 #
0x400650
movaps %xmm0,0x30(%rcx)
movaps 0xe8(%rip),%xmm0 # 0x400640
movaps %xmm0,0x20(%rcx)
movaps 0xcd(%rip),%xmm0 # 0x400630
movaps %xmm0,0x10(%rcx)
movaps 0xb2(%rip),%xmm0 # 0x400620
movaps %xmm0,(%rcx)
mov %rax,-0x50(%rbp)
mov -0x50(%rbp),%rdi
mov $0x0,%al
callq 0x400410 <puts@plt>
xor %edx,%edx
add $0x60,%rsp
pop %rbp
push %rbp
mov %rsp,%rbp
sub $0x50,%rsp
movabs $0x6f77206f6c6c6548,%rax
mov $0xa646c72,%edx
mov %rax,-0x50(%rbp)
mov %rdx,-0x48(%rbp)
movq $0x0,-0x40(%rbp)
movq $0x0,-0x38(%rbp)
movq $0x0,-0x30(%rbp)
movq $0x0,-0x28(%rbp)
movq $0x0,-0x20(%rbp)
movq $0x0,-0x18(%rbp)
mov $0x0,%eax
mov %rax,-0x8(%rbp)
mov -0x8(%rbp),%rax
mov %rax,%rdi
callq 0x400430 <puts@plt>
mov $0x0,%eax
leaveq
retq
Gcc Clang
mov $0x0,%eax
mov %rax,-0x8(%rbp)
mov -0x8(%rbp),%rax
mov %rax,%rdi
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 - 메모리 해제가 강제됨
Tracing 시작 전에 검토하면 좋은 것들
$ uftrace record `which gcc` inline.c
$ uftrace replay > replay.log
$ wc -lc replay.log
4157866 line 392760323 charater replay.log
개발자가 남긴 #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 로 찾은 문제지점
return
return statement
(char *)str;
operand
Address
Expression
?
Valid
Declaration
?
!External?
!Static?
Local?
이건 0이 딱이네!
Warning Message를 통한 문제 해결?
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);
}
}
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
미완의 해결책
$ gcc inline.c -o 1st_code_modify
$ ./1st_code_modify
Hello world
$ gcc -O1 inline.c 1st_code_modify
$ ./1st_code_modify
컴파일 과정 어디에 문제가 생긴걸까?
push %rbp
mov %rsp,%rbp
sub $0x50,%rsp
movabs $0x6f77206f6c6c6548,%rax
mov $0xa646c72,%edx
mov %rax,-0x50(%rbp)
mov %rdx,-0x48(%rbp)
movq $0x0,-0x40(%rbp)
movq $0x0,-0x38(%rbp)
movq $0x0,-0x30(%rbp)
movq $0x0,-0x28(%rbp)
movq $0x0,-0x20(%rbp)
movq $0x0,-0x18(%rbp)
mov $0x0,%eax
mov %rax,-0x8(%rbp)
mov -0x8(%rbp),%rax
mov %rax,%rdi
callq 0x400430 <puts@plt>
mov $0x0,%eax
leaveq
retq
sub $0x48,%rsp
mov %rsp,%rdi
callq 0x400430 <puts@plt>
mov $0x0,%eax
add $0x48,%rsp
retq
push %rbp
mov %rsp,%rbp
sub $0x50,%rsp
movabs $0x6f77206f6c6c6548,%rax
mov $0xa646c72,%edx
mov %rax,-0x50(%rbp)
mov %rdx,-0x48(%rbp)
movq $0x0,-0x40(%rbp)
movq $0x0,-0x38(%rbp)
movq $0x0,-0x30(%rbp)
movq $0x0,-0x28(%rbp)
movq $0x0,-0x20(%rbp)
movq $0x0,-0x18(%rbp)
lea -0x50(%rbp),%rax
mov %rax,-0x8(%rbp)
mov -0x8(%rbp),%rax
mov %rax,%rdi
callq 0x400430 <puts@plt>
mov $0x0,%eax
leaveq
retq
개발자가 남긴 #2 Debug 용 로그
$ gcc –O1 –fdump-tree-all-details inline.c
;; 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;
}
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
특정 오픈소스를 분석해야 하는 경우
Language Files Blank Comment Code Line
C 708 22952 28339 95886
C++ 735 44751 49065 325759
AOSP - art
최소화한 Tracing 데이터가 중요!
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 에 도움이 되는 요소들
최소화 해도 100만 가량의 함수호출이 일어납니다
1144476 0.088 us [ 99631] | art::FaultManager::~FaultManager();
1144477 0.055 us [ 99631] | std::__1::__vector_base::~__vector_base();
1144478 0.060 us [ 99631] | std::__1::__vector_base::~__vector_base();
1144479 0.054 us [ 99631] | art::JDWP::JdwpOptions::~JdwpOptions();
1144480 0.052 us [ 99631] | std::__1::__vector_base::~__vector_base();
1144481 0.078 us [ 99631] | std::__1::__vector_base::~__vector_base();
1144482 0.053 us [ 99631] | std::__1::unique_ptr::~unique_ptr();
1144483 0.063 us [ 99631] | std::__1::unique_ptr::~unique_ptr();
1144484 0.150 us [ 99631] | std::__1::unique_ptr::~unique_ptr();
1 # DURATION TID FUNCTION
2 [ 99631] | main(4, 0x7ffc2b7a7bb8) {
3 0.595 us [ 99631] | setvbuf();
4 0.922 us [ 99631] | operator new[](48) = 0x55b99858f5b0;
5 0.415 us [ 99631] | memset(0x55b99858f5b0, 0, 48) = 0x55b99858f5b0;
6 147.427 us[ 99631] | strncmp("-cp", "-XXlib:", 7) = 11;
7 0.305 us [ 99631] | strcmp("-cp", "-classpath") = 4;
8 0.166 us [ 99631] | strcmp("-cp", "-cp") = 0;
9 0.198 us [ 99631] | strncmp("Main.apk", "-XXlib:", 7) = 32;
10 0.146 us [ 99631] | strcmp("Main.apk", "-classpath") = 32;
11 0.111 us [ 99631] | strcmp("Main.apk", "-cp") = 32;
Tracing 에 도움이 되는 요소들
거대 오픈소스를 분석할 때는 관련 배경지식이 매우 중요합니다
Android VM
HEAP DATA
INTERPRETER
CLASSLOADER
Tables
INPUT
1
Execution
2
OUTPUT
3
Tracing 에 도움이 되는 요소들
거대 오픈소스를 분석할 때는 관련 배경지식이 매우 중요합니다
VM
Interprete
r
Bytecode
동작 방식에
대한 이해
DEX 파일
포맷에
대한 지식
Bytecode에
대한 지식
Tracing 에 도움이 되는 요소들
Tracing 시각화의 유용성
Tracing 에 도움이 되는 요소들
Tracing 시각화의 유용성
art::interpreter::DoCall
└ art::interpreter::ArtInterpreterToInterpreterBridge
└ art::interpreter::Execute
└ MterpInvokeDirect
Interpreting Path
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/Exit 호출 구조
0
find_me_a
Javascript Stackframe
READ
WRITE stdout
JavaScriptFrame::PrintTop(isolate, stdout, true, false);
void JavaScriptFrame::PrintTop(Isolate* isolate, FILE* file,
stdout
stdout
Calling Convention – 인자 값 전달
방식
JavaScriptFrame::PrintTop(isolate, stdout, true, false);
stdout
TBD
Calling convention 관련된 인자 값 읽고 쓰기 다루기
Javascript 함수를 Tracing 에
포함시키기
JavaScriptFrame::PrintTop(isolate, stdout, true, false);
void JavaScriptFrame::PrintTop(Isolate* isolate, FILE* file,
0
find_me_a
Javascript Stackframe
READ
WRITE
stdout
Uftrace <Dynamic_entry>
REDIRECT
Uftrace Stream
stdout
조작
stdout
Tracing 데이터에 일부를 치환하는
Plugin
Tracing Data
가공
정보
Parser 정보
Plugin #1
UFTRACE
Tracing Data 에 일부를 치환한 결과
[ 30053] | ~find_me_a+0(this=0x2c3bb9284de9 <JSGlobal Object>, 0x081d3615ead1 …)
[ 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() {
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
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 함수의 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
Javascript Compile 함수
v8::internal::Compiler::Compile(Handle<JSFunction> function, ClearExceptionFlag, IsCompiledScope*)
v8::internal::JSFunction
Internal
Export
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
눈발자국
NodeJS의 로깅 + Tracing 데이터 + 정보 가공 Plugin
v8::internal::Runtime_TraceExit
Javascript 함수 호출
v8::internal::Runtime_TraceEnter
Tracing
Data
눈발자국
NodeJS의 로깅 + Tracing 데이터 + 정보 가공 Plugin
v8::internal::Compiler::Compile
v8::internal:__RT_imple_Runtime_CompileLazy
v8::internal::Runtime_CompileLazy
Javascript 함수 컴파일
Uftrace plugin
Tracing
Data
오픈소스 접근성 높히기
오픈소스
Tracing
Data
문맥 #1
문맥 #2
문맥 #3
문맥 #4
문맥 #5
문맥 #6
디버깅메시지 추가정보 배경지식
오픈소스에 문맥 정보를 부여
오픈소스
오픈소스 분석 내용을 접목시킨 Plugin을 통한 오픈소스의 접근성을 향상
Uftrace
plugin 시장
오픈소스
Tracing 오픈소스
활성화
가치창출
접근성향상
수요
Q & A
Thank You

More Related Content

Similar to DEVIEW-FULL-감독판.pptx

NDC11_김성익_슈퍼클래스
NDC11_김성익_슈퍼클래스NDC11_김성익_슈퍼클래스
NDC11_김성익_슈퍼클래스Sungik Kim
 
[170327 1주차]C언어 A반
[170327 1주차]C언어 A반[170327 1주차]C언어 A반
[170327 1주차]C언어 A반arundine
 
[Gpg2권 조진현] 1.2 인라인 함수 대 매크로
[Gpg2권 조진현] 1.2 인라인 함수 대 매크로[Gpg2권 조진현] 1.2 인라인 함수 대 매크로
[Gpg2권 조진현] 1.2 인라인 함수 대 매크로진현 조
 
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 2Chris Ohk
 
TABLE ACCESS 패턴을 이용한 SQL 튜닝_Wh oracle
TABLE ACCESS 패턴을 이용한 SQL 튜닝_Wh oracleTABLE ACCESS 패턴을 이용한 SQL 튜닝_Wh oracle
TABLE ACCESS 패턴을 이용한 SQL 튜닝_Wh oracle엑셈
 
NDC11_슈퍼클래스
NDC11_슈퍼클래스NDC11_슈퍼클래스
NDC11_슈퍼클래스noerror
 
스플렁크 머신러닝 연동
스플렁크 머신러닝 연동스플렁크 머신러닝 연동
스플렁크 머신러닝 연동Seung-Woo Kang
 
스플렁크 Machine Learning Integration
스플렁크 Machine Learning Integration스플렁크 Machine Learning Integration
스플렁크 Machine Learning IntegrationTIMEGATE
 
Javascript개발자의 눈으로 python 들여다보기
Javascript개발자의 눈으로 python 들여다보기Javascript개발자의 눈으로 python 들여다보기
Javascript개발자의 눈으로 python 들여다보기지수 윤
 
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 crowJaeseung Ha
 
C Language I
C Language IC Language I
C Language ISuho Kwon
 
파이썬 데이터과학 레벨1 - 초보자를 위한 데이터분석, 데이터시각화 (2020년 이태영)
파이썬 데이터과학 레벨1 - 초보자를 위한 데이터분석, 데이터시각화 (2020년 이태영) 파이썬 데이터과학 레벨1 - 초보자를 위한 데이터분석, 데이터시각화 (2020년 이태영)
파이썬 데이터과학 레벨1 - 초보자를 위한 데이터분석, 데이터시각화 (2020년 이태영) Tae Young Lee
 
[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 문제풀이GangSeok Lee
 
온라인 게임에서 사례로 살펴보는 디버깅 in NDC2010
온라인 게임에서 사례로 살펴보는 디버깅 in NDC2010온라인 게임에서 사례로 살펴보는 디버깅 in NDC2010
온라인 게임에서 사례로 살펴보는 디버깅 in NDC2010Ryan Park
 
온라인 게임에서 사례로 살펴보는 디버깅 in NDC10
온라인 게임에서 사례로 살펴보는 디버깅 in NDC10온라인 게임에서 사례로 살펴보는 디버깅 in NDC10
온라인 게임에서 사례로 살펴보는 디버깅 in NDC10Ryan Park
 
KTH_Detail day_화성에서 온 개발자 금성에서 온 기획자 시리즈_5차_데이터분석_조범석_20120613
KTH_Detail day_화성에서 온 개발자 금성에서 온 기획자 시리즈_5차_데이터분석_조범석_20120613KTH_Detail day_화성에서 온 개발자 금성에서 온 기획자 시리즈_5차_데이터분석_조범석_20120613
KTH_Detail day_화성에서 온 개발자 금성에서 온 기획자 시리즈_5차_데이터분석_조범석_20120613KTH, 케이티하이텔
 
C언어 세미나 - 함수
C언어 세미나 - 함수C언어 세미나 - 함수
C언어 세미나 - 함수SeungHyun Lee
 
[SOPT] 데이터 구조 및 알고리즘 스터디 - #02 : 스택, 큐, 수식 연산
[SOPT] 데이터 구조 및 알고리즘 스터디 - #02 : 스택, 큐, 수식 연산[SOPT] 데이터 구조 및 알고리즘 스터디 - #02 : 스택, 큐, 수식 연산
[SOPT] 데이터 구조 및 알고리즘 스터디 - #02 : 스택, 큐, 수식 연산S.O.P.T - Shout Our Passion Together
 
ffmpeg optimization using CUDA
ffmpeg optimization using CUDAffmpeg optimization using CUDA
ffmpeg optimization using CUDAyyooooon
 

Similar to DEVIEW-FULL-감독판.pptx (20)

NDC11_김성익_슈퍼클래스
NDC11_김성익_슈퍼클래스NDC11_김성익_슈퍼클래스
NDC11_김성익_슈퍼클래스
 
[170327 1주차]C언어 A반
[170327 1주차]C언어 A반[170327 1주차]C언어 A반
[170327 1주차]C언어 A반
 
[Gpg2권 조진현] 1.2 인라인 함수 대 매크로
[Gpg2권 조진현] 1.2 인라인 함수 대 매크로[Gpg2권 조진현] 1.2 인라인 함수 대 매크로
[Gpg2권 조진현] 1.2 인라인 함수 대 매크로
 
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
 
TABLE ACCESS 패턴을 이용한 SQL 튜닝_Wh oracle
TABLE ACCESS 패턴을 이용한 SQL 튜닝_Wh oracleTABLE ACCESS 패턴을 이용한 SQL 튜닝_Wh oracle
TABLE ACCESS 패턴을 이용한 SQL 튜닝_Wh oracle
 
NDC11_슈퍼클래스
NDC11_슈퍼클래스NDC11_슈퍼클래스
NDC11_슈퍼클래스
 
스플렁크 머신러닝 연동
스플렁크 머신러닝 연동스플렁크 머신러닝 연동
스플렁크 머신러닝 연동
 
스플렁크 Machine Learning Integration
스플렁크 Machine Learning Integration스플렁크 Machine Learning Integration
스플렁크 Machine Learning Integration
 
IPython
IPythonIPython
IPython
 
Javascript개발자의 눈으로 python 들여다보기
Javascript개발자의 눈으로 python 들여다보기Javascript개발자의 눈으로 python 들여다보기
Javascript개발자의 눈으로 python 들여다보기
 
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
 
C Language I
C Language IC Language I
C Language I
 
파이썬 데이터과학 레벨1 - 초보자를 위한 데이터분석, 데이터시각화 (2020년 이태영)
파이썬 데이터과학 레벨1 - 초보자를 위한 데이터분석, 데이터시각화 (2020년 이태영) 파이썬 데이터과학 레벨1 - 초보자를 위한 데이터분석, 데이터시각화 (2020년 이태영)
파이썬 데이터과학 레벨1 - 초보자를 위한 데이터분석, 데이터시각화 (2020년 이태영)
 
[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 문제풀이
 
온라인 게임에서 사례로 살펴보는 디버깅 in NDC2010
온라인 게임에서 사례로 살펴보는 디버깅 in NDC2010온라인 게임에서 사례로 살펴보는 디버깅 in NDC2010
온라인 게임에서 사례로 살펴보는 디버깅 in NDC2010
 
온라인 게임에서 사례로 살펴보는 디버깅 in NDC10
온라인 게임에서 사례로 살펴보는 디버깅 in NDC10온라인 게임에서 사례로 살펴보는 디버깅 in NDC10
온라인 게임에서 사례로 살펴보는 디버깅 in NDC10
 
KTH_Detail day_화성에서 온 개발자 금성에서 온 기획자 시리즈_5차_데이터분석_조범석_20120613
KTH_Detail day_화성에서 온 개발자 금성에서 온 기획자 시리즈_5차_데이터분석_조범석_20120613KTH_Detail day_화성에서 온 개발자 금성에서 온 기획자 시리즈_5차_데이터분석_조범석_20120613
KTH_Detail day_화성에서 온 개발자 금성에서 온 기획자 시리즈_5차_데이터분석_조범석_20120613
 
C언어 세미나 - 함수
C언어 세미나 - 함수C언어 세미나 - 함수
C언어 세미나 - 함수
 
[SOPT] 데이터 구조 및 알고리즘 스터디 - #02 : 스택, 큐, 수식 연산
[SOPT] 데이터 구조 및 알고리즘 스터디 - #02 : 스택, 큐, 수식 연산[SOPT] 데이터 구조 및 알고리즘 스터디 - #02 : 스택, 큐, 수식 연산
[SOPT] 데이터 구조 및 알고리즘 스터디 - #02 : 스택, 큐, 수식 연산
 
ffmpeg optimization using CUDA
ffmpeg optimization using CUDAffmpeg optimization using CUDA
ffmpeg optimization using CUDA
 

DEVIEW-FULL-감독판.pptx

  • 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__,__FILE__, __LINE__, ##args) #else #define DBGPR(a, args...) #endif DBGPR("%d %dn", a, b);
  • 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. 문제의 원인을 추적 push %rbp mov %rsp,%rbp sub $0x60,%rsp lea -0x40(%rbp),%rax movl $0x0,-0x44(%rbp) mov %rax,%rcx movaps 0x103(%rip),%xmm0 # 0x400650 movaps %xmm0,0x30(%rcx) movaps 0xe8(%rip),%xmm0 # 0x400640 movaps %xmm0,0x20(%rcx) movaps 0xcd(%rip),%xmm0 # 0x400630 movaps %xmm0,0x10(%rcx) movaps 0xb2(%rip),%xmm0 # 0x400620 movaps %xmm0,(%rcx) mov %rax,-0x50(%rbp) mov -0x50(%rbp),%rdi mov $0x0,%al callq 0x400410 <puts@plt> xor %edx,%edx add $0x60,%rsp pop %rbp push %rbp mov %rsp,%rbp sub $0x50,%rsp movabs $0x6f77206f6c6c6548,%rax mov $0xa646c72,%edx mov %rax,-0x50(%rbp) mov %rdx,-0x48(%rbp) movq $0x0,-0x40(%rbp) movq $0x0,-0x38(%rbp) movq $0x0,-0x30(%rbp) movq $0x0,-0x28(%rbp) movq $0x0,-0x20(%rbp) movq $0x0,-0x18(%rbp) mov $0x0,%eax mov %rax,-0x8(%rbp) mov -0x8(%rbp),%rax mov %rax,%rdi callq 0x400430 <puts@plt> mov $0x0,%eax leaveq retq Gcc Clang mov $0x0,%eax mov %rax,-0x8(%rbp) mov -0x8(%rbp),%rax mov %rax,%rdi
  • 15. 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 - 메모리 해제가 강제됨
  • 16. Tracing 시작 전에 검토하면 좋은 것들 $ uftrace record `which gcc` inline.c $ uftrace replay > replay.log $ wc -lc replay.log 4157866 line 392760323 charater replay.log
  • 17. 개발자가 남긴 #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;
  • 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); } } Warning Message 로 찾은 문제지점 return return statement (char *)str; operand Address Expression ? Valid Declaration ? !External? !Static? Local? 이건 0이 딱이네!
  • 19. Warning Message를 통한 문제 해결? 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); } } 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
  • 20. 미완의 해결책 $ gcc inline.c -o 1st_code_modify $ ./1st_code_modify Hello world $ gcc -O1 inline.c 1st_code_modify $ ./1st_code_modify
  • 21. 컴파일 과정 어디에 문제가 생긴걸까? push %rbp mov %rsp,%rbp sub $0x50,%rsp movabs $0x6f77206f6c6c6548,%rax mov $0xa646c72,%edx mov %rax,-0x50(%rbp) mov %rdx,-0x48(%rbp) movq $0x0,-0x40(%rbp) movq $0x0,-0x38(%rbp) movq $0x0,-0x30(%rbp) movq $0x0,-0x28(%rbp) movq $0x0,-0x20(%rbp) movq $0x0,-0x18(%rbp) mov $0x0,%eax mov %rax,-0x8(%rbp) mov -0x8(%rbp),%rax mov %rax,%rdi callq 0x400430 <puts@plt> mov $0x0,%eax leaveq retq sub $0x48,%rsp mov %rsp,%rdi callq 0x400430 <puts@plt> mov $0x0,%eax add $0x48,%rsp retq push %rbp mov %rsp,%rbp sub $0x50,%rsp movabs $0x6f77206f6c6c6548,%rax mov $0xa646c72,%edx mov %rax,-0x50(%rbp) mov %rdx,-0x48(%rbp) movq $0x0,-0x40(%rbp) movq $0x0,-0x38(%rbp) movq $0x0,-0x30(%rbp) movq $0x0,-0x28(%rbp) movq $0x0,-0x20(%rbp) movq $0x0,-0x18(%rbp) lea -0x50(%rbp),%rax mov %rax,-0x8(%rbp) mov -0x8(%rbp),%rax mov %rax,%rdi callq 0x400430 <puts@plt> mov $0x0,%eax leaveq retq
  • 22. 개발자가 남긴 #2 Debug 용 로그 $ gcc –O1 –fdump-tree-all-details inline.c ;; 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; }
  • 23. ;; 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; } Debug 용 로그로 찾은 수상한 메시지 inline.c.040t.dse1 Deleted dead store: str = "Hello worldn"; $ gcc –O1 –fdump-tree-all-details inline.c
  • 24. 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)
  • 25. 운이 좋으면 뭘해도 된다. 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
  • 26. 특정 오픈소스를 분석해야 하는 경우 Language Files Blank Comment Code Line C 708 22952 28339 95886 C++ 735 44751 49065 325759 AOSP - art
  • 27. 최소화한 Tracing 데이터가 중요! 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
  • 28. Tracing 에 도움이 되는 요소들 최소화 해도 100만 가량의 함수호출이 일어납니다 1144476 0.088 us [ 99631] | art::FaultManager::~FaultManager(); 1144477 0.055 us [ 99631] | std::__1::__vector_base::~__vector_base(); 1144478 0.060 us [ 99631] | std::__1::__vector_base::~__vector_base(); 1144479 0.054 us [ 99631] | art::JDWP::JdwpOptions::~JdwpOptions(); 1144480 0.052 us [ 99631] | std::__1::__vector_base::~__vector_base(); 1144481 0.078 us [ 99631] | std::__1::__vector_base::~__vector_base(); 1144482 0.053 us [ 99631] | std::__1::unique_ptr::~unique_ptr(); 1144483 0.063 us [ 99631] | std::__1::unique_ptr::~unique_ptr(); 1144484 0.150 us [ 99631] | std::__1::unique_ptr::~unique_ptr(); 1 # DURATION TID FUNCTION 2 [ 99631] | main(4, 0x7ffc2b7a7bb8) { 3 0.595 us [ 99631] | setvbuf(); 4 0.922 us [ 99631] | operator new[](48) = 0x55b99858f5b0; 5 0.415 us [ 99631] | memset(0x55b99858f5b0, 0, 48) = 0x55b99858f5b0; 6 147.427 us[ 99631] | strncmp("-cp", "-XXlib:", 7) = 11; 7 0.305 us [ 99631] | strcmp("-cp", "-classpath") = 4; 8 0.166 us [ 99631] | strcmp("-cp", "-cp") = 0; 9 0.198 us [ 99631] | strncmp("Main.apk", "-XXlib:", 7) = 32; 10 0.146 us [ 99631] | strcmp("Main.apk", "-classpath") = 32; 11 0.111 us [ 99631] | strcmp("Main.apk", "-cp") = 32;
  • 29. Tracing 에 도움이 되는 요소들 거대 오픈소스를 분석할 때는 관련 배경지식이 매우 중요합니다 Android VM HEAP DATA INTERPRETER CLASSLOADER Tables INPUT 1 Execution 2 OUTPUT 3
  • 30. Tracing 에 도움이 되는 요소들 거대 오픈소스를 분석할 때는 관련 배경지식이 매우 중요합니다 VM Interprete r Bytecode 동작 방식에 대한 이해 DEX 파일 포맷에 대한 지식 Bytecode에 대한 지식
  • 31. Tracing 에 도움이 되는 요소들 Tracing 시각화의 유용성
  • 32. Tracing 에 도움이 되는 요소들 Tracing 시각화의 유용성 art::interpreter::DoCall └ art::interpreter::ArtInterpreterToInterpreterBridge └ art::interpreter::Execute └ MterpInvokeDirect Interpreting Path
  • 33. Tracing 에 도움이 되는 요소들 Tracing 시각화의 유용성 art::ClassLinker::FindClass └ art::ClassLinker::DefineClass └ art::ClassLinker::LinkClass └ art::ClassLinker::LinkMethods └ art::ClassLinker::LinkInterfaceMethods Class Path
  • 35. 개발자가 남긴 메시지는 매우 중요!! ;; 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; }
  • 36. 코드는 접근성이 매우 떨어집니다. ;; 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}
  • 37. 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
  • 38. 부족한 배경지식은 강제로 배우게 되니까요 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
  • 39. 누군가 알려줬다면 훨씬 쉬웠을텐데… Open-source tracing data API 디버깅 정보 배경 지식 관련 지식
  • 40. Tracing 배경지식과 개발자 메시지 입히기 (feat, NodeJS/Pinpoint/v8)
  • 41. 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>
  • 42. 또 다른 메시지 —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
  • 43. TraceEnter/Exit 가 추가됨을 확인 function find_me_a() { console.log("hellworld") ; } function find_me_a() { TraceEnter(); console.log("hellworld"); TraceExit(); }
  • 44. TraceEnter/Exit 호출 구조 0 find_me_a Javascript Stackframe READ WRITE stdout JavaScriptFrame::PrintTop(isolate, stdout, true, false); void JavaScriptFrame::PrintTop(Isolate* isolate, FILE* file, stdout stdout
  • 45. Calling Convention – 인자 값 전달 방식 JavaScriptFrame::PrintTop(isolate, stdout, true, false); stdout TBD Calling convention 관련된 인자 값 읽고 쓰기 다루기
  • 46. Javascript 함수를 Tracing 에 포함시키기 JavaScriptFrame::PrintTop(isolate, stdout, true, false); void JavaScriptFrame::PrintTop(Isolate* isolate, FILE* file, 0 find_me_a Javascript Stackframe READ WRITE stdout Uftrace <Dynamic_entry> REDIRECT Uftrace Stream stdout 조작 stdout
  • 47. Tracing 데이터에 일부를 치환하는 Plugin Tracing Data 가공 정보 Parser 정보 Plugin #1 UFTRACE
  • 48. Tracing Data 에 일부를 치환한 결과 [ 30053] | ~find_me_a+0(this=0x2c3bb9284de9 <JSGlobal Object>, 0x081d3615ead1 …) [ 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() {
  • 51. 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
  • 52. 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(); }
  • 53. 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
  • 54. Javascript Compile 함수 v8::internal::Compiler::Compile(Handle<JSFunction> function, ClearExceptionFlag, IsCompiledScope*) v8::internal::JSFunction Internal Export v8::Function v8::Function *func = reinterpret_cast<v8::Function*>(ARG1(regs)); v8::String::Utf8Value name(func->CreationContext()->GetIsolate(), func->GetDebugName());
  • 55. 배경지식 입히기 특정 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 종료 처리
  • 57. 눈발자국 눈 위에 남긴 발자국 뒷사람의 이정표가 된다
  • 58.
  • 59. Tracing Data 눈발자국 NodeJS의 로깅 + Tracing 데이터 + 정보 가공 Plugin v8::internal::Runtime_TraceExit Javascript 함수 호출 v8::internal::Runtime_TraceEnter
  • 60. Tracing Data 눈발자국 NodeJS의 로깅 + Tracing 데이터 + 정보 가공 Plugin v8::internal::Compiler::Compile v8::internal:__RT_imple_Runtime_CompileLazy v8::internal::Runtime_CompileLazy Javascript 함수 컴파일
  • 61. Uftrace plugin Tracing Data 오픈소스 접근성 높히기 오픈소스 Tracing Data 문맥 #1 문맥 #2 문맥 #3 문맥 #4 문맥 #5 문맥 #6 디버깅메시지 추가정보 배경지식 오픈소스에 문맥 정보를 부여
  • 62. 오픈소스 오픈소스 분석 내용을 접목시킨 Plugin을 통한 오픈소스의 접근성을 향상 Uftrace plugin 시장 오픈소스 Tracing 오픈소스 활성화 가치창출 접근성향상 수요
  • 63. Q & A

Editor's Notes

  1. 프로그래밍은 쉬운 적이 없습니다. 시간이 지나고 경력이 쌓여도 마찬가지죠.
  2. 많은 이유가 있겠지만 프로그램이 동작하는 모습이 시각적으로 보이지 않기 때문인 것도 큰 이유이죠. 때문에 거친 야생의 밀림같은 잔혹한 프로그래밍의 세계에서 살아남기 위해서는 Printf같은 함수로 로그를 찍어보는 생존 기술을 모두가 배우게 됩니다.
  3. 이처럼 Printf 같은 함수를 활용하여 프로그램의 실행 동안의 변화를 기록하고 검토하는 작업을 tracing이라고 합니다. 용어의 생소함에 비해 개발자 분들은 자주 해오시던 작업입니다. Tracing을 우리말로 ‘추적' 정도로 번역할 수 있으니, 개발자는 동시에 추적자들이기도 합니다.
  4. 경력과 실력이 쌓이고 무리에 중역을 담당할 때가 되면 더 많은 먹이를 위해 활동반경이 넓어지게 되고, 이에 따라 더 다양하고 넓은 범위에 추적기술이 필요해지게 됩니다. 경우에 따라서는 디버깅이라는 새로운 생존기술을 배우게 되기도 합니다. 하지만 필요하고 원하는 곳에 Printf를 찍어보던 전과 다르게, 내 필요로 Printf를 찍어 볼 수 없는 상황에 처하게 됩니다. 이럴 때는 블랙박스처럼 라이브러리, 플랫폼 등에서 출력해주는 로그 메시지, 디버그 메시지에 의존할 수 밖에 없게 되죠. 내 프로그램부터 내가 프로그램이 기반하는 전 영역에서 Printf 를 쓸 수 있으면 어떨까요?
  5. 일반적인 실행프로그램의 구조는 대충 이렇습니다. C로 짜여진 함수 하나를 가지고 예를 들어보겠습니다. funcA 라는 함수는 integer형 arg를 인자로 받습니다. 그리고 getpid()라는 API를 호출하고 그 결과를 arg로 나눈 결과를 반환하죠. 이 과정에 전부 Printf를 찍는 상상을 해봅시다. - 가장 먼저FuncA가 호출될 때 FunA호출됐고 인자 arg 받았다고 찍고 - 다음에는 getpid()가 호출된 걸 찍고 - getpid() 내부에서 sys_getpid()를 호출하는 시스템콜을 날린걸 찍고 - 마지막으로 funcA가 getpid()의 값에 arg를 나눈 나머지를 반환하는걸 찍는 말로 설명하면 긴 과정입니다.
  6. Printf 를 찍기 위한 노력!!! UFTRACE는 함수 시작부의 일부 instruction을 패치하여 instrument를 설치하여 함수 호출을 기록하게 됩니다.
  7. 그 결과는 이렇게 나옵니다. Uftrace로 instrument 하여 호출된 함수들을 출력한 결과입니다.
  8. Uftrace라는 툴을 통해 개발자가 본인의 영역을 넘는 범위의 다양한 데이터를 모을 수 있고, 이를 검토하는데 도움을 받을 수 있습니다. 물론 ‘도움'만 받을 수 있습니다. 이제부터의 삽질은 모두 당신의 능력에 달렸습니다.
  9. Gcc에서 특정한 문법이 버그를 Segmentation fault를 발생하는 상황
  10. 디버깅으로 확인해보니 무조건 NULL이 반환되도록 코드가 바뀌어있음!? 보시는 부분이 무조건 0을 반환하도록 코드가 바뀐 부분
  11. Results : 4,157,866 lines function call record 400만 줄의 Tracing 기록을 검토하기 전에, Tracing을 시작하기 전에 찾아보면 좋을 것 들이 있습니다. 바로 Tracing하고자 하는 대상의 개발자들이 남겨놓은 발자국들이죠.
  12. 이번 경우에는 GCC가 경고 메시지를 출력해주기에 먼저 GCC 개발자들이 만들어 놓은 Printf들, 즉 디버깅용 메시지를 찾아보자. 생각해보면 우리가 Printf 로 우리 프로그램을 추적한 거처럼, 다른 개발자들도 자신들이 개발한 프로그램에 Printf를 달아 추적을 하는 건 당연한 것.
  13. Warning 메시지를 보고 찾은 문제 발생 지점. 문제가 발생한 원인은, 현재 함수의 선언이 inline임에도 불구하고 왜인지는 모르겠으나 inline에 대한 검사를 하지 않고 NULL로 바꾸기 때문이었음
  14. 그래서 inline 선언이 된 경우에는 NULL로 바꾸지 않도록 코드를 변경.
  15. 두근거리는 마음으로 다시 컴파일을 해봤습니다. 동작하네요! 근데 좀 이상합니다! 동작을 하긴 하는데 문제는 최적화 옵션을 주면 Segmentation Fault를 발생시키지도 않고 그저 아무 동작도 안합니다.
  16. 디버거를 통해 확인해보니 최적화 옵션을 주면 뭔가 많이 없어지는게 보임. 원래 있어야 할 텍스트가 사라져버리는 것.
  17. 이번에는 warning 메시지도 안뜨기 때문에 다른 발자국을 찾음. 바로 –fdump-tree-all-details, 이 옵션을 추가하면 GCC의 Front-end에서 각 패스가 실행될 때마다 변화되는 내용을 TEXT로 출력해줌.
  18. 이번에는 warning 메시지도 안뜨기 때문에 다른 발자국을 찾음. 바로 –fdump-tree-all-details, 이 옵션을 추가하면 GCC의 Front-end에서 각 패스가 실행될 때마다 변화되는 내용을 TEXT로 출력해줌.
  19. 이것은 최적화 관련 이슈이므로 최적화를 못하도록 만들어 보면?
  20. 원하는 결과가 나오고 동작하는 것을 확인할 수 있음!
  21. Android에서 특정 HACK을 하기 위해서 Android에서 Class load 관련된 로직을 분석해야 할 때가 있었죠.
  22. 이럴 때는 대상을 Tracing 하되, 하고자 하는 부분만을 최소화하는게 중요합니다.
  23. 왜냐면 아무리 최소화해도, 보시는 것처럼 100만 줄이 넘는 트레이싱 기록이 생기기 때문이죠. 그래도 GCC에 비하면 4분의 1이니 생각보다 만족스럽습니다.
  24. 소스코드를 분석하는 데는 그와 관련된 배경지식이 많은 영향을 미치는 것을 경험해보신 분들은 모두 아실 겁니다. 꼭 모든 내용을 세세히 알아야 할 필요는 없습니다, 가상머신이 어떻게 동작할지를 머리속에 그려보는 정도면 됩니다. 예를 들어, Android 가상머신은 Java code가 컴파일 된 bytecode를 입력으로 받아 각종 Class를 로드하고 호출된 method를 실행하여 결과를 보여주는 형태일 겁니다.
  25. Tracing에는 배경지식이 큰 도움이 된다. 함수의 이름만 봐도 기능을 추측할 수 있고, 때문에 함수 호출의 순서만 봐도 무엇을 위한 행위인지 파악할 수 있게 만든다.
  26. 또한 시각화가 도움이 되기도 합니다. 보이는 그림은 FlameGraph라는 시각화 형태입니다. 이와 같은 형태는 특히 Android의 가상머신 같은 반복적으로 같은 동작을 수행하는 오픈소스를 분석할 때에 도움이 많이 됩니다. Android에서 동일한 함수 이름이 반복해서 호출되는 것을 이를 통해 쉽게 확인할 수 있습니다. 저의 경우 앞서 생각했던 가상머신에 대한 대강의 실행 방식 ‘Class를 로드하고, 호출된 Method를 실행한다.'를 되뇌이면서 살펴봤고 FindClass, interpreter, Execute 와 같은 함수들이 보이는데 이들이 클래스 로드나 메소드 실행을 않을까? 하는 생각이 들어 카운팅을 해보니 FindClass가 36%, Interpreter가 56% 의 점유율을 차지하는 걸 확인할 수 있었습니다.
  27. 특히 FlameGraph의 경우 특정 노드를 선택하면 해당 노드에서 호출된 함수들을 확대하여 보여주므로, Top->Down 방힉으로 호출되는 모양을 살펴보기에 매우 좋습니다. Interpreting의 보시는 것과 같은 과정으로 이뤄집니다. 잘 만들어진 함수 이름, 예를 들어, MterpInvokeDirect처럼 invoke, Direct 등 명사의 의미를 알면 어떤 동작을 하는 함수인지 추측할 수 있는 함수들이 많습니다. 물론 여기서 Direct같은 용어는 내부 배경지식이 좀 필요한 용어인데, 이에 관련된 내용은 후장에서 설명드리겠습니다.
  28. 하지만 배경지식이 없으면, FindClass와 같은 함수의 동작은 쉽게 추측할 수 있지만, LinkClass같은 함수의 동작은 쉽사리 추측하기 어렵습니다. Link라는 단어의 느낌상 뭔가 연결할 것이라는 것만 추측할 수 있는 것이죠. 이런 경우는 직접 분석을 하며 관련 정보와 지식을 축적하는 방법밖에 없습니다. Tracing의 정의 중에 “검토하는 일"이 이와 관련된 내용입니다. 모든 Tracing은 결국은 미지의 영역에 도달하고 그와 관련된 정보와 지식을 축적하게 되는 일종의 과정인 셈 입니다.
  29. Gcc의 경우처럼 디버그나 경고용 메시지가 문제 발생의 원인을 추적하고 원인을 규명하고 최종적으로 수정하는데 도움이 됨.
  30. Gcc의 경우처럼 디버그나 경고용 메시지가 문제 발생의 원인을 추적하고 원인을 규명하고 최종적으로 수정하는데 도움이 됨.
  31. 사소한 것이라도 도움이 됩니다. 그렇다고 너무 걱정하진 않으셔도 됩니다. 부족한 배경지식은 트레이싱 과정에서 전부 강제로 습득하게 되실 거니까요.
  32. GCC의 문제를 추적하면서 저는 몇 가지 배경지식을 강제로 습득해야 했습니다.
  33. 배경지식, 각종 메시지에도 불구하고 문제의 원인을 찾아가 문제를 찾아내는 것은 결국 여정이다. 개발자의 노력과 시간의 투자를 필요로 한다. 하지만 그 과정에서 개발자는 그에 관련된 정보와 지식을 추가로 습득하게 된다.
  34. 지금까지 언급한 노하우? 를 종합하여 광산을 캐는 사례를 살펴보려고 합니다. 이번에 살펴보려는 케이스는 Nodejs에서 Javascript의 함수호출을 기록하고, 그 기록을 APM에 연동해 간단한 NodeJS용 APM을 만들어보는 튜토리얼입니다.
  35. 먼저 NodeJS 개발자들이 남겨놓은 발자국을 찾아봅시다. Nodejs는 Javscript engin으로 v8을 사용하고 있습니다. V8은 Javascript 함수 호출을 출력해주는 --trace 옵션이 있습니다.
  36. 이게 어떻게 동작하는지, 확인하는 옵션 또한 존재합니다. Bytecode를 출력하는 옵션을 통해 –trace옵션을 줬을 때 어떤 변화가 생겻는지 확인해보면 TraceEnter와 TraceExit를 호출하는 코드가 함수의 시작과 종료 시점에 추가됐음을 알 수 있습니다.
  37. 이를 키워드로 추적해 들어가면, Runtime 함수 TraceEnter는 실질적으로 JavaScriptFrame의 PrintTop을 호출하여 함수의 이름을 출력하고 TraceExit는 함수가 종료됐음을 출력하는 것을 확인할 수 있습니다.
  38. 좀더 상세히 살펴보겠습니다. Runtime 함수 TraceEnter는 실질적으로 JavaScriptFrame의 PrintTop을 호출한다고 말씀드렸는데 이 함수는 현재 StackFrame에서 최상단 Frame에 저장된 Javascript를 두 번째 인자인 stdout에 출력하게 됩니다. 여기서 마지막으로 호출된 Javascript함수는 “CallRuntime [TraceEnter]” 이기 때문에, 이를 호출한 Javascript 함수인 find_me_a가 Javascript stackframe의 최상단에 위치하게 되고, 따라서 find_me_a가 출력됩니다.
  39. Uftrace는 함수 각 함수 호출에 instrument를 설치해놨으므로, 이를 통해 2번째 인자인 stdout의 값을 변경하여, 별도의 stream으로 연결할 수 있습니다.
  40. 눈발자국은 Uftrace Plugin 형태로 개발되어 Tracing 데이터의 일부를 목적에 따라 가공할 수 있습니다. 저는 NodeJS에서 특정 함수의 호출만을 기록하여 APM에 사용하기 위해 Plugin을 개발했습니다. 이 과정에서 NodeJS의 –trace 옵션을 통해 v8 개발자들이 남겨놓은 발자국을 백분 활용했던 것 같습니다.
  41. 이에 따라 Uftrace가 원하는 곳에 함수 이름을 기록하여 활용할 수 있습니다. 다음은 외부 데이터에 저장하여 uftrace의 replay 기능으로 찍어본 실행 기록이며, 이를 통해 특정 Javascript의 실행을 Tracing 할 수 있을 뿐 아니라 그 내부 동작도 확인 할 수 있습니다.
  42. 이를 통해 아주 간단한 NodeJS용 APM을 만들어봅시다. Nodejs와 express를 활용해 간단한 웹서버를 구축하면서, trace옵션을 주고 Uftrace에서 이를 가로채 함수의 시작과 종료 시점을 기록합니다. 가로챈 함수의 시작과 종료시점을 GRPC를 통하여 Pinpoint에 데이터를 보내 기록시킵니다.
  43. NodeJS의 구조는 Javascript의 실행 엔진인 v8과 Javascript의 Native를 담당하는 node로 구성되어 있습니다. Node는 script를 입력 받고 이를 v8을 통해 컴파일하고 실행한 결과를 반환받아 추가 작업을 수행하고, V8은 node가 전달해준 실행하기 위해 여러가지 동작을 수행하죠.
  44. 왼쪽의 예제 소스는 v8에 의해 우측처럼 파싱됩니다. 세개의 함수와 하나의 함수 호출문.
  45. 이를 uftrace를 통해 추적해보면 하나의 함수가 호출될 때, 각 함수를 지연 컴파일하는 절차를 확인할 수 있습니다.
  46. 여기서 v8::internal::Compiler::Compile 함수는 컴파일할 함수를 전달받게 됩니다. 분석과정에서 흥미롭게도 이는 nodejs처럼 외부에서도 사용할 수 있는 데이터 형으로 변환 가능한 타입이었습니다. 아래 소스코드를 통해 그에 대한 사용 예를 확인하실 수 있습니다. 이런 건 대체 왜 알아야 하냐구요? 다음 장에서 설명할 업그레이드를 위해 섭니다.
  47. 이에 따라 만든 Plugin의 실행 순서를 그려봤습니다. V8::internal::Compiler::compile이 호출되면 Uftrace가 설치한 instrumen로 가로채지고, 여기서 Plugin을 호출하게 구조를 만듭니다. Plugin에서는 첫 번째 인자인 Function의 이름을 읽고, APM에 필요한 함수일 경우 FLAG_trace를 활성화하여 Runtime_TraceEnter가 삽입되도록 유도합니다. 그리고 v8::internal::Compiler::Compile가 종료될 때 FLAG_trace를 비활성화하여 다른 함수에 Runtime_TraceEnter가 삽입되는 것을 방지합니다. 이런 과정을 통해 특정 함수만 기록하도록 기능을 업그레이드 할 수 있게 됩니다.
  48. 눈발자국은 두 가지 목적에서 시작된 실험입니다. 하나는, 개발자가 남긴 발자국을 추적하는게 큰 도움이 된다는 경험을 GCC에서 했는데 이게 다른 오픈소스에도 동일하게 적용될 수 있는지 확인해보고 싶었고 다른 하나는, 어찌됐든 추적을 하면서 소스코드를 분석하고 배경지식이 쌓이게 되는데 그 자체를 어떤 형태로든 가공해 전달할 수 없을까 하는 필요가 생겼기 때문입니다.
  49. 그리고 앞선 만든 간단한 NodeJS APM을 업그레이드 하면서 실험 내용을 공유해보려고 합니다.
  50. Nodejs로 console에 log를 찍는 javascript를 실행하면 실행 과정에서 약 1200만번 가량의 함수 호출 일어납니다. Nodejs용 plugin을 개발하여 그 과정에서 Javascript가 이번 과정을 통해 이 중에 Javascript 컴파일 과정, 함수 호출의 시작과 끝을 분석하고
  51. Nodejs로 console에 log를 찍는 javascript를 실행하면 실행 과정에서 약 1200만번 가량의 함수 호출 일어납니다. Nodejs용 plugin을 개발하여 그 과정에서 Javascript가 이번 과정을 통해 이 중에 Javascript 컴파일 과정, 함수 호출의 시작과 끝을 분석하고
  52. 또한 이런 식으로 Tracing 기록을 치환하고 가공하여 문맥 정보를 변경할 수 있는 Plugin 형태로 개발하여 다른 여러 오픈소스에도 적용하거나 같은 오픈소스에도 다른 문맥 정보를 보여줄 수 있습니다. 이런 문맥정보를 통해 마치 눈 발자국을 남기듯 오픈소스에 접근성을 높힐 수 있다는게 제 결론입니다. 그리고 NodeJS의 APM을 만드는게 가능했 듯이, 더 많은 분야와 범위에 Performance monitoring 등의 목적으로 Plugin을 개발하고자 합니다.
  53. NodeJS APM Plugin을 만든 이유 -> 오픈소스 분석을 통해 가치 창출 할 수 있는 Plugin을 제작할 수 있다면, 1. 오픈소스 Tracing이 활성화 될 수 있을 것이고, 2. 오픈소스 Tracing이 활성화 되면 더 많은 오픈소스 대상으로 다양한 Plugin이 제작되어 3. 오픈소스에 대한 진입 장벽을 낮출 수 있을 것