2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

LinuxのKernel Crash Dumpの読み方・解析方法

Posted at

1. crashの準備

まず、カーネルのシンボル情報の表示などに必要な、dbgsymパッケージ(debuginfo)をインストールする。
dbgsymパッケージはddebs.ubuntu.comで公開されているため、ddebs.ubuntu.comをリポジトリに追加する。

U=http://ddebs.ubuntu.com
D=$(lsb_release -cs)
cat <<EOF | sudo tee /etc/apt/sources.list.d/ddebs.list
deb ${U} ${D} main restricted universe multiverse
#deb ${U} ${D}-security main restricted universe multiverse
deb ${U} ${D}-updates main restricted universe multiverse
deb ${U} ${D}-proposed main restricted universe multiverse
EOF

GPGキーをインポート。

wget -O - http://ddebs.ubuntu.com/dbgsym-release-key.asc | sudo apt-key add -

アップデート&インストール。

sudo apt update -y
sudo apt install linux-image-$(uname -r)-dbgsym -y

kexec-toolsを入れる

sudo apt install kexec-tools -y

Configuring kexec-toolsにはNoと答える

kdump-toolsを入れる

sudo apt install kdump-tools -y

Configuring kdump-toolsはYesと答える

linux-crashdumpを入れる

sudo apt install linux-crashdump -y

次にサービスを確認する。

sudo systemctl status kexec
sudo systemctl status kexec-load
sudo systemctl status kdump-tools
sudo systemctl status kdump-tools-dump

一番下だけstoppedでも正常に動作した。

設定を編集するには次の2つ手法がある

dpkg-reconfigure kdump-tools
sudo vim /etc/default/kdump-tools

設定を編集した後は次のコマンドで更新する必要がある

sudo systemctl stop kdump-tools
kdump-config load
sudo systemctl start kdump-tools

また、/etc/default/grub.d/kdump-tools.cfgを以下のように編集する。

sudo vim /etc/default/grub.d/kdump-tools.cfg
- GRUB_CMDLINE_LINUX_DEFAULT="$GRUB_CMDLINE_LINUX_DEFAULT crashkernel=512M-:192M"
+ GRUB_CMDLINE_LINUX_DEFAULT="$GRUB_CMDLINE_LINUX_DEFAULT crashkernel=384M-2G:64M,2G-:128M"

/etc/default/grub.d/kdump-tools.cfgが存在しない場合は/etc/default/grubを代わりに編集する。

うまくkdumpが動作しない場合は、予約メモリの容量を増やしてみると良い(これで解決するパターンは結構ある)。

GRUB_CMDLINE_LINUX_DEFAULT="$GRUB_CMDLINE_LINUX_DEFAULT crashkernel=384M-:512M"

更新する。

sudo update-grub
sudo reboot

現在の設定を見てみる。

sudo kdump-config show

出力:

DUMP_MODE:        kdump
USE_KDUMP:        1
KDUMP_SYSCTL:     kernel.panic_on_oops=1
KDUMP_COREDIR:    /var/crash
crashkernel addr: 0x25000000
   /var/lib/kdump/vmlinuz: symbolic link to /boot/vmlinuz-4.15.0-50-generic
kdump initrd:
   /var/lib/kdump/initrd.img: symbolic link to /var/lib/kdump/initrd.img-4.15.0-50-generic
current state:    ready to kdump

kexec command:
  /sbin/kexec -p --command-line="BOOT_IMAGE=/boot/vmlinuz-4.15.0-50-generic root=UUID=a49267ec-7042-11e8-9198-005056ba3e55 ro maybe-ubiquity pti=off maybe-ubiquity nr_cpus=1 systemd.unit=kdump-tools-dump.service irqpoll nousb ata_piix.prefer_ms_hyperv=0" --initrd=/var/lib/kdump/initrd.img /var/lib/kdump/vmlinuz

kdump-configコマンドにはsudoを付けないと、crashkernel addrが正常に表示されないので注意。
sudoで実行しても正しくcrashkernel addrが表示されない場合は何か問題がある。

テストをする。

sysrqが有効になっているか確認。

sudo cat /proc/sys/kernel/sysrq

これが0になっている場合は、無効になっている。
これが1よりも大きな数値になっている場合は、よくわからない感じになっている。

sysrqの有効化。

sudo sysctl -w kernel.sysrq=1

/etc/sysctl.d/99-sysctl.confkernel.sysrq = 1 を書き込んでsysrqの有効化を永続化する。

sudo vim /etc/sysctl.d/99-sysctl.conf

カーネルパニックを発生させる。

sudo su
echo c > /proc/sysrq-trigger

成功すれば、カーネルパニックになった瞬間に再起動が始まり、/var/crash/{timestamp}/以下にダンプファイルが作成される。
ディレクトリ名を変更しておかないといつか消えてしまうので注意。

crash起動

cd /var/crash/20190630901/
sudo crash /usr/lib/debug/boot/vmlinux-$(uname -r) dump.201906030901

2. crash

モジュール関連 (mod, grep)

モジュール一覧取得

crash> mod
...
ffffffffc0625140  coretemp                    16384  (not loaded)  [CONFIG_KALLSYMS]
ffffffffc06a5580  xxx_mod                   110592  (not loaded)  [CONFIG_KALLSYMS]
...

grep

crash> mod | grep xxx
ffffffffc06a5580  xxx_mod                   110592  (not loaded)  [CONFIG_KALLSYMS]

not loadedになっていることがわかる。
モジュールのロードをするためには、まずはmodules.depを生成しなければならない。
depmodについてはこちら

sudo mkdir -p /lib/modules/$(uname -r)/kernel/drivers/block/xxx
sudo cp -a xxx-mod.ko /lib/modules/$(uname -r)/kernel/drivers/block/xxx/
sudo depmod $(uname -r)

モジュールのロード

crash> mod -s xxx_mod
mod: xxx_mod: last symbol: pmd_to_page is not _MODULE_END_xxx_mod?
     MODULE       NAME                         SIZE  OBJECT FILE
ffffffffc06a5580  xxx_mod                   110592  /lib/modules/4.15.0-50-generic/kernel/drivers/block/xxx/xxx-mod.ko
crash> mod | grep xxx
ffffffffc06a5580  xxx_mod                   110592  /lib/modules/4.15.0-50-generic/kernel/drivers/block/xxx/xxx-mod.ko

モジュールのアンロード

crash> mod -d xxx_mod
crash> mod | grep xxx
ffffffffc06a5580  xxx_mod                   110592  (not loaded)  [CONFIG_KALLSYMS]

一括ロード

crash> mod -S

ネットワーク関連 (net)

net

crash> net
   NET_DEVICE     NAME   IP ADDRESS(ES)
ffff8f24acd08000  lo     127.0.0.1
ffff8f249d3e4000  ens160 10.224.146.206

arpキャッシュ

crash> net -a
NEIGHBOUR        IP ADDRESS      HW TYPE    HW ADDRESS         DEVICE  STATE
ffff8f249fe4fe00 0.0.0.0         UNKNOWN    00 00 00 00 00 00  lo      NOARP
ffff8f24a800c000 10.224.144.1    ETHER      10:05:ca:a3:e3:4a  ens160  STALE
ffff8f249d26f400 10.224.146.1    ETHER      00:50:56:a2:23:38  ens160  STALE

プロセスがopenしているソケットの情報を表示

crash> net -s <PID>
PID: 4364   TASK: ffff880031f62f10  CPU: 0   COMMAND: "nc"
FD      SOCKET            SOCK       FAMILY:TYPE SOURCE-PORT DESTINATION-PORT
 3 ffff8800390e5000 ffff880035ee8000 INET6:STREAM
 4 ffff8800390e3c00 ffff88003b3d3640 INET:STREAM  0.0.0.0-11111 0.0.0.0-0

数値をIPアドレスに変換

crash> net -N 1041236234
10.1.16.62

特定の条件で絞り込み(説明略)

crash> foreach net -s -R 3

逆アセンブル (dis)

逆アセンブル

crash> dis do_fork
0xffffffff81084950 <do_fork>:   data32 data32 data32 xchg %ax,%ax [FTRACE NOP]
0xffffffff81084955 <do_fork+5>: push   %rbp
0xffffffff81084956 <do_fork+6>: mov    %rsp,%rbp
0xffffffff81084959 <do_fork+9>: push   %r15
0xffffffff8108495b <do_fork+11>:        push   %r14
0xffffffff8108495d <do_fork+13>:        xor    %r14d,%r14d
...

逆アセンブルとソースコードの行番号を照らし合わせる

crash> dis -l do_fork
/usr/src/debug/kernel-3.10.0-514.el7/linux-3.10.0-514.el7.x86_64/kernel/fork.c: 1690
0xffffffff81084950 <do_fork>:   data32 data32 data32 xchg %ax,%ax [FTRACE NOP]
0xffffffff81084955 <do_fork+5>: push   %rbp
0xffffffff81084956 <do_fork+6>: mov    %rsp,%rbp
0xffffffff81084959 <do_fork+9>: push   %r15
0xffffffff8108495b <do_fork+11>:        push   %r14
/usr/src/debug/kernel-3.10.0-514.el7/linux-3.10.0-514.el7.x86_64/kernel/fork.c: 1692
0xffffffff8108495d <do_fork+13>:        xor    %r14d,%r14d
...

ソースコード表示

crash> dis -s do_fork
FILE: kernel/fork.c
LINE: 1690

  1685  long do_fork(unsigned long clone_flags,
  1686                unsigned long stack_start,
  1687                unsigned long stack_size,
  1688                int __user *parent_tidptr,
  1689                int __user *child_tidptr)
* 1690  {
  1691          struct task_struct *p;
  1692          int trace = 0;
  1693          long nr;
...

関数呼び出しの箇所を探す

crash> dis -l neigh_probe
/usr/src/debug/kernel-3.10.0-514.el7/linux-3.10.0-514.el7.x86_64/net/core/neighbour.c: 869
0xffffffff8157b1e0 <neigh_probe>:       data32 data32 data32 xchg %ax,%ax [FTRACE NOP]
0xffffffff8157b1e5 <neigh_probe+5>:     push   %rbp
...
0xffffffff8157b222 <neigh_probe+66>:    callq  *0x8(%rax)  <==★
crash> dis -s 0xffffffff8157b222
FILE: net/core/neighbour.c
LINE: 875

  870           struct sk_buff *skb = skb_peek(&neigh->arp_queue);
  871           /* keep skb alive even if arp_queue overflows */
  872           if (skb)
  873                   skb = skb_copy(skb, GFP_ATOMIC);
  874           write_unlock(&neigh->lock);
* 875         ★neigh->ops->solicit(neigh, skb);
  876           atomic_inc(&neigh->probes);
  877           kfree_skb(skb);
  878   }

データ調査 (whatis, rb)

変数の調査

crash> whatis vmap_area_root
struct rb_root vmap_area_root;

構造体の調査

crash> whatis rb_root
struct rb_root {
    struct rb_node *rb_node;
}
SIZE: 8

変数のアドレス表示

crash> rd vmap_area_root
ffffffff8b4637d0:  ffff8f24a6d626d8                    .&..$...

構造体のオフセット調査

crash> whatis -o vmap_area
struct vmap_area {
    [0] unsigned long va_start;
    [8] unsigned long va_end;
   [16] unsigned long flags;
   [24] struct rb_node rb_node;
   [48] struct list_head list;
crash> whatis -o vmap_area | grep rb_node
  [24] struct rb_node rb_node;

データ調査 (tree)

vmap_area 構造体を管理する rbtree (red black tree) の root ノードを指す変数である vmap_area_root の調査 (各ノードは vmap_area.rb_node である)

crash> tree -t rbtree -o vmap_area.rb_node vmap_area_root -p
ffff8f24a6d626c0
  position: root
ffff8f24a10156c0
  position: root/l
ffff8f249d2925a0
  position: root/l/l
ffff8f249dd021e0
  position: root/l/l/l
...
ffff8f24af0579c0
  position: root/l/l/l/l/l/l/l/l/r
ffff8f24af057c60
  position: root/l/l/l/l/l/l/l/r
ffff8f24ad17e6c0
  position: root/l/l/l/l/l/l/l/r/l
ffff8f24af0578a0
  position: root/l/l/l/l/l/l/l/r/l/l
...

vmap_area構造体の特定のメンバを調査

crash> tree -t rbtree -o vmap_area.rb_node vmap_area_root -s vmap_area.va_start,va_end -x
ffff8f24a6d626c0
  va_start = 0xffffae4884188000
  va_end = 0xffffae488418d000
ffff8f24a10156c0
  va_start = 0xffffae48837b8000
  va_end = 0xffffae48837bd000
ffff8f249d2925a0
  va_start = 0xffffae4882034000
  va_end = 0xffffae4882039000

...

データ調査 (list)

file_system_type という構造体 (next メンバが fd) が繋がっている一方向リンクリストを調査 (先頭アドレスは file_systems というポインタ変数に格納されている)

crash> rd file_systems
ffffffff8b4c0688:  ffffffff8af3c040                    @.......
crash> list file_system_type.next -s file_system_type.name,fs_flags ffffffff8af3c040                    
ffffffff8af3c040
  name = 0xffffffff8ab2cefe "sysfs"
  fs_flags = 8
ffffffff8ae12440
  name = 0xffffffff8aaf7a37 "rootfs"
  fs_flags = 0
ffffffff8af47fc0
  name = 0xffffffff8aab830f "ramfs"
  fs_flags = 8
...

双方向リンクリストを調査する場合は fd の部分を fd, bk をもつ構造体の名前に変更し、先頭アドレスを -h オプションの引数に指定するだけ。

crash> list task_struct.tasks -s task_struct.comm -h ffffffff8ae12480
ffffffff8ae12480
  comm = "swapper/0\000\000\000\000\000\000"
ffff8f24ad685b00
  comm = "systemd\000\000\000\000\000\000\000\000"
ffff8f24ad680000
...

プロセスコンテキストの調査 (set, ps, bt)

引数無しで実行すると現在のプロセスコンテキストを調査することができる

crash> set
    PID: 0
COMMAND: "swapper/0"
   TASK: ffffffff8ae12480  (1 of 8)  [THREAD_INFO: ffffffff8ae12480]
    CPU: 0
  STATE: TASK_RUNNING (PANIC)

プロセス一覧を表示
ここで各プロセスのPIDを確認する

   PID    PPID  CPU       TASK        ST  %MEM     VSZ    RSS  COMM
>     0      0   0  ffffffff8ae12480  RU   0.0       0      0  [swapper/0]
>     0      0   1  ffff8f24ad6e2d80  RU   0.0       0      0  [swapper/1]
>     0      0   2  ffff8f24ad6e4440  RU   0.0       0      0  [swapper/2]
>     0      0   3  ffff8f24ad6e5b00  RU   0.0       0      0  [swapper/3]
>     0      0   4  ffff8f24ad6e0000  RU   0.0       0      0  [swapper/4]
>     0      0   5  ffff8f24ad6e96c0  RU   0.0       0      0  [swapper/5]
>     0      0   6  ffff8f24ad6ead80  RU   0.0       0      0  [swapper/6]
>     0      0   7  ffff8f24ad6ec440  RU   0.0       0      0  [swapper/7]
      1      0   0  ffff8f24ad685b00  IN   0.1  225876   9644  systemd
      2      0   4  ffff8f24ad680000  IN   0.0       0      0  [kthreadd]
      3      2   0  ffff8f24ad6816c0  UN   0.0       0      0  [kworker/0:0]
      4      2   0  ffff8f24ad682d80  UN   0.0       0      0  [kworker/0:0H]
      6      2   0  ffff8f24ad6b8000  UN   0.0       0      0  [mm_percpu_wq]
      7      2   0  ffff8f24ad6b96c0  IN   0.0       0      0  [ksoftirqd/0]
      8      2   6  ffff8f24ad6bad80  UN   0.0       0      0  [rcu_sched]
      9      2   0  ffff8f24ad6bc440  UN   0.0       0      0  [rcu_bh]
...

第1引数にPIDを指定することでプロセスコンテキストを変更することができる

crash> set 1
    PID: 1
COMMAND: "systemd"
   TASK: ffff88003bec8000  [THREAD_INFO: ffff88003bed0000]
    CPU: 0
  STATE: TASK_INTERRUPTIBLE

バックトレースを表示

crash> bt
PID: 0      TASK: ffffffff8ae12480  CPU: 0   COMMAND: "swapper/0"
 #0 [ffff8f24bfc03c40] machine_kexec at ffffffff89a63ee3
 #1 [ffff8f24bfc03ca0] __crash_kexec at ffffffff89b2bf59
 #2 [ffff8f24bfc03d68] crash_kexec at ffffffff89b2ccc1
 #3 [ffff8f24bfc03d88] oops_end at ffffffff89a30df8
 #4 [ffff8f24bfc03db0] die at ffffffff89a314c2
 #5 [ffff8f24bfc03de0] do_general_protection at ffffffff89a2da7d
 #6 [ffff8f24bfc03e10] general_protection at ffffffff8a401575
    [exception RIP: run_timer_softirq+864]
    RIP: ffffffff89b0b010  RSP: ffff8f24bfc03ec8  RFLAGS: 00010086
    RAX: dead000000000200  RBX: ffff8f24bfc1a700  RCX: ffff8f24bfc03ed8
    RDX: 0000000000000100  RSI: ffff8f24bfc1a728  RDI: 0000000000000008
    RBP: ffff8f24bfc03f50   R8: ffff8f24bfc03ee0   R9: ffff8f24bfc1af30
    R10: ffff8f24bfc03ee0  R11: ffff8f24bfc1a770  R12: ffff8f24bfc03ed8
    R13: dead000000000200  R14: 0000000000000001  R15: ffff8f24a2dd1dd0
    ORIG_RAX: ffffffffffffffff  CS: 0010  SS: 0018
 #7 [ffff8f24bfc03ec0] run_timer_softirq at ffffffff89b0af18
 #8 [ffff8f24bfc03f58] __softirqentry_text_start at ffffffff8a6000e4
 #9 [ffff8f24bfc03fc0] irq_exit at ffffffff89a947f8
#10 [ffff8f24bfc03fd0] smp_apic_timer_interrupt at ffffffff8a402dd9
#11 [ffff8f24bfc03ff0] apic_timer_interrupt at ffffffff8a401ff4
--- <IRQ stack> ---
#12 [ffffffff8ae03d50] apic_timer_interrupt at ffffffff8a401ff4
    [exception RIP: unknown or invalid address]
    RIP: 0000000000000000  RSP: 0000000000000000  RFLAGS: 00000000
    RAX: 0000000000000000  RBX: 0000000000000000  RCX: 0000000000000082
    RDX: ffffffff8ae03dc8  RSI: 0000000000000000  RDI: 0000000000000002
    RBP: ffffffff8a4001a0   R8: ffffffff8ae03e28   R9: 0000000000000000
    R10: 0000000000000000  R11: 0000000000000000  R12: ffffffff8a400194
    R13: ffffffff8a4001a0  R14: ffffffff8a400194  R15: ffffffff8a4001a0
    ORIG_RAX: ffffffff8a3a2110  CS: 0000  SS: ffffffffffffff11
bt: WARNING: possibly bogus exception frame
#13 [ffffffff8ae03df8] native_safe_halt at ffffffff8a3a2472
#14 [ffffffff8ae03e30] default_idle at ffffffff8a3a2130
#15 [ffffffff8ae03e58] arch_cpu_idle at ffffffff89a39025
#16 [ffffffff8ae03e68] default_idle_call at ffffffff8a3a2603
#17 [ffffffff8ae03e78] do_idle at ffffffff89ad4b12
#18 [ffffffff8ae03ea8] cpu_startup_entry at ffffffff89ad4d73
#19 [ffffffff8ae03ed0] rest_init at ffffffff8a394d3e
#20 [ffffffff8ae03ee0] start_kernel at ffffffff8b0a50d6
#21 [ffffffff8ae03f28] x86_64_start_reservations at ffffffff8b0a44d2
#22 [ffffffff8ae03f38] x86_64_start_kernel at ffffffff8b0a4548
#23 [ffffffff8ae03f50] secondary_startup_64 at ffffffff89a000d5

task_struct構造体の調査 (task)

現在のプロセスコンテキストの task_struct を表示

crash> task
PID: 0      TASK: ffffffff8ae12480  CPU: 0   COMMAND: "swapper/0"
struct task_struct {
  thread_info = {
    flags = 2147483648,
    status = 0
  },
...

現在のプロセスコンテキストの task_struct.state と task_struct.stack を表示

crash> task -R state,stack
PID: 0      TASK: ffffffff8ae12480  CPU: 0   COMMAND: "swapper/0"
  state = 0,
  stack = 0xffffffff8ae00000,

現在のプロセスコンテキストの task_struct.se.on_rq を表示

crash> task -R se.on_rq
PID: 0      TASK: ffffffff8ae12480  CPU: 0   COMMAND: "swapper/0"
  se.on_rq = 0,

ファイル関連 (files, fuser)

プロセスごとに関係のある /var/log 以下のファイル一覧を表示

crash> foreach files -R /var/log
PID: 551    TASK: ffff8f249cd8db00  CPU: 4   COMMAND: "vmtoolsd"
ROOT: /    CWD: /
 FD       FILE            DENTRY           INODE       TYPE PATH
  3 ffff8f24a07ada00 ffff8f249e2ee300 ffff8f24ac71d5e8 REG  /var/log/vmware-vmsvc.log

PID: 552    TASK: ffff8f249cd116c0  CPU: 5   COMMAND: "systemd-journal"
ROOT: /    CWD: /
 FD       FILE            DENTRY           INODE       TYPE PATH
 20 ffff8f249f673700 ffff8f24a096cc00 ffff8f24a20933e8 REG  /var/log/journal/eeb4c85c6e7542f7995c6e87999275e4/system@4398f41d8f674d07839cef96fcc6637c-0000000000000001-00058a671e796c61.journal
 32 ffff8f24a0534800 ffff8f24aadc56c0 ffff8f24aa9f0968 REG  /var/log/journal/eeb4c85c6e7542f7995c6e87999275e4/system.journal
 36 ffff8f24a140cd00 ffff8f24aa568900 ffff8f24a08177e8 REG  /var/log/journal/eeb4c85c6e7542f7995c6e87999275e4/user-1000.journal

プロセスごとに関係のある TCP ソケットファイルの一覧表示
TCP の他にも UDP, NETLINK, UNIX などを指定できる。

crash> foreach files -R TCP
PID: 1169   TASK: ffff8f249cd8c440  CPU: 5   COMMAND: "systemd-resolve"
ROOT: /    CWD: /
 FD       FILE            DENTRY           INODE       TYPE PATH
 13 ffff8f24a82cd600 ffff8f24abbead80 ffff8f24abbccb30 SOCK TCP

PID: 1697   TASK: ffff8f249e9fad80  CPU: 0   COMMAND: "sshd"
ROOT: /    CWD: /
 FD       FILE            DENTRY           INODE       TYPE PATH
  3 ffff8f24a1f26400 ffff8f24aac663c0 ffff8f24aae09bb0 SOCK TCP
  4 ffff8f24a1f26b00 ffff8f24aac66180 ffff8f24aae0a130 SOCK TCPv6
...

指定したファイルと関係のあるプロセスを表示
ファイル名の代わりにinode番号を指定することもできる。

crash> fuser /var/log/vmware-vmsvc.log
 PID         TASK        COMM             USAGE
  551  ffff8f249cd8db00  "vmtoolsd"       fd

シンボル関連 (sym)

シンボル一覧を表示

crash> sym -l
0 (D) __per_cpu_start
0 (D) irq_stack_union
4000 (D) cpu_debug_store
5000 (D) cpu_tss_rw
8000 (D) gdt_page
9000 (d) exception_stacks
...

モジュール指定

crash> sym -m xxx_mod
ffffffffc0690000 MODULE START: xxx_mod
ffffffffc0690000 (t) xxx_open
ffffffffc0690030 (t) xxx_release
ffffffffc0690050 (t) xxxlog_open
...

文字列指定

crash> sym -q super
ffffffff89a3bfc2 (t) xfeature_is_supervisor
ffffffff89bacbe0 (t) bpf_fill_super
ffffffff89bf08b0 (t) shmem_put_super
ffffffff89bf1800 (T) shmem_fill_super
ffffffff89c7b090 (t) ns_test_super

前後表示 (説明略)

crash> sym -p ip_forward
crash> sym -n ip_forward

その他 (swap, repeat)

swapデバイスの情報を表示

crash> swap
SWAP_INFO_STRUCT    TYPE       SIZE       USED     PCT  PRI  FILENAME
ffff8f24a74f0000    FILE     4194300k      0k       0%   -2  /swap.img

説明略

crash> set scroll off
crash> ps chronyd
crash> set 505
crash> repeat -2 files

3. 参考URL

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?