ประการแรกฉันต้องขออภัยสำหรับความยาวของคำถามนี้
ผมผู้เขียนของIronScheme เมื่อเร็ว ๆ นี้ฉันทำงานอย่างหนักในการส่งข้อมูลการดีบักที่เหมาะสมเพื่อที่ฉันจะสามารถใช้ดีบักเกอร์. NET 'เนทีฟ' ได้
แม้ว่าสิ่งนี้จะประสบความสำเร็จบางส่วน แต่ฉันก็ประสบปัญหาการงอกของฟัน
ปัญหาแรกเกี่ยวข้องกับการก้าว
เนื่องจาก Scheme เป็นภาษานิพจน์ทุกอย่างจึงมีแนวโน้มที่จะอยู่ในวงเล็บซึ่งแตกต่างจากภาษา. NET หลักซึ่งดูเหมือนจะเป็นคำสั่ง (หรือบรรทัด)
รหัสเดิม (Scheme) มีลักษณะดังนี้:
(define (baz x)
(cond
[(null? x)
x]
[(pair? x)
(car x)]
[else
(assertion-violation #f "nooo" x)]))
ฉันมีจุดประสงค์ในการกำหนดแต่ละนิพจน์ในบรรทัดใหม่
โค้ดที่ปล่อยออกมาจะแปลงเป็น C # (ผ่าน ILSpy) มีลักษณะดังนี้:
public static object ::baz(object x)
{
if (x == null)
{
return x;
}
if (x is Cons)
{
return Builtins.Car(x);
}
return #.ironscheme.exceptions::assertion-violation+(
RuntimeHelpers.False, "nooo", Builtins.List(x));
}
อย่างที่คุณเห็นค่อนข้างเรียบง่าย
หมายเหตุ: หากรหัสถูกเปลี่ยนเป็นนิพจน์เงื่อนไข (? :) ใน C # สิ่งทั้งหมดจะเป็นเพียงขั้นตอนการดีบักหนึ่งขั้นตอนโปรดจำไว้ว่า
นี่คือเอาต์พุต IL พร้อมแหล่งที่มาและหมายเลขบรรทัด:
.method public static object '::baz'(object x) cil managed
{
// Code size 56 (0x38)
.maxstack 6
.line 15,15 : 1,2 ''
//000014:
//000015: (define (baz x)
IL_0000: nop
.line 17,17 : 6,15 ''
//000016: (cond
//000017: [(null? x)
IL_0001: ldarg.0
IL_0002: brtrue IL_0009
.line 18,18 : 7,8 ''
//000018: x]
IL_0007: ldarg.0
IL_0008: ret
.line 19,19 : 6,15 ''
//000019: [(pair? x)
.line 19,19 : 6,15 ''
IL_0009: ldarg.0
IL_000a: isinst [IronScheme]IronScheme.Runtime.Cons
IL_000f: ldnull
IL_0010: cgt.un
IL_0012: brfalse IL_0020
IL_0017: ldarg.0
.line 20,20 : 7,14 ''
//000020: (car x)]
IL_0018: tail.
IL_001a: call object [IronScheme]IronScheme.Runtime.Builtins::Car(object)
IL_001f: ret
IL_0020: ldsfld object
[Microsoft.Scripting]Microsoft.Scripting.RuntimeHelpers::False
IL_0025: ldstr "nooo"
IL_002a: ldarg.0
IL_002b: call object [IronScheme]IronScheme.Runtime.Builtins::List(object)
.line 22,22 : 7,40 ''
//000021: [else
//000022: (assertion-violation #f "nooo" x)]))
IL_0030: tail.
IL_0032: call object [ironscheme.boot]#::
'ironscheme.exceptions::assertion-violation+'(object,object,object)
IL_0037: ret
} // end of method 'eval-core(033)'::'::baz'
หมายเหตุ:เพื่อป้องกันไม่ให้ดีบักเกอร์เพียงแค่เน้นเมธอดทั้งหมดฉันจึงกำหนดจุดเข้าเมธอดให้กว้างเพียง 1 คอลัมน์
อย่างที่คุณเห็นนิพจน์แต่ละรายการจะจับคู่กับเส้นอย่างถูกต้อง
ตอนนี้ปัญหาเกี่ยวกับการก้าว (ทดสอบบน VS2010 แต่ปัญหาเดียวกัน / คล้ายกันใน VS2008):
สิ่งเหล่านี้IgnoreSymbolStoreSequencePoints
ไม่ได้ใช้
- เรียก baz ด้วย null arg มันทำงานได้อย่างถูกต้อง (null? x) ตามด้วย x
- โทรหา baz ด้วย Cons arg มันทำงานได้อย่างถูกต้อง (null? x) แล้ว (คู่? x) แล้ว (รถ x)
- โทรหา baz กับอาร์กิวเมนต์อื่นมันล้มเหลว (null? x) แล้ว (คู่? x) จากนั้น (รถ x) ตามด้วย (การยืนยัน - ละเมิด ... )
เมื่อสมัครIgnoreSymbolStoreSequencePoints
(ตามคำแนะนำ):
- เรียก baz ด้วย null arg มันทำงานได้อย่างถูกต้อง (null? x) ตามด้วย x
- โทรหา baz ด้วย Cons arg มันล้มเหลว (null? x) แล้ว (คู่? x)
- โทรหา baz กับอาร์กิวเมนต์อื่นมันล้มเหลว (null? x) แล้ว (คู่? x) จากนั้น (รถ x) ตามด้วย (การยืนยัน - ละเมิด ... )
ฉันยังพบในโหมดนี้ว่ามีการไฮไลต์บางบรรทัด (ไม่แสดงที่นี่) อย่างไม่ถูกต้องซึ่งจะปิดด้วย 1
นี่คือแนวคิดบางประการที่อาจเป็นสาเหตุ:
- Tailcalls ทำให้ดีบักเกอร์สับสน
- ตำแหน่งที่ทับซ้อนกัน (ไม่แสดงที่นี่) ทำให้ดีบักเกอร์สับสน (ทำงานได้ดีมากเมื่อตั้งค่าเบรกพอยต์)
- ????
ปัญหาที่สอง แต่ก็ร้ายแรงเช่นกันคือดีบักเกอร์ล้มเหลวในการทำลาย / ตีเบรกพอยต์ในบางกรณี
ที่เดียวที่ฉันจะทำให้ดีบักเกอร์แตกอย่างถูกต้อง (และสม่ำเสมอ) คือที่จุดเริ่มต้นของวิธีการ
สถานการณ์จะดีขึ้นเล็กน้อยเมื่อIgnoreSymbolStoreSequencePoints
ไม่ได้ใช้
สรุป
อาจเป็นไปได้ว่าดีบักเกอร์ VS เป็นเพียงบั๊กกี้ธรรมดา :(
อ้างอิง:
อัปเดต 1:
Mdbg ไม่ทำงานสำหรับแอสเซมบลี 64 บิต นั่นคือออก ฉันไม่มีเครื่อง 32 บิตให้ทดสอบอีกต่อไป Update:ฉันแน่ใจว่านี่ไม่ใช่ปัญหาใหญ่ใครมีวิธีแก้ไขหรือไม่? แก้ไข:ใช่ฉันโง่แค่เริ่ม mdbg ภายใต้พรอมต์คำสั่ง x64 :)
อัปเดต 2:
ฉันได้สร้างแอป C # และพยายามแยกข้อมูลบรรทัด
การค้นพบของฉัน:
- หลังจาก
brXXX
คำสั่งใด ๆคุณต้องมีจุดลำดับ (หากไม่ถูกต้องหรือที่เรียกว่า '#line hidden' ให้ปล่อย anop
) - ก่อน
brXXX
คำสั่งใด ๆให้ปล่อย '#line hidden' และ anop
.
อย่างไรก็ตามการใช้สิ่งนี้ไม่ได้ช่วยแก้ไขปัญหา (เพียงอย่างเดียว?)
แต่เพิ่มสิ่งต่อไปนี้ให้ผลลัพธ์ที่ต้องการ :)
- หลังจากนั้น
ret
ปล่อย '#line hidden' และ anop
.
นี่คือการใช้โหมดที่IgnoreSymbolStoreSequencePoints
ไม่ได้ใช้ เมื่อนำไปใช้แล้วบางขั้นตอนจะยังคงข้าม :(
นี่คือเอาต์พุต IL เมื่อใช้ข้างต้น:
.method public static object '::baz'(object x) cil managed
{
// Code size 63 (0x3f)
.maxstack 6
.line 15,15 : 1,2 ''
IL_0000: nop
.line 17,17 : 6,15 ''
IL_0001: ldarg.0
.line 16707566,16707566 : 0,0 ''
IL_0002: nop
IL_0003: brtrue IL_000c
.line 16707566,16707566 : 0,0 ''
IL_0008: nop
.line 18,18 : 7,8 ''
IL_0009: ldarg.0
IL_000a: ret
.line 16707566,16707566 : 0,0 ''
IL_000b: nop
.line 19,19 : 6,15 ''
.line 19,19 : 6,15 ''
IL_000c: ldarg.0
IL_000d: isinst [IronScheme]IronScheme.Runtime.Cons
IL_0012: ldnull
IL_0013: cgt.un
.line 16707566,16707566 : 0,0 ''
IL_0015: nop
IL_0016: brfalse IL_0026
.line 16707566,16707566 : 0,0 ''
IL_001b: nop
IL_001c: ldarg.0
.line 20,20 : 7,14 ''
IL_001d: tail.
IL_001f: call object [IronScheme]IronScheme.Runtime.Builtins::Car(object)
IL_0024: ret
.line 16707566,16707566 : 0,0 ''
IL_0025: nop
IL_0026: ldsfld object
[Microsoft.Scripting]Microsoft.Scripting.RuntimeHelpers::False
IL_002b: ldstr "nooo"
IL_0030: ldarg.0
IL_0031: call object [IronScheme]IronScheme.Runtime.Builtins::List(object)
.line 22,22 : 7,40 ''
IL_0036: tail.
IL_0038: call object [ironscheme.boot]#::
'ironscheme.exceptions::assertion-violation+'(object,object,object)
IL_003d: ret
.line 16707566,16707566 : 0,0 ''
IL_003e: nop
} // end of method 'eval-core(033)'::'::baz'
อัปเดต 3:
ปัญหาด้านบน 'กึ่งแก้ไข' Peverify รายงานข้อผิดพลาดในทุกวิธีเนื่องจากnop
หลังจากret
นั้น ฉันไม่เข้าใจปัญหาจริงๆ การnop
ตรวจสอบความผิดพลาดหลังจากไฟล์ret
. มันเหมือนโค้ดที่ตายแล้ว (ยกเว้นว่ามันไม่ใช่โค้ด) ... อืมการทดลองดำเนินต่อ
อัปเดต 4:
กลับมาที่บ้านตอนนี้ลบรหัส 'ที่ไม่สามารถตรวจสอบได้' ซึ่งทำงานบน VS2008 และสิ่งต่างๆก็แย่ลงมาก บางทีการเรียกใช้โค้ดที่ไม่สามารถตรวจสอบได้เพื่อการดีบักที่เหมาะสมอาจเป็นคำตอบ ในโหมด 'ปล่อย' ผลลัพธ์ทั้งหมดจะยังคงตรวจสอบได้
อัปเดต 5:
ตอนนี้ฉันได้ตัดสินใจแล้วว่าแนวคิดข้างต้นของฉันเป็นทางเลือกเดียวที่ใช้ได้ในตอนนี้ แม้ว่ารหัสที่สร้างขึ้นจะไม่สามารถตรวจสอบได้ แต่ฉันก็ยังไม่พบอะไรVerificationException
เลย ฉันไม่รู้ว่าจะมีผลกระทบต่อผู้ใช้ปลายทางอย่างไรในสถานการณ์นี้
เพื่อเป็นโบนัสปัญหาที่สองของฉันได้รับการแก้ไขแล้ว :)
นี่คือscreencastเล็ก ๆ น้อย ๆของสิ่งที่ฉันลงเอยด้วย มันกระทบจุดพักไม่ก้าวที่เหมาะสม (เข้า / ออก / เหนือ) ฯลฯ สรุปแล้วเอฟเฟกต์ที่ต้องการ
อย่างไรก็ตามฉันยังไม่ยอมรับว่านี่เป็นวิธีที่จะทำ ฉันรู้สึกแฮ็คมากเกินไป มีคำยืนยันในประเด็นที่แท้จริงก็น่าจะดี
อัปเดต 6:
เพิ่งมีการเปลี่ยนแปลงเพื่อทดสอบโค้ดบน VS2010 ดูเหมือนว่าจะมีปัญหา:
การโทรครั้งแรกไม่ถูกต้อง (การยืนยันการละเมิด ... ) ถูกตี กรณีอื่น ๆ ใช้ได้ดีรหัสเก่าบางตำแหน่งแสดงตำแหน่งที่ไม่จำเป็น ลบโค้ดแล้วใช้งานได้ตามที่คาดไว้ :)- ที่สำคัญกว่านั้นคือเบรกพอยต์ล้มเหลวในการเรียกโปรแกรมครั้งที่สอง (โดยใช้การคอมไพล์ในหน่วยความจำการถ่ายโอนแอสเซมบลีไปยังไฟล์ดูเหมือนจะทำให้เบรกพอยต์มีความสุขอีกครั้ง)
ทั้งสองกรณีนี้ทำงานได้อย่างถูกต้องภายใต้ VS2008 ความแตกต่างที่สำคัญคือภายใต้ VS2010 แอปพลิเคชันทั้งหมดจะถูกคอมไพล์สำหรับ. NET 4 และภายใต้ VS2008 โดยคอมไพล์เป็น. NET 2 ทั้งที่รัน 64 บิต
อัปเดต 7:
ดังที่กล่าวไว้ฉันได้ mdbg ทำงานภายใต้ 64 บิต น่าเสียดายที่มันยังมีปัญหาเบรกพอยต์ที่ไม่สามารถพังได้หากฉันรันโปรแกรมอีกครั้ง (ซึ่งหมายความว่ามันได้รับการคอมไพล์ใหม่ดังนั้นจึงไม่ใช้แอสเซมบลีเดียวกัน แต่ยังคงใช้ซอร์สเดียวกัน)
อัปเดต 8:
ฉันได้ยื่นข้อบกพร่องที่ไซต์ MS Connect เกี่ยวกับปัญหาเบรกพอยต์
อัปเดต: แก้ไข
อัปเดต 9:
หลังจากคิดอยู่นานวิธีเดียวที่จะทำให้ดีบักเกอร์มีความสุขดูเหมือนว่าจะทำ SSA ดังนั้นทุกขั้นตอนสามารถแยกได้และเป็นลำดับ ฉันยังไม่ได้พิสูจน์ความคิดนี้ แต่ดูเหมือนว่ามีเหตุผล เห็นได้ชัดว่าการล้าง Temps จาก SSA จะทำให้การดีบักแตก แต่ง่ายต่อการสลับและการปล่อยทิ้งไว้ก็ไม่มีค่าใช้จ่ายมากนัก