ORC เป็นเครื่องมือสำหรับค้นหาการละเมิด One Definition Rule ของ C++ บน Toolchain OSX
ORC คือการเล่นกับคนแคระซึ่งเป็นการเล่นกับเอลฟ์ ORC เป็นตัวย่อ ในขณะที่ O ย่อมาจาก ODR ในการประชด R และ C แสดงถึงคำหลายคำ (อาจขัดแย้งกัน)
มีบทความมากมายเกี่ยวกับ One Definition Rule (ODR) รวมถึง C++ Standard ด้วย สาระสำคัญของกฎคือ ถ้ามีการกำหนดสัญลักษณ์ในโปรแกรม จะอนุญาตให้กำหนดได้เพียงครั้งเดียวเท่านั้น สัญลักษณ์บางตัวได้รับข้อยกเว้นสำหรับกฎนี้ และอนุญาตให้กำหนดได้หลายครั้ง อย่างไรก็ตาม สัญลักษณ์เหล่านั้นจะต้องถูกกำหนดโดยลำดับโทเค็น ที่เหมือนกัน
โปรดทราบว่าการตั้งค่าคอมไพเลอร์บางอย่างอาจส่งผลต่อลำดับโทเค็นด้วย ตัวอย่างเช่น การเปิดหรือปิดใช้งาน RTTI อาจเปลี่ยนแปลงคำจำกัดความของสัญลักษณ์ (ในกรณีนี้คือ vtable ของคลาส)
สัญลักษณ์ใดๆ ที่ฝ่าฝืนกฎข้างต้นถือเป็นการละเมิด ODR (ODRV) ในบางกรณี ตัวเชื่อมโยงอาจจับคำจำกัดความของสัญลักษณ์ที่ซ้ำกันและปล่อยคำเตือนหรือข้อผิดพลาด อย่างไรก็ตาม มาตรฐานระบุว่าไม่จำเป็นต้องใช้ตัวเชื่อมโยงในการดำเนินการดังกล่าว Andy G อธิบายได้ดี:
ด้วยเหตุผลด้านประสิทธิภาพ มาตรฐาน C++ กำหนดว่าหากคุณละเมิดกฎคำจำกัดความเดียวที่เกี่ยวข้องกับเทมเพลต ลักษณะการทำงานนั้นจะไม่ได้ถูกกำหนดไว้ เนื่องจากตัวเชื่อมโยงไม่สนใจ การละเมิดกฎนี้จึงเงียบ แหล่งที่มา
ODRV ที่ไม่ใช่เทมเพลตสามารถทำได้ และตัวเชื่อมโยงก็อาจจะเงียบเกี่ยวกับสิ่งเหล่านั้นเช่นกัน
โดยทั่วไป ODRV หมายความว่าคุณมีสัญลักษณ์ที่มีรูปแบบไบนารี่แตกต่างกันไปขึ้นอยู่กับหน่วยการคอมไพล์ที่สร้างขึ้น เนื่องจากกฎนี้ เมื่อตัวเชื่อมโยงพบคำจำกัดความหลายคำ คุณสามารถเลือกคำจำกัดความ ใดก็ได้ และใช้เป็นโครงร่างไบนารีสำหรับสัญลักษณ์ได้ฟรี เมื่อเค้าโครงที่เลือกไม่ตรงกับเค้าโครงไบนารีภายในของสัญลักษณ์ในหน่วยการคอมไพล์ ลักษณะการทำงานจะไม่ถูกกำหนด
บ่อยครั้งที่ดีบักเกอร์ไม่มีประโยชน์ในสถานการณ์เหล่านี้ มันก็จะใช้คำจำกัดความเดียวของสัญลักษณ์สำหรับทั้งโปรแกรม และเมื่อคุณพยายามแก้ไขจุดบกพร่อง ODRV ตัวดีบักเกอร์อาจให้ข้อมูลที่ไม่ถูกต้องแก่คุณ หรือชี้ไปที่ตำแหน่งในไฟล์ที่ดูเหมือนไม่ถูกต้อง ท้ายที่สุดแล้ว โปรแกรมดีบักเกอร์จะดูเหมือนกำลังโกหกคุณ แต่ก็ไม่ได้ให้เบาะแสใด ๆ ว่าปัญหาที่แท้จริงคืออะไร
เช่นเดียวกับข้อบกพร่องอื่นๆ ODRV ต้องใช้เวลาในการแก้ไข ดังนั้นเหตุใดคุณจึงควรแก้ไขการละเมิด ODR ในโค้ดที่ทดสอบแล้ว (และน่าจะใช้งานได้)
ORC เป็นเครื่องมือที่ดำเนินการดังต่อไปนี้:
หากยกเว้นจุดบกพร่องในเครื่องมือ ORC จะไม่สร้างผลบวกลวง สิ่งใดก็ตามที่รายงานคือ ODRV
ในขณะนี้ ORC ตรวจไม่พบการละเมิดกฎคำจำกัดความเดียวที่เป็นไปได้ทั้งหมด เราหวังว่าจะขยายและปรับปรุงสิ่งที่สามารถทำได้เมื่อเวลาผ่านไป จนกว่าจะถึงตอนนั้น นั่นหมายความว่าแม้ว่า ORC จะเป็นการตรวจสอบที่มีคุณค่า แต่การสแกนใหม่ทั้งหมดไม่ได้รับประกันว่าโปรแกรมจะปราศจาก ODRV
ORC สามารถค้นหา:
หมายเหตุเกี่ยวกับ vtables: ORC จะตรวจจับวิธีการเสมือนที่อยู่ในช่องที่แตกต่างกัน (ซึ่งเป็นโปรแกรมที่เสียหายอย่างน่ารังเกียจ) ณ จุดนี้ มันจะตรวจไม่พบคลาสที่มีวิธีการเสมือนที่เป็น "superset" ของ ODR ที่ละเมิดคลาสที่ซ้ำกัน
นอกเหนือจากแหล่งที่มา ORC หลักแล้ว เรายังพยายามจัดเตรียมแอปพลิเคชันตัวอย่างจำนวนมากที่มี ODRV ที่เครื่องมือควรตรวจจับได้
ORC เดิมทีเกิดขึ้นบน macOS แม้ว่าการใช้งานในปัจจุบันจะเน้นไปที่นั้น แต่ก็ไม่จำเป็นต้องถูกจำกัดอยู่ที่ toolchain นั้น
ORC ได้รับการจัดการโดย cmake และถูกสร้างขึ้นโดยใช้แบบแผนการสร้างทั่วไปของโครงการที่จัดการโดย CMake:
mkdir build
cd build
cmake -GXcode ..
มีแอปพลิเคชันตัวอย่างจำนวนหนึ่งที่รวม ORC ไว้เพื่อวัตถุประสงค์ในการทดสอบ สามารถเลือกได้ผ่านป๊อปอัปเป้าหมายใน Xcode
ORC ใช้ Tracy เป็นเครื่องมือสร้างโปรไฟล์ที่เลือกใช้ และจะ เปิดใช้งาน ตามค่าเริ่มต้น หากต้องการปิดใช้งาน Tracy ให้ระบุบรรทัดคำสั่ง cmake ดังนี้:
cmake .. -GXcode -DTRACY_ENABLE=OFF
จำเป็นต้องมีการพึ่งพา Tracy แม้ว่าโปรไฟล์จะถูกปิดใช้งาน (จะถูกคอมไพล์จากรันไทม์) โปรดทราบว่าตัวเลือกนี้ถูกแคชไว้ ดังนั้นคุณต้อง OFF
หรือ ON
อย่างชัดเจน การรันการเรียกใช้บรรทัดคำสั่งอีกครั้งโดยที่ไม่มีตัวเลือกจะทำให้มีการใช้ค่าก่อนหน้า
สามารถเรียก ORC ได้โดยตรงจากบรรทัดคำสั่ง หรือแทรกลงในห่วงโซ่เครื่องมือในขั้นตอนตัวเชื่อมโยง เอาต์พุตไม่เปลี่ยนแปลง มันเป็นเพียงเรื่องของความสะดวกในขั้นตอนการทำงานของคุณ
โหมดนี้มีประโยชน์หากคุณมีคำสั่ง linker และอาร์กิวเมนต์ และต้องการค้นหา ODRV ที่แยกจาก build จริง
ไฟล์กำหนดค่า (ดูด้านล่าง)
'forward_to_linker' = false
'standalone_mode' = false
คุณต้องมีอาร์กิวเมนต์บรรทัดคำสั่ง ld
จาก XCode สร้างด้วย Xcode (หากคุณลิงก์ไม่ได้ ORC ก็ช่วยไม่ได้) คัดลอกคำสั่งลิงก์แล้ววางหลังการเรียกใช้ ORC บางอย่างเช่น:
/path/to/orc /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++ -target ... Debug/lem_mac
(เป็นบรรทัดคำสั่งขนาดใหญ่ ย่อไว้ที่นี่)
ORC จะดำเนินการและบันทึกการละเมิด ODR ไปยังคอนโซล
หากคุณมีรายการไฟล์ไลบรารีสำหรับ ORC ที่จะประมวลผล ก็สามารถทำได้เช่นกัน
ไฟล์กำหนดค่า (ดูด้านล่าง)
'forward_to_linker' = false
'standalone_mode' = true
ในโหมดนี้ เพียงส่งรายการไฟล์ libary ไปที่ ORC เพื่อดำเนินการ
ไฟล์กำหนดค่า (ดูด้านล่าง)
'forward_to_linker' = true
'standalone_mode' = false
หากต้องการใช้ ORC ภายในโปรเจ็กต์การสร้าง Xcode ของคุณ ให้แทนที่ตัวแปรต่อไปนี้ด้วยเส้นทางที่มีคุณสมบัติครบถ้วนไปยังสคริปต์ ORC:
"LIBTOOL": "/absolute/path/to/orc",
"LDTOOL": "/absolute/path/to/orc",
"ALTERNATE_LINKER": "/absolute/path/to/orc",
เมื่อตั้งค่าเหล่านี้แล้ว Xcode ควรใช้ ORC เป็นเครื่องมือสำหรับทั้งเฟส libtool
และ ld
ของการสร้างโปรเจ็กต์ เนื่องจากการตั้ง forward_to_linker
ORC จะเรียกใช้เครื่องมือลิงก์ที่เหมาะสมเพื่อสร้างไฟล์ไบนารี เมื่อเสร็จสิ้น ORC จะเริ่มการสแกน
ท่ามกลางการตั้งค่าอื่นๆ ORC สามารถกำหนดค่าให้ออกหรือเตือนเมื่อตรวจพบ ODRV เท่านั้น
ORC จะนำไดเร็กทอรีปัจจุบันขึ้นไปโดยค้นหาไฟล์ปรับแต่งชื่อ:
.orc-config
หรือ_orc-config
หากพบสวิตช์จำนวนมากจะสามารถควบคุมตรรกะของ ORC ได้ โปรดดู _orc_config
ในพื้นที่เก็บข้อมูลเพื่อดูตัวอย่าง ORC ต้องการ .orc-config
ดังนั้นจึงเป็นเรื่องง่ายที่จะคัดลอก _orc_config
ดั้งเดิมและเปลี่ยนค่าในเครื่องใน . .orc-config
ตัวอย่างเช่น:
error: ODRV (structure:byte_size); conflict in `object`
compilation unit: a.o:
definition location: /Volumes/src/orc/extras/struct0/src/a.cpp:3
calling_convention: pass by value; 5 (0x5)
name: object
byte_size: 4 (0x4)
compilation unit: main.o:
definition location: /Volumes/src/orc/extras/struct0/src/main.cpp:3
calling_convention: pass by value; 5 (0x5)
name: object
byte_size: 1 (0x1)
structure:byte_size
เรียกว่าหมวดหมู่ ODRV และให้รายละเอียดอย่างชัดเจนว่าข้อผิดพลาดนี้หมายถึงการละเมิดประเภทใด จากนั้นหน่วยการคอมไพล์ทั้งสองที่ขัดแย้งกันจะถูกส่งออกพร้อมกับข้อมูล DWARF ที่ทำให้เกิดการชนกัน
struct object { ... }
In ao:
และ In main.o
คือไฟล์อ็อบเจ็กต์หรือไฟล์เก็บถาวร 2 ไฟล์ที่ไม่ตรงกัน ODR น่าจะเกิดจากการคอมไพล์ที่ไม่ตรงกันหรือการตั้งค่า #define ในการคอมไพล์ไฟล์เก็บถาวรเหล่านี้ byte_size
คือค่าจริงที่ทำให้เกิดข้อผิดพลาด
definition location: /Volumes/src/orc/extras/struct0/src/a.cpp:3
บรรทัดและไฟล์ใดที่วัตถุถูกประกาศ ดังนั้นบรรทัดที่ 3 ของ a.cpp
ในตัวอย่างนี้
สำหรับ ORC เวอร์ชันเดียวกันและอินพุตเดียวกัน ORC จะเขียนเอาต์พุตเดียวกันเสมอ โดยที่ "เดียวกัน" เป็นไบต์ที่เหมือนกันและเครื่องมือ diff จะไม่แสดงความแตกต่าง
การบรรลุ (และมีแนวโน้มที่จะรักษา) เอาต์พุตที่สม่ำเสมอนั้นเป็นเรื่องที่ท้าทายอย่างน่าประหลาดใจในแอปพลิเคชันแบบมัลติเธรดสูง
อย่างไรก็ตาม โปรดทราบว่าสิ่งนี้ใช้ไม่ได้กับ ORC เวอร์ชันอื่น การเปลี่ยนแปลง ORC เกือบจะส่งผลให้เกิดการเปลี่ยนแปลงเอาต์พุตอย่างแน่นอน
ไม่มีการรับประกันว่าการเปลี่ยนแปลง "เล็กน้อย" ในไฟล์อินพุตจะรับประกันการเปลี่ยนแปลง "เล็กน้อย" ในเอาต์พุต ORC พฤติกรรมนี้เป็นที่พึงปรารถนาและมีแนวโน้มที่จะเป็นพื้นที่ที่ต้องปรับปรุงในอนาคต
orc_test
) มีแอปพลิเคชันทดสอบหน่วยเพื่อให้แน่ใจว่า ORC จับสิ่งที่ตั้งใจจะจับได้ orc_test
แนะนำ "ระบบบิลด์" ขนาดเล็กเพื่อสร้างไฟล์อ็อบเจ็กต์จากแหล่งที่รู้จักเพื่อสร้างการละเมิด ODR ที่ทราบ จากนั้นจะประมวลผลไฟล์อ็อบเจ็กต์โดยใช้กลไกเดียวกับเครื่องมือบรรทัดคำสั่ง ORC และเปรียบเทียบผลลัพธ์กับรายการรายงาน ODRV ที่คาดหวัง
การทดสอบทุกหน่วยในแบตเตอรี่เป็นแบบแยกส่วน และประกอบด้วย:
odrv_test.toml
เป็นไฟล์ TOML ระดับสูงที่อธิบายพารามิเตอร์ของการทดสอบโดยทั่วไป การทดสอบครั้งเดียวควรทำให้เกิดการละเมิด ODR เพียงครั้งเดียว แต่อาจไม่สามารถทำได้ในทุกกรณี
ไฟล์เหล่านี้เป็นไฟล์ต้นฉบับ C++ มาตรฐาน ปริมาณและขนาดควรมีขนาดเล็กมาก - ใหญ่เพียงพอตามความจำเป็นเท่านั้นที่จะทำให้เกิด ODRV ตามที่ตั้งใจไว้
odrv_test.toml
ไฟล์การตั้งค่าจะอธิบายให้แอปพลิเคชันทดสอบทราบว่าต้องรวบรวมแหล่งที่มาใด แฟล็กการคอมไพล์ใดที่ควรใช้สำหรับการทดสอบ และ ODRV ใดที่ระบบต้องระวังอันเป็นผลมาจากการเชื่อมโยงไฟล์อ็อบเจ็กต์ที่สร้างขึ้น ) ด้วยกัน.
แหล่งทดสอบระบุด้วยคำสั่ง [[source]]
:
[[ source ]]
path = " one.cpp "
obj = " one "
flags = [
" -Dfoo=1 "
]
ฟิลด์ path
อธิบายพาธไปยังไฟล์ที่เกี่ยวข้องกับ odrv_test.toml
เป็นฟิลด์บังคับเท่านั้น
ฟิลด์ obj
ระบุชื่อของไฟล์อ็อบเจ็กต์ (ชั่วคราว) ที่จะสร้าง หากละเว้นชื่อนี้ ระบบจะใช้ชื่อสุ่มหลอก
ฟิลด์ flags
ระบุแฟล็กการคอมไพล์ที่จะใช้สำหรับหน่วยการคอมไพล์นี้โดยเฉพาะ เมื่อใช้ฟิลด์นี้ คุณจะสามารถนำไฟล์ต้นฉบับเดิมที่มีแฟล็กการคอมไพล์ต่างกันมาใช้ซ้ำเพื่อกระตุ้น ODRV ได้
ODRV ถูกระบุด้วยคำสั่ง [[odrv]]
:
[[ odrv ]]
category = " subprogram:vtable_elem_location "
linkage_name = " _ZNK6object3apiEv "
ช่อง category
จะอธิบายประเภทการละเมิด ODR เฉพาะที่แอปทดสอบควรคาดว่าจะพบ
ฟิลด์ linkage_name
อธิบายสัญลักษณ์เฉพาะที่ทำให้เกิด ODRV ขณะนี้ไม่ได้ใช้งาน แต่จะมีการบังคับใช้เมื่อแอปทดสอบครบกำหนด
แฟล็กต่อไปนี้ไม่ได้ใช้งานอยู่หรือจะมีการเปลี่ยนแปลงครั้งใหญ่เนื่องจากแอปทดสอบหน่วยยังคงพัฒนาต่อไป
[compile_flags]
: ชุดของแฟล็กการคอมไพล์ที่ควรใช้กับทุกไฟล์ต้นฉบับในการทดสอบหน่วย
[orc_test_flags]
: ชุดการตั้งค่ารันไทม์ที่จะส่งผ่านไปยังแอปทดสอบสำหรับการทดสอบนี้
[orc_flags]
: ชุดการตั้งค่ารันไทม์ที่จะส่งผ่านไปยังกลไก ORC สำหรับการทดสอบนี้