오디오 디버깅

이 도움말에서는 Android 오디오 디버깅에 관한 도움말 및 유용한 정보를 설명합니다.

티 싱크

'티 싱크'는 맞춤 빌드에서만 사용할 수 있는 AudioFlinger 디버깅 기능으로, 최근 오디오의 짧은 부분을 나중에 분석할 수 있도록 유지합니다. 이를 통해 실제로 재생 또는 녹음된 것이 예상과 어떻게 다른지 비교할 수 있습니다.

티 싱크는 개인 정보 보호를 위해 컴파일 시간 및 재생 시간에 기본적으로 사용 중지됩니다. 티 싱크를 사용하려면 다시 컴파일하고 속성을 설정하여 사용 설정해야 합니다. 디버깅을 완료한 후에는 이 기능을 사용 중지해야 하며 프로덕션 빌드에서 티 싱크가 사용 설정되어 있으면 안 됩니다.

이 섹션의 안내는 Android 7.x 이상을 대상으로 합니다. Android 5.x 및 6.x의 경우 /data/misc/audioserver/data/misc/media로 바꿉니다. 또한 userdebug 또는 eng 빌드를 사용해야 합니다. 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.cppAudioFlinger::AudioFlinger()에 있는 코드를 참조하세요. 간단히 설명하자면 다음과 같습니다.

  • 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. 그다음 원하는 /data/misc/audioserver/*.wav 파일을 adb pull합니다. 트랙별 덤프 파일 이름은 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 매크로

Android SDK의 표준 자바 언어 로깅 API는 android.util.Log입니다.

이에 상응하는 Android NDK의 C 언어 API는 <android/log.h>에서 선언된 __android_log_print입니다.

Android 프레임워크의 네이티브 부분에서는 이름이 ALOGE, ALOGW, ALOGI, ALOGV 등인 매크로가 선호됩니다. 이 매크로는 <utils/Log.h>에서 선언되며 이 도움말의 목적에 맞게 ALOGx로 총칭됩니다.

이 API는 모두 사용하기 쉽고 잘 알려져 있기 때문에 Android 플랫폼에서 널리 사용됩니다. 특히 AudioFlinger 사운드 서버를 포함하는 mediaserver 프로세스는 ALOGx를 폭넓게 사용합니다.

그러나 ALOGx 등의 매크로에는 다음과 같은 제한사항이 있습니다.

  • 이러한 매크로는 '로그 스팸'에 취약합니다. 로그 버퍼는 공유 리소스이므로 관련 없는 로그 항목으로 인해 쉽게 오버플로우되면서 정보 누락이 발생할 수 있습니다. ALOGV 변이는 컴파일 시간에 기본적으로 사용 중지되지만, 사용 설정되더라도 로그 스팸이 당연히 발생될 수 있습니다.
  • 기본 커널 시스템 호출에서 차단하면서 우선순위 역전이 발생하여 측정 방해 및 부정확한 결과를 초래할 수 있습니다. 이는 시간이 큰 영향을 미치는 FastMixerFastCapture 스레드에 특히 중요한 사항입니다.
  • 로그 스팸을 줄이기 위해 특정 로그를 사용 중지하면 로그에서 캡처할 수도 있었던 모든 정보가 손실됩니다. 특정 로그가 관심 대상이었을 수 있음이 분명해진 에는 특정 로그를 소급하여 사용 설정할 수 없습니다.

NBLOG, media.log, MediaLogService

NBLOG API, 관련 media.log 프로세스, MediaLogService 서비스는 미디어의 최신 로깅 시스템을 구성하며 위 문제를 해결하도록 특별히 설계되었습니다. 'media.log'라는 용어는 세 가지를 모두 지칭하는 데 사용되지만, 엄격히 말하면 NBLOG는 C++ 로깅 API이고 media.log는 Linux 프로세스 이름이며 MediaLogService는 로그를 검사하는 Android 바인더 서비스입니다.

media.log '타임라인'은 상대적인 순서가 보존되는 일련의 로그 항목입니다. 규칙에 따라 각 스레드는 자체 타임라인을 사용해야 합니다.

이점

media.log 시스템의 이점은 다음과 같습니다.

  • 필요하기 전까지는 메인 로그를 스팸으로 처리하지 않습니다.
  • mediaserver가 비정상 종료되거나 중단될 때에도 검사될 수 있습니다.
  • 타임라인별 비블로킹입니다.
  • 성능에 미치는 영향이 더 적습니다. 물론 전혀 영향을 미치지 않는 로깅 형식은 없습니다.

아키텍처

아래의 다이어그램은 media.log가 도입되기 전의 mediaserver 프로세스 및 init 프로세스의 관계를 보여줍니다.

media.log 이전 아키텍처

그림 1. media.log 이전 아키텍처

중요 사항:

  • initmediaserver를 포크하고 실행합니다.
  • initmediaserver의 종료를 감지하고 필요한 경우 다시 분기합니다.
  • ALOGx 로깅이 표시되지 않습니다.

아래의 다이어그램은 아키텍처에 media.log가 추가된 후의 새로운 구성요소 관계를 보여줍니다.

media.log 이후 아키텍처

그림 2. media.log 이후 아키텍처

중요 변경사항:

  • 클라이언트는 NBLOG API를 사용하여 로그 항목을 구성하고 공유 메모리의 순환 버퍼에 추가합니다.
  • MediaLogService는 순환 버퍼의 내용을 언제든지 덤프할 수 있습니다.
  • 순환 버퍼는 공유 메모리의 손상으로 인해 MediaLogService가 비정상 종료되지 않도록 설계되었으며, 이러한 손상의 영향을 받지 않는 버퍼만큼 덤프할 수 있습니다.
  • 순환 버퍼는 새로운 항목을 쓰고 기존 항목을 읽을 때 비블로킹이며 잠금이 없습니다.
  • 선택사항인 타임스탬프를 제외한 커널 시스템 호출은 순환 버퍼에서의 쓰기 또는 읽기에 필요하지 않습니다.

용도

Android 4.4의 경우 media.log 시스템을 사용하는 AudioFlinger에 로그 포인트가 많지 않습니다. 새 API는 ALOGx만큼 사용이 쉽지는 않지만, 아주 어렵지도 않습니다. 반드시 필요한 경우에는 새로운 로깅 시스템을 배우는 것이 좋습니다. FastMixerFastCapture 스레드와 같은 블로킹 없이 정기적으로 자주 실행되어야 하는 AudioFlinger 스레드의 경우에 특히 권장되는 사항입니다.

사용 방법

로그 추가

먼저 코드에 로그를 추가해야 합니다.

FastMixerFastCapture 스레드에서는 다음과 같은 코드를 사용합니다.

logWriter->log("string");
logWriter->logf("format", parameters);
logWriter->logTimestamp();

NBLog 타임라인은 FastMixerFastCapture 스레드에서만 사용되기 때문에 상호 배제할 필요가 없습니다.

다른 AudioFlinger 스레드에서는 다음과 같이 mNBLogWriter를 사용합니다.

mNBLogWriter->log("string");
mNBLogWriter->logf("format", parameters);
mNBLogWriter->logTimestamp();

FastMixerFastCapture를 제외한 기타 스레드의 경우 스레드의 NBLog 타임라인은 스레드 자체와 바인더 작업에서 모두 사용될 수 있습니다. NBLog::Writer는 타임라인별 암시적 상호 배제를 제공하지 않으므로, 스레드의 뮤텍스 mLock이 보관되는 컨텍스트 내에서 모든 로그가 발생하도록 해야 합니다.

로그를 추가한 후 AudioFlinger를 다시 빌드합니다.

주의: 타임라인은 설계상 뮤텍스를 생략하기 때문에 스레드 안전을 위해 스레드마다 별도의 NBLog::Writer 타임라인이 필요합니다. 동일한 타임라인을 두 개 이상의 스레드에서 사용하려는 경우 위의 mLock 부분에 설명된 대로 기존 뮤텍스를 통해 보호할 수 있습니다. 또는 NBLog::Writer 대신 NBLog::LockedWriter 래퍼를 사용할 수도 있습니다. 하지만 이 경우 API의 주요 이점인 비블로킹 동작은 유효하지 않습니다.

전체 NBLog API는 frameworks/av/include/media/nbaio/NBLog.h에 위치합니다.

media.log 사용 설정

media.log는 기본적으로 사용 중지됩니다. 속성 ro.test_harness1인 경우에만 활성화됩니다. 다음과 같이 사용 설정할 수 있습니다.

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

나중을 위해 mediaserver의 프로세스 ID를 적어두세요.

타임라인 표시

언제든지 수동으로 로그 덤프를 요청할 수 있습니다. 이 명령어는 모든 활성 및 최근 타임라인의 로그를 표시한 다음 삭제합니다.

dumpsys media.log

설계상 타임라인은 독립적이며 타임라인 병합 기능은 없습니다.

mediaserver 종료 후 로그 복구

앞서 언급한 #가 프로세스 ID인 kill -9 #을 통해 이제 mediaserver 프로세스 종료를 시도해 보세요. 메인 logcatmedia.log에서 덤프가 표시되어 비정상 종료가 발생하는 모든 로그를 확인할 수 있습니다.

dumpsys media.log