Upgrade to Pro — share decks privately, control downloads, hide ads and more …

本番環境でPHPコードに触れずに「使われていないコード」を調べるにはどうしたらよいか?

 本番環境でPHPコードに触れずに「使われていないコード」を調べるにはどうしたらよいか?

長年運用されたPHPアプリケーションには、使われなくなったコード(デッドコード)が蓄積していきます。しかし実際に本番環境で使われているのか調べるのは困難です。

本セッションでは、PHPがもともと備えているトレースポイント(DTrace)の仕組みとeBPFを活用して本番環境で実際にコンパイルされたPHPファイルを記録し、デッドコードを検出する方法を自作のツールphp-dcrの実装を通して紹介します。

Avatar for Sohei Iwahori

Sohei Iwahori

April 10, 2026

More Decks by Sohei Iwahori

Other Decks in Technology

Transcript

  1. who? » Sohei Iwahori (@egmc) » גࣜձࣾάϦʔ γχΞϦʔυΤϯδχΞ » Πϯϑϥͱ؂ࢹγεςϜ

    » SRE NEXT ίΞελοϑʢ2024ɺ2025ʣ » eBPF Japan MeetupӡӦϝϯόʔ » PHPͷΧϯϑΝϨϯεͰʮτʔΫʯ͢Δͷ ͸͸͡Ίͯ » খాٸઢͰདྷ·ͨ͠
  2. ϐηΧʢ2025ʣͷ࿩ » τʔΫ͸ͨ͜͠ͱͳ͍͕ϫʔΫγϣοϓ͸΍ͬͨ » 2025/02/28 PHP Sessionless Conference » eBPFͱपลٕज़Λར༻ͯ͠PHPΞϓϦέʔγϣϯίʔυΛมߋ͠

    ͳ͍ՄࢹԽΛ΍ͬͯΈΔ » ຊ೔ͷ࿩ʹؔ࿈ͨ͠ϫʔΫγϣοϓࢿྉ͋Γ·͢1 1 https://github.com/egmc/php-sessionless-conference-ebpf-workshop
  3. σουίʔυͷ໰୊ » όʔδϣϯΞοϓͷোน » σϓϩΠ࣌ؒ΁ͷӨڹ » ຊ౰ʹ࢖ͬͯΔͷʁΛ֬ೝ͢Δͷ͕େม » Web͚ͩ͡Όͳ͍ »

    ͨ·ʹ͔͠ಈ͔ͳ͍όονͳͲ » ࣗ৴Λ࣋ͬͯʮ࢖ͬͯͳ͍ʯͱݴ͍੾Εͳ͍ͳΒ࢒͢΄͏͕҆શ
  4. σϞࣦഊ࣌༻εϥΠυ2 $ curl -s http://localhost:8080/v1/stats | jq . { "uptime_seconds":

    39398.365603362, "total_files": 6634, "compiled_files": 469, "code_coverage_rate": 7.069641242086222 }
  5. σϞࣦഊ࣌༻εϥΠυ3 $ curl -s http://localhost:8080/v1/report | jq . | head

    -n20 { "script": { "start_time_unix": 1774486040, "start_time_rfc3339": "2026-03-26T09:47:20+09:00" }, "report": [ { "filepath": "/var/www/html2/error.php", "compiled_time_unix": 1774486057, "compiled_time_rfc3339": "2026-03-26T09:47:37+09:00" }, { "filepath": "/var/www/html2/exception.php", "compiled_time_unix": 1774486057, "compiled_time_rfc3339": "2026-03-26T09:47:37+09:00" }, { "filepath": "/var/www/html2/mem.php", "compiled_time_unix": 1774486057, "compiled_time_rfc3339": "2026-03-26T09:47:37+09:00"
  6. σϞࣦഊ࣌༻εϥΠυ4 $ curl -s http://localhost:8080/v1/report | jq '.report[] | select(.compiled_time_unix

    == -1)' | head -n10 { "filepath": "/var/www/html2/path1/a.php", "compiled_time_unix": -1, "compiled_time_rfc3339": "" } { "filepath": "/var/www/html2/path2/a.php", "compiled_time_unix": -1, "compiled_time_rfc3339": "" }
  7. php-dcrͷ֓ཁ(ಈ࡞֓ཁ) » ىಈ࣌ʹҎԼΛߦ͏ » τϨʔεϙΠϯτ(DTrace)ͷcompileΠϕϯτʹϑοΫ͢ΔeBPFϓϩάϥϜΛϩʔυ » ύεΛ୳ࡧͯͦ͠ΕͬΆ͍PHPόΠφϦΛΈ͚ͭͯΞλον͢Δ » ର৅ͱͳΔPHPϓϩηεͰDTrace͕༗ޮԽ͞ΕͯΔඞཁ͕͋Γ·͢ʢޙड़ʣ »

    ίϯύΠϧΠϕϯτ͕૸ΔͱϑοΫͨ͠eBPFϓϩάϥϜ͕ಈ࡞ͯ͠BPF_MAPΛߋ৽ʢϑϥά͕༗ޮ Ͱ͋Ε͹OTelϩά΋ੜ੒ʣ » goroutineͰఆظతʹMAP͔Β৘ใΛऔಘͯ͠ঢ়ଶߋ৽ » HTTP APIΞΫηε࣌ʹϨϙʔτΛJSONͰฦ͢
  8. php-dcrͷ֓ཁʢσʔλϑϩʔʣ php-dcr (Go) Linux Kernel PHP ϓϩηε USDT probe USDT

    probe USDT probe BPF Map εΩϟϯ HTTP Apache (mod_php) PHP-FPM PHP CLI eBPF USDT: compile__file__return → ϑΝΠϧύε + ࣌ ࠁΛه࿥ BPFϚοϓಡΈऔΓ (5ඵ͝ͱϙʔϦϯά) HTTP API :8080 /v1/report, /v1/stats ର৅σΟϨΫτϦ *.php ֎෦ΫϥΠΞϯτ
  9. isԿ » ྺ࢙తʹ͸2000೥୅ͷSolaris޲͚ʹ։ൃ͞ΕͨτϨʔεͷ࢓૊Έ͕ىݯ » STDʢStatically Defined Tracingʣͱ͍͏੩తͳτϨʔεϙΠϯτΛ࢓ࠐΉ࢓૊Έ͕͋ΓɺUserland༻ͷ΋ͷ͸ USDTͱݺͿ » Linux؀ڥʹ͓͍ͯ͸SystemTap͕ఏڙ͢Δػೳ͕࢖ΘΕ͍ͯΔ

    » ΦϦδφϧͷDTraceͱͷΠϯλʔϑΣʔεʹ͓͚Δޓ׵ੑ͕ҙࣝ͞Ε͍ͯΔ » Ϣʔβʔۭؒʹ͓͍ͯ͸SystemTap͕ఏڙ͢ΔCϚΫϩ܈ sys/sdt.h Λ࢖ͬͯ੩తͳτϨʔεϙΠϯτΛ࡞੒͢Δ » ֤ݴޠϥϯλΠϜͷ --enable-dtrace Φϓγϣϯ͸͜ΕΛߦ͏ » τϨʔεϙΠϯτͷΞυϨεɺҾ਺ͷ৘ใͳͲ͕όΠφϦͷELFηΫγϣϯʹຒΊࠐ·ΕΔ » PHPʹ͓͍ͯ͸2010೥ࠒɺ5.3͋ͨΓ͔Βଘࡏ
  10. όΠφϦΛ֬ೝͯ͠ΈΔ $ readelf -n /usr/bin/php ... Displaying notes found in:

    .note.stapsdt Owner Data size Description stapsdt 0x00000042 NT_STAPSDT (SystemTap probe descriptors) Provider: php Name: request__startup Location: 0x000000000027b1bf, Base: 0x0000000000440aa0, Semaphore: 0x00000000005487a8 Arguments: 8@%rax 8@%rdx 8@%rcx stapsdt 0x00000043 NT_STAPSDT (SystemTap probe descriptors) Provider: php Name: request__shutdown Location: 0x000000000027b7b1, Base: 0x0000000000440aa0, Semaphore: 0x00000000005487a6 Arguments: 8@%rax 8@%rdx 8@%rcx stapsdt 0x0000003f NT_STAPSDT (SystemTap probe descriptors) Provider: php Name: compile__file__entry Location: 0x00000000002d01bf, Base: 0x0000000000440aa0, Semaphore: 0x00000000005487a4 Arguments: 8@%rax 8@%rdx stapsdt 0x00000040 NT_STAPSDT (SystemTap probe descriptors) Provider: php Name: compile__file__return Location: 0x00000000002d01dc, Base: 0x0000000000440aa0, Semaphore: 0x00000000005487a2 Arguments: 8@%rdx 8@%rcx ... $ readelf -n /usr/bin/python3 ... Displaying notes found in: .note.stapsdt Owner Data size Description stapsdt 0x00000033 NT_STAPSDT (SystemTap probe descriptors) Provider: python Name: audit Location: 0x0000000000074b61, Base: 0x00000000004eb5f0, Semaphore: 0x00000000005a9d96 Arguments: 8@%rbp 8@%r13 stapsdt 0x00000035 NT_STAPSDT (SystemTap probe descriptors) Provider: python Name: gc__start Location: 0x000000000007815a, Base: 0x00000000004eb5f0, Semaphore: 0x00000000005a9d8e Arguments: -4@36(%rsp) stapsdt 0x00000030 NT_STAPSDT (SystemTap probe descriptors) Provider: python Name: gc__done Location: 0x00000000000781fc, Base: 0x00000000004eb5f0, Semaphore: 0x00000000005a9d90 Arguments: -8@%r12 stapsdt 0x00000045 NT_STAPSDT (SystemTap probe descriptors) Provider: python Name: function__entry Location: 0x000000000007ea33, Base: 0x00000000004eb5f0, Semaphore: 0x00000000005a9d80 Arguments: 8@%r14 8@%r15 -4@%eax ...
  11. ༗ޮʹ͢ΔʢPHPͷ৔߹ʣ » --enable-dtraceͰϏϧυͨ͠όΠφϦΛ༻ҙ͢Δʢ༗ޮԽ͞ΕͨύοέʔδΛ࢖͏ or ΦϓγϣϯΛ͚ͭͯϏϧυ » ؀ڥม਺ USE_ZEND_DTRACE=1 Λλʔήοτϓϩηεʹηοτ͢Δ »

    ଞݴޠͷ࣮૷Ͱ͸୯ʹprobe͕Ξλον͞Ε͔ͨͲ͏͔Ͱ൑ఆ͞ΕΔ » PHPͷΈ͜Ε͕ඞཁʢ7.0.14͔Βʣ » ౰࣌ͷίϝϯτͱͯ͠significant overhead͕ൃੜ͢Δͱͷ͜ͱΒ͍͠3ʢݸਓత ʹ͸ٙ໰ʣ » ಺෦తʹ͸؀ڥม਺ΛΈͯɺτϨʔεϙΠϯτΛؚΉؔ਺ʹϙΠϯλΛมߋ͍ͯ͠Δ ʢޙड़ʣ DTrace: . Disabled PHP call tracing by default (it makes significant overhead). This may be enabled again using envirionment variable USEZENDDTRACE=1. (Dmitry) $ php -i |grep -i dtrace DTrace Support => available, disabled $ USE_ZEND_DTRACE=1 php -i |grep -i dtrace DTrace Support => enabled USE_ZEND_DTRACE => 1 3 https://github.com/php/php-src/commit/0c78fe4bb55a9d39afc79cbcbadb9a273f2ec2ef
  12. ؔ਺ϙΠϯλΛτϨʔεΛؚΉϥούʔʹ Zend/zend.c4 #ifdef HAVE_DTRACE /* build with dtrace support */

    { char *tmp = getenv("USE_ZEND_DTRACE"); if (tmp && ZEND_ATOL(tmp)) { zend_dtrace_enabled = 1; zend_compile_file = dtrace_compile_file; zend_execute_ex = dtrace_execute_ex; zend_execute_internal = dtrace_execute_internal; zend_observer_error_register(dtrace_error_notify_cb); } else { zend_compile_file = compile_file; zend_execute_ex = execute_ex; zend_execute_internal = NULL; } } #else zend_compile_file = compile_file; zend_execute_ex = execute_ex; zend_execute_internal = NULL; #endif /* HAVE_DTRACE */ Zend/zend_dtrace.c5 ZEND_API zend_op_array *dtrace_compile_file(zend_file_handle *file_handle, int type) { zend_op_array *res; DTRACE_COMPILE_FILE_ENTRY(ZSTR_VAL(file_handle->opened_path), ZSTR_VAL(file_handle->filename)); res = compile_file(file_handle, type); DTRACE_COMPILE_FILE_RETURN(ZSTR_VAL(file_handle->opened_path), ZSTR_VAL(file_handle->filename)); return res; } 5 https://github.com/php/php-src/blob/PHP-8.5.0/Zend/zend_dtrace.c#L44-L52 4 https://github.com/php/php-src/blob/PHP-8.5.0/Zend/zend.c#L972-L993
  13. PHP͸άϧʔͰ͋Δ(2/2) » PHPຊମ͔ΒϦϯΫ » libcryptoͳͲ » γεςϜϥΠϒϥϦʢlibxxxʣʹϦϯΫͯ͠PHPଆʹΠϯλʔϑ ΣʔεΛఏڙ͢Δ » memcached.so

    + libmemcached » curl.so + libcurl » ... » C֦ுϨΠϠʔࣗମͰͷػೳఏڙ » mysqlnd » apcu » phpredis » ...
  14. php-dcrͷeBPFίʔυ php.bpf.c #include "vmlinux.h" #include <bpf/bpf_helpers.h> #include <bpf/usdt.bpf.h> #define MAX_STR_LEN

    512 char filename[MAX_STR_LEN]; struct { __uint(type, BPF_MAP_TYPE_LRU_HASH); __uint(max_entries, 65536); __type(key, char[MAX_STR_LEN]); __type(value, u64); } php_compile_file SEC(".maps"); SEC("usdt//usr/lib/apache2/modules/libphp8.1.so:php:compile__file__return") int BPF_USDT(compile_file_return, char *arg0, char *arg1) { // ࣌ࠁʢىಈ࣌ࠁ͔Βͷܦաɺφϊඵʣ u64 ts = bpf_ktime_get_ns(); static const char fmtstr[] = "compile file return: %s, %s\n"; bpf_trace_printk(fmtstr, sizeof(fmtstr), arg0, arg1); bpf_probe_read_user_str(&filename, sizeof(filename), arg0); bpf_map_update_elem(&php_compile_file, &filename, &ts, BPF_ANY); return 0; } char LICENSE[] SEC("license") = "GPL";
  15. ૝ఆQA » DTrace͸Φϓγϣϯ͚ͩͲ࣮ࡍʹ࢖͑Δʁ » PHPʹ͓͍ͯ͸ओཁͳσΟετϩͷύοέʔδͰ༗ޮԽ͞ΕͯΔ » ʢௐ΂ͨൣғͰ͸DebianͷύοέʔδҎ֎ʣ7ʣ » ίϯςφରԠ »

    k8sʹ͓͍ͯ΋Ξϓϩʔν͸͋Δ͕೉͍͠ » ϓϩηεͷ௥੻ɺΞλονઌͷಛఆ » ίϯύΠϧΠϕϯτ͕ىಈޙ͙͢ʹ૸ΔͷͰΞλον͕ؒʹ߹Θͳ͍ͱࠔΔ » ؔ਺΍Ϋϥε୯ҐͰͷௐࠪ » function-entry΍function-retrun͕͋ΔͷͰݪཧతʹ͸Մೳ͕ͩ » ͕͢͞ʹΦʔόʔϔου͕໨ʹݟ͑ΔΑ͏ʹͳΔͱࢥΘΕΔ 7 DTraceͷ༗ޮԽঢ়گௐࠪ2026
  16. ར༻ऀΛ૿΍͍ͨ͠ » Ubuntuͷ࠷৽LTSʢ24.04 nobleʣͰ͸DTrace ͷαϙʔτ͕੾ΒΕ͍ͯΔ » UpstreamͰͷϏϧυΤϥʔ͕ݪҼ » Debian͕ݱঢ়Φϑͳͷ΋ଟ෼͜Ε »

    IssueΛ͋͛ͨΒϝϯςφͷํʹରԠ͍͚ͨͩ ͨ » ࢖ΘΕͯͳ͍ػೳ͸੾ΒΕͯ΋࢓ํ͕ͳ͍ » ͔͠͠΋͍ͬͨͳ͍ » ࢖ͬͯΔΑʂͱ͍͏੠͸େࣄ