บทความนี้จะอธิบายเคล็ดลับและคำแนะนำบางประการสำหรับการดีบักเสียงของ Android
อ่างล้างจาน
"tee sink" เป็นคุณลักษณะการแก้ไขจุดบกพร่อง AudioFlinger ซึ่งมีเฉพาะในรุ่นที่กำหนดเองเท่านั้น สำหรับการเก็บส่วนสั้นๆ ของเสียงล่าสุดไว้เพื่อการวิเคราะห์ในภายหลัง ซึ่งช่วยให้สามารถเปรียบเทียบระหว่างสิ่งที่เล่นหรือบันทึกจริงกับสิ่งที่คาดหวังได้
เพื่อความเป็นส่วนตัว tee sink จะถูกปิดใช้งานโดยค่าเริ่มต้น ทั้งในเวลาคอมไพล์และรันไทม์ หากต้องการใช้ tee sink คุณจะต้องเปิดใช้งานโดยการคอมไพล์ใหม่ และโดยการตั้งค่าคุณสมบัติด้วย อย่าลืมปิดการใช้งานคุณสมบัตินี้หลังจากที่คุณแก้ไขจุดบกพร่องเสร็จแล้ว ไม่ควรเปิดใช้งานทีซิงก์ทิ้งไว้ในบิลด์ที่ใช้งานจริง
คำแนะนำในส่วนนี้ใช้สำหรับ Android 7.x และสูงกว่า สำหรับ Android 5.x และ 6.x ให้แทนที่ /data/misc/audioserver
ด้วย /data/misc/media
นอกจากนี้ คุณต้องใช้ userdebug หรือ eng build หากคุณใช้บิลด์ userdebug ให้ปิดการใช้งาน verity ด้วย:
adb root && adb disable-verity && adb reboot
การตั้งค่าเวลาคอมไพล์
-
cd frameworks/av/services/audioflinger
- แก้ไข
Configuration.h
- ไม่แสดงความคิดเห็น
#define TEE_SINK
- สร้าง
libaudioflinger.so
ใหม่อีกครั้ง -
adb root
-
adb remount
- พุชหรือซิงค์
libaudioflinger.so
ใหม่กับ/system/lib
ของอุปกรณ์
การตั้งค่ารันไทม์
-
adb shell getprop | grep ro.debuggable
ยืนยันว่าเอาต์พุตคือ:[ro.debuggable]: [1]
-
adb shell
-
ls -ld /data/misc/audioserver
ยืนยันว่าผลลัพธ์คือ:
drwx------ media media ... media
หากไม่มีไดเร็กทอรี ให้สร้างดังนี้:
mkdir /data/misc/audioserver
chown media:media /data/misc/audioserver
-
echo af.tee=# > /data/local.prop
โดยที่ค่าaf.tee
เป็นตัวเลขที่อธิบายด้านล่าง -
chmod 644 /data/local.prop
-
reboot
ค่าทรัพย์สิน af.tee
ค่าของ af.tee
คือตัวเลขระหว่าง 0 ถึง 7 ซึ่งแสดงผลรวมของหลายบิต หนึ่งบิตต่อคุณลักษณะ ดูโค้ดที่ AudioFlinger::AudioFlinger()
ใน AudioFlinger.cpp
สำหรับคำอธิบายของแต่ละบิต แต่โดยย่อ:
- 1 = อินพุต
- 2 = เอาต์พุต FastMixer
- 4 = AudioRecord และ AudioTrack ต่อแทร็ก
ยังไม่มีบิตสำหรับบัฟเฟอร์ลึกหรือมิกเซอร์ปกติ แต่คุณสามารถรับผลลัพธ์ที่คล้ายกันได้โดยใช้ "4"
ทดสอบและรับข้อมูล
- เรียกใช้การทดสอบเสียงของคุณ
-
adb shell dumpsys media.audio_flinger
- ค้นหาบรรทัดในเอาต์พุต
dumpsys
เช่นนี้:
tee copied to /data/misc/audioserver/20131010101147_2.wav
นี่คือไฟล์ PCM .wav - จากนั้น
adb pull
ไฟล์/data/misc/audioserver/*.wav
ใด ๆ ที่น่าสนใจ โปรดทราบว่าชื่อไฟล์ดัมพ์เฉพาะแทร็กจะไม่ปรากฏในเอาต์พุตdumpsys
แต่ยังคงบันทึกลงใน/data/misc/audioserver
เมื่อปิดแทร็ก - ตรวจสอบไฟล์ดัมพ์เพื่อดูข้อกังวลด้านความเป็นส่วนตัวก่อนแชร์กับผู้อื่น
ข้อเสนอแนะ
ลองใช้แนวคิดเหล่านี้เพื่อให้ได้ผลลัพธ์ที่เป็นประโยชน์มากขึ้น:
- ปิดใช้งานเสียงสัมผัสและการคลิกแป้นเพื่อลดการหยุดชะงักในผลการทดสอบ
- เพิ่มปริมาณทั้งหมดให้สูงสุด
- ปิดใช้งานแอปที่สร้างเสียงหรือบันทึกจากไมโครโฟน หากแอปเหล่านั้นไม่สนใจการทดสอบของคุณ
- การดัมพ์เฉพาะแทร็กจะถูกบันทึกเฉพาะเมื่อแทร็กถูกปิดเท่านั้น คุณอาจต้องบังคับปิดแอปเพื่อถ่ายโอนข้อมูลเฉพาะแทร็ก
- ทำ
dumpsys
ทันทีหลังการทดสอบ มีพื้นที่ในการบันทึกจำนวนจำกัด - เพื่อให้แน่ใจว่าไฟล์ดัมพ์ของคุณจะไม่สูญหาย ให้อัปโหลดไฟล์เหล่านั้นไปยังโฮสต์ของคุณเป็นระยะๆ ไฟล์ดัมพ์จำนวนจำกัดเท่านั้นที่จะถูกเก็บรักษาไว้ ดัมพ์เก่าจะถูกลบออกหลังจากถึงขีดจำกัดนั้นแล้ว
คืนค่า
ตามที่ระบุไว้ข้างต้น ไม่ควรเปิดใช้งานคุณลักษณะทีซิงก์ทิ้งไว้ กู้คืนบิลด์และอุปกรณ์ของคุณดังนี้:
- เปลี่ยนกลับการเปลี่ยนแปลงซอร์สโค้ดเป็น
Configuration.h
- สร้าง
libaudioflinger.so
ใหม่อีกครั้ง - พุชหรือซิงค์
libaudioflinger.so
ที่กู้คืนไปยัง/system/lib
ของอุปกรณ์ -
adb shell
-
rm /data/local.prop
-
rm /data/misc/audioserver/*.wav
-
reboot
สื่อ.log
มาโคร ALOGx
API การบันทึกภาษา Java มาตรฐานใน Android SDK คือ android.util.Log
API ภาษา C ที่สอดคล้องกันใน Android NDK คือ __android_log_print
ประกาศใน <android/log.h>
ภายในส่วนดั้งเดิมของเฟรมเวิร์ก Android เราชอบมาโครที่ชื่อ ALOGE
, ALOGW
, ALOGI
, ALOGV
ฯลฯ โดยมีการประกาศไว้ใน <utils/Log.h>
และสำหรับวัตถุประสงค์ของบทความนี้ เราจะเรียกรวมกันว่า ALOGx
.
API ทั้งหมดนี้ใช้งานง่ายและเข้าใจดี ดังนั้นจึงแพร่หลายไปทั่วแพลตฟอร์ม Android โดยเฉพาะอย่างยิ่งกระบวนการ mediaserver
ซึ่งรวมถึงเซิร์ฟเวอร์เสียง AudioFlinger นั้นใช้ ALOGx
อย่างกว้างขวาง
อย่างไรก็ตาม มีข้อจำกัดบางประการสำหรับ ALOGx
และเพื่อนๆ:
- สิ่งเหล่านี้เสี่ยงต่อ "บันทึกสแปม": บัฟเฟอร์บันทึกเป็นทรัพยากรที่ใช้ร่วมกัน จึงสามารถล้นได้อย่างง่ายดายเนื่องจากรายการบันทึกที่ไม่เกี่ยวข้อง ส่งผลให้ข้อมูลสูญหาย ตัวแปร
ALOGV
ถูกปิดใช้งานในเวลาคอมไพล์ตามค่าเริ่มต้น แต่แน่นอนว่ามันอาจส่งผลให้เกิดสแปมบันทึกได้หากเปิดใช้งาน - การเรียกระบบเคอร์เนลที่ซ่อนอยู่สามารถบล็อกได้ ซึ่งอาจส่งผลให้เกิดการผกผันลำดับความสำคัญ และส่งผลให้เกิดการรบกวนและความไม่ถูกต้องในการวัด นี่เป็นข้อกังวลเป็นพิเศษสำหรับเธรดที่มีความสำคัญด้านเวลา เช่น
FastMixer
และFastCapture
- หากบันทึกใดถูกปิดใช้งานเพื่อลดสแปมบันทึก ข้อมูลใดๆ ที่บันทึกนั้นบันทึกไว้จะสูญหายไป ไม่สามารถเปิดใช้งานบันทึกเฉพาะย้อนหลังได้ หลังจาก ที่เห็นได้ชัดว่าบันทึกนั้นน่าสนใจ
NBLOG, media.log และ MediaLogService
NBLOG
API และกระบวนการ media.log
ที่เกี่ยวข้องและบริการ MediaLogService
ร่วมกันสร้างระบบการบันทึกใหม่สำหรับสื่อ และได้รับการออกแบบมาโดยเฉพาะเพื่อแก้ไขปัญหาข้างต้น เราจะใช้คำว่า "media.log" อย่างคร่าวๆ เพื่ออ้างถึงทั้งสามคำ แต่หากพูดอย่างเคร่งครัด NBLOG
คือ API การบันทึก C++, media.log
คือชื่อกระบวนการของ Linux และ MediaLogService
เป็นบริการ Binder ของ Android สำหรับตรวจสอบบันทึก
"ไทม์ไลน์" media.log
คือชุดของรายการบันทึกที่ยังคงลำดับที่เกี่ยวข้องไว้ ตามแบบแผน แต่ละเธรดควรใช้ไทม์ไลน์ของตัวเอง
ประโยชน์
ข้อดีของระบบ media.log
คือ:
- ไม่สแปมบันทึกหลัก เว้นแต่และจนกว่าจะจำเป็น
- สามารถตรวจสอบได้แม้ในขณะที่
mediaserver
ขัดข้องหรือแฮงค์ - ไม่มีการบล็อกต่อไทม์ไลน์
- เสนอการรบกวนประสิทธิภาพการทำงานน้อยลง (แน่นอนว่าไม่มีรูปแบบการบันทึกใดที่ไม่ล่วงล้ำโดยสิ้นเชิง)
สถาปัตยกรรม
แผนภาพด้านล่างแสดงความสัมพันธ์ของกระบวนการ mediaserver
และกระบวนการ init
ก่อนที่ media.log
จะถูกนำมาใช้:
![สถาปัตยกรรมก่อน media.log](https://source.android.google.cn/static/docs/core/audio/images/medialog_before.png?authuser=4&hl=th)
รูปที่ 1 สถาปัตยกรรมก่อน media.log
ประเด็นเด่น:
-
init
forks และ execsmediaserver
-
init
ตรวจพบการตายของmediaserver
และแยกใหม่ตามความจำเป็น - การบันทึก
ALOGx
ไม่แสดง
แผนภาพด้านล่างแสดงความสัมพันธ์ใหม่ของส่วนประกอบ หลังจากที่เพิ่ม media.log
ลงในสถาปัตยกรรมแล้ว:
![สถาปัตยกรรมหลัง media.log](https://source.android.google.cn/static/docs/core/audio/images/medialog_after.png?authuser=4&hl=th)
รูปที่ 2 สถาปัตยกรรมหลัง media.log
การเปลี่ยนแปลงที่สำคัญ:
- ไคลเอนต์ใช้
NBLOG
API เพื่อสร้างรายการบันทึกและผนวกเข้ากับบัฟเฟอร์แบบวงกลมในหน่วยความจำที่ใช้ร่วมกัน -
MediaLogService
สามารถถ่ายโอนข้อมูลเนื้อหาของบัฟเฟอร์แบบวงกลมได้ตลอดเวลา - บัฟเฟอร์แบบวงกลมได้รับการออกแบบในลักษณะที่ความเสียหายของหน่วยความจำที่ใช้ร่วมกันจะไม่ทำให้
MediaLogService
เสียหาย และจะยังสามารถถ่ายโอนข้อมูลบัฟเฟอร์ได้มากเท่าที่ไม่ได้รับผลกระทบจากความเสียหาย - บัฟเฟอร์แบบวงกลมไม่มีการปิดกั้นและไม่มีการล็อคสำหรับทั้งการเขียนรายการใหม่และการอ่านรายการที่มีอยู่
- ไม่จำเป็นต้องเรียกระบบเคอร์เนลเพื่อเขียนหรืออ่านจากบัฟเฟอร์แบบวงกลม (นอกเหนือจากการประทับเวลาเผื่อเลือก)
จะใช้ที่ไหน
ตั้งแต่ Android 4.4 มีจุดบันทึกเพียงไม่กี่จุดใน AudioFlinger ที่ใช้ระบบ media.log
แม้ว่า API ใหม่จะไม่ได้ใช้งานง่ายเท่ากับ ALOGx
แต่ก็ไม่ได้ยากมากเช่นกัน เราขอแนะนำให้คุณเรียนรู้ระบบการบันทึกแบบใหม่สำหรับโอกาสเหล่านั้นเมื่อจำเป็น โดยเฉพาะอย่างยิ่ง ขอแนะนำสำหรับเธรด AudioFlinger ที่ต้องทำงานบ่อยครั้ง เป็นระยะๆ และไม่มีการบล็อก เช่น เธรด FastMixer
และ FastCapture
วิธีใช้
เพิ่มบันทึก
ขั้นแรก คุณต้องเพิ่มบันทึกลงในโค้ดของคุณ
ในเธรด FastMixer
และ FastCapture
ให้ใช้โค้ดดังนี้:
logWriter->log("string"); logWriter->logf("format", parameters); logWriter->logTimestamp();
เนื่องจากไทม์ไลน์ NBLog
นี้ถูกใช้โดยเธรด FastMixer
และ FastCapture
เท่านั้น จึงไม่จำเป็นต้องแยกออกร่วมกัน
ในเธรด AudioFlinger อื่น ให้ใช้ mNBLogWriter
:
mNBLogWriter->log("string"); mNBLogWriter->logf("format", parameters); mNBLogWriter->logTimestamp();
สำหรับเธรดอื่นที่ไม่ใช่ FastMixer
และ FastCapture
ไทม์ไลน์ NBLog
ของเธรดสามารถใช้ได้ทั้งกับเธรดเอง และโดยการดำเนินการของ Binder NBLog::Writer
ไม่มีการยกเว้นร่วมกันโดยนัยต่อไทม์ไลน์ ดังนั้นโปรดตรวจสอบให้แน่ใจว่าบันทึกทั้งหมดเกิดขึ้นภายในบริบทที่ mLock
ของ mutex ของเธรดถูกเก็บไว้
หลังจากที่คุณเพิ่มบันทึกแล้ว ให้สร้าง AudioFlinger ใหม่
ข้อควรระวัง: จำเป็นต้องมีไทม์ไลน์ NBLog::Writer
แยกต่างหากต่อเธรด เพื่อรับรองความปลอดภัยของเธรด เนื่องจากไทม์ไลน์ละเว้น mutexes ตามการออกแบบ หากคุณต้องการให้เธรดมากกว่าหนึ่งเธรดใช้ไทม์ไลน์เดียวกัน คุณสามารถป้องกันด้วย mutex ที่มีอยู่ได้ (ตามที่อธิบายไว้ข้างต้นสำหรับ mLock
) หรือคุณสามารถใช้ wrapper NBLog::LockedWriter
แทน NBLog::Writer
อย่างไรก็ตาม สิ่งนี้จะลบล้างข้อดีที่สำคัญของ API นี้ นั่นคือพฤติกรรมที่ไม่บล็อก
NBLog
API ฉบับเต็มอยู่ที่ frameworks/av/include/media/nbaio/NBLog.h
เปิดใช้งาน media.log
media.log
ถูกปิดใช้งานตามค่าเริ่มต้น โดยจะใช้งานได้เมื่อคุณสมบัติ ro.test_harness
เป็น 1
เท่านั้น คุณสามารถเปิดใช้งานได้โดย:
adb root
adb shell
echo ro.test_harness=1 > /data/local.prop
chmod 644 /data/local.prop
reboot
การเชื่อมต่อขาดหายไประหว่างการรีบูต ดังนั้น:
adb shellคำสั่ง
ps media
จะแสดงสองกระบวนการ:- สื่อ.log
- มีเดียเซิร์ฟเวอร์
จดบันทึกรหัสกระบวนการของ mediaserver
ไว้ใช้ในภายหลัง
แสดงไทม์ไลน์
คุณสามารถขอดัมพ์บันทึกด้วยตนเองได้ตลอดเวลา คำสั่งนี้แสดงบันทึกจากไทม์ไลน์ที่ใช้งานอยู่และล่าสุดทั้งหมด จากนั้นล้างข้อมูล:
dumpsys media.log
โปรดทราบว่าตามไทม์ไลน์การออกแบบนั้นมีความเป็นอิสระ และไม่มีสิ่งอำนวยความสะดวกในการรวมไทม์ไลน์
กู้คืนบันทึกหลังจากเซิร์ฟเวอร์สื่อเสียชีวิต
ตอนนี้ลองฆ่ากระบวนการ mediaserver
: kill -9 #
โดยที่ # คือ ID กระบวนการที่คุณจดบันทึกไว้ก่อนหน้านี้ คุณควรเห็นดัมพ์จาก media.log
ใน logcat
หลัก ซึ่งแสดงบันทึกทั้งหมดที่นำไปสู่ข้อขัดข้อง
dumpsys media.log