2016년 5월 13일 금요일

WebEngine Preload


https://webkit.org/blog/166/optimizing-page-loading-in-web-browser/
http://ariya.ofilabs.com/2013/04/css-preload-scanner-in-webkit.html

ResourceFetcher를 보면 preloading을 구별하고 있다.
third_party/WebKit/Source/core/html/parser/HTMLPreloadScanner.cpp

1. HTMLDocumentParser는 근본적으로는 One time pass through 방식으로 동작한다.
    HTML 페이지를 Top에서 Bottom까지 한번에 읽고 넘어가지, 다시 뒤로 돌아가지 않는다.

2. 그런데 javascript는 HTML을 생산할 수 있다.

3. javascript가 HTML을 생산하고, HTMLDocumentParser가 다시 뒤로 돌아가기를 하지 않으려면,
    결국 javascript가 download 되고(별도 파일이라면), 실행완료 되기를 기다릴 수 밖에 없다.

4. 이 기다림이 동안 페이지 로딩 지연이 발생하게 된다.

5. 때문에 blocking 동안, 별도의 scanner를 돌려서, img, css 등 리소스 파일을 다운받게 한다.
    그게 HTMLPreloadScanner이다.

6. 실제 preloading은 ResourceFetcher가 담당한다.

WebCore Resource의 handle 관리

WebCore는 Resource를 가지고 메모리를 관리한다.
Resource는 image, js, css 등 web server에서 받아온 encoded data, 그리고 그것을 '해석'한 decoded data를 모두 포함 한다.

Resource (리소스 당 하나)는
(1) ResourceFetcher (Document당 하나),
(2) ImageLoader (이미지 당 하나)
(3) MemoryCache (Render Process 당 하나)
등에서도 ResourcePtr을 통해 관리 되고 참조 된다.

이렇게 여러 Object에 의해서 참조되기 때문에 handleCount를 유지하는데, 그 handleCount가 0이 되어야 Resource::~Resource() 가 불려서 Destroy 되고 메모리에서 해제 되게 된다.

Resource Destroy에 관련 된 함수들.
unregisterHandle()
deleteIfPossible()
canDelete()

결국 handleCount를 0으로 만드는 조건을 만들어줘야 메모리를 줄일 수 있다.

[ResourceFetcher]
chrome --type=renderer (+0x512676c) [0x7f7defc4a76c]
chrome --type=renderer (+0x51269a9) [0x7f7defc4a9a9]
chrome --type=renderer (+0x4b2e894) [0x7f7def652894]
chrome --type=renderer (+0x4b2e812) [0x7f7def652812]
chrome --type=renderer (+0x52e3f9a) [0x7f7defe07f9a]
chrome --type=renderer (+0x44b40b8) [0x7f7deefd80b8]
chrome --type=renderer (+0x44b38f8) [0x7f7deefd78f8]
chrome --type=renderer (+0x44708f9) [0x7f7deef948f9]
chrome --type=renderer (+0x52fb01c) [0x7f7defe1f01c]
chrome --type=renderer (+0x440e7cc) [0x7f7deef327cc]
chrome --type=renderer (+0x4409ddd) [0x7f7deef2dddd]


sonchulmin@sonchulmin-H81MDV3:~/work/src/out/Debug$ addr2line -e chrome -afp 0x512676c
0x000000000512676c: _ZN5blink4dumpEv at ../../third_party/WebKit/Source/core/fetch/ResourceFetcher.cpp:235
sonchulmin@sonchulmin-H81MDV3:~/work/src/out/Debug$ addr2line -e chrome -afp 0x4b2e894
0x0000000004b2e894: _ZN5blink15ResourceFetcher6createEN3WTF10PassOwnPtrINS_12FetchContextEEE at ../../third_party/WebKit/Source/core/fetch/ResourceFetcher.h:72
sonchulmin@sonchulmin-H81MDV3:~/work/src/out/Debug$ addr2line -e chrome -afp 0x4b2e812
0x0000000004b2e812: _ZN5blink17FrameFetchContext23createContextAndFetcherEPNS_14DocumentLoaderE at ../../third_party/WebKit/Source/core/loader/FrameFetchContext.h:55
sonchulmin@sonchulmin-H81MDV3:~/work/src/out/Debug$


./third_party/WebKit/Source/core/loader/DocumentLoader.cpp:    , m_fetcher(FrameFetchContext::createContextAndFetcher(this))
./third_party/WebKit/Source/core/dom/Document.cpp:        m_fetcher = FrameFetchContext::createContextAndFetcher(nullptr);

backtrace

사용 : 어떤 함수를 호출하는 녀석들이 너무 다양할 때, 문제 상황에서 정확히 어떤 녀석이 불린 것인지 알고자 할 때 사용.

gdb로 일일이 callstack을 출력할 수 있지만 귀찮을 때는 backtrace 사용하면 편리함.
http://man7.org/linux/man-pages/man3/backtrace.3.html

       #include <execinfo.h>
       #include <stdio.h>
       #include <stdlib.h>
       #include <unistd.h>

       #define BT_BUF_SIZE 100

       void
       dump(void)
       {
           int j, nptrs;
           void *buffer[BT_BUF_SIZE];
           char **strings;

           nptrs = backtrace(buffer, BT_BUF_SIZE);
           printf("backtrace() returned %d addresses\n", nptrs);

           /* The call backtrace_symbols_fd(buffer, nptrs, STDOUT_FILENO)
              would produce similar output to the following: */

           strings = backtrace_symbols(buffer, nptrs);
           if (strings == NULL) {
               perror("backtrace_symbols");
               exit(EXIT_FAILURE);
           }

           for (j = 0; j < nptrs; j++)
               printf("%s\n", strings[j]);

           free(strings);
       }


그리고 출력하고자 하는 함수에서 조건을 걸어 주면 좀더 결과가 간략하게 출력 될 수 있음.

if (url().string().find(".jpg") != std::string::npos)
   dump();


많은 심볼을 한꺼번에 addr2line할 때는 아래와 같이 awk를 이용 가능.




awk 'match($0, /+0x[0-9a-f]*/) {
    addr = substr($0, RSTART, RLENGTH);
    print $addr
    system("addr2line -e lib/libcbe.so -apf "addr);
}
' log.txt