[STR] 진단 라인 VM 이슈 조사 (원인 특정)
개요
#31 세션 관찰: crownyc run <toau> 실행 시 [STR] count=... 진단 라인이 반복 출력되며
기대 출력이 안 나오는 현상. learn/lookup 경로와 무관한 별도 VM 이슈로 추정 — 조사 결과
추정이 맞았다: 이 진단은 문자열 풀(heap) 자원 게이트 로직이며, 학습DB/lookup 파이프라인과 무관.
공유 crownyc/crownyc.c는 이번 작업에서 수정하지 않았음(조사·재현·원인특정만, 결재 옴).
[STR] 출력 지점 3곳 (grep 확정, crownyc.c)
| 라인 | 스트림 | 조건 | 반복성 |
|---|---|---|---|
| 2328 | stderr | 문자열 풀 최초 malloc 실패 (기동 즉시 fatal) | 1회, exit(1) |
| 2469 | stderr | str_new(): GC + 2배 성장 시도 후에도 여전히 str_cap/str_pool_size 부족 (= 절대 상한(STR_HARD_COUNT=64M슬롯, STR_HARD_POOL=1GB)에 도달) | 반복 — 실패할 때마다 매 호출 출력, streak>=50에서 자가종료(exit 42). CROWNY_NO_STREXIT=1이면 무한 반복 |
| 9750 | stdout | opcode 365 (문자열통계/STR_STATS) — 사용자가 명시 호출한 진단 빌트인 | 호출한 만큼 |
[VM] 문자열 핸들 상한(480000) 초과... (line 2491, exit 43)은 접두어가 다르지만 같은 문자열-자원 계열 게이트라 함께 조사.재현 (실측, 공유 VM 미수정 — 격리 빌드로 검증)
- 동적 성장 정상 동작 확인:
-DSTR_INIT_COUNT=64 -DSTR_INIT_POOL=4096(테스트 파일
tests/STR풀성장스트레스.한선의 문서화된 소캡 빌드)으로 별도 바이너리를 /tmp에 빌드해
실행 → [STR] 메시지 전혀 없이 300개 문자열 생성/검증 전부 통과(OK STR풀성장).
즉 정상 워크로드에서는 캡이 작아도 GC+동적성장이 투명하게 처리한다.
- 절대상한 도달 재현: `-DSTR_INIT_COUNT=8 -DSTR_INIT_POOL=64 -DSTR_HARD_COUNT=32
[STR] 풀한계(성장 상한 도달)! count=9/16 pool=504/512 len=8 gc_runs=19 gc_freed=36 streak=1
... (매 실패 호출마다 1줄, streak 1→50 증가) ...
[STR] 풀고갈 지속(50회) — wedge 방지 자가재기동 종료(코드 42)
exit code = 42. stdout은 완전히 비어있음 — 프로그램이 의도한 출력값("생성 = 300") 등
실제 출력이 단 한 줄도 안 나감(지연이 아니라 소실 — VM이 루프 도중 exit(42)로 죽어버림).정확한 임계치 (실측)
[STR] 풀한계메시지는 str_cap과 str_pool_size 양쪽 모두 GC+2배성장을 거쳐도 절대상한
STR_HARD_COUNT=64M 슬롯 / STR_HARD_POOL=1GB)에 도달해야만 발화한다. 이 절대상한은
#ifndef 가드가 있어 -D로 낮출 수 있지만(테스트 전용), 프로덕션 빌드 기본값은 초기캡의
32배(64M/2M) 여유가 있어 통상 워크로드로는 사실상 도달 불가.
- 같은 코드 경로에 있는
STR_ID_MAX=480000(crownyc.c:2307,#define하드코딩,-D재정의
str_cap(초기 2,097,152) < STR_ID_MAX보다 커서,
살아있는 문자열이 실제로 480,000개에 근접하는 워크로드에서는 이 게이트가 먼저 걸린다 —
이때 메시지는 [STR]이 아니라 `[VM] 문자열 핸들 상한(480000) 초과 — GC 후에도 N 생존, 풀
누수 의심`이며 1회성 exit(43)(반복 안 됨, 즉시 프로세스 종료).
코드 주석에 명시된 실제 발병 사례: "2026-07-05 게이트웨이 2분주기 사망 원인 — GC 0회로 exit43".영향 범위 (오늘 스택)
str_new()(2469, [STR]) 경로: 64M슬롯/1GB급 살아있는 문자열이 필요 — 브레인번역
STR_ID_MAX(480000, [VM]) 경로: 장시간 상주 프로세스가 요청마다 문자열을 대량 생성하고
- opcode 365(
문자열통계, stdout) 경로: CrownyOS/crownyc, crowny-butler, CrownyTVM,
.한선/.rpn 소스가 전혀 없음. 즉 지금 이 순간 이 경로가 자동으로
발화할 소스는 확인되지 않음 — 발화하려면 (a) 세션이 임시/휘발성 디버그 코드에서 직접 호출했거나
(b) 소스에서는 이미 제거됐지만 재컴파일 안 된 구 .toau 바이너리가 여전히 그 호출을 담고
있어야 함(소스-바이너리 불일치 함정, TOAU 오염 함정과 유사 계열이나 다른 메커니즘).원인 특정: 무해한 로깅인가 vs 실제 고갈 전조인가
둘 다 아니고, 셋으로 나뉜다 (실측 근거):
str_new()의[STR] 풀한계메시지 — 무해한 로깅이 아니다. 발화 자체가 곧 실질적 자원
STR_ID_MAX초과의[VM]메시지 — 마찬가지로 실해악(exit 43로 프로세스 즉시 죽음).
- opcode 365
문자열통계(stdout) — 설계상 진단용으로 무해(GC/카운트 스냅샷 출력일 뿐,
부수 발견 — 흩어진 crownyc 바이너리 (버전 혼선 리스크)
파일시스템에 crownyc 바이너리가 5개 존재:
| 경로 | 빌드일 | 풀한계 문자열 | 핸들 상한(STR_ID_MAX) 문자열 |
|---|---|---|---|
/Users/ef/CrownyOS/crownyc/crownyc (정본) | 7/5 | 있음 | 있음 |
/Users/ef/crowny-engine/dist-mac/crownyc | 6/27 | 있음 | 없음 — 2026-07-03~05 수정 이전 |
/Users/ef/CrownyOS/crownyc/pkg/crownyc | 4/18 | 있음 | 없음 — 마찬가지로 구버전 |
/Users/ef/crowny-hanseon-dist/linux/bin/crownyc | 6/21 | (미검사, Linux ELF) | (미검사) |
/Users/ef/Downloads/CrownyTVM/crownyc | 3/28 | 없음(구형 최소 VM) | 없음 |
crownyc alias/심볼릭은 없음(which -a crownyc → not found) — 즉 항상 explicit path로
실행돼야 하나, 여러 프로젝트 디렉토리에 구버전 사본이 방치돼 있어 잘못된 디렉토리에서
./crownyc run을 실행하면 2026-07-03(STR_HANDLE_BASE 충돌 수정)·2026-07-05(STR_ID_MAX GC
보강) 이전 로직으로 실행될 위험이 있다. 이번 세션 관찰이 정본이 아닌 이 구버전 중 하나를
탔을 가능성은 세션 #31 실행 경로를 직접 확인 못 해 추정으로 남긴다.회피 / 수리 제안
회피(셸/래퍼 수준, 즉시 적용 가능, 공유 VM 미수정):
[STR]/[VM]계열은 전부 stderr이므로, 파이프라인에서 실제 산출물(stdout)만 소비한다면
2>/dev/null이나 2> log파일로 분리하면 산출물 자체는 오염되지 않는다. 단, 발화했다는 것
자체가 이미 데이터 손상/프로세스 조기종료를 의미하므로 필터링은 증상 은폐일 뿐 근본대응
아님 — exit code(42/43) 체크를 반드시 병행해야 한다.
- 정본 실행 시 항상
/Users/ef/CrownyOS/crownyc/crownyc의 절대경로를 쓰거나, 실행 스크립트
cd /Users/ef/CrownyOS/crownyc를 강제해 구버전 사본(dist-mac, pkg, CrownyTVM)이
실수로 잡히지 않게 한다.
- opcode 365(
문자열통계)를 디버그용으로 쓸 일이 있으면 stdout이 곧 프로그램 출력과 같은
수리(공유 VM 변경 필요 — 안건 티켓, 수정 금지·문안만):
티켓: opcode 365(STR_STATS/>문자열통계) stdout→stderr 전환
- 위치:
crownyc.c:9750printf("[STR] count=...")- 문제: 같은 파일의 다른 두 STR 진단(2328, 2469)은 전부
fprintf(stderr,...)인데 이 지점만printf(stdout)로 구현돼 있어, 향후 누군가 디버깅 목적으로 이 빌트인을 루프에 넣으면 실제 프로그램 출력과 뒤섞인다. 단순 일관성 수정:printf(...)→fprintf(stderr, ...).에서 파싱하는 기존 스크립트가 있는지 사전 확인 필요(현재 조사 범위 내에서는 없음).
- 리스크: 낮음(순수 진단 출력 스트림 변경, 로직/오퍼란드 불변). 회귀 대상: STR_STATS를 stdout
티켓(참고, 선택): STR_ID_MAX 근접 시 사전 경고 레벨 추가stderr로 남겨, 운영 중 "곧 죽는다"를 조기 관측 가능하게(현재는 exit(43) 순간에만 알 수 있음). 2026-07-05 게이트웨이 사망 재발 방지 관측성 개선 성격, 급하지 않음.
- 위치:
crownyc.c:2485부근- 제안:
str_count가STR_ID_MAX의 예: 90%(432,000)를 넘으면 exit 전에 한 번 저강도 경고를
결론 요약
- 재현조건(실측):
[STR] 풀한계는 str_cap/pool이 절대 상한(64M/1GB)까지 GC+성장해도 부족할
- 더 낮고 현실적인 게이트는
STR_ID_MAX=480000(exit 43,[VM]접두어) — 이미 게이트웨이에서
- 세션이 실제 본 문자열이
[STR] count=...라면, opcode 365(문자열통계, stdout)일 가능성이
- 공유 VM(
crownyc/crownyc.c) 수정은 하지 않았음 — 위 두 티켓만 안건으로 남김.
관련 파일
/Users/ef/CrownyOS/crownyc/crownyc.c(2328, 2459-2503, 9745-9764 — STR 진단 3지점 + 게이트 로직)/Users/ef/CrownyOS/crownyc/hanseonc_high.c:1859(문자열통계키워드→opcode 365 매핑)/Users/ef/CrownyOS/crownyc/tests/STR풀성장스트레스.한선(소캡 빌드로 동적성장 검증하는 기존 스트레스 테스트, 재현에 그대로 활용)- 흩어진 구버전 바이너리:
/Users/ef/crowny-engine/dist-mac/crownyc,/Users/ef/CrownyOS/crownyc/pkg/crownyc,/Users/ef/Downloads/CrownyTVM/crownyc,/Users/ef/crowny-hanseon-dist/linux/bin/crownyc
잔여 이슈
- 세션 #31이 실제로 어느 crownyc 바이너리/어느 .toau를 실행했는지 원본 커맨드를 확보 못함 —
- opcode 365 stdout→stderr 티켓은 결재 대기(수정 금지 지시로 미적용).
/Users/ef/crowny-hanseon-dist/linux/bin/crownyc(Linux ELF)는 이번 조사에서 문자열 grep을