Skip to content

PHP trait extraction consumes 64 GB memory and continues after MCP request timeout #1265

Closed

Description

@yemao688

Version

codebase-memory-mcp 0.9.0

Platform

macOS (Apple Silicon)

Install channel

GitHub release archive / install.sh / install.ps1

Binary variant

ui

What happened, and what did you expect?

Summary

Indexing a small PHP/TypeScript source tree causes codebase-memory-mcp to consume over 64 GB of physical memory.

Sampling the process shows the PHP extractor repeatedly working inside:

process_class_for_fields -> flatten_trait_into_class -> memmove

The indexing process also continues running after the MCP request has timed out.

Environment

  • macOS 26.5.1 (25F80)
  • Apple Silicon / ARM64
  • codebase-memory-mcp binary reports version 0
  • Source project: BuildAdmin v2.3.7
  • Source size:
    • Full tree: approximately 16 MB
    • Reduced tree used for the final reproduction: approximately 3.5 MB
  • No vendor, node_modules, Git history, public assets, or runtime data were included in the reduced tree

The same timeout behavior was also observed with full and moderate modes.

Actual behavior
The MCP request times out after approximately 120 seconds.
The indexing process continues running after the client request has timed out.
Memory continues increasing.
After approximately 10 minutes, the process reaches:
Physical footprint: 64.3G
Physical footprint (peak): 64.3G
No indexed project is produced.
The process has to be terminated manually.

Sample stack
main
handle_index_repository
cbm_pipeline_run
cbm_parallel_extract_ex
cbm_parallel_for
cbm_thread_join
The active extraction worker is:
pthread_worker
extract_worker
cbm_extract_file
cbm_run_php_lsp
php_lsp_collect_class_fields
process_class_for_fields
flatten_trait_into_class
_platform_memmove

The main thread is waiting for the extraction worker to finish.

Expected behavior
Trait expansion should have bounded memory usage.
Recursive or repeated trait expansion should detect cycles and deduplicate already-expanded traits.
fast mode should not consume tens of gigabytes for a source tree of only a few megabytes.
When the MCP request is cancelled or times out, the indexing pipeline and worker threads should terminate.
A failed index should clean up partial state and return a useful error.
Suspected cause
The stack suggests algorithmic or recursive growth in:

BuildAdmin/ThinkPHP uses PHP traits extensively. Class-field collection may be repeatedly flattening the same trait members, missing cycle detection, or copying an exponentially growing field collection.

There may be two separate issues:

Unbounded memory growth during PHP trait flattening.
Missing cancellation propagation after the MCP request timeout.
Suggested safeguards
Track visited traits while flattening each class.
Detect direct and indirect trait cycles.
Deduplicate inherited/flattened fields.
Add limits for trait recursion depth and expanded field count.
Propagate request cancellation to extraction workers.
Abort indexing when memory or expansion limits are exceeded.
Include the current file/class/trait name in diagnostic errors.
Additional information
After the runaway process exited, the remaining MCP processes returned to approximately 11 MB and 17 MB RSS, confirming that the 64 GB footprint belonged to the indexing task.

I can provide the complete macOS sample report if needed.

Reproduction

Create a standalone copy containing only:

git clone https://github.com/build-admin/buildadmin

Then call:

{
  "repo_path": "/tmp/buildadmin-php-v2.3.7-index-lite",
  "name": "buildadmin-php-v2.3.7",
  "mode": "fast",
  "persistence": false
}

### Logs

```text

Diagnostics trajectory (memory / performance / leak issues)

Analysis of sampling codebase-memory-mcp (pid 9654) every 1 millisecond
Process: codebase-memory-mcp [9654]
Path: /Users/USER/*/codebase-memory-mcp
Load Address: 0x100488000
Identifier: codebase-memory-mcp
Version: 0
Code Type: ARM64
Platform: macOS
Parent Process: codebase-memory-mcp [99275]
Target Type: live task

Date/Time: 2026-07-25 19:48:31.386 +0800
Launch Time: 2026-07-25 19:37:49.125 +0800
OS Version: macOS 26.5.1 (25F80)
Report Version: 7
Analysis Tool: /usr/bin/sample

Physical footprint: 64.3G
Physical footprint (peak): 64.3G
Idle exit: untracked
Call graph:
2591 Thread_51647992 DispatchQueue_1: com.apple.main-thread (serial)
+ 2591 start (in dyld) + 6992 [0x18507be00]
+ 2591 main (in codebase-memory-mcp) + 3552 [0x10048b800]
+ 2591 handle_index_repository (in codebase-memory-mcp) + 1308 [0x1004c11b0]
+ 2591 cbm_pipeline_run (in codebase-memory-mcp) + 3664 [0x1004dbd90]
+ 2591 cbm_parallel_extract_ex (in codebase-memory-mcp) + 1520 [0x1004e0994]
+ 2591 cbm_parallel_for (in codebase-memory-mcp) + 312 [0x1004e0314]
+ 2591 cbm_thread_join (in codebase-memory-mcp) + 28 [0x100491028]
+ 2591 _pthread_join (in libsystem_pthread.dylib) + 616 [0x18543c114]
+ 2591 __ulock_wait (in libsystem_kernel.dylib) + 8 [0x1853f7af8]
2591 Thread_51647993
+ 2591 thread_start (in libsystem_pthread.dylib) + 8 [0x185434c1c]
+ 2591 _pthread_start (in libsystem_pthread.dylib) + 136 [0x185439c58]
+ 2591 parent_watchdog_thread (in codebase-memory-mcp) + 44 [0x10048bd04]
+ 2591 usleep (in libsystem_c.dylib) + 68 [0x1852d5bd8]
+ 2591 nanosleep (in libsystem_c.dylib) + 220 [0x1852d5cc0]
+ 2591 __semwait_signal (in libsystem_kernel.dylib) + 8 [0x1853f9308]
2591 Thread_51648075
2591 thread_start (in libsystem_pthread.dylib) + 8 [0x185434c1c]
2591 _pthread_start (in libsystem_pthread.dylib) + 136 [0x185439c58]
2591 pthread_worker (in codebase-memory-mcp) + 48 [0x1004e0374]
2591 extract_worker (in codebase-memory-mcp) + 1224 [0x1004e1248]
2591 cbm_extract_file (in codebase-memory-mcp) + 1172 [0x10055bd84]
2591 cbm_run_php_lsp (in codebase-memory-mcp) + 2420 [0x1013ad8c4]
2591 php_lsp_collect_class_fields (in codebase-memory-mcp) + 436 [0x1013d6394]
2591 process_class_for_fields (in codebase-memory-mcp) + 6800 [0x101620ac4]
2591 flatten_trait_into_class (in codebase-memory-mcp) + 576 [0x101621ed8]
2591 _platform_memmove (in libsystem_platform.dylib) + 96,88 [0x1854433c0,0x1854433b8]

Total number in stack (recursive counted multiple, when >=5):

Sort by top of stack, same collapsed (when >= 5):
__semwait_signal (in libsystem_kernel.dylib) 2591
__ulock_wait (in libsystem_kernel.dylib) 2591
_platform_memmove (in libsystem_platform.dylib) 2591

Binary Images:
0x100488000 - 0x10e8affd7 +codebase-memory-mcp (0) <820B82B7-1B82-37C4-B071-EFEFF951B698> /Users/*/codebase-memory-mcp
0x184fd4000 - 0x185026b4b libobjc.A.dylib (951.7) <FF1D8AE4-ABEF-35F1-A30A-1183B9CB414F> /usr/lib/libobjc.A.dylib
0x185027000 - 0x18505bd07 libdyld.dylib (1378) <B147D877-D0C2-388F-B38D-D3132E39905F> /usr/lib/system/libdyld.dylib
0x18505c000 - 0x185102217 dyld (1.0.0 - 1378) <A237EF81-B68B-37BA-A165-92C965529534> /usr/lib/dyld
0x185103000 - 0x185106228 libsystem_blocks.dylib (96) <CC947089-488D-316B-A9C7-46DEC0F73145> /usr/lib/system/libsystem_blocks.dylib
0x185107000 - 0x18515b55f libxpc.dylib (3102.120.13) <C40FCAE8-7A17-3D82-87CB-3DA4802ADEF1> /usr/lib/system/libxpc.dylib
0x18515c000 - 0x18517c9ff libsystem_trace.dylib (1861.120.4) <D0FABAB3-5AE7-3F10-8C62-60CBF5D70850> /usr/lib/system/libsystem_trace.dylib
0x18517d000 - 0x18522b5f7 libcorecrypto.dylib (1922.121.1) <A01A158D-6FE7-3B1C-A2C4-6BCB90D7314A> /usr/lib/system/libcorecrypto.dylib
0x18522c000 - 0x18527c1d7 libsystem_malloc.dylib (812.100.31) <5FAE4807-4D2B-3A95-A63A-DD96D3DA11B4> /usr/lib/system/libsystem_malloc.dylib
0x18527d000 - 0x1852c423f libdispatch.dylib (1542.100.32) <F071EFE4-299F-3089-ACC4-0025B8FFB52A> /usr/lib/system/libdispatch.dylib
0x1852c5000 - 0x1852c7ffb libsystem_featureflags.dylib (103) <71FCFFBD-85F8-3365-AFE0-BECEA4369122> /usr/lib/system/libsystem_featureflags.dylib
0x1852c8000 - 0x185348d9f libsystem_c.dylib (1752.120.2) <694B7881-1BF3-3D0F-8F19-B50AE4E8EF8A> /usr/lib/system/libsystem_c.dylib
0x185349000 - 0x1853d9ae7 libc++.1.dylib (2100.43) <5013C0F8-84A9-3B56-9FEB-C353816A750F> /usr/lib/libc++.1.dylib
0x1853da000 - 0x1853f475f libc++abi.dylib (2100.43) <E482D257-E5A0-3816-BD97-7B39342958DA> /usr/lib/libc++abi.dylib
0x1853f5000 - 0x1854322af libsystem_kernel.dylib (12377.121.6) <CC1CF985-BC65-3725-809F-4C1E36B8F4BA> /usr/lib/system/libsystem_kernel.dylib
0x185433000 - 0x18543fb3b libsystem_pthread.dylib (539.100.4) <4F33683C-18C8-39A1-800B-2E3BD43BCC13> /usr/lib/system/libsystem_pthread.dylib
0x185440000 - 0x185448963 libsystem_platform.dylib (375.120.2) <160FD864-8D15-36FC-9E97-9725388CFAFE> /usr/lib/system/libsystem_platform.dylib
0x185449000 - 0x1854786eb libsystem_info.dylib (600) <3D62F2D9-F516-31BF-83C7-E203D872300B> /usr/lib/system/libsystem_info.dylib
0x189410000 - 0x189419e5f libsystem_darwin.dylib (1752.120.2) <8858F025-745A-388E-9544-080781CC9A69> /usr/lib/system/libsystem_darwin.dylib
0x18987b000 - 0x18988d1b6 libsystem_notify.dylib (348.120.4) <64D76451-1E0C-369B-A631-54DA3140E083> /usr/lib/system/libsystem_notify.dylib
0x18bb48000 - 0x18bb62f7b libsystem_networkextension.dylib (2226.121.1) <0331EE20-A1FB-3C0D-990A-F65939CFF505> /usr/lib/system/libsystem_networkextension.dylib
0x18bbe5000 - 0x18bbfcfdf libsystem_asl.dylib (406) <F40838DF-2F15-3F62-970F-DEE25706BF1B> /usr/lib/system/libsystem_asl.dylib
0x18d7a3000 - 0x18d7ab387 libsystem_symptoms.dylib (2169.120.7) <F5029F5C-1BF3-31C7-892E-BC8606AC829E> /usr/lib/system/libsystem_symptoms.dylib
0x191a47000 - 0x191a7f257 libsystem_containermanager.dylib (725.120.2) <518BE44D-AA31-31E6-B44B-A37AD5E47040> /usr/lib/system/libsystem_containermanager.dylib
0x192e58000 - 0x192e5c5d7 libsystem_configuration.dylib (1405.120.5) <0F7BBC1C-3D50-3E59-A31F-E4672A80BCEB> /usr/lib/system/libsystem_configuration.dylib
0x192e5d000 - 0x192e638cf libsystem_sandbox.dylib (2680.120.12) <EC3A75EA-44C0-3A0B-9BF7-71C117B0A9C6> /usr/lib/system/libsystem_sandbox.dylib
0x1944b7000 - 0x1944ba1fb libquarantine.dylib (196.120.2) <5849009B-ACA3-3796-94C4-BBDF8D300EBA> /usr/lib/system/libquarantine.dylib
0x194c56000 - 0x194c5cd03 libsystem_coreservices.dylib (191.4.5) <9576143B-C303-35A3-BA63-1FD06EDA2BB1> /usr/lib/system/libsystem_coreservices.dylib
0x1951d2000 - 0x1951e0d97 libz.1.dylib (100.120.1) <863BB312-F115-32B0-889F-D3B42857A7A6> /usr/lib/libz.1.dylib
0x1951e1000 - 0x19521ea77 libsystem_m.dylib (3312.100.1) <A7E0863C-101A-31A0-922B-ED9B02626151> /usr/lib/system/libsystem_m.dylib
0x195220000 - 0x195223527 libmacho.dylib (1378) <755441B4-C034-3E3A-8E3C-F424FBB28261> /usr/lib/system/libmacho.dylib
0x19523d000 - 0x19524a3a7 libcommonCrypto.dylib (600035) <CE357BB1-2D49-37CC-9FFE-D093936B1475> /usr/lib/system/libcommonCrypto.dylib
0x19524b000 - 0x195254ca3 libunwind.dylib (2100.2) <FC8F0C4E-6FB0-3E27-ABEF-B0FA720CD5E4> /usr/lib/system/libunwind.dylib
0x19525d000 - 0x1952677ff libcopyfile.dylib (240) <8ACFAC7A-88E9-3EF4-853F-A9140FF110E5> /usr/lib/system/libcopyfile.dylib
0x195268000 - 0x19526b987 libcompiler_rt.dylib (103.3) <AA79151F-C19D-3C7C-92F8-B10F04D82C1C> /usr/lib/system/libcompiler_rt.dylib
0x19526c000 - 0x19527078b libsystem_collections.dylib (1752.120.2) <72DB5BE7-EF78-37D0-8AAD-53E37CFE4287> /usr/lib/system/libsystem_collections.dylib
0x195271000 - 0x1952744cf libsystem_secinit.dylib (168.100.7) <E525C566-CDA4-3D99-B12B-1615FD54A1F3> /usr/lib/system/libsystem_secinit.dylib
0x195275000 - 0x195277bf7 libremovefile.dylib (85.100.6) <635E661D-80A7-340D-A165-3F7D318FDFC9> /usr/lib/system/libremovefile.dylib
0x195278000 - 0x195278f27 libkeymgr.dylib (31) <8C08891A-DF1C-3DCC-AB64-4742FD6BE9BA> /usr/lib/system/libkeymgr.dylib
0x195279000 - 0x195281e37 libsystem_dnssd.dylib (2881.120.11) <6F45DF3F-233B-3BC3-BA8C-B43E7DA884BF> /usr/lib/system/libsystem_dnssd.dylib
0x195282000 - 0x19528709b libcache.dylib (95) <189B576E-6176-3F82-8FA4-5914B0BEAEE7> /usr/lib/system/libcache.dylib
0x195288000 - 0x195289ce3 libSystem.B.dylib (1356) <D65E4112-C151-3FF8-BEB7-4841436012D6> /usr/lib/libSystem.B.dylib
0x2884bf000 - 0x2884c6349 libRosetta.dylib (367.9) <8651ED4F-6506-3788-B56B-32EE414A85CA> /usr/lib/libRosetta.dylib
0x28a0dc000 - 0x28a0dfa4b libsystem_darwindirectory.dylib (122) <CF277A2C-C818-3F16-8B3F-3C7B39FCD164> /usr/lib/system/libsystem_darwindirectory.dylib
0x28a0e0000 - 0x28a0e95d3 libsystem_eligibility.dylib (319.121.1) <DDAC0531-0262-3DDC-8EC8-80EB9438847A> /usr/lib/system/libsystem_eligibility.dylib
0x28a0ea000 - 0x28a0f188b libsystem_sanitizers.dylib (26.1) <44C4234F-8B2D-38D3-A774-B4B3C75AF3A7> /usr/lib/system/libsystem_sanitizers.dylib
0x28a0f2000 - 0x28a0f2bb7 libsystem_trial.dylib (474.2.18.2) <68C583BA-AFF5-3916-86AE-6E38BFEF98D6> /usr/lib/system/libsystem_trial.dylib

Project scale (if relevant)

No response

Confirmations

  • I searched existing issues and this is not a duplicate.
  • My reproduction uses shareable code (a dummy snippet or a public OSS repository), not proprietary code.

Activity

  1. added
    parsing/qualityGraph extraction bugs, false positives, missing edges
    stability/performanceServer crashes, OOM, hangs, high CPU/memory
    ux/behaviorDisplay bugs, docs, adoption UX
    windowsWindows-specific issues
    on Jul 25, 2026
  2. added
    priority/highNeeds near-term maintainer attention; high-impact bug, regression, safety issue, or release blocker.
    on Jul 28, 2026
  3. added this to the 0.9.1-rc milestone on Jul 28, 2026
  4. DeusData commented on Jul 28, 2026

    @DeusData
    Owner

    Thanks for the public reproducer and the process sample; the stack makes this much more actionable than a generic OOM report.

    Current main's PHP field pass still calls flatten_trait_into_class from process_class_for_fields, and there is no visited-trait/cycle set around that expansion path (internal/cbm/lsp/php_lsp.c:3451, 3550, 3610-3624). The main thread waiting in the worker join is consistent with the extractor continuing after the client-side timeout.

    This is routed to 0.9.1-rc at priority/high under stability. The trait-expansion bound and request-cancellation behavior should be treated as two binding acceptance criteria, even if one fix addresses both. Thank you for reducing the source tree and sharing the exact stack.

  5. DeusData commented on Sep 26, 2026

    @DeusData
    Owner

    Thanks so much for the reduced BuildAdmin tree and the process sample! The flatten_trait_into_class → memmove stack pointed straight at the cause.

    Your v0.9.0 build predates the fix. PR #920 (2154edf, first released in v0.10.0) stops a trait from being flattened into itself, and it snapshots the method table before copying. Without that, the loop could keep re-copying methods it had just added, which is exactly the unbounded growth you measured. A synthetic set of self-using, mutually-using and aliased traits now indexes instantly on current main, and indexing runs in a supervised worker.

    Could you re-run BuildAdmin v2.3.7 on v0.11.0 and confirm that memory stays bounded and the index completes?

  6. added
    awaiting-reporterWaiting on the reporter for info/repro; stale bot will warn then close
    on Sep 26, 2026
  7. github-actions commented on Sep 26, 2026

    @github-actions

    Thanks for the report! To move this forward we need a bit more so we can reproduce it ourselves:

    • the codebase-memory-mcp --version you're on
    • the exact steps or command you ran
    • a public repo (or a small dummy snippet) that shows the problem — please don't paste proprietary code

    Once that's here we'll pick it straight back up. Heads-up: issues left awaiting-reporter are automatically closed after a few weeks of silence, but a comment reopens the door anytime.

  8. added a commit that references this issue on Oct 19, 2026
  9. DeusData commented on Oct 19, 2026

    @DeusData
    Owner

    Thank you again for the reduced BuildAdmin tree and the process sample. They made this traceable.

    PR #2706 is now on main (32445b2). It completes the work on the trait-flattening path that #920 started:

    • fix(lsp/php): prevent OOM when a PHP trait uses itself #920 (v0.10.0) stopped a trait from being flattened into itself and snapshots the method table before copying. That removed the unbounded re-copying behind the memory growth you measured.
    • fix(php): index trait methods by receiver while flattening (#1265) #2706 removes the remaining quadratic cost. Flattening used to scan every registered function for each trait-using class. It now reads a per-receiver index, so the work grows linearly with the number of trait-using classes (deterministic work counter: 100 -> 200 classes went from a 2.97x to a 1.92x increase). The graph output is byte-identical on laravel/framework, filamentphp/filament and grokability/snipe-it.

    One part of your report is not changed by this PR: the indexing process continuing after the MCP request has timed out. If you still see indexing keep running after a client timeout on the next release, please comment here or open a new issue with the version and a public reproducer, and we will pick it up as its own fix.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    awaiting-reporterWaiting on the reporter for info/repro; stale bot will warn then closebugSomething isn't workingparsing/qualityGraph extraction bugs, false positives, missing edgespriority/highNeeds near-term maintainer attention; high-impact bug, regression, safety issue, or release blocker.stability/performanceServer crashes, OOM, hangs, high CPU/memoryux/behaviorDisplay bugs, docs, adoption UX

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions