Korzystanie ze śledzenia w celu uzyskania statystyk wydajności systemu

Użyj śledzenia, aby rejestrować zdarzenia i liczniki w systemie oraz wizualizować je na osi czasu. Standardowym narzędziem do śledzenia w Androidzie jest Perfetto. Więcej informacji znajdziesz w artykule Śledzenie – podstawy.

Instrumentacja kodu

Aby otrzymywać zdarzenia z aplikacji, musisz dodać do kodu punkty śledzenia. Sekcje, które nie są instrumentowane, nie będą widoczne w logach czasu.

Przykładowy log czasu SDV to demonstracja integracji śledzenia, która zawiera instrukcje i przykład konfiguracji śledzenia. Znajduje się on w system/software_defined_vehicle/core_services/samples/tracing/.

Rdza

W przypadku języka Rust zalecamy używanie pakietu tracing do emitowania zdarzeń ATrace. Perfetto obsługuje ATrace jako źródło danych. Planujemy przejść na pakiet SDK Perfetto, gdy będą dostępne powiązania z językiem Rust, i w zależności od tego, jak będą się rozwijać przypadki użycia.

Dodaj domyślne ustawienia śledzenia do pliku Android.bp:

rust_binary {
...
    defaults: [
        ...
        "sdv_tracing@rust_defaults",
    ],
...
}

Zainicjuj subskrybenta. Możesz to zrobić tylko raz na proces:

fn main() {
    // Initialize the subscriber, panic if it fails.
    // sdv_tracing::try_init_tracing() is the version that returns a Result.
    sdv_tracing::init_tracing()
    ...
}

Możesz pominąć wywołanie inicjujące. Spowoduje to, że śledzenie nie zostanie zainicjowane, a Perfetto nie będzie zbierać zdarzeń instrumentacji z aplikacji.

Dodaj punkty śledzenia. Więcej przykładów znajdziesz w system/software_defined_vehicle/core_services/samples/tracing/rust_tracing_api_demo/tracing.rs.

use tracing::{instrument, info_span};

// #[tracing::instrument] wraps the method into a tracing span and records arguments.
// Use #[instrument(skip(num))] if you don't want to record the argument.
#[instrument]
fn mul_by_100(num: i32) -> i32 {
    // Create and enter a span with INFO verbosity, name, and a debug field annotation.
    // The span will exit when dropped.
    let _span = info_span!("This is a span", var=123).entered();
    let result = num * 100;
    // Emit an instant INFO event that records the result value.
    // We recommend to fully qualify the crate when using events to avoid confusion with log records.
    tracing::info!(result, "Completed");
    result
}

C++

Śledzenie w C++ używa pakietu SDK Perfetto do śledzenia zdarzeń. Dodaj domyślne ustawienia śledzenia do pliku Android.bp:

cc_binary {
...
    defaults: [
...
        "sdv_tracing@cc_defaults",
    ],
...
}

Zdefiniuj kategorie. Jeśli używasz kategorii w wielu modułach, przenieś je do wspólnej biblioteki. Na przykład system/software_defined_vehicle/core_services/samples/tracing/cpp_service/tracing_categories.h.

W nagłówku:

#include "perfetto/tracing/tracing.h"
#include "perfetto/tracing/track_event.h"

PERFETTO_DEFINE_CATEGORIES(
        perfetto::Category("sample")
                .SetTags("tag")
                .SetDescription("Sample events"));

Umieść makro statycznej pamięci masowej w pliku źródłowym .cpp, a nie w metodzie. Jeśli udostępniasz kategorie między komponentami, użyj pliku źródłowego odpowiadającego nagłówkowi z kategoriami.

PERFETTO_TRACK_EVENT_STATIC_STORAGE();

int main() {
    ...
}

Aby zainicjować Perfetto, zainicjuj backend systemu i zarejestruj zdarzenia śledzenia:


#include <sdv/tracing_init.h>

int main() {
  ...
  android::sdv::InitPerfettoWithTrackEvents<perfetto::TrackEvent>();
  ...
}

Dodaj instrumentację. Więcej przykładów znajdziesz w system/software_defined_vehicle/core_services/samples/tracing/cpp_service/client.cpp.

int32_t mulBy100(int32_t num) {
    // Start a slice that will get closed at the end of the scope.
    TRACE_EVENT("client", "mulBy100", "num", num);

    TRACE_EVENT("client", "This is a slice", "var", 123);
    int32_t result = num * 100;

    // Instant events have zero duration. They are drawn as markers on the track.
    TRACE_EVENT_INSTANT("client", "Completed", "result", result);
    return result;
}

Podobnie jak w przykładzie w języku Rust ten kod tworzy 2 zagnieżdżone wycinki i znacznik zdarzenia natychmiastowego w interfejsie. Gdy wybierzesz zdarzenie, wyświetlą się wartości argumentów debugowania.

Zbieranie logu czasu

Aby zarejestrować log czasu i wyświetlić go w internetowym interfejsie Perfetto, użyj skryptu wiersza poleceń record\_android\_trace.

Konfigurowanie przechwytywania

Musisz podać konfigurację dla record_android_trace w formacie textproto. Więcej informacji znajdziesz w dokumentacji Perfetto.

Repozytorium SDV zawiera przykładową konfigurację (system/software_defined_vehicle/core_services/samples/tracing/config/trace_cfg.pbtx). Ten plik zawiera kilka źródeł danych i można go dostosować lub użyć w takiej postaci.

Generowanie konfiguracji za pomocą interfejsu Perfetto

Możesz skonfigurować niestandardową konfigurację i poznać dostępne opcje, otwierając opcję Record new trace (Zarejestruj nowy log czasu) w interfejsie Perfetto UI, dostosowując ustawienia nagrywania i sondy. Następnie możesz otworzyć widok „Recording command” (Polecenie nagrywania), aby zobaczyć wygenerowane polecenie i uzyskać zawartość konfiguracji.

Konfigurowanie widoczności instrumentacji w aplikacji

Instrumentacja w języku Rust używa ATrace. Jest ona skonfigurowana w sekcji ftrace_config dokumentu. Komponenty SDV mają tag ATRACE_TAG_APP i można je włączyć w przypadku poszczególnych aplikacji. Przykładowa konfiguracja włącza wszystkie aplikacje.

data_sources: {
    config {
        name: "linux.ftrace"
        ftrace_config {
            # Setting atrace_apps to "*" enable ATrace events for all apps.
            # You can set it to a pattern to match specific processes by name.
            # Use multiple atrace_apps entries to enable multiple processes.
            atrace_apps: "*"
        }
    }
}

W pakiecie SDK Perfetto w C++ używamy zdarzeń śledzenia. Jest to track_event źródło danych (dokumentacja).

Kategorie i tagi możesz włączać i wyłączać w polu track_event_config Domyślnie włączony jest każdy tag końca kategorii, z wyjątkiem specjalnych tagów slow i debug. Jeśli chcesz włączyć tylko określone kategorie, musisz wyłączyć wszystkie inne, na przykład za pomocą disabled_categories: "*" jak tutaj:

data_sources: {
    config {
        name: "track_event"
        track_event_config {
            enabled_categories: "the_best_category_in_the_world"
            disabled_categories: "*"
        }
    }
}

Rejestrowanie logu czasu

Otwórz terminal w katalogu głównym repozytorium Androida. Nie musisz wykonywać envsetup. Skrypt nagrywania znajduje się w external/perfetto/tools/record_android_trace.

Uruchom skrypt z przykładową konfiguracją:

external/perfetto/tools/record_android_trace --config system/software_defined_vehicle/core_services/samples/tracing/config/trace_cfg.pbtx

Aby wcześniej zatrzymać nagrywanie, naciśnij Ctrl + C.

Spowoduje to, że adb shell perfetto zarejestruje log czasu, a następnie pobierze go na hosta, zwykle do katalogu ~/traces. Gdy log czasu zostanie zebrany, narzędzie otworzy okno przeglądarki, aby go wyświetlić.

Przydatne argumenty:

  • -s SERIAL – użycie urządzenia o danym numerze seryjnym. Na przykład -s 0.0.0.0:6520

  • --no-open-browser – utworzenie adresu URL do udostępniania logu czasu, ale nieotwieranie przeglądarki. Jest to przydatne w przypadku sesji zdalnych, gdy masz skonfigurowane przekierowanie portów (zwykle 9001).

  • -n, --no-open – po sesji śledzenia nieotwieranie przeglądarki ani nieutworzenie adresu URL do udostępniania logu czasu. Możesz nadal otwierać pliki w interfejsie Perfetto, klikając „Open trace file” (Otwórz plik śledzenia) i wybierając plik.

  • -o <path> – ustawienie ścieżki wyjściowej.

Dane o wykorzystaniu

W tej sekcji znajdziesz informacje, które mogą być przydatne podczas korzystania z systemu śledzenia.

Instrumentacja śledzenia w komponentach SDV

W agentach z instrumentacją śledzenia śledzenie jest domyślnie dostępne w kompilacjach z możliwością debugowania (-eng, -userdebug), chyba że określono inaczej. Gdy zbierzesz log czasu, powinny być widoczne zdarzenia procesów bez dodatkowej konfiguracji.

Biblioteki zwykle nie inicjują śledzenia automatycznie. W języku Rust plik binarny, który używa biblioteki, musi zainicjować śledzenie procesu za pomocą sdv_tracing::init_tracing(). Więcej informacji znajdziesz w sekcji Instrumentacja kodu.

Oprogramowanie pośredniczące

Biblioteka publikowania/subskrybowania: libsdv_middleware_dt

Wydarzenia:

  • Wydawca: publikowanie i rejestrowanie tematów.
  • Subskrybent: subskrybowanie i odpytywanie.

Włączanie: w pliku binarnym wywołaj sdv_tracing::init_tracing() lub sdv_tracing::try_init_tracing().

Biblioteka gRPC: libsdvmiddleware_rpc_grpc_transport

Wydarzenia:

  • Klient RPC: uruchamianie, łączenie się z serwerem i wywoływanie metod RPC.
  • Serwer RPC: uruchamianie, rejestrowanie w Service Discovery, dodawanie i wywoływanie metod RPC.

Włączanie: w pliku binarnym wywołaj sdv_tracing::init_tracing() lub sdv_tracing::try_init_tracing().

SOME/IP
  • Proces: sdv_someip_broker_agent. Example Source: This is a test Example Translation: Dies ist ein Test Source: This is a test with another placeholder Translation: Dies ist ein Test mit einem anderen Platzhalter
  • Wydarzenia: przetwarzanie i tłumaczenie wiadomości, subskrypcja zdarzeń.
Menedżer cyklu życia
  • Proces: sdv_lifecycle_agent. Example Source: This is a test Example Translation: Dies ist ein Test Source: This is a test with another placeholder Translation: Dies ist ein Test mit einem anderen Platzhalter
  • Wydarzenia: operacje na usługach – uruchamianie, zatrzymywanie, rejestrowanie, wyrejestrowywanie.
Tryb zasilania pojazdu
  • Proces: sdv_vpm_agent. Example Source: This is a test Example Translation: Dies ist ein Test Source: This is a test with another placeholder Translation: Dies ist ein Test mit einem anderen Platzhalter
  • Wydarzenia: zmiany stanu zasilania i subskrypcje.
Tunel danych

Planujemy wprowadzić integrację śledzenia w przyszłości.

Obciążenie wydajności związane ze śledzeniem

Pomiary obciążenia są obarczone zwykłym zastrzeżeniem, że wydajność może się różnić w zależności od systemu, a zwłaszcza między emulatorem a rzeczywistym sprzętem.

Rdza

Nieprzetworzone dane testów porównawczych są dostępne w AOSP. Dane zostały zebrane na maszynie wirtualnej Cuttlefish.

  • Pojedynczy zakres: tracing::info_span!(), #[tracing::instrument] i podobne:
    • Śledzenie nie zostało zainicjowane: 1 ns.
    • Śledzenie zostało zainicjowane i wyłączone (nie jest rejestrowany żaden log czasu): 30 ns.
    • Śledzenie jest włączone: 3 µs. Adnotacje pola debugowania mogą dodać 1–2 µs, w zależności od złożoności konwersji na ciąg znaków.
  • Pojedyncze zdarzenie: tracing::info!() i podobne:
    • Śledzenie nie zostało zainicjowane: 1 ns.
    • Śledzenie zostało zainicjowane i wyłączone: 30 ns.
    • Śledzenie jest włączone: 1,5 µs. Adnotacje pola debugowania mogą dodać 0,5–1 µs, w zależności od stringification złożoności.
C++

Dane o wydajności pochodzą z sekcji Performance (Wydajność) w dokumentacji Track events (Zdarzenia śledzenia) w Perfetto. Czasy dla Pixela 3 w tabeli są zgodne z naszymi obserwacjami na maszynie wirtualnej Cuttlefish.

Pojedynczy wycinek: TRACE_EVENT() i podobne. Śledzenie:

  • Wyłączone: 2 ns.
  • Włączone: 300 ns. Używanie adnotacji pola debugowania może dodać 50–100 ns.