2015-05-02 8 views
10

चला रहा है मुझे खेद है कि मैं इसे पुन: उत्पन्न करने के लिए कोड पोस्ट नहीं कर सकता। मेरी समस्या यह है कि मुझे नहीं पता कि इस मुद्दे को डीबग करने के बारे में कैसे जाना है।कभी-कभी PTRACE_EVENT_VFORK गायब होने पर ptrace

मैं एक प्रक्रिया और उसके बच्चों (और बच्चों के बच्चों) का पता लगाने के लिए PTRACE_O_TRACEFORK | PTRACE_O_TRACEEXEC | PTRACE_O_TRACEVFORK | PTRACE_O_TRACEVFORKDONE | PTRACE_O_TRACECLONE के साथ ptrace का उपयोग कर रहा हूं। तंत्र strace जैसा है, लेकिन थोड़ा अलग उद्देश्यों के साथ, क्योंकि मैं केवल उन फ़ाइलों को ट्रैक कर रहा हूं जो पढ़ या संशोधित हैं।

मेरा कोड (सी में लिखा गया) x86-64 आर्किटेक्चर (और i386 पर भी कम परीक्षण) पर डेबियन wheezy और डेबियन जेसी पर ठीक काम करता है। जब मैं उबंटू सटीक x86-64 आभासी मशीन (जो 3.2.0 कर्नेल का उपयोग करता है) पर संकलन और चलाने की कोशिश करता है, तो मैं परेशानी में भाग लेता हूं।

सटीक मशीन पर, मैं कभी कभी लगता है कि मैं प्राप्त नहीं एक PTRACE_EVENT_VFORK के तुरंत बाद एक vfork कॉल होता है, लेकिन इसके बजाय घटनाओं प्राप्त करना शुरू (SIGSTOP घटनाओं की एक जोड़ी है, और कुछ सिस्टम कॉल) कभी प्राप्त किए बिना PTRACE_EVENT_VFORK घटना। मुझे सिस्टम कॉल किए जाने में कुछ भी संदिग्ध दिखाई नहीं देता है, और व्यवहार अनुमानित नहीं है।

मुझे यकीन नहीं है कि इसे कम से कम त्रुटि मामले में कम करने का प्रयास करने के लिए क्या करना है, और मुझे वास्तव में कोई जानकारी नहीं है कि क्या गलत हो रहा है, कभी भी गुम घटनाओं के इस व्यवहार को नहीं देखा। यह कल्पना की जा सकती है कि अंतर कर्नेल नहीं है, बल्कि निर्माण उपकरण जो मैं ट्रेसिंग कर रहा हूं (जो अजगर + जीसीसी का संयोजन है)।

कोई सुझाव?

+0

यदि कोई यहां सहायता नहीं कर सकता है, तो linux-kernel मेलिंग सूची पर पूछने का प्रयास करें। (मदद करने की संभावना नहीं है, लेकिन शॉट के लायक है।) 'Ptrace' के विकल्प के रूप में, आप' ओपन' 'पर कॉल को रोकने के लिए [LD_PRELOAD चाल] (http://stackoverflow.com/q/426230/) का उपयोग कर सकते हैं, पढ़ें ',' लिखें ', और' बंद करें '। और शुभकामनाएं; यह बुरा लगता है। – Nemo

+1

मैंने एलडी_PRELOAD से परहेज किया है, क्योंकि मैं चाहता हूं कि मेरा कोड स्थिर रूप से जुड़ी बाइनरी का पता लगाने में सक्षम हो। और स्पष्ट रूप से, मैं लिनक्स-कर्नेल से डर रहा हूँ! lol :) –

+0

मैं सहमत हूं कि एलडी_PRELOAD ऐसा करने के लिए एक सौहार्दपूर्ण/वैध तरीका नहीं है। दुर्भाग्य से मैं vfork ट्रेस विफलताओं का कारण नहीं जानता। यदि आप लीगेसी पार्स शैली की बजाय seccomp ट्रेसिंग मोड का उपयोग कर सकते हैं, तो यह कम त्रुटि-प्रवण और अधिक पोर्टेबल हो सकता है। –

उत्तर

2

मैं हाल ही में कुछ इसी तरह काम कर रहा था। मुझे संदेह है कि आपने अपनी समस्या को बहुत पहले हल कर लिया है या छोड़ दिया है, लेकिन चलो पोस्टरिटी के लिए यहां एक उत्तर लिखें।

PTRACE_SETOPTIONS के साथ पंजीकृत विभिन्न कार्यक्रम सामान्य ptrace ईवेंट से अलग संदेश उत्पन्न करते हैं। लेकिन सामान्य घटनाएं अभी भी उत्पन्न हुई हैं। एक सामान्य घटना यह है कि एक नई फोर्क प्रक्रिया शुरू हो जाती है और ट्रेसर से जारी रहना पड़ता है।

इसका अर्थ यह है कि यदि आपके पास PTRACE_O_TRACEFORK (या VFORK) waitpid के साथ आप पंजीकृत घटनाएं हैं तो एक ही प्रक्रिया के लिए दो बार ट्रिगर करेंगे।

WIFSTOPPED(status) && (WSTOPSIG(status) & 0xff == SIGSTOP) 

एक दूसरे के साथ हो जाएगा::

एक एक स्थिति यह है कि साथ होगा

WIFSTOPPED(status) && (WSTOPSIG(status) & 0xff == 0) && 
    ((status >> 16) == PTRACE_EVENT_FORK) /* or VFORK */ 

वहाँ कर्नेल से किसी भी गारंटी हो प्रतीत नहीं होता है जो आदेश वे करेंगे में पहुंचें। मैंने पाया कि यह मेरे सिस्टम पर 50/50 के करीब है। बनाने इस काम था PTRACE_CONT नहीं भेजने के लिए जब तक हम दोनों घटनाओं प्राप्त करने के लिए

static void 
proc_register(struct magic *pwi, pid_t pid, bool fork) { 
    /* 
    * When a new process starts two things happen: 
    * - We get a wait with STOPPED, SIGTRAP, PTRACE_EVENT_{CLONE,FORK,VFORK} 
    * - We get a wait with STOPPED, SIGSTOP 
    * 
    * Those can come in any order, so to get the proc in the right 
    * state this function should be called twice on every new proc. If 
    * it's called with fork first, we set the state to NEW_FORKED, if 
    * it's called with STOP first, we set NEW_STOPPED. Then when the 
    * other call comes, we set the state to TRACED and continue the 
    * process. 
    */ 
    if ((p = find_proc(pwi, pid)) == NULL) { 
      p = calloc(1, sizeof(*p)); 
      p->pid = pid; 
      TAILQ_INSERT_TAIL(&pwi->procs, p, list); 
      if (fork) { 
        p->state = NEW_FORKED; 
      } else { 
        p->state = NEW_STOPPED; 
      } 
    } else { 
      assert((fork && p->state == NEW_STOPPED) || (!fork && p->state == NEW_FORKED)); 
      p->state = TRACED; 
      int flags = PTRACE_O_TRACEEXEC|PTRACE_O_TRACEEXIT|PTRACE_O_TRACEFORK|PTRACE_O_TRACEVFORK; 

      if (ptrace(PTRACE_SETOPTIONS, pid, NULL, flags)) 
        err(1, "ptrace(SETOPTIONS, %d)", pid); 
      if (ptrace(PTRACE_CONT, pid, NULL, signal) == -1) 
        err(1, "ptrace(CONT, %d, %d)", pid, signal); 
    } 
} 
[...] 
    pid = waitpid(-1, &status, __WALL); 
    if (WIFSTOPPED(status) && (WSTOPSIG(status) & 0xff == SIGSTOP)) { 
      proc_register(magic, pid, false); 
    } else if (WIFSTOPPED(status) && (WSTOPSIG(status) & 0xff == 0) && ((status >> 16) == PTRACE_EVENT_FORK)) { 
      proc_register(magic, pid, true); 
    } else { 
      /* ... */ 
    } 

कुंजी:

यह मेरा कोड कुछ इस तरह दिखता को संभालने के लिए। यह पता लगाने के दौरान कि यह कैसे काम करता है मैं PTRACE_CONT रास्ता बहुत अधिक भेज रहा था और कर्नेल ने उन्हें खुशी से स्वीकार कर लिया था, जो कभी-कभी PTRACE_EVENT_FORK से पहले लंबे समय से बाहर निकलने वाली मेरी प्रक्रियाओं का कारण बनता था। इसने डीबग करना मुश्किल बना दिया।

एनबी। मुझे इस बारे में कोई भी दस्तावेज नहीं मिला है या कुछ भी कह रहा है कि यह वही तरीका होना चाहिए।मुझे अभी पता चला है कि यह चीजें काम करता है क्योंकि चीजें आज हैं। YMMV।

संबंधित मुद्दे