W tym artykule opisano kilka wskazówek i wskazówek dotyczących debugowania dźwięku w systemie Android.
Zlew trójnikowy
„Tee umywalka” to funkcja debugowania AudioFlingera, dostępna tylko w niestandardowych kompilacjach, służąca do zachowywania krótkiego fragmentu najnowszego dźwięku do późniejszej analizy. Pozwala to na porównanie tego, co faktycznie zostało odtworzone lub nagrane, z tym, czego oczekiwano.
Ze względu na prywatność, tee-sink jest domyślnie wyłączony zarówno w czasie kompilacji, jak i w czasie wykonywania. Aby skorzystać z tee-sink, musisz go włączyć poprzez ponowną kompilację, a także ustawienie właściwości. Pamiętaj o wyłączeniu tej funkcji po zakończeniu debugowania; nie należy pozostawiać włączonego zlewu tee w kompilacjach produkcyjnych.
Instrukcje zawarte w tej sekcji dotyczą systemu Android 7.x i nowszych wersji. W przypadku Androida 5.x i 6.x zamień /data/misc/audioserver
na /data/misc/media
. Dodatkowo musisz użyć debugowania użytkownika lub kompilacji eng. Jeśli używasz kompilacji userdebug, wyłącz prawdziwość za pomocą:
adb root && adb disable-verity && adb reboot
Konfiguracja w czasie kompilacji
-
cd frameworks/av/services/audioflinger
- Edytuj
Configuration.h
. - Odkomentuj
#define TEE_SINK
. - Zbuduj ponownie plik
libaudioflinger.so
. -
adb root
-
adb remount
- Wciśnij lub zsynchronizuj nowy
libaudioflinger.so
z plikiem/system/lib
urządzenia.
Konfiguracja w czasie wykonywania
-
adb shell getprop | grep ro.debuggable
Potwierdź, że wynik to:[ro.debuggable]: [1]
-
adb shell
-
ls -ld /data/misc/audioserver
Potwierdź, że dane wyjściowe to:
drwx------ media media ... media
Jeśli katalog nie istnieje, utwórz go w następujący sposób:
mkdir /data/misc/audioserver
chown media:media /data/misc/audioserver
-
echo af.tee=# > /data/local.prop
Gdzie wartośćaf.tee
jest liczbą opisaną poniżej. -
chmod 644 /data/local.prop
-
reboot
Wartości właściwości af.tee
Wartość af.tee
to liczba z zakresu od 0 do 7, wyrażająca sumę kilku bitów, po jednym na funkcję. Zobacz kod w AudioFlinger::AudioFlinger()
w AudioFlinger.cpp
, aby uzyskać wyjaśnienie każdego bitu, ale krótko:
- 1 = wejście
- 2 = wyjście FastMixer
- 4 = AudioRecord i AudioTrack na ścieżkę
Nie ma jeszcze bitu dla głębokiego bufora lub normalnego miksera, ale możesz uzyskać podobne wyniki, używając „4”.
Testuj i zbieraj dane
- Uruchom test dźwięku.
-
adb shell dumpsys media.audio_flinger
- Poszukaj linii na wyjściu
dumpsys
, takiej jak ta:
tee copied to /data/misc/audioserver/20131010101147_2.wav
To jest plik .wav PCM. - Następnie
adb pull
wszystkie interesujące Cię pliki/data/misc/audioserver/*.wav
; zwróć uwagę, że nazwy plików zrzutu specyficzne dla ścieżki nie pojawiają się w wynikachdumpsys
, ale nadal są zapisywane w/data/misc/audioserver
po zamknięciu ścieżki. - Przed udostępnieniem plików zrzutów innym osobom przejrzyj pliki zrzutu pod kątem ochrony prywatności.
Propozycje
Wypróbuj te pomysły, aby uzyskać bardziej przydatne wyniki:
- Wyłącz dźwięki dotyku i kliknięcia klawiszy, aby ograniczyć przerwy w wynikach testu.
- Maksymalizuj wszystkie woluminy.
- Wyłącz aplikacje, które wydają dźwięk lub nagrywają z mikrofonu, jeśli nie są interesujące dla Twojego testu.
- Zrzuty specyficzne dla toru są zapisywane tylko wtedy, gdy tor jest zamknięty; może być konieczne wymuszenie zamknięcia aplikacji, aby zrzucić dane dotyczące utworu
- Wykonaj
dumpsys
natychmiast po teście; dostępna jest ograniczona ilość miejsca na nagrania. - Aby mieć pewność, że nie stracisz plików zrzutów, przesyłaj je okresowo do swojego hosta. Zachowana zostanie tylko ograniczona liczba plików zrzutów; starsze zrzuty są usuwane po osiągnięciu tego limitu.
Przywrócić
Jak wspomniano powyżej, funkcja zlewu tee nie powinna być włączona. Przywróć kompilację i urządzenie w następujący sposób:
- Przywróć zmiany w kodzie źródłowym do
Configuration.h
. - Zbuduj ponownie plik
libaudioflinger.so
. - Prześlij lub zsynchronizuj przywrócony plik
libaudioflinger.so
z plikiem/system/lib
urządzenia. -
adb shell
-
rm /data/local.prop
-
rm /data/misc/audioserver/*.wav
-
reboot
media.log
Makra ALOGx
Standardowym interfejsem API rejestrowania w języku Java w zestawie SDK systemu Android jest android.util.Log .
Odpowiedni interfejs API języka C w systemie Android NDK to __android_log_print
zadeklarowany w <android/log.h>
.
W natywnej części Androida preferujemy makra o nazwach ALOGE
, ALOGW
, ALOGI
, ALOGV
itp. Są one zadeklarowane w <utils/Log.h>
i na potrzeby tego artykułu będziemy je zbiorczo nazywać ALOGx
.
Wszystkie te interfejsy API są łatwe w obsłudze i dobrze zrozumiałe, dzięki czemu są wszechobecne na platformie Android. W szczególności proces mediaserver
, który obejmuje serwer dźwięku AudioFlinger, intensywnie wykorzystuje ALOGx
.
Niemniej jednak istnieją pewne ograniczenia dotyczące ALOGx
i znajomych:
- Są podatne na „spam w dziennikach”: bufor dziennika jest zasobem współdzielonym, więc może łatwo zostać przepełniony z powodu niepowiązanych wpisów w dzienniku, co skutkuje pominięciem informacji. Wariant
ALOGV
jest domyślnie wyłączony w czasie kompilacji. Ale oczywiście nawet to może skutkować spamem w logach, jeśli jest włączone. - Podstawowe wywołania systemowe jądra mogą się blokować, co może skutkować odwróceniem priorytetów, a w konsekwencji zakłóceniami i niedokładnościami pomiarów. Jest to szczególnie istotne w przypadku wątków, w których czas jest krytyczny, takich jak
FastMixer
iFastCapture
. - Jeśli określony dziennik zostanie wyłączony w celu ograniczenia spamu w dziennikach, wszelkie informacje, które zostałyby przechwycone przez ten dziennik, zostaną utracone. Nie jest możliwe włączenie konkretnego dziennika z mocą wsteczną, gdy stanie się jasne, że dziennik byłby interesujący.
NBLOG, media.log i MediaLogService
Interfejsy API NBLOG
i powiązany proces media.log
oraz usługa MediaLogService
tworzą razem nowszy system rejestrowania multimediów i zostały zaprojektowane specjalnie z myślą o rozwiązaniu powyższych problemów. Będziemy luźno używać terminu „media.log” w odniesieniu do wszystkich trzech, ale ściśle rzecz biorąc, NBLOG
to interfejs API rejestrowania w C++, media.log
to nazwa procesu w systemie Linux, a MediaLogService
to usługa segregatora systemu Android służąca do sprawdzania dzienników.
„Oś czasu” media.log
to seria wpisów dziennika, których względna kolejność jest zachowana. Zgodnie z konwencją każdy wątek powinien używać własnej osi czasu.
Korzyści
Zaletami systemu media.log
jest to, że:
- Nie spamuje głównego dziennika, chyba że jest to potrzebne.
- Można sprawdzić nawet w przypadku awarii lub zawieszenia
mediaserver
. - Nie blokuje na osi czasu.
- Zapewnia mniej zakłóceń w działaniu. (Oczywiście żadna forma rejestrowania nie jest całkowicie nieinwazyjna.)
Architektura
Poniższy diagram pokazuje związek procesu mediaserver
z procesem init
przed wprowadzeniem media.log
:
Godne uwagi punkty:
-
init
fork i execsmediaserver
. -
init
wykrywa śmierćmediaserver
i w razie potrzeby ponownie forkuje. - Rejestrowanie
ALOGx
nie jest wyświetlane.
Poniższy diagram przedstawia nową relację komponentów po dodaniu media.log
do architektury:
Ważne zmiany:
- Klienci używają interfejsu
NBLOG
API do tworzenia wpisów dziennika i dołączania ich do bufora cyklicznego w pamięci współdzielonej. -
MediaLogService
może w dowolnym momencie zrzucić zawartość bufora cyklicznego. - Bufor cykliczny został zaprojektowany w taki sposób, że jakiekolwiek uszkodzenie pamięci współdzielonej nie spowoduje awarii
MediaLogService
i nadal będzie w stanie zrzucić taką część bufora, na którą nie ma wpływu uszkodzenie. - Bufor cykliczny nie blokuje się i nie blokuje zarówno zapisu nowych wpisów, jak i odczytu istniejących wpisów.
- Do zapisu lub odczytu z bufora cyklicznego nie są wymagane żadne wywołania systemowe jądra (inne niż opcjonalne znaczniki czasu).
Gdzie używać
Począwszy od Androida 4.4, tylko kilka punktów dziennika w AudioFlingerze korzysta z systemu media.log
. Chociaż nowe interfejsy API nie są tak łatwe w użyciu jak ALOGx
, nie są też niezwykle trudne. Zachęcamy do zapoznania się z nowym systemem logowania, na wypadek sytuacji, gdy jest on niezbędny. W szczególności jest to zalecane w przypadku wątków AudioFlinger, które muszą działać często, okresowo i bez blokowania, takich jak wątki FastMixer
i FastCapture
.
Jak używać
Dodaj logi
Najpierw musisz dodać logi do swojego kodu.
W wątkach FastMixer
i FastCapture
użyj kodu takiego jak ten:
logWriter->log("string"); logWriter->logf("format", parameters); logWriter->logTimestamp();
Ponieważ ta oś czasu NBLog
jest używana tylko przez wątki FastMixer
i FastCapture
, nie ma potrzeby wzajemnego wykluczania.
W innych wątkach AudioFlinger użyj mNBLogWriter
:
mNBLogWriter->log("string"); mNBLogWriter->logf("format", parameters); mNBLogWriter->logTimestamp();
W przypadku wątków innych niż FastMixer
i FastCapture
oś czasu NBLog
wątku może być używana zarówno przez sam wątek, jak i przez operacje powiązania. NBLog::Writer
nie zapewnia żadnego ukrytego wzajemnego wykluczania na osi czasu, więc upewnij się, że wszystkie dzienniki występują w kontekście, w którym przechowywany jest muteks mLock
wątku.
Po dodaniu dzienników zbuduj ponownie AudioFlinger.
Przestroga: dla każdego wątku wymagana jest osobna oś czasu NBLog::Writer
, aby zapewnić bezpieczeństwo wątku, ponieważ osie czasu z założenia pomijają muteksy. Jeśli chcesz, aby więcej niż jeden wątek korzystał z tej samej osi czasu, możesz zabezpieczyć go za pomocą istniejącego muteksu (jak opisano powyżej dla mLock
). Możesz też użyć opakowania NBLog::LockedWriter
zamiast NBLog::Writer
. Neguje to jednak główną zaletę tego interfejsu API: jego brak blokowania.
Pełne API NBLog
znajduje się pod frameworks/av/include/media/nbaio/NBLog.h
.
Włącz media.log
media.log
jest domyślnie wyłączony. Jest aktywny tylko wtedy, gdy właściwość ro.test_harness
ma wartość 1
. Możesz to włączyć poprzez:
adb root
adb shell
echo ro.test_harness=1 > /data/local.prop
chmod 644 /data/local.prop
reboot
Połączenie zostaje utracone podczas ponownego uruchamiania, więc:
adb shellPolecenie
ps media
wyświetli teraz dwa procesy:- media.log
- serwer mediów
Zanotuj identyfikator procesu mediaserver
na później.
Wyświetl osie czasu
W dowolnym momencie możesz ręcznie zażądać zrzutu dziennika. To polecenie wyświetla logi ze wszystkich aktywnych i ostatnich osi czasu, a następnie je czyści:
dumpsys media.log
Należy pamiętać, że z założenia osie czasu są niezależne i nie ma możliwości łączenia osi czasu.
Odzyskaj logi po śmierci serwera multimediów
Teraz spróbuj zabić proces mediaserver
: kill -9 #
, gdzie # to identyfikator procesu, który zanotowałeś wcześniej. Powinieneś zobaczyć zrzut z media.log
w głównym logcat
, pokazujący wszystkie logi prowadzące do awarii.
dumpsys media.log