2022-04-24

[php-src読書録]その16: gdbserver

php-srcのコードリーディングした内容をコツコツ残すテスト。その16

今日からopcache

の前にmacOSでsoのgdbデバッグがうまくいかなかったので備忘録。

発生していた事象

–enable-debugでビルドしても opcache.so がうまく解釈されずこんな感じに ?? となってしまってブレークポイントも効かない状態に。

(gdb) b compile_file
Breakpoint 2 at 0x100458c32: file Zend/zend_language_scanner.l, line 642.
(gdb) r
Starting program: sapi/cli/php -dopcache.enable_cli=1 -dopcache.opt_debug_level=0x10000 -dopcache.jit=disable -dzend_extension=/path/to/modules/opcache.so 1.php
[New Thread 0x260b of process 93229]
[New Thread 0x2303 of process 93229]
warning: unhandled dyld version (17)

Thread 2 hit Breakpoint 2, compile_file (file_handle=0x7ff7bfefe228, type=8) at Zend/zend_language_scanner.l:642
642             zend_op_array *op_array = NULL;
(gdb) bt
#0  compile_file (file_handle=0x7ff7bfefe228, type=8) at Zend/zend_language_scanner.l:642
#1  0x0000000100217e69 in phar_compile_file (file_handle=0x7ff7bfefe228, type=8) at ext/phar/phar.c:3351
#2  0x0000000102da18e4 in ?? ()
#3  0x0000000264e8ae9a in ?? ()
#4  0x0100000102a5b5a0 in ?? ()
#5  0x0000000100ecd508 in ?? ()
#6  0x0000000000000000 in ?? ()

本当はこうなってほしい

(gdb) bt
#0  compile_file (file_handle=0x7fffffffd2f0, type=8) at Zend/zend_language_scanner.l:641
#1  0x00005555557f8f04 in phar_compile_file (file_handle=0x7fffffffd2f0, type=8) at /root/php-src-php-8.1.4/ext/phar/phar.c:3351
#2  0x00007ffff56935c5 in opcache_compile_file (file_handle=0x7fffffffd2f0, type=8, op_array_p=0x7fffffffaca8) at /root/php-src-php-8.1.4/ext/opcache/ZendAccelerator.c:1789
#3  0x00007ffff5694a2a in persistent_compile_file (file_handle=0x7fffffffd2f0, type=8) at /root/php-src-php-8.1.4/ext/opcache/ZendAccelerator.c:2160
#4  0x0000555555a4c0bc in zend_execute_scripts (type=8, retval=0x0, file_count=3) at /root/php-src-php-8.1.4/Zend/zend.c:1754
#5  0x00005555559a90c4 in php_execute_script (primary_file=0x7fffffffd2f0) at /root/php-src-php-8.1.4/main/main.c:2538
#6  0x0000555555bc1f6d in do_cli (argc=5, argv=0x5555568366c0) at /root/php-src-php-8.1.4/sapi/cli/php_cli.c:965
#7  0x0000555555bc3075 in main (argc=5, argv=0x5555568366c0) at /root/php-src-php-8.1.4/sapi/cli/php_cli.c:1367

set solib-search-path /path/to/modules/ など色々試みましたがうまくいかず…orz

gdbserver + Docker(Ubuntu)を使ったデバッグ

Docker上のUbuntuでPHPを同じようにビルド・デバッグしたところ問題なく動いたのでUbuntu上でgdbでデバッグしました。

こんな感じでDockerfileを用意

FROM ubuntu

ENV TZ=Asia/Tokyo
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
ENV PHP_VERSION 8.1.4

RUN apt update
RUN apt install -y wget unzip gdb autoconf gcc bison re2c pkg-config libxml2-dev libsqlite3-dev make vim

WORKDIR /root
RUN wget https://github.com/php/php-src/archive/refs/tags/php-${PHP_VERSION}.zip && unzip php-${PHP_VERSION}.zip

WORKDIR /root/php-src-php-${PHP_VERSION}

RUN ./buildconf --force
RUN ./configure --enable-debug
RUN make -j4

CMD /bin/bash

でビルドして立ち上げ

$ docker build . -t php-gdb
$ docker run -it \
    -v $(pwd)/tmp:/tmp \
    -p 8001:8001 \
    --cap-add=SYS_PTRACE --security-opt="seccomp=unconfined" \
    php-gdb

> cp /usr/local/bin/php /tmp/php # Docker上のPHPのバイナリを使うのでvolumesで共有する用にコピー

このDocker上のgdbのTUIモードでデバッグしても良かったが、どうせならVSCodeでデバッグしてみたかったので以下のようにしてgdbserverを使ってみました。

Docker上で以下のコマンドでgdbserverを立ち上げ

gdbserver localhost:8001 \
  /usr/local/bin/php \
  -dopcache.enable_cli=1 \
  -dopcache.opt_debug_level=0x10000 \
  -dzend_extension=$(pwd)/modules/opcache.so 1.php

ホストのVSCodeのlaunch.json

{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "(gdb) Launch",
      "type": "cppdbg",
      "request": "launch",
      "program": "${workspaceFolder}/tmp/php",
      "stopAtEntry": true,
      "cwd": "${workspaceFolder}",
      "environment": [],
      "externalConsole": true,
      "MIMode": "gdb",
      "miDebuggerPath": "/usr/local/bin/gdb",
      "miDebuggerServerAddress": "localhost:8001",
      "setupCommands": [
        {
          "description": "Enable pretty-printing for gdb",
          "text": "-enable-pretty-printing",
          "ignoreFailures": true
        },
        {
          "description": "Relace absolute path of source code",
          "ignoreFailures": false,
          "text": "set substitute-path /root/php-src-php-8.1.4 ${workspaceFolder}"
        }
      ]
    }
  ]
}

programでホストOS用のビルドしたPHPだとダメでゲストOS=Docker用のビルドしたPHPを指定する必要があり、 ${workspaceFolder}/tmp/php に設置している。

set substitute-path /root/php-src-php-8.1.4 ${workspaceFolder} によってリモートのパスとローカルのパスのマッピングを行っている。

あとはこの状態でVSCodeでデバッグすればOK。

VSCodeを使わない場合はこんな感じでデバッグできる。

$ gdb

> target remote localhost:8001
> set substitute-path /root/php-src-php-8.1.4 ${workspaceFolder}
> b main
> continue
このエントリーをはてなブックマークに追加