विषय पर बढ़ें

अंतिम अद्यतन:2026-10-12

C++ मेमोरी लीक जांच का फील्ड सेशन

हाल ही में एक डेटाबेस कर्नल (Greenplum/PostgreSQL परिवार) की मेमोरी समस्या की जांच करते समय मुझे WARNINGs की एक बहुत ही क्लासिक श्रृंखला मिली: problem in alloc set ExecutorState: bad single-chunk। इस लॉग का मतलब यह है: मेमोरी पूल (alloc set) ने सत्यापन के दौरान पाया कि किसी chunk का हेडर खराब हो चुका है—— दस में से नौ बार कहीं कोड ने सीमा के बाहर लिखकर पड़ोसी chunk के मेटाडेटा को रौंद दिया होगा।

ऐसी समस्याओं की कठिनाई यह है: गलती जहाँ दिखती है, वहीं होती नहीं। WARNING तब छपता है जब मेमोरी फ्री होकर सत्यापन होता है, जबकि out-of-bounds लिखावट बहुत पहले किसी भी कोड में हुई हो सकती है। नीचे उस समय की पूरी जांच प्रक्रिया चिपकाई गई है, साथ देखें कि gdb से "अपराध स्थल" कैसे वापस लाया जाता है।

पहले मौके की जानकारी पढ़ लें:

regression=# SELECT t1.a, t1.c, t2.b, t2.c FROM prt1_e t1, prt2_e t2 WHERE (t1.a + t1.b)/2 = (t2.b + t2.a)/2 AND t1.c = 0 ORDER BY t1.a, t2.b;
WARNING:  problem in alloc set ExecutorState: bad single-chunk 0x7cfdd48 in block 0x7cfc580  (seg1 slice1 127.0.1.1:7003 pid=4295)
WARNING:  problem in alloc set ExecutorState: found inconsistent memory block 0x7cfc580  (seg1 slice1 127.0.1.1:7003 pid=4295)
WARNING:  problem in alloc set ExecutorState: bad single-chunk 0x7cfdd48 in block 0x7cfc580  (seg1 slice1 127.0.1.1:7003 pid=4295)
WARNING:  problem in alloc set ExecutorState: found inconsistent memory block 0x7cfc580  (seg1 slice1 127.0.1.1:7003 pid=4295)
WARNING:  problem in alloc set ExecutorState: bad single-chunk 0x87f85c8 in block 0x87f6e00  (seg0 slice1 127.0.1.1:7002 pid=4294)
WARNING:  problem in alloc set ExecutorState: found inconsistent memory block 0x87f6e00  (seg0 slice1 127.0.1.1:7002 pid=4294)
WARNING:  problem in alloc set ExecutorState: bad single-chunk 0x7d3c5c8 in block 0x7d3ae00  (seg2 slice1 127.0.1.1:7004 pid=4296)
WARNING:  problem in alloc set ExecutorState: found inconsistent memory block 0x7d3ae00  (seg2 slice1 127.0.1.1:7004 pid=4296)
WARNING:  problem in alloc set ExecutorState: bad single-chunk 0x87f85c8 in block 0x87f6e00  (seg0 slice1 127.0.1.1:7002 pid=4294)
WARNING:  problem in alloc set ExecutorState: bad single-chunk 0x7d3c5c8 in block 0x7d3ae00  (seg2 slice1 127.0.1.1:7004 pid=4296)
WARNING:  problem in alloc set ExecutorState: found inconsistent memory block 0x87f6e00  (seg0 slice1 127.0.1.1:7002 pid=4294)
WARNING:  problem in alloc set ExecutorState: found inconsistent memory block 0x7d3ae00  (seg2 slice1 127.0.1.1:7004 pid=4296)

WARNING से कई मुख्य जानकारियाँ मिलती हैं:

(1) खराब chunk का पता निश्चित है: 0x7cfdd48 (bad single-chunk इसी की ओर इशारा करता है);

(2) जिस block में यह है वह 0x7cfc580 है, और उसी block के लिए "found inconsistent memory block" का मतलब है कि पूरे block की चेन-सत्यापन भी फेल हो गया;

(3) कई segment (seg0/seg1/seg2) एक साथ रिपोर्ट कर रहे हैं, यानी यह environment का लरझक नहीं बल्कि निश्चित logic bug है।

सटीक पता हाथ में होने पर रणनीति बहुत सीधी है: मेमोरी एलोकेटर (aset.c का AllocSetAlloc) पर conditional breakpoint लगाकर उस एक कॉल को पकड़ें जो "आवंटन के समय यह पता लौटाता है", फिर उस पते पर hardware watchpoint लगाएँ—— जो इसे लिखेगा, वही अपराधी है।

gdb सेशन इस प्रकार है:

डिबगिंग:

(gdb) b aset.c:1109 if chunk=0x7cfdd48
Breakpoint 1 at 0x105d420: file aset.c, line 1109.
(gdb) c
Continuing.

Thread 1 "postgres" hit Breakpoint 1, AllocSetAlloc (context=0x787ac50, size=4096) at aset.c:1109
1109        block->freeptr += (chunk_size + ALLOC_CHUNKHDRSZ);
(gdb) p chunk
$1 = (AllocChunk) 0x7cfdd48
(gdb) p chunk->size
$2 = 9187201950435737471
(gdb) p &chunk->size
$3 = (Size *) 0x7cfdd48
(gdb) watch (*((Size *) 0x7cfdd48))
Hardware watchpoint 2: (*((Size *) 0x7cfdd48))
(gdb) i b
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x000000000105d420 in AllocSetAlloc at aset.c:1109
    stop only if chunk=0x7cfdd48
    breakpoint already hit 1 time
2       hw watchpoint  keep y                      (*((Size *) 0x7cfdd48))
(gdb) d 1
(gdb) c

पुनरावलोकन

इस केस की विधि किसी भी C/C++ प्रोजेक्ट की मेमोरी करप्शन जांच में सीधे इस्तेमाल हो सकती है:

(1) पहले एरर लॉग से सटीक पता निकालें — यही एकमात्र भरोसेमंद एंकर है;

(2) conditional breakpoint b aset.c:1109 if chunk=0x7cfdd48 से आवंटन की जगह पकड़ें और पुष्टि करें कि यह पता किसे मिला;

(3) watch (*(Size*)0x7cfdd48) — hardware watchpoint — अपराधी के सामने आने का इंतज़ार करता है; hardware breakpoint मेमोरी रीराइट नहीं करते और प्रोग्राम के व्यवहार को प्रभावित नहीं करते, इसलिए मेमोरी करप्शन की समस्याओं में सबसे भरोसेमंद हैं;

(4) चलाने पर watchpoint हिट होने के समय का कॉल स्टैक ही out-of-bounds लिखावट का पूरा दृश्य है।

ध्यान देने योग्य है कि पहले चरण में p chunk->size ने 9187201950435737471 (0x7F7F7F7F7F7F7F7F) छापा—— 0x7F का ढेर, मेमोरी रौंदे जाने की विशिष्ट निशानी। इसका मतलब है कि मेमोरी कुचलने वाला कोड flags/फिल वैल्यू का ढेर लिख रहा था; कोड में इस वैल्यू की ग्लोबल सर्च करने पर अक्सर सीधे मुख्य दोषी मिल जाता है।

एक सवाल छोड़ता हूँ: अगर out-of-bounds लिखावट fork से बने child process में हो और आप parent process डिबग कर रहे हों, तो ऊपर की प्रक्रिया में क्या बदलाव करने होंगे? कमेंट सेक्शन में चर्चा का स्वागत है।

संबंधित विषय

टिप्पणियाँ