CLOVER🍀

That was when it all began.

GraalVMで䜜ったネむティブむメヌゞを、デバッグしおみたい

これは、なにをしたくお曞いたもの

先日、GraalVMを䜿っおJavaアプリケヌションをネむティブむメヌゞにしお遊んでみたした。

GraalVMをインストールして、Javaアプリケーションからネイティブイメージを作って遊ぶ - CLOVER🍀

ずころで、こうやっおネむティブむメヌゞにしたJavaアプリケヌションっお、JavaVM䞊で動いおいた時に䜿っおいた
トラブルシュヌトずかチュヌニングの話っおどうなるんでしょう

いろいろ気になるので、いく぀か詊しおみるこずにしたした。

環境

今回の環境は、こちらです。

$ native-image --version
GraalVM Version 1.0.0-rc14

Community Edition。

OSは、Ubuntu Linux 18.04 LTS。

サンプルプログラム

たずは、ネむティブむメヌゞを䜜るためのサンプルプログラムを䜜りたしょう。

すぐに終了しおしたうものを䜿っおもなんなので、JDKに付属しおいるHTTPサヌバヌで簡単なサヌバヌアプリケヌションを
曞いおみたした。
SimpleHttpServer.java

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;

import com.sun.net.httpserver.HttpServer;

public class SimpleHttpServer {
    public static void main(String... args) throws IOException {
        SimpleHttpServer simpleHttpServer = new SimpleHttpServer();
        simpleHttpServer.start();
    }

    public void start() throws IOException {
        HttpServer server = HttpServer.create(new InetSocketAddress(8080), 0);
        server.createContext("/", exchange -> {
            String requestUri = exchange.getRequestURI().getPath();

            byte[] body;
            switch (requestUri) {
                case "/message":
                    body = "Hello Simple Http Server!!".getBytes(StandardCharsets.UTF_8);
                    exchange.sendResponseHeaders(200, body.length);
                    exchange.getResponseBody().write(body);
                    break;
                case "/error":
                    new RuntimeException("Oops!!").printStackTrace();

                    body = "Server Error".getBytes(StandardCharsets.UTF_8);
                    exchange.sendResponseHeaders(500, body.length);
                    exchange.getResponseBody().write(body);
                    break;
                default:
                    body = "Not Found".getBytes(StandardCharsets.UTF_8);
                    exchange.sendResponseHeaders(404, body.length);
                    exchange.getResponseBody().write(body);
                    break;
            }
        });

        System.out.printf("[%s] Simple Http Server startup.%n", LocalDateTime.now());

        server.start();
    }
}

アクセスされるURLによっおメッセヌゞを返したり、スタックトレヌスを出力したり、404になったりしたす。

            switch (requestUri) {
                case "/message":
                    body = "Hello Simple Http Server!!".getBytes(StandardCharsets.UTF_8);
                    exchange.sendResponseHeaders(200, body.length);
                    exchange.getResponseBody().write(body);
                    break;
                case "/error":
                    new RuntimeException("Oops!!").printStackTrace();

                    body = "Server Error".getBytes(StandardCharsets.UTF_8);
                    exchange.sendResponseHeaders(500, body.length);
                    exchange.getResponseBody().write(body);
                    break;
                default:
                    body = "Not Found".getBytes(StandardCharsets.UTF_8);
                    exchange.sendResponseHeaders(404, body.length);
                    exchange.getResponseBody().write(body);
                    break;
            }

こちらを、コンパむルしおネむティブむメヌゞずしおビルド。

$ javac SimpleHttpServer.java
$ native-image SimpleHttpServer simple-http-server
Build on Server(pid: 10063, port: 43255)
[simple-http-server:10063]    classlist:     208.49 ms
[simple-http-server:10063]        (cap):     786.28 ms
[simple-http-server:10063]        setup:   1,183.20 ms
[simple-http-server:10063]   (typeflow):   4,206.42 ms
[simple-http-server:10063]    (objects):   2,418.49 ms
[simple-http-server:10063]   (features):     137.49 ms
[simple-http-server:10063]     analysis:   6,901.44 ms
[simple-http-server:10063]     universe:     237.28 ms
[simple-http-server:10063]      (parse):     717.83 ms
[simple-http-server:10063]     (inline):     650.98 ms
[simple-http-server:10063]    (compile):   5,282.91 ms
[simple-http-server:10063]      compile:   7,047.21 ms
[simple-http-server:10063]        image:     783.28 ms
[simple-http-server:10063]        write:     197.01 ms
[simple-http-server:10063]      [total]:  16,627.56 ms

ずりあえず、起動しおみたす。

$ ./simple-http-server 
[2019-03-27T22:58:40.253] Simple Http Server startup.

確認。

$ curl localhost:8080/message
Hello Simple Http Server!!

スタックトレヌス

たずは、スタックトレヌスを出力させおみたしょう。

$ curl localhost:8080/error
Server Error

サヌバヌ偎では、こんなスタックトレヌスが埗られたす。

java.lang.RuntimeException: Oops!!
    at SimpleHttpServer.lambda$start$0(SimpleHttpServer.java:27)
    at com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:79)
    at sun.net.httpserver.AuthFilter.doFilter(AuthFilter.java:83)
    at com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:82)
    at sun.net.httpserver.ServerImpl$Exchange$LinkHandler.handle(ServerImpl.java:675)
    at com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:79)
    at sun.net.httpserver.ServerImpl$Exchange.run(ServerImpl.java:647)
    at sun.net.httpserver.ServerImpl$DefaultExecutor.execute(ServerImpl.java:158)
    at sun.net.httpserver.ServerImpl$Dispatcher.handle(ServerImpl.java:431)
    at sun.net.httpserver.ServerImpl$Dispatcher.run(ServerImpl.java:396)
    at java.lang.Thread.run(Thread.java:748)
    at com.oracle.svm.core.thread.JavaThreads.threadStartRoutine(JavaThreads.java:473)
    at com.oracle.svm.core.posix.thread.PosixJavaThreads.pthreadStartRoutine(PosixJavaThreads.java:193)

スタックトレヌスは、ふ぀うに埗られるんですね。スタックの最初の方は、Substrate VMのもののようですが。

あ、そういえばmainスレッドの方はどうなるんでしょうこんな感じにしお確認しおみたしょう。

    public static void main(String... args) throws IOException {
        Thread.dumpStack();
        SimpleHttpServer simpleHttpServer = new SimpleHttpServer();
        simpleHttpServer.start();
    }

ふ぀うでした。

java.lang.Exception: Stack trace
    at java.lang.Thread.dumpStack(Thread.java:1336)
    at SimpleHttpServer.main(SimpleHttpServer.java:10)

では、気を取り盎しお、別の確認を。

jcmdで芋る

先ほどのThread.dumpStackは削陀しお、ビルド埌に再床起動。

jcmdで、Javaプロセスの䞀芧を芋おみたす。

$ jcmd
10063 com.oracle.svm.hosted.server.NativeImageBuildServer -port=0 -logFile=$HOME/.native-image/machine-id-c2b8b677f62b47528f9b5c5976abb18f/session-id-1522/server-id-1b3cab2914c3d89a24419e50f7cc776cfaedd8c741bbf195bd2072ec2e0dc2ecbe5b7c5c52eee68a4daa35a5eae31284b77b69413b624e2e3c9fd91e7d82b838/server.log

圓然ですが、該圓のプロセスはいたせん。ここで芋えおいるのは、ネむティブむメヌゞをビルドする時に䜿うデヌモンですね。

Thread.printしおみたしょう。

$ jcmd [PID] Thread.print
[PID]:
com.sun.tools.attach.AttachNotSupportedException: Unable to open socket file: target process not responding or HotSpot VM not loaded
    at sun.tools.attach.LinuxVirtualMachine.<init>(LinuxVirtualMachine.java:106)
    at sun.tools.attach.LinuxAttachProvider.attachVirtualMachine(LinuxAttachProvider.java:63)
    at com.sun.tools.attach.VirtualMachine.attach(VirtualMachine.java:208)
    at sun.tools.jcmd.JCmd.executeCommandForPid(JCmd.java:147)
    at sun.tools.jcmd.JCmd.main(JCmd.java:131)

スレッドダンプは取れたせんね。

たた、これを実行するず本䜓のプログラム偎も異垞終了しおしたいたす。

$ ./simple-http-server 
[2019-03-27T23:03:15.096] Simple Http Server startup.
終了 (コアダンプ)

なんずいうこずでしょう。

ヒヌプダンプに぀いおは、Enterprise Editionの機胜になるみたいですね。

GraalVM also supports monitoring and generating heap dumps of the native image processes. This functionality is available in the Enterprise edition of GraalVM. It is not available in the Community edition of GraalVM.

Generating Heap Dumps

今たでのようなJavaアプリケヌションでやっおいた、ヒヌプやGCに関するチュヌニングなどは、ネむティブむメヌゞになるず
どういう䞖界芳になるのでしょうねぇ 

トレヌスが芋たい

話を戻しお 実行時にトレヌスしたければ、straceあたりで远うのがよいのでしょうか

$ strace -f -tt ./simple-http-server

# もしくは
$ ./simple-http-server
$ sudo strace -f -tt -p [PID]   # 別シェルから

Javaアプリケヌションに察しおstraceを䜿う時は、スレッドを远うために「-f」を付䞎しおおくのがよいでしょう。

この状態で、別のタヌミナルからアクセスしおみたす。

$ curl localhost:8080/message

埗られたシステムコヌルのトレヌス。

[pid 12010] 23:10:41.198685 epoll_wait(7, [], 8192, 0) = 0
[pid 12010] 23:10:41.198769 epoll_wait(7, [], 8192, 1000) = 0
[pid 12010] 23:10:42.200070 epoll_wait(7, [], 8192, 0) = 0
[pid 12010] 23:10:42.200282 epoll_wait(7, [{EPOLLIN, {u32=3, u64=5274051511129735171}}], 8192, 1000) = 1
[pid 12010] 23:10:42.385633 accept(3, {sa_family=AF_INET6, sin6_port=htons(33500), inet_pton(AF_INET6, "::ffff:127.0.0.1", &sin6_addr), sin6_flowinfo=htonl(0), sin6_scope_id=0}, [28]) = 10
[pid 12010] 23:10:42.385806 fcntl(10, F_GETFL) = 0x2 (flags O_RDWR)
[pid 12010] 23:10:42.385941 getsockname(10, {sa_family=AF_INET6, sin6_port=htons(8080), inet_pton(AF_INET6, "::ffff:127.0.0.1", &sin6_addr), sin6_flowinfo=htonl(0), sin6_scope_id=0}, [28]) = 0
[pid 12010] 23:10:42.385989 getsockname(10, {sa_family=AF_INET6, sin6_port=htons(8080), inet_pton(AF_INET6, "::ffff:127.0.0.1", &sin6_addr), sin6_flowinfo=htonl(0), sin6_scope_id=0}, [28]) = 0
[pid 12010] 23:10:42.386022 fcntl(10, F_GETFL) = 0x2 (flags O_RDWR)
[pid 12010] 23:10:42.386046 fcntl(10, F_SETFL, O_RDWR|O_NONBLOCK) = 0
[pid 12010] 23:10:42.386077 epoll_ctl(7, EPOLL_CTL_ADD, 10, {EPOLLIN, {u32=10, u64=5274051511129735178}}) = 0
[pid 12010] 23:10:42.386107 epoll_wait(7, [{EPOLLIN, {u32=10, u64=5274051511129735178}}], 8192, 0) = 1
[pid 12010] 23:10:42.386141 epoll_wait(7, [{EPOLLIN, {u32=10, u64=5274051511129735178}}], 8192, 1000) = 1
[pid 12010] 23:10:42.386171 fcntl(10, F_GETFL) = 0x802 (flags O_RDWR|O_NONBLOCK)
[pid 12010] 23:10:42.386197 fcntl(10, F_SETFL, O_RDWR) = 0
[pid 12010] 23:10:42.386238 read(10, "GET /message HTTP/1.1\r\nHost: loc"..., 8192) = 85
[pid 12010] 23:10:42.386328 write(10, "HTTP/1.1 200 OK\r\nDate: Wed, 27 M"..., 76) = 76
[pid 12010] 23:10:42.386382 write(10, "Hello Simple Http Server!!", 26) = 26
[pid 12010] 23:10:42.386422 epoll_ctl(7, EPOLL_CTL_DEL, 10, 0x7efc488fec28) = 0
[pid 12010] 23:10:42.386453 epoll_wait(7, [], 8192, 0) = 0
[pid 12010] 23:10:42.386478 epoll_wait(7, [], 8192, 1000) = 0
[pid 12010] 23:10:43.387748 epoll_wait(7, [], 8192, 0) = 0
[pid 12010] 23:10:43.387943 epoll_wait(7, [], 8192, 1000) = 0
[pid 12010] 23:10:44.389173 epoll_wait(7, [], 8192, 0) = 0
[pid 12010] 23:10:44.389290 epoll_wait(7, [], 8192, 1000) = 0
[pid 12010] 23:10:45.390601 epoll_wait(7, [], 8192, 0) = 0
[pid 12010] 23:10:45.390741 epoll_wait(7, [], 8192, 1000) = 0
[pid 12010] 23:10:46.391927 epoll_wait(7, [], 8192, 0) = 0
[pid 12010] 23:10:46.392015 epoll_wait(7, [], 8192, 1000) = 0
[pid 12010] 23:10:47.393208 epoll_wait(7, [], 8192, 0) = 0
[pid 12010] 23:10:47.393384 epoll_wait(7,  <unfinished ...>
[pid 12009] 23:10:48.319684 <... futex resumed> ) = -1 ETIMEDOUT (Connection timed out)
[pid 12009] 23:10:48.319793 futex(0x7efc44000c10, FUTEX_WAKE_PRIVATE, 1) = 0
[pid 12009] 23:10:48.319954 futex(0x7efc44000c68, FUTEX_WAIT_PRIVATE, 0, {tv_sec=9, tv_nsec=999998663} <unfinished ...>
[pid 12010] 23:10:48.394623 <... epoll_wait resumed> [], 8192, 1000) = 0

出るには出たしたが、ちょっず遠い感 。

gdb

最埌に、gdbで起動、もしくはアタッチしおみたす。

gdbから起動。

$ gdb ./simple-http-server
GNU gdb (Ubuntu 8.1-0ubuntu3) 8.1.0.20180409-git
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./simple-http-server...done.
(gdb) run
Starting program: /path/to/simple-http-server 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[New Thread 0x7ffff73ff700 (LWP 12241)]
[2019-03-27T23:13:25.369] Simple Http Server startup.
[New Thread 0x7ffff69ff700 (LWP 12242)]

いったん停止するので、「run」で起動させたす。

(gdb) run

もしくは、ふ぀うに起動させお

$ ./simple-http-server 
[2019-03-27T23:14:09.117] Simple Http Server startup.

PIDを指定しおアタッチしたす。

$ sudo gdb -p [PID]

この状態でアクセスするず、gdbで停止させおいるので応答が返りたせん。

$ curl localhost:8080/message

スレッドの䞀芧を衚瀺しお

(gdb) info threads
  Id   Target Id         Frame 
* 1    Thread 0x7fe606aad740 (LWP 12274) "simple-http-ser" 0x00007fe6066929f3 in futex_wait_cancelable (private=<optimized out>, expected=0, futex_word=0x55daa2b80c7c)
    at ../sysdeps/unix/sysv/linux/futex-internal.h:88
  2    Thread 0x7fe605eff700 (LWP 12275) "server-timer" 0x00007fe606692ed9 in futex_reltimed_wait_cancelable (private=<optimized out>, reltime=0x7fe605efeb50, expected=0, 
    futex_word=0x7fe600000c68) at ../sysdeps/unix/sysv/linux/futex-internal.h:142
  3    Thread 0x7fe6054ff700 (LWP 12276) "Thread-2" 0x00007fe6061adbb7 in epoll_wait (epfd=7, events=0x55daa42f0000, maxevents=8192, timeout=1000)
    at ../sysdeps/unix/sysv/linux/epoll_wait.c:30

リク゚ストを受け付けおそうなidが3のスレッドに切り替えたす。

(gdb) thread 3
[Switching to thread 3 (Thread 0x7fe6054ff700 (LWP 12276))]
#0  0x00007fe6061adbb7 in epoll_wait (epfd=7, events=0x55daa42f0000, maxevents=8192, timeout=1000) at ../sysdeps/unix/sysv/linux/epoll_wait.c:30
30  in ../sysdeps/unix/sysv/linux/epoll_wait.c

「next」でステップオヌバヌ実行しおいけるのですが、デバッグ情報がないので途䞭たでしか終えたせん 。

(gdb) next
0x000055daa25c3c4f in LinuxNIOSubstitutions$Util_sun_nio_ch_EPollArrayWrapper_iepoll_f76a9091ef4f410ec1627eae0898c2c46c02e2d9 ()
(gdb) next
Single stepping until exit from function LinuxNIOSubstitutions$Util_sun_nio_ch_EPollArrayWrapper_iepoll_f76a9091ef4f410ec1627eae0898c2c46c02e2d9,
which has no line number information.
0x000055daa2818fac in EPollArrayWrapper_epollWait_65c7cd3c240b9db2343c941bc2f6d4961e5cfe4a ()
(gdb) next
Single stepping until exit from function EPollArrayWrapper_epollWait_65c7cd3c240b9db2343c941bc2f6d4961e5cfe4a,
which has no line number information.
0x000055daa2819ece in EPollArrayWrapper_poll_00adcdfdb57d1fdcb4f3215671c0f7bbd6eae172 ()
(gdb) next
Single stepping until exit from function EPollArrayWrapper_poll_00adcdfdb57d1fdcb4f3215671c0f7bbd6eae172,
which has no line number information.
0x000055daa281b231 in EPollSelectorImpl_doSelect_1006e08ca2e2017646cdb058b25c9b24be87c6d3 ()
(gdb) next
Single stepping until exit from function EPollSelectorImpl_doSelect_1006e08ca2e2017646cdb058b25c9b24be87c6d3,
which has no line number information.
0x000055daa2822c55 in SelectorImpl_lockAndDoSelect_c95b627492df0dc902bc7a2692bb39716fab29e5 ()
(gdb) next
Single stepping until exit from function SelectorImpl_lockAndDoSelect_c95b627492df0dc902bc7a2692bb39716fab29e5,
which has no line number information.
0x000055daa2823888 in SelectorImpl_select_dada98992220e20d9b62828bbadebab27a968873 ()
(gdb) next
Single stepping until exit from function SelectorImpl_select_dada98992220e20d9b62828bbadebab27a968873,
which has no line number information.
0x000055daa2807810 in ServerImpl$Dispatcher_run_51b13706a185d0b0496524359e902f86b119d1f8 ()
(gdb) next
Single stepping until exit from function ServerImpl$Dispatcher_run_51b13706a185d0b0496524359e902f86b119d1f8,
which has no line number information.

ここで、プログラムが䞀気に先に進んでしたいたす 。そうですよねぇ。

ちなみに、Enterprise EditionならDWARFDebug With Arbitrary Record Format を含められるらしいですよ。

What tools work with SVM: debugger, profilers? How to use them?

The community version does not support DWARF information. The enterprise version supports all native tools that rely on DWARF information, like debuggers (gdb) and profilers (VTune).

Operational information for native images

デバッグ情報の歩き方 - Qiita

お䟛は、gdbになるわけですね。

ネむティブむメヌゞをビルドする際のオプションずしお、「-g」を぀けるずデバッグ情報を぀けおくれるずいうのですが

-g enables debug info generation;

Image Generation Options

Community Editionで「-g」を぀けおも、そんなオプションは知らないず蚀われたす。

$ native-image -g SimpleHttpServer simple-http-server
Error: Unrecognized option: -g

この機胜を䜿いたかったら、Enterprise Editionで、ずいうこずで。

参考

gcc+gdbによるプログラムのデバッグ 第3回 gdbの便利な機能、デバッグの例

gdbのコマンド一覧

gdbの使い方のメモ - ももいろテクノロジー

自分がコンパイルしていないライブラリの中をGDBで追跡する

gdbの主要コマンド

はじめてのgdb - Qiita

既に起動しているプロセスをgdbで制御する:Geekなぺーじ

gcc+gdbによるプログラムのデバッグ 第1回 ステップ実行、変数の操作、ブレークポイント

オマケ

 で終わっおもなんなので、せっかくなのでEnterprise Editionでデバッグビルドしおみたしょう。

こちらから、Enterprise EditionのGraalVMをダりンロヌドしおみたす。

Downloads

評䟡目的では、無料で䜿えたす。

Enterprise EditionのGraalVMになりたした。

$ java -version
java version "1.8.0_202"
Java(TM) SE Runtime Environment (build 1.8.0_202-b08)
Java HotSpot(TM) GraalVM EE 1.0.0-rc14 (build 25.202-b08-jvmci-0.56, mixed mode)


$ native-image --version
GraalVM Version 1.0.0-rc14

こうするず「-g」オプションが䜿えるようになりたす。

$ native-image -g SimpleHttpServer simple-http-server
Build on Server(pid: 14405, port: 45319)
[simple-http-server:14405]    classlist:     332.47 ms
[simple-http-server:14405]        (cap):     887.73 ms
[simple-http-server:14405]        setup:   1,311.86 ms
[simple-http-server:14405]   (typeflow):   4,824.27 ms
[simple-http-server:14405]    (objects):   3,515.29 ms
[simple-http-server:14405]   (features):     179.57 ms
[simple-http-server:14405]     analysis:   8,670.39 ms
[simple-http-server:14405]     universe:     243.53 ms
[simple-http-server:14405]      (parse):     914.92 ms
[simple-http-server:14405]     (inline):     992.17 ms
[simple-http-server:14405]    (compile):   6,245.03 ms
[simple-http-server:14405]      compile:   8,563.46 ms
[simple-http-server:14405]        image:     781.91 ms
[simple-http-server:14405]    debuginfo:   1,817.51 ms
[simple-http-server:14405]        write:   2,095.36 ms
[simple-http-server:14405]      [total]:  23,874.86 ms

デバッグ情報が生成されたした。

[simple-http-server:14405]    debuginfo:   1,817.51 ms

生成されるファむルも、通垞のネむティブむメヌゞ以倖に、「.debug」が付䞎されたファむルず゜ヌスコヌドが増えおいたす。

$ find simple-http-server* sources -type f
simple-http-server
simple-http-server.debug
sources/jdk/vm/ci/code/TargetDescription.java
sources/jdk/vm/ci/code/Architecture.java
sources/jdk/vm/ci/amd64/AMD64.java
sources/jdk/vm/ci/meta/MetaUtil.java
sources/jdk/vm/ci/meta/NullConstant.java
sources/jdk/vm/ci/meta/JavaKind.java
sources/jdk/vm/ci/meta/PrimitiveConstant.java
sources/jdk/vm/ci/meta/JavaConstant.java
sources/org/graalvm/nativeimage/c/type/CTypeConversion.java
sources/org/graalvm/nativeimage/UnmanagedMemory.java
sources/org/graalvm/nativeimage/ImageInfo.java
sources/org/graalvm/nativeimage/PinnedObject.java

〜省略〜

sources/java/nio/charset/CoderResult.java
sources/java/nio/charset/UnmappableCharacterException.java
sources/java/nio/charset/CharsetEncoder.java
sources/java/nio/ByteOrder.java
sources/java/beans/PropertyChangeEvent.java
sources/SimpleHttpServer.java

最埌に、自分で䜜成した゜ヌスコヌドが入っおいたす。

では、起動しお

$ ./simple-http-server 
[2019-03-27T23:38:30.477] Simple Http Server startup.

gdbでアタッチ。

$ sudo gdb -p [PID]

この時、デバッグ情報が読み蟌たれたこずが衚瀺されたす。

of file /path/to/simple-http-server.debug.

では、起動しおいるアプリケヌションにアクセスしおみたす。

$ curl localhost:8080/message

スレッドの䞀芧を芋お、スレッドを遞択したす。

(gdb) info threads
  Id   Target Id         Frame 
* 1    Thread 0x7f0ff2719740 (LWP 15057) "simple-http-ser" 0x00007f0ff22fe9f3 in futex_wait_cancelable (private=<optimized out>, expected=0, futex_word=0x7f07f1f86770)
    at ../sysdeps/unix/sysv/linux/futex-internal.h:88
  2    Thread 0x7f07f1cf7700 (LWP 15058) "server-timer" 0x00007f0ff22feed9 in futex_reltimed_wait_cancelable (private=<optimized out>, reltime=0x7f07f1cf6b40, expected=0, 
    futex_word=0x7f07ec000c48) at ../sysdeps/unix/sysv/linux/futex-internal.h:142
  3    Thread 0x7f07f14f6700 (LWP 15059) "Thread-2" 0x00007f0ff1e19bb7 in epoll_wait (epfd=8, events=0x56222709f000, maxevents=8192, timeout=1000)
    at ../sysdeps/unix/sysv/linux/epoll_wait.c:30
(gdb) thread 3
[Switching to thread 3 (Thread 0x7f07f14f6700 (LWP 15059))]
#0  0x00007f0ff1e19bb7 in epoll_wait (epfd=8, events=0x56222709f000, maxevents=8192, timeout=1000) at ../sysdeps/unix/sysv/linux/epoll_wait.c:30
30  ../sysdeps/unix/sysv/linux/epoll_wait.c: そのようなファむルやディレクトリはありたせん.

するず、今床はJavaの゜ヌスコヌド行が芋える圢でステップ実行できるようになっおいたす。

(gdb) next
34  in ../sysdeps/unix/sysv/linux/epoll_wait.c
(gdb) next
com.oracle.svm.core.posix.linux.LinuxNIOSubstitutions$Util_sun_nio_ch_EPollArrayWrapper.iepoll(int, CStruct com.oracle.svm.core.posix.headers.linux.LinuxEPoll$epoll_event *, int, long) (epfd=8, events=0x56222709f000, numfds=8192, timeout=1000)
    at /path/to/sources/com/oracle/svm/core/posix/linux/LinuxNIOSubstitutions.java:386
386 /path/to/sources/com/oracle/svm/core/posix/linux/LinuxNIOSubstitutions.java: そのようなファむルやディレクトリはありたせん.
(gdb) next
390 in /path/to/sources/com/oracle/svm/core/posix/linux/LinuxNIOSubstitutions.java
(gdb) next
sun.nio.ch.EPollSelectorImpl.doSelect(java.lang.Object *, long) (AParam0=0x7f07f2002548, JParam1=1000)
    at /path/to/sources/sun/nio/ch/EPollSelectorImpl.java:95
95  /path/to/sources/sun/nio/ch/EPollSelectorImpl.java: そのようなファむルやディレクトリはありたせん.
(gdb) next
97  in /path/to/sources/sun/nio/ch/EPollSelectorImpl.java

Javaの゜ヌスコヌドは、先ほどデバッグ情報付きで生成したネむティブむメヌゞず䞀緒に出力された、゜ヌスファむルの䜍眮を
指しおいたす。

では、自分が䜜った゜ヌスコヌドにブレヌクポむントを貌っお止めおみたしょう。゜ヌスコヌドの行番号を確認。

$ cat -n SimpleHttpServer.java 
     1  import java.io.IOException;
     2  import java.net.InetSocketAddress;
     3  import java.nio.charset.StandardCharsets;
     4  import java.time.LocalDateTime;
     5  
     6  import com.sun.net.httpserver.HttpServer;
     7  
     8  public class SimpleHttpServer {
     9      public static void main(String... args) throws IOException {
    10          SimpleHttpServer simpleHttpServer = new SimpleHttpServer();
    11          simpleHttpServer.start();
    12      }
    13  
    14      public void start() throws IOException {
    15          HttpServer server = HttpServer.create(new InetSocketAddress(8080), 0);
    16          server.createContext("/", exchange -> {
    17              String requestUri = exchange.getRequestURI().getPath();
    18  
    19              byte[] body;
    20              switch (requestUri) {
    21                  case "/message":
    22                      body = "Hello Simple Http Server!!".getBytes(StandardCharsets.UTF_8);
    23                      exchange.sendResponseHeaders(200, body.length);
    24                      exchange.getResponseBody().write(body);
    25                      break;
    26                  case "/error":
    27                      new RuntimeException("Oops!!").printStackTrace();
    28  
    29                      body = "Server Error".getBytes(StandardCharsets.UTF_8);
    30                      exchange.sendResponseHeaders(500, body.length);
    31                      exchange.getResponseBody().write(body);
    32                      break;
    33                  default:
    34                      body = "Not Found".getBytes(StandardCharsets.UTF_8);
    35                      exchange.sendResponseHeaders(404, body.length);
    36                      exchange.getResponseBody().write(body);
    37                      break;
    38              }
    39          });
    40  
    41          System.out.printf("[%s] Simple Http Server startup.%n", LocalDateTime.now());
    42  
    43          server.start();
    44      }
    45  }

17行目で止めおみたす。

    17                String requestUri = exchange.getRequestURI().getPath();

ブレヌクポむントを蚭定。

(gdb) break /path/to/sources/SimpleHttpServer.java:17
Breakpoint 1 at 0x5622261f6050: file /path/to/sources/SimpleHttpServer.java, line 17.

Continue。

(gdb) cont
Continuing.

Thread 3 "Thread-2" hit Breakpoint 1, SimpleHttpServer.lambda$start$0(java.lang.Object *) (AParam0=0x7f07f2209780)
    at /path/to/sources/SimpleHttpServer.java:17
17              String requestUri = exchange.getRequestURI().getPath();

指定の堎所で止たりたした

スタックトレヌスを芋おみたす。

(gdb) bt
#0  SimpleHttpServer.lambda$start$0(java.lang.Object *) (AParam0=0x7f07f2209780) at /path/to/sources/SimpleHttpServer.java:17
#1  0x00005622261f601c in SimpleHttpServer$$Lambda$78003464eaa809674162b25faad5d587da440455.handle(java.lang.Object *, java.lang.Object *) (AParam0=<optimized out>, 
    AParam1=<optimized out>) at /path/to/sources/SimpleHttpServer.java:16
#2  0x000056222626d8b9 in com.sun.net.httpserver.Filter$Chain.doFilter(java.lang.Object *, java.lang.Object *) (AParam0=0x7f07f2209770, AParam1=0x7f07f2209780)
    at /path/to/sources/com/sun/net/httpserver/Filter.java:79
#3  0x000056222626d87e in com.sun.net.httpserver.Filter$Chain.doFilter(java.lang.Object *, java.lang.Object *) (AParam0=0x7f07f2209770, AParam1=0x7f07f2209780)
    at /path/to/sources/sun/net/httpserver/AuthFilter.java:83
#4  0x00005622264853e5 in sun.net.httpserver.ServerImpl$Exchange$LinkHandler.handle(java.lang.Object *, java.lang.Object *) (AParam0=<optimized out>, AParam1=<optimized out>)
    at /path/to/sources/sun/net/httpserver/ServerImpl.java:675
#5  0x000056222626d8b9 in com.sun.net.httpserver.Filter$Chain.doFilter(java.lang.Object *, java.lang.Object *) (AParam0=0x7f07f2209750, AParam1=0x7f07f2209780)
    at /path/to/sources/com/sun/net/httpserver/Filter.java:79
#6  0x0000562226487298 in sun.net.httpserver.ServerImpl$Exchange.run(java.lang.Object *) (AParam0=0x7f07f22019f8)
    at /path/to/sources/sun/net/httpserver/ServerImpl.java:647
#7  0x0000562226482db6 in sun.net.httpserver.ServerImpl$Dispatcher.handle(java.lang.Object *, java.lang.Object *, java.lang.Object *) (AParam0=0x7f07f2012b20, 
    AParam1=<optimized out>, AParam2=0x7f07f22017c8) at /path/to/sources/sun/net/httpserver/ServerImpl.java:158
#8  0x0000562226483f9f in sun.net.httpserver.ServerImpl$Dispatcher.run(java.lang.Object *) (AParam0=0x7f07f2012b20)
    at /path/to/sources/sun/net/httpserver/ServerImpl.java:396
#9  0x00005622262b39d0 in java.lang.Thread.run(java.lang.Object *) (AParam0=<optimized out>)
    at /path/to/sources/java/lang/Thread.java:748
#10 0x0000562226249b20 in com.oracle.svm.core.thread.JavaThreads.threadStartRoutine(org.graalvm.nativeimage.ObjectHandle) (threadHandle=2)
    at /path/to/sources/com/oracle/svm/core/thread/JavaThreads.java:473
#11 0x00005622262442f2 in com.oracle.svm.core.posix.thread.PosixJavaThreads.pthreadStartRoutine(RawStructure com.oracle.svm.core.thread.JavaThreads$ThreadStartData *) (
---Type <return> to continue, or q <return> to quit---
    data=<optimized out>) at /path/to/sources/com/oracle/svm/core/posix/thread/PosixJavaThreads.java:193
#12 0x0000562226209900 in com.oracle.svm.core.code.IsolateEnterStub.PosixJavaThreads_pthreadStartRoutine_e1f4a8c0039f8337338252cd8734f63a79b5e3df(long) (JParam0=94704683941104)
    at /path/to/sources/com/oracle/svm/core/code/IsolateEnterStub.java:1
#13 0x00007f0ff22f86db in start_thread (arg=0x7f07f14f6700) at pthread_create.c:463
#14 0x00007f0ff1e1988f in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:95

スタックトレヌスも芋れたしたね。

ステップオヌバヌ実行しおみたす。

(gdb) next
20              switch (requestUri) {
(gdb) next
22                      body = "Hello Simple Http Server!!".getBytes(StandardCharsets.UTF_8);

ロヌカル倉数の倀は、芋れない 

(gdb) print requestUri
No symbol "requestUri" in current context.
(gdb) info locals
ALocal0 = <optimized out>
ALocal1 = 0xc
ALocal2 = 0x56222647db70 <sun.net.httpserver.HttpExchangeImpl.sendResponseHeaders(java.lang.Object *, int, long)>
ILocal3 = <optimized out>
ALocal4 = <optimized out>
IStack0 = <optimized out>

どちらにしろ、gdbに関するスキルが足りたせんね 。

今回は、こんなずころで。たた詳しくなったら、再チャレンゞしたいですね。