การดีบักเสียง

บทความนี้จะอธิบายเคล็ดลับและเทคนิคบางประการสำหรับการดีบักเสียงของ Android

ทีอ่างล้างจาน

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

เพื่อความเป็นส่วนตัว 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

การตั้งค่าเวลาคอมไพล์

  1. cd frameworks/av/services/audioflinger
  2. แก้ไข Configuration.h
  3. #define TEE_SINK
  4. สร้าง libaudioflinger.so
  5. adb root
  6. adb remount
  7. พุชหรือซิงค์ libaudioflinger.so ใหม่กับ /system/lib ของอุปกรณ์

การตั้งค่ารันไทม์

  1. adb shell getprop | grep ro.debuggable
    ยืนยันว่าผลลัพธ์คือ: [ro.debuggable]: [1]
  2. adb shell
  3. ls -ld /data/misc/audioserver

    ยืนยันว่าผลลัพธ์คือ:

    drwx------ media media ... media
    

    หากไม่มีไดเร็กทอรี ให้สร้างดังนี้:

    mkdir /data/misc/audioserver
    chown media:media /data/misc/audioserver
    
  4. echo af.tee=# > /data/local.prop
    โดยที่ค่า af.tee เป็นตัวเลขที่อธิบายไว้ด้านล่าง
  5. chmod 644 /data/local.prop
  6. reboot

ค่าสำหรับทรัพย์สิน af.tee

ค่าของ af.tee คือตัวเลขระหว่าง 0 ถึง 7 ซึ่งแสดงผลรวมของหลายบิต หนึ่งรายการต่อหนึ่งคุณลักษณะ ดูรหัสที่ AudioFlinger::AudioFlinger() ใน AudioFlinger.cpp สำหรับคำอธิบายของแต่ละบิต แต่โดยสังเขป:

  • 1 = อินพุต
  • 2 = เอาต์พุต FastMixer
  • 4 = AudioRecord ต่อแทร็กและ AudioTrack

ยังไม่มีบัฟเฟอร์ลึกหรือเครื่องผสมปกติ แต่คุณสามารถได้ผลลัพธ์ที่คล้ายกันโดยใช้ "4"

ทดสอบและรับข้อมูล

  1. เรียกใช้การทดสอบเสียงของคุณ
  2. adb shell dumpsys media.audio_flinger
  3. ค้นหาบรรทัดในเอาต์พุต dumpsys เช่นนี้:
    tee copied to /data/misc/audioserver/20131010101147_2.wav
    นี่คือไฟล์ PCM .wav
  4. จากนั้น adb pull ไฟล์ /data/misc/audioserver/*.wav ที่น่าสนใจ โปรดทราบว่าชื่อไฟล์ดัมพ์เฉพาะแทร็กจะไม่ปรากฏในเอาต์พุต dumpsys แต่ยังคงบันทึกไว้ใน /data/misc/audioserver เมื่อแทร็กปิด
  5. ตรวจสอบไฟล์ดัมพ์สำหรับข้อกังวลด้านความเป็นส่วนตัวก่อนแชร์กับผู้อื่น

ข้อเสนอแนะ

ลองใช้แนวคิดเหล่านี้เพื่อผลลัพธ์ที่เป็นประโยชน์มากขึ้น:

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

คืนค่า

ดังที่กล่าวไว้ข้างต้น ไม่ควรเปิดใช้งานคุณลักษณะทีซิงก์ กู้คืนบิลด์และอุปกรณ์ของคุณดังนี้:

  1. เปลี่ยนการเปลี่ยนซอร์สโค้ดเป็น Configuration.h
  2. สร้าง libaudioflinger.so
  3. พุชหรือซิงค์ libaudioflinger.so ที่กู้คืนไปยัง /system/lib ของอุปกรณ์
  4. adb shell
  5. rm /data/local.prop
  6. rm /data/misc/audioserver/*.wav
  7. reboot

media.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 ใช้ ALOGx อย่างกว้างขวาง

อย่างไรก็ตาม มีข้อจำกัดบางประการสำหรับ ALOGx และผองเพื่อน:

  • สิ่งเหล่านี้มีความอ่อนไหวต่อ "บันทึกสแปม": บัฟเฟอร์บันทึกเป็นทรัพยากรที่ใช้ร่วมกัน ดังนั้นจึงสามารถโอเวอร์โฟลว์ได้อย่างง่ายดายเนื่องจากรายการบันทึกที่ไม่เกี่ยวข้อง ส่งผลให้มีข้อมูลที่ไม่ได้รับ ตัวแปร ALOGV ถูกปิดใช้งานในขณะคอมไพล์โดยค่าเริ่มต้น แต่แน่นอนว่ามันอาจส่งผลให้เกิดสแปมบันทึกได้หากเปิดใช้งาน
  • การเรียกระบบเคอร์เนลที่อยู่เบื้องหลังอาจปิดกั้น ซึ่งอาจส่งผลให้เกิดการผกผันของลำดับความสำคัญ และส่งผลให้เกิดการรบกวนการวัดและความไม่ถูกต้อง นี่เป็นข้อกังวลพิเศษสำหรับเธรดที่มีความสำคัญต่อเวลา เช่น FastMixer และ FastCapture
  • หากบันทึกเฉพาะถูกปิดใช้งานเพื่อลดสแปมบันทึก ข้อมูลใดๆ ที่บันทึกนั้นจะถูกบันทึกไว้จะสูญหายไป เป็นไปไม่ได้ที่จะเปิดใช้งานบันทึกเฉพาะแบบย้อนหลัง หลังจาก ที่เห็นได้ชัดว่าบันทึกนั้นน่าสนใจ

NBLOG, media.log และ MediaLogService

NBLOG API และกระบวนการ media.log ที่เกี่ยวข้องและบริการ MediaLogService ร่วมกันสร้างระบบการบันทึกที่ใหม่กว่าสำหรับสื่อ และได้รับการออกแบบมาโดยเฉพาะเพื่อแก้ไขปัญหาข้างต้น เราจะใช้คำว่า "media.log" อย่างหลวม ๆ เพื่ออ้างถึงทั้งสาม แต่พูดอย่างเคร่งครัด NBLOG คือ API การบันทึก C++, media.log เป็นชื่อกระบวนการ Linux และ MediaLogService เป็นบริการผูก Android สำหรับตรวจสอบบันทึก

"ไทม์ไลน์" ของ media.log คือชุดของรายการบันทึกที่มีการรักษาลำดับที่สัมพันธ์กัน ตามธรรมเนียมแล้ว แต่ละเธรดควรใช้ไทม์ไลน์ของตัวเอง

ประโยชน์

ข้อดีของระบบ media.log คือ:

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

สถาปัตยกรรม

แผนภาพด้านล่างแสดงความสัมพันธ์ของกระบวนการ media.log และกระบวนการ init ต้น ก่อนเปิดตัว mediaserver :

สถาปัตยกรรมก่อน media.log

รูปที่ 1. สถาปัตยกรรมก่อน media.log

จุดสังเกต:

  • init forks และ execs mediaserver
  • init ตรวจพบการตายของ mediaserver และแยกใหม่ตามความจำเป็น
  • ไม่แสดงการบันทึก ALOGx

แผนภาพด้านล่างแสดงความสัมพันธ์ใหม่ของคอมโพเนนต์ หลังจากเพิ่ม media.log ลงในสถาปัตยกรรมแล้ว:

สถาปัตยกรรมหลัง media.log

รูปที่ 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 ไม่ได้จัดเตรียมการยกเว้นร่วมกันโดยนัยต่อไทม์ไลน์ ดังนั้น ตรวจสอบให้แน่ใจว่าบันทึกทั้งหมดเกิดขึ้นภายในบริบทที่ mutex mLock ของเธรดถูกเก็บไว้

หลังจากที่คุณเพิ่มบันทึกแล้ว ให้สร้าง AudioFlinger ใหม่

ข้อควรระวัง: จำเป็นต้องมี NBLog::Writer timeline แยกต่างหากต่อเธรด เพื่อความปลอดภัยของเธรด เนื่องจากไทม์ไลน์ละเว้น 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 จะแสดงสองกระบวนการ:
  • media.log
  • มีเดียเซิร์ฟเวอร์

หมายเหตุ ID กระบวนการของ mediaserver สำหรับภายหลัง

กำลังแสดงไทม์ไลน์

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

dumpsys media.log

โปรดทราบว่าโดยไทม์ไลน์การออกแบบจะเป็นอิสระ และไม่มีสิ่งอำนวยความสะดวกในการรวมไทม์ไลน์

การกู้คืนบันทึกหลังจากเซิร์ฟเวอร์มีเดียเสียชีวิต

ตอนนี้ให้ลองฆ่ากระบวนการ mediaserver : kill -9 # โดยที่ # คือ ID กระบวนการที่คุณระบุไว้ก่อนหน้านี้ คุณควรเห็นดัมพ์จาก media.log ใน logcat หลัก ซึ่งแสดงบันทึกทั้งหมดที่นำไปสู่การแครช

dumpsys media.log