ניפוי באגים באודיו

במאמר הזה נתאר כמה טיפים וטריקים לניפוי באגים בנושא אודיו ב-Android.

כיור טי

"כיור טי" תואם לערך תכונת ניפוי באגים של AudioFlinger, שזמינה בגרסאות build בהתאמה אישית בלבד, לשמירת קטע קצר מהאודיו האחרון לניתוח מאוחר יותר. כך ניתן להשוות בין התוכן שהושמע או הוקלט בפועל לעומת מה שציפיתי.

מטעמי פרטיות, tee sink מושבת כברירת מחדל, גם בזמן ההידור וגם בזמן הריצה. כדי להשתמש ב-tee sink, תצטרכו להפעיל אותו על ידי הידור מחדש, וגם הגדרת נכס. חשוב להשבית את התכונה הזו אחרי לבצע ניפוי באגים. אין להשאיר את ה-teesink מופעלים בגרסאות build לסביבת הייצור.

ההוראות בקטע הזה מיועדות ל-Android מגרסה 7.x ואילך. ב-Android 5.x ו-6.x, מחליפים את /data/misc/audioserver ב- /data/misc/media. כמו כן, צריך להשתמש ב-userdebug או יצירת עניין. אם משתמשים ב-build של ניפוי באגים במשתמש, משביתים את האימות באמצעות:

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
    זהו קובץ .wav של PCM.
  4. לאחר מכן adb pull קבצים של /data/misc/audioserver/*.wav שעשויים לעניין אותך; שימו לב ששמות קבצים של קובצי dump ספציפיים לטראק לא מופיעים פלט dumpsys, אבל עדיין נשמרות ב-/data/misc/audioserver לאחר סגירת המסלול.
  5. לפני שמשתפים את קובצי ה-Dump עם אחרים, כדאי לבדוק אם יש בהם חשש לפרטיות.

הצעות

לקבלת תוצאות מועילות יותר, כדאי לנסות את הרעיונות הבאים:

  • כדי לצמצם את ההפרעות בפלט הבדיקה, צריך להשבית את צלילי המגע והקליקים על המקשים.
  • להגדיל את כל עוצמת הקול.
  • משביתים אפליקציות שמשמיעות צלילים או מקליטים מהמיקרופון, אם הם לא יעניינו את הבדיקה שלך.
  • קובצי פיד ספציפיים לטראק נשמרים רק כשהטראק נסגר; ייתכן שיהיה צורך לאלץ סגירה של אפליקציה כדי למחוק את הנתונים הספציפיים למסלול
  • מבצעים את הפעולה dumpsys מיד לאחר הבדיקה. יש מקום מוגבל להקלטה.
  • כדי לוודא שלא תאבדו את קובצי ה-Dump, להעלות אותם למארח שלך מדי פעם. רק מספר מוגבל של קובצי dump נשמרים; פריטים ישנים יותר מוסרים לאחר הגעה למגבלה הזו.

שחזור

כפי שצוין למעלה, אין להשאיר את התכונה tee sink. אפשר לשחזר את ה-build ואת המכשיר באופן הבא:

  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 framework, עדיף להשתמש בפקודות מאקרו בשם ALOGE, ALOGW, ALOGI, ALOGV וכו'. הן הוצהרו ב- <utils/Log.h>, ולצורכי המאמר הזה אנחנו נתייחס אליהם יחד בתור ALOGx.

כל ממשקי ה-API האלה קלים לשימוש ומבינים היטב, ולכן הם חודרניים בכל פלטפורמת Android. באופן ספציפי, mediaserver שכולל את שרת הצלילים AudioFlinger, משתמש ב- ALOGx במידה רבה.

עם זאת, יש מספר מגבלות על ALOGx ועל חברים:

  • הם חשופים ל'רישום ספאם': מאגר הנתונים הזמני של היומן הוא משאב משותף כך שהוא יכול לגלוש בקלות עקב רשומות יומן שאינן קשורות, וכתוצאה מכך מידע חסר. הווריאנט ALOGV מושבת ב כברירת מחדל. כמובן שאפילו זה יכול לגרום לרישום ספאם ביומן. אם היא מופעלת.
  • הקריאות למערכת הליבה (kernel) הבסיסית עשויות לחסום, מה שעלול להוביל וכתוצאה מכך, הפרעות במדידה של אי-דיוקים. זה מתוך חשש מיוחד לשרשורים קריטיים בזמן, כמו FastMixer ו-FastCapture.
  • אם יומן מסוים מושבת כדי לצמצם את הספאם של היומנים, כל המידע שהיה מתועד ביומן הזה יאבד. אין אפשרות להפעיל יומן ספציפי באופן רטרואקטיבי, אחרי שיתברר שהיומן יהיה מעניין.

NBLOG, media.log ו-MediaLogService

ממשקי ה-API של NBLOG וה-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

איור 1. ארכיטקטורה לפני media.log

נקודות חשובות:

  • init מזלגות ומנהלים mediaserver.
  • init מזהה את המוות של mediaserver ומחבר מחדש לפי הצורך.
  • הרישום ביומן של ALOGx לא מוצג.

בתרשים הבא מוצג הקשר החדש בין הרכיבים, אחרי הוספת media.log לארכיטקטורה:

ארכיטקטורה אחרי media.log

איור 2. ארכיטקטורה אחרי media.log

שינויים חשובים:

  • לקוחות משתמשים ב-API של NBLOG כדי ליצור רשומות ביומן ולצרף אותן של מאגר נתונים זמני בזיכרון משותף.
  • אפשר להזין את התוכן של מאגר הנתונים הזמני על 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 של השרשור יכול לשמש גם ל-thread עצמו, וגם באמצעות פעולות של קלסר. NBLog::Writer אינו מספק החרגה הדדית מרומזת לכל ציר זמן, לכן חשוב לוודא שכל היומנים בהקשר שבו נשמר ההשתקה mLock של השרשור.

אחרי שמוסיפים את היומנים, בונים מחדש את AudioFlinger.

אזהרה: צריך להגדיר ציר זמן נפרד של NBLog::Writer לכל שרשור, כדי לשמור על בטיחות השרשורים, כי צירי הזמן מושמטים מהמושג 'מוטמעים'. אם אם רוצים שיותר משרשור אחד ישתמש באותו ציר זמן, אפשר להגן באמצעות mutex קיים (כפי שמתואר למעלה לגבי mLock). לחלופין אפשר צריך להשתמש ב-wrapper של NBLog::LockedWriter במקום ב-NBLog::Writer. עם זאת, הוא מבטל את היתרון הראשוני של ה-API הזה: הוא לא חוסם או התנהגות המשתמשים.

ה-API המלא של NBLog נמצא ב-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
  • שרת מדיה

חשוב לזכור את מזהה התהליך של mediaserver לשימוש במועד מאוחר יותר.

הצגת לוחות הזמנים

תמיד אפשר לבקש העלאת נתונים באופן ידני. הפקודה הזו מציגה יומנים מכל צירי הזמן הפעילים והאחרונים, ואז מוחקת אותם:

dumpsys media.log

שימו לב שמעצם טבעו, לוחות זמנים הם בלתי תלויים ואין אפשרות למיזוג צירי זמן.

שחזור יומנים לאחר מות של שרת המדיה

עכשיו אפשר לנסות להרוג את mediaserver: kill -9 #, כאשר # מזהה התהליך שציינת קודם. אמורה להופיע תמונת מצב של media.log ב-logcat הראשי, שמוצגים בו כל היומנים שהובילו לקריסה.

dumpsys media.log