मैमोरी लीक का पता लगाने के लिए मैं वेलग्रिंड का उपयोग कैसे करूं


183

मैं किसी प्रोग्राम में मेमोरी लीक को खोजने के लिए वेलग्रिंड का उपयोग कैसे करूं?

कृपया किसी ने मेरी मदद की और प्रक्रिया को पूरा करने के चरणों का वर्णन किया?

मैं उबंटू 10.04 का उपयोग कर रहा हूं और मेरा एक कार्यक्रम है a.c, कृपया मेरी मदद करें।


16
आप अपने संकलित कार्यक्रम का परीक्षण करने के लिए वेलग्राइंड का उपयोग करते हैं , स्रोत कोड का नहीं।
टोनी

6
@RageD द्वारा नीचे दिया गया उत्तर सही है, आप इसे स्वीकार क्यों नहीं करते?
प्रतीक सिंघल

1
एक रिसाव के कारण कुछ ऐसा होता है जिसे आप करने में असफल होते हैं - यानी। मुफ्त आवंटित स्मृति। इसलिए वैलग्राइंड आपको "जहां" लीक नहीं दिखा सकता है - केवल आप जानते हैं कि आवंटित स्मृति की अब आवश्यकता नहीं है। हालाँकि, यह बताकर कि आपके प्रोग्राम के माध्यम से उस मेमोरी का उपयोग ट्रेस करके कौन सा आवंटन फ्री () डी नहीं हो रहा है, आपको यह निर्धारित करने में सक्षम होना चाहिए कि उसे कहाँ फ्री होना चाहिए () डी। एक आम गलती आवंटित मेमोरी को मुक्त किए बिना एक फ़ंक्शन से बाहर निकलने में त्रुटि है।
15

1
संबंधित: किसी भी उपकरण के साथ: stackoverflow.com/questions/6261201/…
Ciro Santilli 病:::

जवाबों:


297

वैलग्राइंड को कैसे चलाएं

ओपी का अपमान करने के लिए नहीं, लेकिन उन लोगों के लिए जो इस सवाल पर आते हैं और अभी भी लिनक्स के लिए नए हैं- आपको अपने सिस्टम पर Valgrind स्थापित करना पड़ सकता है

sudo apt install valgrind  # Ubuntu, Debian, etc.
sudo yum install valgrind  # RHEL, CentOS, Fedora, etc.

Valgrind C / C ++ कोड के लिए आसानी से प्रयोग करने योग्य है, लेकिन अन्य भाषाओं के लिए भी उपयोग किया जा सकता है जब ठीक से कॉन्फ़िगर किया गया हो ( यह पायथन के लिए देखें )।

Valgrind चलाने के लिए, निष्पादन योग्य को एक तर्क के रूप में (कार्यक्रम के किसी भी पैरामीटर के साथ) पास करें।

valgrind --leak-check=full \
         --show-leak-kinds=all \
         --track-origins=yes \
         --verbose \
         --log-file=valgrind-out.txt \
         ./executable exampleParam1

झंडे, संक्षेप में हैं:

  • --leak-check=full: "प्रत्येक व्यक्तिगत रिसाव को विस्तार से दिखाया जाएगा"
  • --show-leak-kinds=all: "पूर्ण" रिपोर्ट में सभी "निश्चित, अप्रत्यक्ष, संभव, पहुंच योग्य" लीक प्रकार दिखाएं।
  • --track-origins=yes: गति पर अनुकूल उपयोगी आउटपुट। यह असमान मूल्यों की उत्पत्ति को ट्रैक करता है, जो मेमोरी त्रुटियों के लिए बहुत उपयोगी हो सकता है। मान लें कि यदि Valgrind अस्वीकार्य रूप से धीमा है।
  • --verbose: आप अपने कार्यक्रम के असामान्य व्यवहार के बारे में बता सकते हैं। अधिक वाचालता के लिए दोहराएं।
  • --log-file: फ़ाइल में लिखें। उपयोगी जब आउटपुट टर्मिनल स्पेस से अधिक हो।

अंत में, आप एक Valgrind रिपोर्ट देखना चाहेंगे जो इस तरह दिखाई देती है:

HEAP SUMMARY:
    in use at exit: 0 bytes in 0 blocks
  total heap usage: 636 allocs, 636 frees, 25,393 bytes allocated

All heap blocks were freed -- no leaks are possible

ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

मेरे पास एक रिसाव है, लेकिन कहां है ?

तो, आपके पास स्मृति रिसाव है, और Valgrind कुछ भी सार्थक नहीं कह रहा है। शायद, कुछ इस तरह:

5 bytes in 1 blocks are definitely lost in loss record 1 of 1
   at 0x4C29BE3: malloc (vg_replace_malloc.c:299)
   by 0x40053E: main (in /home/Peri461/Documents/executable)

आइए मेरे द्वारा लिखे गए सी कोड पर एक नज़र डालें:

#include <stdlib.h>

int main() {
    char* string = malloc(5 * sizeof(char)); //LEAK: not freed!
    return 0;
}

खैर, वहाँ 5 बाइट्स खो गए थे। यह कैसे हुआ? त्रुटि रिपोर्ट सिर्फ कहते हैं mainऔर malloc। एक बड़े कार्यक्रम में, जो नीचे शिकार करने के लिए गंभीर रूप से परेशानी होगी। इसका कारण यह है कि निष्पादन योग्य कैसे संकलित किया गया था । हम वास्तव में लाइन-बाय-लाइन विवरण प्राप्त कर सकते हैं कि क्या गलत हुआ। अपने प्रोग्राम को डीबग फ़्लैग के साथ पुन: संयोजित करें (मैं gccयहाँ उपयोग कर रहा हूँ ):

gcc -o executable -std=c11 -Wall main.c         # suppose it was this at first
gcc -o executable -std=c11 -Wall -ggdb3 main.c  # add -ggdb3 to it

अब इस डिबग बिल्ड के साथ, Valgrind कोड की सटीक लाइन को इंगित करता है जो कि लीक हुई मेमोरी को आवंटित करता है! (शब्दांकन महत्वपूर्ण है: यह ठीक नहीं हो सकता है कि आपका रिसाव कहां है, लेकिन जो लीक हुआ है। ट्रेस आपको यह पता लगाने में मदद करता है कि कहां है ।)

5 bytes in 1 blocks are definitely lost in loss record 1 of 1
   at 0x4C29BE3: malloc (vg_replace_malloc.c:299)
   by 0x40053E: main (main.c:4)

मेमोरी लीक्स एंड एरर डिबगिंग की तकनीक

  • Www.cplusplus.com का उपयोग करें ! यह C / C ++ फ़ंक्शन पर महान प्रलेखन है।
  • मेमोरी लीक के लिए सामान्य सलाह:
    • सुनिश्चित करें कि आपकी गतिशील रूप से आवंटित स्मृति वास्तव में मुक्त हो जाती है।
    • मेमोरी आवंटित न करें और पॉइंटर असाइन करना न भूलें।
    • जब तक पुरानी मेमोरी को मुक्त नहीं किया जाता है, तब तक एक नए के साथ एक सूचक को अधिलेखित न करें।
  • मेमोरी त्रुटियों के लिए सामान्य सलाह:
    • उन पते और सूचकांकों तक पहुँचें और लिखें जिन्हें आप सुनिश्चित करते हैं कि वे आपके हैं। मेमोरी त्रुटियां लीक से अलग हैं; वे अक्सर सिर्फ IndexOutOfBoundsException समस्याएं टाइप करते हैं।
    • इसे खाली करने के बाद मेमोरी में एक्सेस या राइट न करें।
  • कभी-कभी आपकी लीक / त्रुटियों को एक दूसरे से जोड़ा जा सकता है, एक आईडीई की तरह बहुत कुछ पता चलता है कि आपने अभी तक एक समापन ब्रैकेट टाइप नहीं किया है। एक मुद्दे को हल करने से दूसरों को हल किया जा सकता है, इसलिए एक को देखें जो एक अच्छा अपराधी दिखता है और इन विचारों में से कुछ को लागू करता है:

    • अपने कोड में उन कार्यों को सूचीबद्ध करें जो / पर निर्भर करते हैं / "ऑफ़ेंडिंग" कोड पर निर्भर हैं जिनमें मेमोरी त्रुटि है। कार्यक्रम के निष्पादन का पालन करें (शायद में भी gdb), और पूर्व शर्त / पोस्टकंडिशन त्रुटियों की तलाश करें। आवंटित स्मृति के जीवनकाल पर ध्यान केंद्रित करते हुए अपने कार्यक्रम के निष्पादन का पता लगाना है।
    • कोड के "अपमानजनक" ब्लॉक के कारण टिप्पणी करने की कोशिश करें (कारण, इसलिए आपका कोड अभी भी संकलित है)। यदि Valgrind त्रुटि चली जाती है, तो आपने पाया है कि वह कहाँ है।
  • यदि अन्य सभी विफल हो जाते हैं, तो इसे देखने का प्रयास करें। Valgrind के पास प्रलेखन भी है!

आम लीक्स और त्रुटियों पर एक नज़र

अपने संकेत देखो

60 bytes in 1 blocks are definitely lost in loss record 1 of 1
   at 0x4C2BB78: realloc (vg_replace_malloc.c:785)
   by 0x4005E4: resizeArray (main.c:12)
   by 0x40062E: main (main.c:19)

और कोड:

#include <stdlib.h>
#include <stdint.h>

struct _List {
    int32_t* data;
    int32_t length;
};
typedef struct _List List;

List* resizeArray(List* array) {
    int32_t* dPtr = array->data;
    dPtr = realloc(dPtr, 15 * sizeof(int32_t)); //doesn't update array->data
    return array;
}

int main() {
    List* array = calloc(1, sizeof(List));
    array->data = calloc(10, sizeof(int32_t));
    array = resizeArray(array);

    free(array->data);
    free(array);
    return 0;
}

एक शिक्षण सहायक के रूप में, मैंने इस गलती को अक्सर देखा है। छात्र एक स्थानीय चर का उपयोग करता है और मूल सूचक को अद्यतन करने के लिए भूल जाता है। यहां त्रुटि ध्यान देने योग्य है जो reallocवास्तव में आवंटित मेमोरी को कहीं और स्थानांतरित कर सकती है और पॉइंटर के स्थान को बदल सकती है। फिर हम resizeArrayयह बताए बिना छोड़ देते हैं array->dataकि सरणी को कहां स्थानांतरित किया गया था।

अवैध लेखन

1 errors in context 1 of 1:
Invalid write of size 1
   at 0x4005CA: main (main.c:10)
 Address 0x51f905a is 0 bytes after a block of size 26 alloc'd
   at 0x4C2B975: calloc (vg_replace_malloc.c:711)
   by 0x400593: main (main.c:5)

और कोड:

#include <stdlib.h>
#include <stdint.h>

int main() {
    char* alphabet = calloc(26, sizeof(char));

    for(uint8_t i = 0; i < 26; i++) {
        *(alphabet + i) = 'A' + i;
    }
    *(alphabet + 26) = '\0'; //null-terminate the string?

    free(alphabet);
    return 0;
}

ध्यान दें कि Valgrind हमें ऊपर कोड की टिप्पणी की गई पंक्ति की ओर इंगित करता है। आकार 26 की श्रेणी अनुक्रमित है [0,25] जिसके कारण *(alphabet + 26)एक अमान्य लेखन है - यह सीमा से बाहर है। अमान्य लेखन ऑफ-बाय-वन त्रुटियों का एक सामान्य परिणाम है। अपने असाइनमेंट ऑपरेशन के बाईं ओर देखें।

अमान्य पढ़ा गया

1 errors in context 1 of 1:
Invalid read of size 1
   at 0x400602: main (main.c:9)
 Address 0x51f90ba is 0 bytes after a block of size 26 alloc'd
   at 0x4C29BE3: malloc (vg_replace_malloc.c:299)
   by 0x4005E1: main (main.c:6)

और कोड:

#include <stdlib.h>
#include <stdint.h>

int main() {
    char* destination = calloc(27, sizeof(char));
    char* source = malloc(26 * sizeof(char));

    for(uint8_t i = 0; i < 27; i++) {
        *(destination + i) = *(source + i); //Look at the last iteration.
    }

    free(destination);
    free(source);
    return 0;
}

Valgrind हमें ऊपर टिप्पणी की गई रेखा की ओर इशारा करता है। यहां अंतिम पुनरावृत्ति को देखें, जो है
*(destination + 26) = *(source + 26);। हालांकि, *(source + 26)फिर से सीमा से बाहर है, इसी तरह से अमान्य लिखना। अमान्य रीड ऑफ़-ऑफ-वन त्रुटियां भी एक सामान्य परिणाम हैं। अपने असाइनमेंट ऑपरेशन के दाईं ओर देखें।


ओपन सोर्स (U / Dys) टोपिया

मुझे कैसे पता चलेगा कि रिसाव मेरा है? जब मैं किसी और के कोड का उपयोग कर रहा हूं तो मुझे अपना रिसाव कैसे मिलेगा? मुझे एक रिसाव मिला जो मेरा नहीं है; क्या मुझे कुछ करना चाहिए? सभी वैध प्रश्न हैं। सबसे पहले, 2 वास्तविक दुनिया के उदाहरण जो आम मुठभेड़ों के 2 वर्गों को दिखाते हैं।

जानसन : एक JSON लाइब्रेरी

#include <jansson.h>
#include <stdio.h>

int main() {
    char* string = "{ \"key\": \"value\" }";

    json_error_t error;
    json_t* root = json_loads(string, 0, &error); //obtaining a pointer
    json_t* value = json_object_get(root, "key"); //obtaining a pointer
    printf("\"%s\" is the value field.\n", json_string_value(value)); //use value

    json_decref(value); //Do I free this pointer?
    json_decref(root);  //What about this one? Does the order matter?
    return 0;
}

यह एक सरल कार्यक्रम है: यह JSON स्ट्रिंग पढ़ता है और इसे पार्स करता है। मेकिंग में, हम अपने लिए पार्सिंग करने के लिए लाइब्रेरी कॉल का उपयोग करते हैं। Jansson गतिशील रूप से आवश्यक आवंटन करता है क्योंकि JSON में स्वयं के नेस्टेड संरचनाएँ हो सकती हैं। हालांकि, इसका मतलब यह नहीं है कि हम decrefहर फंक्शन से हमें दी गई मेमोरी को "फ्री" या "फ्री" करते हैं। वास्तव में, यह कोड मैंने ऊपर लिखा है, दोनों एक "अमान्य रीड" और एक "अमान्य लेखन"। जब आप के लिए decrefलाइन निकालते हैं तो वे त्रुटियां दूर हो जाती हैं value

क्यों? चर valueको जैनसन एपीआई में "उधार संदर्भ" माना जाता है। Jansson आपके लिए इसकी मेमोरी का ट्रैक रखता है, और आपको बस decref JSON संरचनाओं को एक दूसरे से स्वतंत्र रखना है । यहाँ सबक: प्रलेखन पढ़ें । वास्तव में। कभी-कभी यह समझना मुश्किल होता है, लेकिन वे आपको बता रहे हैं कि ये चीजें क्यों होती हैं। इसके बजाय, इस मेमोरी त्रुटि के बारे में हमारे पास मौजूदा प्रश्न हैं।

एसडीएल : एक ग्राफिक्स और गेमिंग लाइब्रेरी

#include "SDL2/SDL.h"

int main(int argc, char* argv[]) {
    if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO) != 0) {
        SDL_Log("Unable to initialize SDL: %s", SDL_GetError());
        return 1;
    }

    SDL_Quit();
    return 0;
}

इस कोड में क्या गलत है ? यह मेरे लिए लगातार ~ 212 KiB मेमोरी लीक करता है। इसके बारे में सोचने के लिए कुछ समय निकालें। हम एसडीएल को चालू करते हैं और फिर बंद कर देते हैं। उत्तर? कुछ भी गलत नहीं है।

यह पहली बार में अजीब लग सकता है । सच कहा जाए, तो ग्राफिक्स गड़बड़ हैं और कभी-कभी आपको मानक पुस्तकालय का हिस्सा होने के नाते कुछ लीक को स्वीकार करना पड़ता है। यहां सबक: आपको हर मेमोरी लीक को छोड़ने की आवश्यकता नहीं है । कभी-कभी आपको केवल लीक को दबाने की आवश्यकता होती है क्योंकि वे ज्ञात मुद्दे हैं जिनके बारे में आप कुछ नहीं कर सकते हैं । (यह आपकी खुद की लीक को अनदेखा करने की मेरी अनुमति नहीं है!)

शून्य का उत्तर

मुझे कैसे पता चलेगा कि रिसाव मेरा है?
यह है। (99% यकीन है, वैसे भी)

जब मैं किसी और के कोड का उपयोग कर रहा हूं तो मुझे अपना रिसाव कैसे मिलेगा?
संभावना है कि कोई और पहले से ही मिल जाए। Google को आज़माएं! यदि वह विफल रहता है, तो मेरे द्वारा दिए गए कौशल का उपयोग करें। यदि वह विफल हो जाता है और आप ज्यादातर एपीआई कॉल और अपने स्वयं के स्टैक ट्रेस से थोड़ा देखते हैं, तो अगला प्रश्न देखें।

मुझे एक रिसाव मिला जो मेरा नहीं है; क्या मुझे कुछ करना चाहिए?
हाँ! अधिकांश एपीआई में बग और मुद्दों की रिपोर्ट करने के तरीके हैं। उन्हें इस्तेमाल करें! अपने प्रोजेक्ट में आपके द्वारा उपयोग किए जा रहे टूल को वापस देने में मदद करें!


आगे की पढाई

मेरे साथ लंबे समय तक रहने के लिए धन्यवाद। मुझे आशा है कि आपने कुछ सीखा है, जैसा कि मैंने इस उत्तर पर पहुंचने वाले लोगों के व्यापक स्पेक्ट्रम के लिए प्रयास किया। कुछ चीजें जो मुझे आशा है कि आपने रास्ते में पूछी हैं: सी की मेमोरी एलोकेटर कैसे काम करती है? क्या वास्तव में एक स्मृति रिसाव और एक स्मृति त्रुटि है? वे segfaults से कैसे अलग हैं? Valgrind कैसे काम करता है? यदि आपके पास इनमें से कोई भी हो, तो कृपया अपनी जिज्ञासा को पूरा करें:


4
बेहतर उत्तर, शर्म की बात है कि यह स्वीकृत उत्तर नहीं है।
ए स्मोलक

मेरा मानना ​​है कि इस तरह का काम करना एक अच्छा अभ्यास है, मैंने खुद कुछ किया
ए। स्मोलक

1
क्या मैं इसका उत्तर दे सकता हूँ और इसे अपने लिए भविष्य के संदर्भ के रूप में उपयोग कर सकता हूँ? अच्छा कार्य!
जैप

क्या memcheckउपकरण डिफ़ॉल्ट रूप से सक्षम है?
अभियोरा

@abhiarora हाँ। मैन पेज हमें बताता है कि memcheckडिफ़ॉल्ट टूल है:--tool=<toolname> [default: memcheck]
जोशुआ डेट्वेलर

146

इसे इस्तेमाल करे:

valgrind --leak-check=full -v ./your_program

जब तक वेलग्रिंड इंस्टॉल हो जाता है तब तक यह आपके प्रोग्राम के माध्यम से जाएगा और आपको बताएगा कि क्या गलत है। यह आपको संकेत और अनुमानित स्थान दे सकता है जहां आपके लीक पाए जा सकते हैं। यदि आप segfault'ing कर रहे हैं, तो इसे माध्यम से चलाने का प्रयास करें gdb


"Your_program" का क्या अर्थ है? क्या यह स्रोत कोड स्थान या एप्लिकेशन का नाम जैसे एपीके फ़ाइल है?
होवंगू

7
your_program== निष्पादन योग्य नाम या जो भी आदेश आप अपने आवेदन को चलाने के लिए उपयोग करते हैं।
रेज


1

आप निम्न रूप से .bashrc फ़ाइल में एक उपनाम बना सकते हैं

alias vg='valgrind --leak-check=full -v --track-origins=yes --log-file=vg_logfile.out'

इसलिए जब भी आप मेमोरी लीक की जांच करना चाहते हैं, तो बस करें

vg ./<name of your executable> <command line parameters to your executable>

यह वर्तमान निर्देशिका में एक Valgrind लॉग फ़ाइल उत्पन्न करेगा।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.