ก่อนอื่นฉันรู้ว่านี่ไม่ใช่คำถามสไตล์คำถาม & คำตอบที่สมบูรณ์แบบด้วยคำตอบที่สมบูรณ์ แต่ฉันไม่สามารถคิดถ้อยคำใด ๆ ที่จะทำให้มันดีขึ้น ฉันไม่คิดว่าจะมีวิธีแก้ปัญหาที่สมบูรณ์สำหรับเรื่องนี้และนี่คือหนึ่งในเหตุผลที่ฉันโพสต์ไว้ที่นี่แทนที่จะเป็น Stack Overflow
เมื่อเดือนที่แล้วฉันได้เขียนรหัสเซิร์ฟเวอร์ (mmorpg) ที่ค่อนข้างเก่าเพื่อให้ทันสมัยและง่ายต่อการขยาย / ดัดแปลง ฉันเริ่มต้นด้วยส่วนเครือข่ายและใช้ห้องสมุดบุคคลที่สาม (libevent) เพื่อจัดการสิ่งต่าง ๆ สำหรับฉัน ด้วยการเปลี่ยนแฟคตอริ่งและการเปลี่ยนรหัสใหม่ฉันได้แนะนำคอร์รัปชั่นหน่วยความจำบางแห่งและฉันพยายามดิ้นรนหาว่ามันเกิดอะไรขึ้น
ฉันดูเหมือนจะไม่สามารถทำซ้ำได้อย่างน่าเชื่อถือบนสภาพแวดล้อมการพัฒนา / การทดสอบของฉันแม้ว่าจะใช้บอตแบบดั้งเดิมเพื่อจำลองการโหลดบางอย่างฉันจะไม่เกิดข้อผิดพลาดอีกต่อไป (ฉันแก้ไขปัญหาเรื่องเสรีภาพซึ่งทำให้บางสิ่ง)
ฉันได้ลองแล้ว:
Valgrinding นรกออกมาจากมัน - ไม่มีการเขียนที่ไม่ถูกต้องจนกว่าสิ่งที่จะเกิดปัญหา (ซึ่งอาจใช้เวลา 1+ วันในการผลิต .. หรือเพียงหนึ่งชั่วโมง) ซึ่งทำให้ฉันงงงันจริง ๆ ในบางครั้งมันจะเข้าถึงหน่วยความจำที่ไม่ถูกต้อง โอกาส? (มีวิธีการ "กระจาย" ช่วงที่อยู่หรือไม่)
เครื่องมือวิเคราะห์รหัส ได้แก่ ความครอบคลุมและ cppcheck ในขณะที่พวกเขาชี้ให้เห็นถึง .. ความร้ายกาจและคดีขอบในรหัสไม่มีอะไรร้ายแรง
บันทึกกระบวนการจนกว่ามันจะล้มเหลวด้วย gdb (ผ่าน undodb) แล้วทำงานย้อนกลับ นี้ / ฟัง / ชอบมันควรจะเป็นไปได้ แต่ฉันท้าย crashing gdb โดยใช้คุณสมบัติ auto-complete หรือฉันท้ายในโครงสร้าง libevent ภายในที่ฉันหลงทางเนื่องจากมีสาขาที่เป็นไปได้มากเกินไป บน). ฉันเดาว่าคงจะดีถ้าฉันเห็นว่าตัวชี้เป็นของ / ตำแหน่งที่จัดสรรไว้ซึ่งจะกำจัดปัญหาการแตกแขนงส่วนใหญ่ออกไป ฉันไม่สามารถเรียกใช้ valgrind ด้วย undodb ได้และฉันบันทึก gdb ปกติช้ามาก (ถ้าใช้งานร่วมกับ valgrind ได้)
ตรวจสอบรหัส! ด้วยตัวเอง (ถี่ถ้วน) และให้เพื่อนบางคนดูรหัสของฉันแม้ว่าฉันจะสงสัยว่ามันละเอียดเพียงพอ ฉันกำลังคิดว่าอาจจ้างนักพัฒนาเพื่อทำการตรวจสอบรหัส / ตรวจแก้จุดบกพร่องกับฉัน แต่ฉันไม่สามารถใส่เงินมากเกินไปและฉันก็ไม่รู้จะหาคนที่ยินดีทำงานน้อย - ไม่มีเงินถ้าเขาไม่พบปัญหาหรือใครมีคุณสมบัติเลย
ฉันควรทราบด้วย: ฉันมักจะได้รับ backtraces ที่สอดคล้องกัน มีบางสถานที่ที่เกิดความผิดพลาดส่วนใหญ่เกี่ยวข้องกับคลาสซ็อกเก็ตเสียหายอย่างใด มันเป็นตัวชี้ที่ไม่ถูกต้องชี้ไปที่สิ่งที่ไม่ใช่ซ็อกเก็ตหรือคลาสซ็อกเก็ตตัวเองกลายเป็นเขียนทับ (บางส่วน?) ด้วยซึ่งพูดพล่อยๆ แม้ว่าฉันจะสงสัยว่ามันทำงานล้มเหลวที่นั่นมากที่สุดเนื่องจากเป็นหนึ่งในชิ้นส่วนที่ใช้บ่อยที่สุดดังนั้นจึงเป็นหน่วยความจำที่เสียหายครั้งแรกที่ถูกนำมาใช้
โดยรวมแล้วปัญหานี้ทำให้ฉันยุ่งเป็นเวลาเกือบ 2 เดือน (เปิดและปิดโครงการงานอดิเรกมากขึ้น) และทำให้ฉันหงุดหงิดจนถึงจุดที่ฉันกลายเป็นคนไม่พอใจ IRL และคิดจะยอมแพ้ ฉันไม่สามารถคิดเกี่ยวกับสิ่งอื่นที่ฉันควรทำเพื่อค้นหาปัญหา
มีเทคนิคใดที่มีประโยชน์ที่ฉันพลาดไปหรือไม่? คุณจัดการกับสิ่งนั้นได้อย่างไร (มันเป็นเรื่องธรรมดาไม่ได้เพราะไม่มีข้อมูลมากมายเกี่ยวกับเรื่องนี้ .. หรือฉันแค่ตาบอดจริงๆ?)
แก้ไข:
รายละเอียดบางอย่างในกรณีที่มันสำคัญ:
ใช้ c ++ (11) ผ่าน gcc 4.7 (เวอร์ชั่นที่จัดทำโดย debian wheezy)
codebase อยู่ที่ประมาณ 150k บรรทัด
แก้ไขเพื่อตอบสนองต่อ david.pfx โพสต์: (ขออภัยสำหรับการตอบสนองช้า)
คุณเก็บบันทึกข้อผิดพลาดอย่างระมัดระวังเพื่อค้นหารูปแบบหรือไม่
ใช่ฉันยังคงมีความผิดพลาดเกิดขึ้นเมื่อไม่นานมานี้
สถานที่บางแห่งมีความคล้ายคลึงกันจริงหรือ อย่างไหนล่ะ, แบบไหนล่ะ?
ในเวอร์ชั่นล่าสุด (ดูเหมือนว่าจะเปลี่ยนแปลงเมื่อใดก็ตามที่ฉันเพิ่ม / ลบรหัสหรือเปลี่ยนแปลงโครงสร้างที่เกี่ยวข้อง) มันจะติดอยู่ในวิธีจับเวลารายการ โดยทั่วไปรายการมีเวลาเฉพาะหลังจากที่มันหมดอายุและมันจะส่งข้อมูลที่ปรับปรุงให้กับลูกค้า ตัวชี้ซ็อกเก็ตที่ไม่ถูกต้องจะอยู่ใน (ยังคงใช้ได้เท่าที่ฉันสามารถบอกได้) คลาสผู้เล่นซึ่งส่วนใหญ่เกี่ยวข้องกับสิ่งนั้น ฉันยังประสบปัญหาการขัดข้องในช่วงการล้างข้อมูลหลังจากการปิดปกติซึ่งเป็นการทำลายคลาสสแตติกทั้งหมดที่ไม่ได้ถูกทำลายอย่างชัดเจน ( __run_exit_handlers
ใน backtrace) ส่วนใหญ่เกี่ยวข้องกับstd::map
ชั้นเรียนโดยคาดเดาว่าเป็นเพียงสิ่งแรกที่เกิดขึ้น
ข้อมูลที่เสียหายมีลักษณะอย่างไร ศูนย์? ascii? รูปแบบ?
ฉันยังไม่พบรูปแบบใดเลยดูเหมือนว่าจะสุ่มให้ฉันบ้าง มันยากที่จะบอกเพราะฉันไม่รู้ว่าการคอร์รัปชั่นเริ่มต้นที่ไหน
มันเกี่ยวข้องกับกองเหรอ?
มันเกี่ยวข้องกับกองทั้งหมด (ฉันเปิดการใช้งาน stack Guard ของ gcc และนั่นไม่ได้จับอะไรเลย)
ความเสียหายเกิดขึ้นหลังจาก
free()
?
คุณจะต้องอธิบายให้ละเอียดเกี่ยวกับสิ่งนั้น คุณหมายถึงการมีพอยน์เตอร์ของวัตถุที่เป็นอิสระอยู่แล้วหรือไม่? ฉันกำลังตั้งค่าการอ้างอิงเป็นโมฆะทุกครั้งที่วัตถุถูกทำลายดังนั้นถ้าฉันไม่ได้พลาดบางสิ่งบางอย่างไม่ ที่ควรปรากฏใน valgrind แม้ว่ามันจะไม่
มีสิ่งที่แตกต่างเกี่ยวกับปริมาณการใช้เครือข่าย (ขนาดบัฟเฟอร์รอบการกู้คืน) หรือไม่
การรับส่งข้อมูลเครือข่ายประกอบด้วยข้อมูลดิบ ดังนั้นอาร์เรย์ char (u) intX_t หรือโครงสร้างแพ็กเกจ (เพื่อลบการขยาย) สำหรับสิ่งที่ซับซ้อนยิ่งขึ้นแต่ละแพ็กเก็ตมีส่วนหัวที่ประกอบด้วย id และขนาดแพ็คเก็ตของตัวเองซึ่งถูกตรวจสอบกับขนาดที่คาดหวัง พวกมันมีขนาดประมาณ 10-60bytes พร้อมแพ็กเก็ตที่ใหญ่ที่สุด ('บูทอัพ' ภายในซึ่งถูกเปิดใช้ครั้งเดียวเมื่อเริ่มต้น) มีขนาดไม่กี่ Mb
การผลิตจำนวนมากและยืนยัน ชนเร็วและคาดการณ์ได้ก่อนที่ความเสียหายจะแพร่กระจาย
ฉันเคยมีปัญหาเกี่ยวกับการstd::map
ทุจริตแต่ละเอนทิตีมีแผนที่ว่า "ดู" แต่ละเอนทิตีที่สามารถดูได้และในทางกลับกันก็อยู่ในนั้น ฉันเพิ่มบัฟเฟอร์ 200byte ทั้งด้านหน้าและหลังเติมด้วย 0x33 และตรวจสอบก่อนการเข้าถึงแต่ละครั้ง การคอร์รัปชั่นหายไปอย่างน่าอัศจรรย์ฉันต้องย้ายบางสิ่งไปรอบ ๆ ซึ่งทำให้สิ่งอื่นเสียหาย
การบันทึกเชิงกลยุทธ์เพื่อให้คุณทราบอย่างถูกต้องว่าเกิดอะไรขึ้นก่อนหน้านี้ เพิ่มการบันทึกเมื่อคุณเข้าใกล้คำตอบมากขึ้น
มันทำงาน .. เพื่อขยาย
คุณสามารถบันทึกสถานะและรีสตาร์ทอัตโนมัติได้หรือไม่? ฉันสามารถนึกถึงซอฟต์แวร์การผลิตสองสามชิ้นที่ทำเช่นนั้น
ฉันค่อนข้างทำอย่างนั้น ซอฟต์แวร์ประกอบด้วยกระบวนการ "แคช" หลักและบางส่วนของผู้ปฏิบัติงานอื่น ๆ ที่เข้าถึงแคชเพื่อรับและบันทึกข้อมูล ดังนั้นต่อความผิดพลาดฉันไม่สูญเสียความคืบหน้ามากนัก แต่ก็ยังตัดการเชื่อมต่อผู้ใช้ทั้งหมดและอื่น ๆ มันไม่ใช่ทางออกแน่นอน
ภาวะพร้อมกัน: เกลียว, สภาพการแข่งขัน, ฯลฯ
มีเธรด mysql ที่จะทำแบบสอบถาม "async" นั่นคือทั้งหมดที่มิได้ถูกแตะต้องและเพียงแบ่งปันข้อมูลไปยังชั้นฐานข้อมูลผ่านฟังก์ชั่นที่มีการล็อคทั้งหมด
ขัดจังหวะ
มีตัวจับเวลาขัดจังหวะเพื่อป้องกันไม่ให้ล็อคที่เพียงแค่ยกเลิกถ้ามันไม่ครบรอบ 30 วินาทีรหัสที่ควรจะปลอดภัยแม้ว่า:
if (!tics) {
abort();
} else
tics = 0;
สำบัดสำนวนvolatile int tics = 0;
ซึ่งเพิ่มขึ้นในแต่ละครั้งที่รอบเสร็จสมบูรณ์ รหัสเก่าเกินไป
events / callbacks / exception: ทำให้เกิดคอร์รัปต์หรือสแต็กอย่างคาดไม่ถึง
มีการใช้การโทรกลับจำนวนมาก (I / O เครือข่าย async, ตัวจับเวลา) แต่พวกเขาไม่ควรทำอะไรที่ไม่ดี
ข้อมูลที่ผิดปกติ: ข้อมูล / เวลา / สถานะที่ผิดปกติ
ฉันมีบางกรณีที่เกี่ยวข้องกับเรื่องนั้น การยกเลิกการเชื่อมต่อซ็อกเก็ตในขณะที่แพ็คเก็ตยังคงถูกประมวลผลส่งผลให้เข้าถึง nullptr และเช่นนั้น แต่สิ่งเหล่านั้นง่ายต่อการตรวจสอบเนื่องจากการอ้างอิงทุกครั้งได้รับการทำความสะอาดทันทีหลังจากบอกชั้นเรียนว่าเสร็จแล้ว (การทำลายตัวเองถูกจัดการโดยการลบลูปที่ถูกทำลายทั้งหมดในแต่ละรอบ)
การพึ่งพากระบวนการภายนอกแบบอะซิงโครนัส
สนใจที่จะทำอย่างละเอียด? นี่เป็นกรณีที่กระบวนการแคชดังกล่าวข้างต้น สิ่งเดียวที่ฉันสามารถจินตนาการได้จากส่วนบนของหัวของฉันจะไม่เสร็จเร็วพอและใช้ข้อมูลขยะ แต่นั่นไม่ใช่กรณีที่ใช้เครือข่ายด้วย โมเดลแพ็กเก็ตเดียวกัน
/analyze
) และMicrosoft Malloc และ Scribble guards ของ Apple เช่นกัน คุณควรใช้คอมไพเลอร์ให้ได้มากที่สุดโดยใช้มาตรฐานให้มากที่สุดเท่าที่จะทำได้เนื่องจากคำเตือนของคอมไพเลอร์เป็นการวินิจฉัยและจะดีขึ้นเมื่อเวลาผ่านไป ไม่มีกระสุนเงินและขนาดเดียวไม่พอดีทั้งหมด ยิ่งคุณใช้เครื่องมือและคอมไพเลอร์มากเท่าไหร่ความครอบคลุมที่สมบูรณ์ยิ่งขึ้นเนื่องจากแต่ละเครื่องมือมีจุดแข็งและจุดอ่อน