คุณมองหาอะไรเมื่อทำการดีบั๊กการหยุดชะงัก


25

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

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

คุณมองหาอะไรเมื่อทำการดีบั๊กการหยุดชะงัก


ฉันถามสิ่งนี้แทน SO เพราะฉันต้องการตัวชี้ทั่วไปเพิ่มเติมเกี่ยวกับการดีบักการหยุดชะงักไม่ใช่คำตอบเฉพาะสำหรับปัญหาของฉัน
Michael K

กลยุทธ์ที่ฉันคิดว่าสามารถเข้าสู่ระบบได้ (อย่างที่หลาย ๆ คนชี้ให้เห็น) จริง ๆ แล้วตรวจสอบกราฟการหยุดชะงักของใครที่รอการล็อกไว้โดยใคร (ดูstackoverflow.com/questions/3483094/ …สำหรับบางคน พอยน์เตอร์) และหมายเหตุประกอบการล็อก (ดูclang.llvm.org/docs/ThreadSafetyAnalysis.html ) แม้ว่ามันไม่ใช่รหัสของคุณคุณอาจพยายามโน้มน้าวให้ผู้เขียนเพิ่มคำอธิบายประกอบ - พวกเขาอาจพบข้อบกพร่องและแก้ไขข้อผิดพลาด (อาจรวมถึงของคุณด้วย) ในกระบวนการ
Don Hatch

คำตอบ:


23

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

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

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

เป็นไปได้อย่างแน่นอนว่าคุณจะไม่พบปัญหาก่อนที่จะหมดเวลาหรือเงิน


4
+1 ว้าวมันเป็นแง่ร้าย ... ไม่ใช่ความจริง เป็นของที่ระบุว่าคุณไม่สามารถหาข้อบกพร่องทั้งหมดได้ ขอบคุณสำหรับคำแนะนำ!
Michael K

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

@ DonHatch - ฉันใช้ถ้อยคำไม่ดี สถานการณ์ที่คุณอธิบายไม่ใช่การหยุดชะงัก ฉันหวังว่าจะนำเสนอความยุ่งเหยิงของการดีบั๊กสถานการณ์ที่มีเธรดการล็อกเธรด A จากนั้นพยายามรับล็อค B ในขณะที่เธรดที่ถือล็อค B กำลังพยายามรับล็อคเอบางที หรืออาจจะเป็นสถานการณ์ที่ซับซ้อนมากขึ้น คุณเพียงแค่ต้องเปิดใจให้มากเกี่ยวกับลำดับการได้มาของการล็อค ตรวจสอบสมมติฐานทั้งหมด ไม่ไว้วางใจอะไรเลย
Bruce Ediger

+1 แนะนำให้อ่านรหัสอย่างละเอียดและตรวจสอบการดำเนินการล็อคทั้งหมดแยกกัน ง่ายกว่ามากในการดูกราฟที่ซับซ้อนโดยการตรวจสอบโหนดเดียวอย่างระมัดระวังกว่าจะลองดูทั้งหมดทันที ฉันพบปัญหากี่ครั้งโดยจ้องมองที่รหัสและเรียกใช้สถานการณ์ที่แตกต่างในหัวของฉัน
Newtopian

11
  1. อย่างที่คนอื่นพูด ... ถ้าคุณได้รับข้อมูลที่เป็นประโยชน์สำหรับการบันทึกให้ลองทำสิ่งนั้นก่อนเพราะมันเป็นสิ่งที่ง่ายที่สุดที่จะทำ

  2. ระบุการล็อคที่เกี่ยวข้อง เปลี่ยน mutex / semaphores ทั้งหมดที่รอตลอดไปเป็นหมดเวลารอ ... บางสิ่งบางอย่างที่ยาวนานเหมือน 5 นาที บันทึกข้อผิดพลาดเมื่อหมดเวลา อย่างน้อยนี้จะชี้ให้คุณไปในทิศทางของหนึ่งในล็อคที่เกี่ยวข้องกับปัญหา ขึ้นอยู่กับความแปรปรวนของจังหวะเวลาที่คุณอาจได้รับโชคดีและพบกับล็อคทั้งสองหลังจากวิ่งไม่กี่ครั้ง ใช้ฟังก์ชัน / เงื่อนไขของรหัสความล้มเหลวในการบันทึกการติดตามสแต็คหลอกหลังจากการรอเวลาที่กำหนดล้มเหลวในการระบุว่าคุณไปถึงที่นั่นในตอนแรกได้อย่างไร สิ่งนี้จะช่วยคุณระบุเธรดที่เกี่ยวข้องกับปัญหา

  3. อีกสิ่งหนึ่งที่คุณสามารถลองได้ก็คือการสร้างไลบรารี่ไลบรารีรอบบริการ mutex / เซมาฟอร์ของคุณ ติดตามว่าเธรดใดมี mutex แต่ละตัวและเธรดใดกำลังรอ Mutex อยู่ สร้างเธรดมอนิเตอร์ที่ตรวจสอบว่าเธรดนั้นยาวนานเท่าไร ทริกเกอร์ในช่วงเวลาที่เหมาะสมและถ่ายโอนข้อมูลสถานะที่คุณกำลังติดตาม

เมื่อถึงจุดหนึ่งการตรวจสอบรหัสธรรมดาแบบเก่าเป็นสิ่งจำเป็น


6

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

อีกทางเลือกหนึ่งคือการลบบางส่วนของรหัสและข้อมูลเพื่อแปลข้อบกพร่อง อาจจะเขียนโปรแกรมเล็ก ๆ ที่ใช้เพียงบางคลาสเท่านั้นและรันเฉพาะการทดสอบขั้นพื้นฐานที่สุด (ยังคงอยู่ในหลายเธรด) ลบ GUI ทุกอย่างที่เกี่ยวข้องเช่นเอาต์พุตใด ๆ เกี่ยวกับสถานะการประมวลผลจริง (ฉันพบว่าส่วนต่อประสานผู้ใช้เป็นแหล่งที่มาของบั๊กบ่อยครั้งพอ)

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


"ข้อยกเว้นสามารถป้องกันการปล่อย" -> ฉันสงสารภาษาที่ไม่มีตัวแปรที่กำหนดขอบเขต: /
Matthieu M.

1
@Matthieu: การมีตัวแปรที่กำหนดขอบเขตและการใช้อย่างถูกต้องอาจเป็นสองสิ่งที่แตกต่างกัน และเขาถามถึงปัญหาที่เป็นไปได้โดยทั่วไปโดยไม่พูดถึงภาษาใดภาษาหนึ่งโดยเฉพาะ นี่คือสิ่งหนึ่งที่อาจมีอิทธิพลต่อการควบคุม
thorsten müller

3

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

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


1
ฉัน debugged ล็อคตายสองสามวันนี้ เคล็ดลับคือการตัด pthread_mutex_lock () ด้วยแมโครที่พิมพ์ฟังก์ชันหมายเลขบรรทัดชื่อไฟล์และชื่อตัวแปร mutex (โดย tokenizing มัน) ก่อนและหลังการรับล็อค ทำเช่นเดียวกันสำหรับ pthread_mutex_unlock () ด้วย เมื่อฉันเห็นว่ากระทู้ของฉันแข็งตัวฉันแค่ต้องดูข้อความสองข้อความสุดท้ายมีสองกระทู้พยายามล็อค แต่ไม่จบเลย! ตอนนี้สิ่งที่เหลืออยู่คือการเพิ่มกลไกในการสลับสิ่งนี้เมื่อรันไทม์ :-)
Plumenator

3

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

ความล้มเหลวนั้นอย่างที่PéterTörökกล่าวว่าการบันทึกอาจเป็นวิธี เท่าที่ฉันรู้ดีบักเกอร์ทำงานได้ไม่ดีกับสภาพแวดล้อมแบบมัลติเธรด พยายามค้นหาว่าล็อคอยู่ที่ไหนรับทรัพยากรทั้งหมดที่กำลังรออยู่และในสภาพที่สภาพการแข่งขันเกิดขึ้น


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

@gbjbaanb ฉันคิดว่ามันเป็นศัตรูของคุณที่รุนแรงเกินไป บางทีมันอาจจะถูกต้องที่จะบอกว่ามันเป็นเพื่อนที่ดีที่สุดของคุณซึ่งจะทำให้คุณผิดหวังเป็นครั้งคราว ฉันจะเห็นด้วยกับคนอื่น ๆ ในหน้านี้ที่บอกว่าการบันทึกเป็นขั้นตอนแรกที่ดีที่จะทำหลังจากการตรวจสอบรหัสล้มเหลว - บ่อยครั้ง (ที่จริงแล้วส่วนใหญ่แล้วในประสบการณ์ของฉัน) กลยุทธ์การบันทึกอย่างง่ายจะค้นหา ปัญหาได้อย่างง่ายดายและคุณทำเสร็จแล้ว ไม่อย่างนั้นก็จะหันไปใช้วิธีอื่น แต่ฉันไม่คิดว่ามันเป็นคำแนะนำที่ดีที่จะหลีกเลี่ยงการพยายามใช้เครื่องมือที่ดีที่สุดสำหรับงานเพียงเพราะมันไม่ได้มีประโยชน์เสมอไป
Don Hatch

0

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


0

หากคุณต้องตรวจแก้จุดบกพร่อง deadlocks แสดงว่าคุณมีปัญหาแล้ว ตามกฎแล้วให้ใช้การล็อคในเวลาที่สั้นที่สุด - หรือไม่เลยถ้าเป็นไปได้ ควรหลีกเลี่ยงสถานการณ์ใด ๆ ที่คุณล็อกไว้และออกไปที่รหัสที่ไม่สำคัญ

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

จากนั้นก็มีกลยุทธ์เก่า ๆ แต่พลาดไม่ได้: กำหนด "ระดับ" ให้กับแต่ละล็อกเริ่มต้นที่ระดับ 0 หากคุณล็อคระดับ 0 คุณจะไม่ได้รับอนุญาตให้ล็อคอื่น ๆ หลังจากล็อคระดับ 1 แล้วคุณสามารถล็อคระดับ 0 ได้ หลังจากล็อคระดับ 10 แล้วคุณสามารถล็อคได้ที่ระดับ 9 หรือต่ำกว่าเป็นต้น

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

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