วิธีแก้ไขข้อผิดพลาดฮีปเสียหาย


166

ฉันกำลังดีบักแอปพลิเคชัน C + + แบบหลายเธรดแบบเธรดภายใต้ Visual Studio 2008 ในโอกาสที่สุ่มฉันได้รับข้อผิดพลาด "Windows เรียกจุดพัก ... " ด้วยข้อผิดพลาดที่อาจเกิดจากการทุจริตใน กอง. ข้อผิดพลาดเหล่านี้จะไม่ทำให้แอปพลิเคชันหยุดทำงานทันทีแม้ว่าจะมีความผิดพลาดหลังจากนั้นก็ตาม

ปัญหาใหญ่ของข้อผิดพลาดเหล่านี้คือเกิดขึ้นหลังจากความเสียหายเกิดขึ้นจริงซึ่งทำให้ยากต่อการติดตามและตรวจแก้จุดบกพร่องโดยเฉพาะอย่างยิ่งในแอปพลิเคชันแบบมัลติเธรด

  • สิ่งใดที่ทำให้เกิดข้อผิดพลาดเหล่านี้ได้บ้าง

  • ฉันจะดีบักได้อย่างไร

ยินดีต้อนรับเคล็ดลับเครื่องมือวิธีการ enlightments ...

คำตอบ:


129

Application Verifierรวมกับเครื่องมือแก้ไขจุดบกพร่องสำหรับ Windowsเป็นการตั้งค่าที่น่าทึ่ง คุณจะได้รับทั้งสองเป็นส่วนหนึ่งของโปรแกรมควบคุม Kit Windows หรือเบา SDK (ค้นพบเกี่ยวกับ Application Verifier เมื่อทำการค้นคว้าคำถามก่อนหน้านี้เกี่ยวกับปัญหาการทุจริตของฮีป ) ฉันเคยใช้ BoundsChecker และ Insure ++ (ที่กล่าวถึงในคำตอบอื่น ๆ ) ในอดีตด้วยเช่นกันถึงแม้ว่าฉันจะประหลาดใจว่าการทำงานของ Application Verifier เป็นเท่าใด

รั้วไฟฟ้า (aka "efence"), dmalloc , valgrindและอื่น ๆ ล้วน แต่เป็นที่กล่าวขวัญ แต่สิ่งเหล่านี้ส่วนใหญ่ง่ายกว่าที่จะทำงานภายใต้ * nix มากกว่า Windows Valgrind ยืดหยุ่นได้อย่างน่าขัน: ฉันได้ดีบั๊กซอฟต์แวร์เซิร์ฟเวอร์ขนาดใหญ่ที่มีปัญหาฮีปจำนวนมากที่ใช้งาน

เมื่อทุกอย่างล้มเหลวคุณสามารถให้ผู้ให้บริการระดับโลกของคุณเองใหม่ / ลบและ malloc / calloc / realloc มากเกินไป - วิธีการทำจะแตกต่างกันเล็กน้อยขึ้นอยู่กับคอมไพเลอร์และแพลตฟอร์ม - และนี่จะเป็นการลงทุนเล็กน้อย - แต่มันอาจจ่ายในระยะยาว รายการคุณสมบัติที่พึงประสงค์ควรดูคุ้นเคยจาก dmalloc และ electricfence และหนังสือยอดเยี่ยมที่น่าประหลาดใจในการเขียนรหัสของแข็ง :

  • ค่ายาม : อนุญาตให้มีพื้นที่เพิ่มขึ้นเล็กน้อยก่อนและหลังการจัดสรรแต่ละครั้งซึ่งเป็นไปตามข้อกำหนดการจัดตำแหน่งสูงสุด กรอกข้อมูลด้วยหมายเลขเวทย์มนตร์ (ช่วยจับบัฟเฟอร์โอเวอร์โฟลและอันเดอร์อันเดอร์และตัวชี้ "ไวด์" เป็นครั้งคราว)
  • alloc fill : เติมการจัดสรรใหม่ด้วยค่า non-0 magic - Visual C ++ จะทำสิ่งนี้ให้คุณใน Debug builds (ช่วยจับการใช้ vars ที่ไม่กำหนดค่าเริ่มต้น)
  • free fill : เติมหน่วยความจำที่ถูกปลดปล่อยด้วยค่า non-0 เวทย์, ออกแบบมาเพื่อกระตุ้น segfault ถ้ามันถูกอ้างอิงในกรณีส่วนใหญ่ (ช่วยจับพอยเตอร์ห้อย)
  • ล่าช้าฟรี : อย่าส่งคืนหน่วยความจำที่ได้รับอิสระไปยังฮีปสักพักเก็บให้เต็ม แต่ไม่สามารถใช้งานได้ (ช่วยจับพอยน์เตอร์ที่ห้อยต่องแต่งมากขึ้น
  • การติดตาม : ความสามารถในการบันทึกในบางครั้งการจัดสรรจะมีประโยชน์

โปรดทราบว่าในระบบโฮมบรูว์ท้องถิ่นของเรา (สำหรับเป้าหมายแบบฝัง) เราแยกการติดตามออกจากสิ่งอื่น ๆ ส่วนใหญ่เนื่องจากค่าใช้จ่ายในการดำเนินการสูงกว่ามาก


หากคุณสนใจที่จะให้เหตุผลมากขึ้นในการโอเวอร์โหลดฟังก์ชั่น / ตัวดำเนินการเหล่านี้ลองดูคำตอบของฉันที่ "เหตุผลใดที่ทำให้โอเวอร์โหลดตัวดำเนินการทั่วโลกใหม่และลบ" ; การโปรโมตตนเองอย่างไร้ยางอายนั้นจะแสดงเทคนิคอื่น ๆ ที่เป็นประโยชน์ในการติดตามข้อผิดพลาดของกองการคอร์รัปชั่นรวมถึงเครื่องมืออื่น ๆ ที่เกี่ยวข้อง


เพราะผมเก็บหาคำตอบของฉันเองที่นี่เมื่อค้นหา alloc / ฟรี / รั้วค่าใช้ MS นี่คือคำตอบที่ครอบคลุม Microsoft dbgheap ค่าเติม


3
สิ่งเล็ก ๆ ที่น่าสังเกตเกี่ยวกับ Application Verifier: คุณต้องลงทะเบียนสัญลักษณ์ของ Application Verifier ข้างหน้าสัญลักษณ์เซิร์ฟเวอร์สัญลักษณ์ microsoft ในพา ธ การค้นหาสัญลักษณ์ของคุณหากคุณใช้ ... ใช้การค้นหาเล็กน้อยเพื่อหาสาเหตุ! avrf ไม่ได้ ค้นหาสัญลักษณ์ที่ต้องการ
พูดที่

Application Verifier ช่วยได้มากและเมื่อรวมกับการคาดเดาบางอย่างฉันก็สามารถแก้ปัญหาได้! ขอบคุณมากและสำหรับคนอื่น ๆ ด้วยเช่นกันสำหรับการให้คะแนนที่เป็นประโยชน์

Application Verifier ต้องใช้กับ WinDbg หรือควรใช้กับดีบักเกอร์ Visual Studio หรือไม่ ฉันพยายามใช้ แต่ไม่ได้เพิ่มข้อผิดพลาดหรือทำอะไรเลยเมื่อฉันดีบักใน VS2012
นาธานรีด

@NathanReed: ฉันเชื่อว่าใช้งานได้กับ VS เช่นกัน - ดูmsdn.microsoft.com/en-us/library/ms220944(v=vs.90).aspx - แม้ว่าจะทราบว่าลิงก์นี้มีไว้สำหรับ VS2008 แต่ฉันไม่ แน่ใจเกี่ยวกับรุ่นที่ใหม่กว่า หน่วยความจำค่อนข้างคลุมเครือ แต่ฉันเชื่อว่าเมื่อฉันมีปัญหาในลิงก์ "คำถามก่อนหน้า" ฉันเพิ่งรัน Application Verifier และบันทึกตัวเลือกรันโปรแกรมและเมื่อมันล้มเหลวฉันเลือก VS เพื่อดีบักด้วย AV ทำให้มันผิดพลาด / ยืนยันก่อนหน้านี้ คำสั่ง! avrf เฉพาะ WinDbg เท่าที่ฉันรู้ หวังว่าคนอื่นสามารถให้ข้อมูลเพิ่มเติม!
อ่าน leander

ขอบคุณ ฉันได้แก้ไขปัญหาดั้งเดิมของฉันแล้วและมันก็ไม่ใช่ว่าจะเป็นกองการคอรัปชั่น แต่ก็เป็นอย่างอื่นดังนั้นจึงอาจอธิบายได้ว่าทำไม App Verifier จึงไม่พบสิ่งใดเลย :)
นาธานรีด

35

คุณสามารถตรวจสอบปัญหาการทุจริตฮีปจำนวนมากได้ด้วยการเปิดใช้งานเพจฮีปสำหรับแอปพลิเคชันของคุณ ในการทำเช่นนี้คุณต้องใช้ gflags.exe ซึ่งเป็นส่วนหนึ่งของเครื่องมือการดีบักสำหรับ Windows

เรียกใช้ Gflags.exe และในตัวเลือกไฟล์รูปภาพสำหรับปฏิบัติการของคุณให้เลือกตัวเลือก "เปิดใช้งาน Heap หน้า"

ตอนนี้รีสตาร์ท exe ของคุณและแนบไปกับดีบักเกอร์ ด้วยการเปิดใช้งาน Page Heap แอปพลิเคชันจะแตกเป็นดีบั๊กเมื่อเกิดความเสียหายของฮีปใด ๆ


ใช่ แต่เมื่อฉันได้รับฟังก์ชั่นการโทรนี้ในการถ่ายโอนข้อมูล callstack ของฉัน (หลังจากหน่วยความจำล่ม): wow64! Wow64NotifyDebugger ฉันควรทำอย่างไร ฉันยังไม่รู้ว่าเกิดอะไรขึ้นในใบสมัครของฉัน
Guillaume07

เพิ่งลองใช้ gflags เพื่อแก้ไขข้อบกพร่องฮีปฮีปที่นี่เครื่องมือเล็ก ๆ ที่มีประโยชน์มากแนะนำเป็นอย่างยิ่ง กลับกลายเป็นว่าฉันกำลังเข้าถึงหน่วยความจำอิสระซึ่งเมื่อใช้กับ gflags ทันทีจะบุกเข้าไปในการดีบัก ... Handy!
เดฟ F

เครื่องมือที่ยอดเยี่ยม! เพิ่งพบข้อผิดพลาดที่ฉันกำลังตามล่าหามาหลายวันเพราะ Windows ไม่ได้พูดถึงที่อยู่ของการคอร์รัปชั่นเพียงแค่ "บางอย่าง" ผิดที่ไม่ได้ช่วยอะไรเลย
Devolus

สายไปงานเลี้ยงเล็กน้อย แต่ฉันสังเกตเห็นการใช้หน่วยความจำเพิ่มขึ้นอย่างมีนัยสำคัญของฉันแอปพลิเคชันที่ฉันกำลังดีบั๊กเมื่อฉันเปิด Page Heap น่าเสียดายที่แอพพลิเคชั่น (32 บิต) หมดหน่วยความจำก่อนที่จะมีการตรวจจับฮีปเสียหาย ความคิดใดที่จะจัดการกับปัญหานั้นได้อย่างไร
uceumern

13

หากต้องการทำให้ช้าลงและทำการตรวจสอบรันไทม์จำนวนมากจริงๆให้ลองเพิ่มสิ่งต่อไปนี้ที่ด้านบนสุดmain()หรือเทียบเท่าใน Microsoft Visual Studio C ++

_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF | _CRTDBG_CHECK_ALWAYS_DF );


8

สิ่งใดที่ทำให้เกิดข้อผิดพลาดเหล่านี้ได้บ้าง

การทำสิ่งที่ซุกซนกับหน่วยความจำเช่นการเขียนหลังจากสิ้นสุดบัฟเฟอร์หรือการเขียนลงในบัฟเฟอร์หลังจากที่ถูกปล่อยกลับไปที่กอง

ฉันจะดีบักได้อย่างไร

ใช้เครื่องมือที่เพิ่มการตรวจสอบขอบเขตอัตโนมัติให้กับปฏิบัติการของคุณ: เช่น valgrind บน Unix หรือเครื่องมือเช่น BoundsChecker (Wikipedia แนะนำ Purify และ Insure ++) บน Windows

ระวังว่าสิ่งเหล่านี้จะทำให้แอปพลิเคชันของคุณช้าลงดังนั้นจึงอาจใช้ไม่ได้หากคุณเป็นแอปพลิเคชันแบบเรียลไทม์

เครื่องมือช่วยเหลือ / เครื่องมือการดีบักที่เป็นไปได้อีกอย่างหนึ่งอาจเป็น HeapAgent ของ MicroQuill


1
การสร้างแอปพลิเคชันใหม่ด้วยการแก้ไขข้อบกพร่องรันไทม์ (/ MDd หรือการตั้งค่าสถานะ / MTd) จะเป็นขั้นตอนแรกของฉัน การตรวจสอบเหล่านี้ดำเนินการเพิ่มเติมที่ malloc และไม่เสียค่าใช้จ่ายและมักจะเลิกใช้อย่างมีประสิทธิภาพเมื่อทำการ จำกัด ตำแหน่งของบั๊กให้แคบลง
ลูกจ้างรัสเซีย

HeapAgent ของ MicroQuill: ไม่ค่อยมีคนเขียนหรือได้ยินเกี่ยวกับเรื่องนี้มากนัก แต่สำหรับความเสียหายของกองมันควรอยู่ในรายชื่อของคุณ
Samrat Patil

1
BoundsChecker ทำงานได้ดีเหมือนการทดสอบควัน แต่อย่าคิดว่าจะรันโปรแกรมภายใต้มันในขณะที่พยายามเรียกใช้โปรแกรมนั้นในการผลิตเช่นกัน การชะลอตัวสามารถอยู่ที่ใดก็ได้ตั้งแต่ 60x ถึง 300x ขึ้นอยู่กับตัวเลือกที่คุณใช้และไม่ว่าคุณจะใช้คุณสมบัติเครื่องมือคอมไพเลอร์หรือไม่ก็ตาม ข้อจำกัดความรับผิดชอบ: ฉันเป็นหนึ่งในผู้ดูแลรักษาผลิตภัณฑ์สำหรับ Micro Focus
Rick Papo

8

เคล็ดลับง่ายๆข้อหนึ่งที่ฉันได้รับจากการตรวจจับการเข้าถึงหน่วยความจำที่ว่างคือ:

หากคุณต้องการค้นหาข้อผิดพลาดอย่างรวดเร็วโดยไม่ตรวจสอบทุกคำสั่งที่เข้าถึงบล็อกหน่วยความจำคุณสามารถตั้งค่าตัวชี้หน่วยความจำให้เป็นค่าที่ไม่ถูกต้องหลังจากพ้นบล็อก:

#ifdef _DEBUG // detect the access to freed memory
#undef free
#define free(p) _free_dbg(p, _NORMAL_BLOCK); *(int*)&p = 0x666;
#endif

5

เครื่องมือที่ดีที่สุดที่ฉันพบว่ามีประโยชน์และใช้งานได้ทุกครั้งคือการตรวจสอบโค้ด (พร้อมผู้ตรวจสอบโค้ดที่ดี)

นอกเหนือจากการตรวจสอบโค้ดฉันจะลองใช้Page Heapก่อน Page Heap ใช้เวลาสองสามวินาทีในการตั้งค่าและอาจโชคดีที่ระบุปัญหาของคุณ

หากไม่มีโชคกับ Page Heap ให้ดาวน์โหลดเครื่องมือดีบั๊กสำหรับ Windowsจาก Microsoft และเรียนรู้การใช้ WinDbg ขออภัยไม่สามารถให้ความช่วยเหลือเพิ่มเติมแก่คุณได้ แต่การดีบักฮีปฮีตหลายเธรดเป็นศิลปะมากกว่าวิทยาศาสตร์ Google สำหรับ "WinDbg heap ทุจริต" และคุณควรหาบทความมากมายในหัวข้อ


4

คุณอาจต้องการตรวจสอบเพื่อดูว่าคุณกำลังเชื่อมโยงกับไลบรารีรันไทม์ C แบบไดนามิกหรือคงที่ หากไฟล์ DLL ของคุณกำลังเชื่อมโยงกับไลบรารีรันไทม์ C แบบคงที่ไฟล์ DLL จะมีฮีปแยกต่างหาก

ดังนั้นหากคุณต้องสร้างวัตถุใน DLL หนึ่งและลองปล่อยมันใน DLL อื่นคุณจะได้รับข้อความเดียวกับที่คุณเห็นด้านบน ปัญหานี้ได้รับการอ้างอิงในคำถามสแต็คโอเวอร์โฟลว์อื่นการเพิ่มหน่วยความจำที่จัดสรรใน DLLอื่น


3

คุณใช้ฟังก์ชันการจัดสรรประเภทใด ฉันเพิ่งพบข้อผิดพลาดที่คล้ายกันโดยใช้ฟังก์ชันการจัดสรรสไตล์ Heap *

มันกลับกลายเป็นว่าฉันกำลังสร้างกองด้วยHEAP_NO_SERIALIZEตัวเลือกผิด สิ่งนี้ทำให้ฟังก์ชั่นฮีปทำงานโดยไม่มีความปลอดภัยของเธรด เป็นการปรับปรุงประสิทธิภาพหากใช้อย่างถูกต้อง แต่ไม่ควรใช้หากคุณใช้ HeapAlloc ในโปรแกรมแบบมัลติเธรด [1] ฉันพูดถึงสิ่งนี้เพราะโพสต์ของคุณระบุว่าคุณมีแอพแบบมัลติเธรด หากคุณใช้ HEAP_NO_SERIALIZE ไม่ว่าที่ใดก็ตามให้ลบออกและอาจจะช่วยแก้ปัญหาของคุณได้

[1] มีบางสถานการณ์ที่สิ่งนี้ถูกกฎหมาย แต่คุณต้องซีเรียลการโทรไปที่ Heap * และโดยทั่วไปแล้วไม่ใช่สำหรับโปรแกรมแบบมัลติเธรด


ใช่: ดูที่ตัวเลือกคอมไพเลอร์ / บิลด์ของแอปพลิเคชันและตรวจสอบให้แน่ใจว่ามันถูกสร้างขึ้นเพื่อเชื่อมโยงกับเวอร์ชัน "มัลติเธรด" ของไลบรารีรันไทม์ C
ChrisW

@ChrisW สำหรับ API สไตล์ HeapAlloc ซึ่งแตกต่างกัน จริงๆแล้วมันเป็นพารามิเตอร์ที่สามารถเปลี่ยนแปลงได้ในเวลาที่สร้างฮีปไม่ใช่เวลาลิงก์
JaredPar

โอ้ ฉันไม่ได้พูดว่า OP อาจจะพูดถึง heap นั้นและไม่ใช่ heap ใน CRT
ChrisW

@ChrisW คำถามค่อนข้างคลุมเครือ แต่ฉันเพิ่งพบปัญหาที่ฉันมีรายละเอียด ~ 1 สัปดาห์ที่ผ่านมาดังนั้นมันสดในใจของฉัน
JaredPar

3

หากข้อผิดพลาดเหล่านี้เกิดขึ้นแบบสุ่มมีความเป็นไปได้สูงที่คุณจะพบกับการแข่งขันข้อมูล กรุณาตรวจสอบ: คุณปรับเปลี่ยนพอยน์เตอร์หน่วยความจำที่แชร์จากเธรดที่แตกต่างกันหรือไม่? Intel Thread Checker อาจช่วยตรวจสอบปัญหาดังกล่าวในโปรแกรมแบบมัลติเธรด


1

นอกเหนือจากการค้นหาเครื่องมือแล้วให้ลองมองหาผู้ร้ายที่น่าจะเป็นไปได้ มีองค์ประกอบใดบ้างที่คุณใช้ซึ่งอาจไม่ได้เขียนโดยคุณซึ่งอาจไม่ได้รับการออกแบบและทดสอบให้ทำงานในสภาพแวดล้อมแบบมัลติเธรดหรือไม่? หรือเพียงแค่หนึ่งที่คุณไม่ทราบว่าได้ทำงานในสภาพแวดล้อมดังกล่าว

ครั้งสุดท้ายที่มันเกิดขึ้นกับฉันมันเป็นแพคเกจดั้งเดิมที่ถูกใช้อย่างประสบความสำเร็จจากงานแบ็ตช์มาหลายปี แต่นี่เป็นครั้งแรกที่ บริษัท นี้มีการใช้งานจากบริการเว็บ. NET (ซึ่งเป็นแบบมัลติเธรด) นั่นคือมัน - พวกเขาโกหกเกี่ยวกับรหัสว่าเป็นเธรดที่ปลอดภัย



0

ฉันต้องการเพิ่มประสบการณ์ของฉัน ในไม่กี่วันที่ผ่านมาฉันแก้ไขข้อผิดพลาดนี้ในแอปพลิเคชันของฉัน ในกรณีของฉันโดยเฉพาะข้อผิดพลาดในรหัสคือ:

  • การลบองค์ประกอบออกจากคอลเลกชัน STL ในขณะที่วนซ้ำ (ฉันเชื่อว่ามีการดีบักแฟล็กใน Visual Studio เพื่อจับสิ่งเหล่านี้ฉันจับมันในระหว่างการตรวจสอบโค้ด)
  • อันนี้ซับซ้อนกว่าฉันจะแบ่งมันเป็นขั้นตอน:
    • จากเธรด C ++ ดั้งเดิมให้โทรกลับไปยังรหัสที่ได้รับการจัดการ
    • ในที่ดินที่มีการจัดการการโทรControl.Invokeและการกำจัดวัตถุที่มีการจัดการซึ่งล้อมรอบวัตถุพื้นเมืองที่เป็นของการติดต่อกลับ
    • เนื่องจากวัตถุยังคงอยู่ภายในเธรดดั้งเดิม (จะยังคงถูกบล็อกในการโทรกลับจนกระทั่งControl.Invokeสิ้นสุด) ฉันควรชี้แจงว่าฉันใช้boost::threadดังนั้นฉันจึงใช้ฟังก์ชั่นสมาชิกเป็นฟังก์ชั่นเธรด
    • วิธีแก้ปัญหา : ใช้Control.BeginInvoke(ทำ GUI ของฉันกับ Winforms) แทนเพื่อให้เธรดดั้งเดิมสามารถจบก่อนที่วัตถุจะถูกทำลาย (วัตถุประสงค์ของการโทรกลับแจ้งเตือนได้อย่างแม่นยำว่าเธรดสิ้นสุดลงและวัตถุสามารถถูกทำลายได้)

0

ฉันมีปัญหาที่คล้ายกัน - และมันโผล่ขึ้นมาค่อนข้างสุ่ม อาจมีบางสิ่งที่เสียหายในไฟล์บิลด์ แต่ฉันลงเอยด้วยการแก้ไขโดยทำความสะอาดโปรเจ็กต์ก่อนแล้วจึงสร้างใหม่

ดังนั้นนอกเหนือจากคำตอบอื่น ๆ ที่ได้รับ:

สิ่งใดที่ทำให้เกิดข้อผิดพลาดเหล่านี้ได้บ้าง มีบางสิ่งที่เสียหายในไฟล์บิลด์

ฉันจะดีบักได้อย่างไร ทำความสะอาดโครงการและสร้างใหม่ หากได้รับการแก้ไขอาจเป็นปัญหา

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.