2011-10-19 19 views
7

मैं एक बदसूरत और विशाल गणित सी पुस्तकालय, शायद एक बार f2c द्वारा उत्पादित डिबग करने के लिए की जरूरत है पर नज़र रखने। कोड स्थानीय स्थैतिक चर का दुरुपयोग करती है, और दुर्भाग्य से कहीं यह तथ्य यह है कि इन स्वचालित रूप से अपने प्रवेश समारोह में दो बार एक ही इनपुट के साथ कहा जाता है 0 पर प्रारंभ कर रहे हैं, यह अलग परिणाम दे रहा है फायदा उठाने के लिए लगता है। अगर मैं लाइब्रेरी को अनलोड करता हूं और इसे फिर से लोड करता हूं, तो यह सही तरीके से काम करता है। इसे तेज़ होने की जरूरत है, इसलिए मैं लोड/अनलोड से छुटकारा पाना चाहता हूं।अप्रारंभीकृत स्थैतिक चर

मेरा प्रश्न है कि कैसे valgrind साथ या मैन्युअल रूप से पूरे कोड के माध्यम से चलने के बिना किसी अन्य उपकरण के द्वारा इन त्रुटियों को उजागर करने के लिए है।

मैं स्थानों पर जहां एक स्थानीय स्थिर चर घोषित किया जाता है, पहले पढ़ा है, और लिखा ही बाद में शिकार कर रहा हूँ। (- यह इतना बदसूरत है हां) समस्या भी आगे तथ्य यह है कि स्थैतिक चर कभी कभी संकेत के माध्यम से आगे पारित कर रहे हैं द्वारा जटिल है।

मैं समझता हूँ कि बहस कर सकते हैं कि है कि इस तरह की गलतियों आवश्यक नहीं है, एक स्वचालित उपकरण द्वारा पता लगाया जाना चाहिए, क्योंकि कुछ परिस्थितियों में यह वास्तव में इच्छित व्यवहार है। फिर भी, ऑटो-प्रारंभिक स्थानीय स्थैतिक चर "गंदी" बनाने का कोई तरीका है?

+0

दिलचस्प सवाल है, लेकिन वालग्रिंड को "स्थानीय" या "घोषित" का कोई ज्ञान नहीं है। मुझे लगता है कि यह कोड विश्लेषण द्वारा किया जाना चाहिए, निष्पादन योग्य विश्लेषण नहीं। – aschepler

+0

हम पीसी लिंट का उपयोग करते हैं, लेकिन यह बहुत सारी चेतावनियों का नरक उत्पन्न करता है ताकि वास्तविक हॉटस्पॉट को कभी-कभी अंधेरे में मछली पकड़ने की तरह मिल सके। –

उत्तर

5

शैतान विवरण में है, लेकिन यह आप के लिए काम कर सकते हैं:

पहले, Frama-C मिलता है। यदि आप यूनिक्स का उपयोग कर रहे हैं, तो आपके वितरण में एक पैकेज हो सकता है। पैकेज अंतिम संस्करण नहीं होगा लेकिन यह काफी अच्छा हो सकता है और यदि आप इसे इस तरह इंस्टॉल करते हैं तो यह आपको कुछ समय बचाएगा।

मान लें कि आपका उदाहरण के रूप में नीचे केवल इतना बड़ा है कि यह स्पष्ट नहीं है कि क्या गलत है,:

int add(int x, int y) 
{ 
    static int state; 
    int result = x + y + state; // I tested it once and it worked. 
    state++; 
    return result; 
} 

प्रकार की तरह एक आदेश:

frama-c -lib-entry -main add -deps ugly.c 

विकल्प -lib-entry -main add मतलब है "समारोह add को देखो "। विकल्प -deps कार्यात्मक निर्भरताओं की गणना करता है। आप लॉग में इन "कार्यात्मक निर्भरता" मिल जाएगा:

[from] Function add: 
    state FROM state; (and default:false) 
    \result FROM x; y; state; (and default:false) 

यह वास्तविक आदानों add के परिणामों पर निर्भर है, और वास्तविक आउटपुट इन इनपुट की गणना से पढ़ा स्थैतिक चर सहित सूचीबद्ध करता है और संशोधित। उपयोग किए जाने से पहले ठीक से प्रारंभ किया गया एक स्थैतिक चर सामान्य रूप से इनपुट के रूप में प्रकट नहीं होता है, जब तक कि विश्लेषक यह निर्धारित करने में असमर्थ था कि इसे हमेशा पढ़ने से पहले प्रारंभ किया गया था।

लॉग \result की निर्भरता के रूप में दिखाता है। यदि आपको लौटाया गया परिणाम केवल तर्कों पर निर्भर करता है (जिसका अर्थ है कि एक ही तर्क के साथ दो कॉल एक ही परिणाम उत्पन्न करते हैं), यह संकेत है कि परिवर्तनीय state के साथ यहां कुछ गड़बड़ हो सकती है।

उपर्युक्त पंक्तियों में दिखाया गया एक और संकेत यह है कि फ़ंक्शन state संशोधित करता है।

इससे मदद मिल सकती है या नहीं। विकल्प का अर्थ है कि विश्लेषक यह नहीं मानता कि किसी भी गैर-स्थिर स्थैतिक चर ने विश्लेषण के दौरान कार्य को उस समय अपना मूल्य रखा है, ताकि आपके कोड के लिए यह बहुत ही कम हो सके। इसके आस-पास के तरीके हैं, लेकिन फिर यह आप पर निर्भर करता है कि क्या आप इन तरीकों को सीखने के लिए समय लेना चाहते हैं।

संपादित करें: यहाँ एक अधिक जटिल उदाहरण है:

void initialize_1(int *p) 
{ 
    *p = 0; 
} 

void initialize_2(int *p) 
{ 
    *p; // I made a mistake here. 
} 

int add(int x, int y) 
{ 
    static int state1; 
    static int state2; 

    initialize_1(&state1); 
    initialize_2(&state2); 

    // This is safe because I have initialized state1 and state2: 
    int result = x + y + state1 + state2; 

    state1++; 
    state2++; 
    return result; 
} 

इस उदाहरण पर, एक ही आदेश परिणाम का उत्पादन:

[from] Function initialize_1: 
     state1 FROM p 
[from] Function initialize_2: 
[from] Function add: 
     state1 FROM \nothing 
     state2 FROM state2 
     \result FROM x; y; state2 

क्या आप initialize_2 के लिए देख निर्भरता की एक खाली सूची है, जिसका अर्थ है कि फ़ंक्शन कुछ भी निर्दिष्ट नहीं करता है। मैं सिर्फ एक खाली सूची के बजाय एक स्पष्ट संदेश प्रदर्शित करके इस मामले को स्पष्ट कर दूंगा। यदि आप जानते हैं कि initialize_1, initialize_2 या add का कोई भी कार्य क्या करना है, तो आप विश्लेषण के परिणामों के लिए एक प्राथमिक ज्ञान की तुलना कर सकते हैं और देख सकते हैं कि initialize_2 और add के लिए कुछ गलत है।

दूसरा संपादित करें: और अब मेरा उदाहरण initialize_1 के लिए कुछ अजीब दिखाता है, तो शायद मुझे यह समझा जाना चाहिए। परिवर्तनीय state1p पर इस बात पर निर्भर करता है कि pstate1 पर लिखने के लिए उपयोग किया जाता है, और यदि p अलग था, तो state1 का अंतिम मान अलग-अलग होगा। यहाँ एक आखिरी उदाहरण है:

int t[10]; 

void initialize_index(int i) 
{ 
    t[i] = 1; 
} 

int main(int argc, char **argv) 
{ 
    initialize_index(argv[1][0]-'0'); 
} 
आदेश frama-c -deps t.c साथ

, निर्भरता initialize_index गणना कर रहे हैं: यदि i है

[from] Function initialize_index: 
     t[0..9] FROM i (and SELF) 

इसका मतलब है कि कोशिकाओं के प्रत्येक i पर निर्भर करता है (यह संशोधित किया जा सकता उस विशेष सेल की अनुक्रमणिका)। प्रत्येक सेल भी अपना मान रख सकता है (यदि i कोई अन्य सेल इंगित करता है): यह नवीनतम संस्करण में (and SELF) उल्लेख के साथ इंगित किया गया है, और पिछले संस्करणों में एक अधिक अस्पष्ट (and default:true) के साथ संकेत दिया गया था।

+0

धन्यवाद! यह उपयोगी लगता है; हालांकि मैं कोशिश नहीं करूँगा, क्योंकि मुझे पहले से ही सबसे पुरानी विधि के साथ मेरी बग मिली है। बीटीडब्ल्यू, फ्रेमा-सी ट्रैक चर पॉइंटर्स के माध्यम से पारित कर सकते हैं? मेरे मामले में statics चारों ओर भेजा जाता है, और वे एक अलग समारोह में शुरू किया जा सकता है। – takbal

+0

@ टाकबल हां, यह हो सकता है, लेकिन पॉइंटर्स की उपस्थिति में, आपको स्मृति राज्यों का वर्णन करने की आवश्यकता हो सकती है जिसमें कार्यों को अधिक सटीक कहा जाता है। मैं अपने उत्तर को एक और पूर्ण उदाहरण के साथ संपादित करूंगा। Http://frama-c.com/download/frama-c-value-analysis.pdf सेक्शन 2.5.2 में, इस विश्लेषण का उपयोग पॉइंटर्स और सभी के साथ एक पूर्ण क्रिप्टोग्राफ़िक हैश फ़ंक्शन की निर्भरताओं की जांच के लिए किया जाता है (लेकिन नहीं सबसे हड़ताली उदाहरण है क्योंकि कोड साफ़ है और कहने के लिए बहुत कुछ नहीं है। –

1

मुझे लगता है कि आप के लिए यह करता है किसी भी पुस्तकालय का पता नहीं है, लेकिन मैं नियमित अभिव्यक्ति का उपयोग कर उन्हें खोजने के लिए इस पर गौर करेंगे।

rgrep "स्थिर \ s * पूर्णांक" path/to/src/जड़ की तरह कुछ | grep -v = | grep -v "("

इसे समान स्थिर चिह्न के बिना घोषित सभी स्थैतिक int चरों को वापस करना चाहिए, और अंतिम पाइप को उनमें सेष्ठक के साथ कुछ भी हटा देना चाहिए (funcions से छुटकारा पाने के लिए)। एक अच्छा बदलाव है कि यह नहीं होगा आप के लिए वास्तव में काम करते हैं, लेकिन ग्रेप के साथ प्रयोग करना तो आप इस को ट्रैक करने के लिए सबसे तेज़ तरीका हो सकता है।

बेशक

, एक बार आप एक काम करता है कि आप के लिए खोज करने के लिए चर के अन्य प्रकार के सभी के साथ पूर्णांक जगह ले सकता है लगता है उन भी। HTH

+0

एक शर्म की बात है लेकिन व्यावहारिक रूप से सभी चर इनिट के बिना स्थैतिक घोषित किए जाते हैं ... शायद यह f2c का गलत कार्य है। – takbal

0

मेरा प्रश्न है कि इन त्रुटियों को उजागर करने के ... है

लेकिन ये त्रुटियां नहीं हैं: उम्मीद है कि एक स्थैतिक चर 0 को प्रारंभ किया गया है, यह अपेक्षाकृत मान्य है, क्योंकि यह कुछ अन्य मूल्य असाइन कर रहा है।

तो ऐसे टूल के लिए पूछना जो स्वचालित रूप से गैर -errors को संतोषजनक परिणाम देने की संभावना नहीं है।

अपने विवरण से, ऐसा लगता है कि somefunc() रिटर्न सही परिणाम पहली बार यह कहा जाता है, और बाद में कॉल पर गलत परिणाम।

ऐसी समस्याओं को डीबग करने का सबसे आसान तरीका है दो जीडीबी सत्र साइड-साइड: एक ताजा लोड (सही उत्तर की गणना करेगा), और "दूसरा पुनरावृत्ति" (गलत जवाब की गणना करेगा) के साथ। फिर "समांतर में" दोनों सत्रों के माध्यम से कदम उठाएं, और देखें कि उनकी गणना या नियंत्रण प्रवाह अलग होने के लिए कहां शुरू होता है।

के बाद से आप आमतौर पर प्रभावी ढंग से आधे में समस्या विभाजित कर सकते हैं, यह अक्सर नहीं देर बग को खोजने के लिए है। हमेशा पुन: उत्पन्न करने के लिए सबसे आसान विकल्प हैं। बस कर दो।

+0

जैसा कि मैंने मूल पोस्ट में कहा था, मैं समझता हूं कि कंपाइलर/लिंकर के दृष्टिकोण से यह एक बग नहीं है। प्रश्न इस तरह के उपयोग को एक बग में बदलने के तरीके की तरह है जो स्वचालित रूप से पता लगाया जा सकता है। – takbal

1

स्टेटिक कोड विश्लेषण उपकरण अप्रारंभीकृत चर का उपयोग जैसे विशिष्ट प्रोग्रामिंग त्रुटियों को ढूँढने में काफी अच्छा कर रहे हैं।Here सी

दुर्भाग्य से मैं सूची में से किसी भी उपकरण की अनुशंसा नहीं कर सकता हूं। मैं केवल दो वाणिज्यिक उत्पादों, Coverity और Klocwork से परिचित हूं। कवरेज बहुत अच्छी है (और महंगा)। Klocwork इतना है (लेकिन कम महंगा)।

+0

मैंने पहले स्प्लिंट की कोशिश की है लेकिन कुछ भी उपयोगी नहीं कहा। – takbal

+0

जब आप घोषित किए जाते हैं तो आप सभी स्थानीय चर को शून्य में क्यों शुरू नहीं करते हैं? क्या यह एक बड़ा कोड आधार है? – Miguel

+0

हां यह बहुत बड़ा है। आप सही हैं, मैं निश्चित रूप से सभी परिभाषाओं को 0-आधारित init, या यहां तक ​​कि NaN के लिए NaN में बदलने के लिए कुछ regexp चाल कर सकता हूं। यह एक रास्ता है जिसकी मैंने अंत में कोशिश नहीं की है। – takbal

1

अंत में मैंने जो किया वह कोड से सभी स्थिर क्वालीफायरों को '# परिभाषित स्थिर' द्वारा हटा दिया गया है। यह अवांछित स्थिर उपयोग को अमान्य उपयोग में बदल देता है, और जिस प्रकार के दुरुपयोग का मैं शिकार कर रहा हूं उसे उपकरण द्वारा अनदेखा किया जा सकता है।

मेरे वास्तविक मामले में यह बग की जगह निर्धारित करने के लिए पर्याप्त था, लेकिन एक सामान्य स्थिति में इसे परिष्कृत किया जाना चाहिए यदि स्थिर वास्तव में कुछ महत्वपूर्ण कर रहे हैं, तो धीरे-धीरे 'स्थैतिक' फिर से जोड़कर कोड विफल हो जाता है जारी रहना।