Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f85fa9da authored by Pablo Gamito's avatar Pablo Gamito
Browse files

Refactor PerfettoProtoLogImpl classes to take the datasource object directly...

Refactor PerfettoProtoLogImpl classes to take the datasource object directly and be enabled and disabled on demand

This avoid us having to pass a builder function to the class for test,
but also means that we can easily re-create an instance when needed and
pass the same datasource so we don't end up creating and registering a
datasource on each instance creation.

We also now have the ability to easily toggle the protolog tracing on
demand to easily switch between implementation instances when needed.

Flag: android.tracing.perfetto_protolog_tracing
Bug: 369560789
Test: atest TracingTests
Change-Id: I8da7123cbd665d1c289c4c994db181c4de433826
parent 33451e9d
Loading
Loading
Loading
Loading
+17 −0
Original line number Diff line number Diff line
@@ -19,6 +19,9 @@ import android.app.Activity;
import android.os.Bundle;
import android.os.ServiceManager.ServiceNotFoundException;
import android.perftests.utils.Stats;
import android.tracing.perfetto.DataSourceParams;
import android.tracing.perfetto.InitArguments;
import android.tracing.perfetto.Producer;

import androidx.test.InstrumentationRegistry;

@@ -70,6 +73,8 @@ public class ProtoLogPerfTest {
    }

    private IProtoLog mProcessedProtoLogger;
    private static ProtoLogDataSource sTestDataSource;
    private static final String TEST_PROTOLOG_DATASOURCE_NAME = "test.android.protolog";
    private static final String MOCK_TEST_FILE_PATH = "mock/file/path";
    private static final perfetto.protos.Protolog.ProtoLogViewerConfig VIEWER_CONFIG =
            perfetto.protos.Protolog.ProtoLogViewerConfig.newBuilder()
@@ -89,6 +94,17 @@ public class ProtoLogPerfTest {

    @BeforeClass
    public static void init() {
        Producer.init(InitArguments.DEFAULTS);

        sTestDataSource = new ProtoLogDataSource(TEST_PROTOLOG_DATASOURCE_NAME);
        DataSourceParams params =
                new DataSourceParams.Builder()
                        .setBufferExhaustedPolicy(
                                DataSourceParams
                                        .PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_DROP)
                        .build();
        sTestDataSource.register(params);

        ProtoLog.init(TestProtoLogGroup.values());
    }

@@ -98,6 +114,7 @@ public class ProtoLogPerfTest {
        TestProtoLogGroup.TEST_GROUP.setLogToLogcat(mLogToLogcat);

        mProcessedProtoLogger = new ProcessedPerfettoProtoLogImpl(
                sTestDataSource,
                MOCK_TEST_FILE_PATH,
                () -> new AutoClosableProtoInputStream(VIEWER_CONFIG.toByteArray()),
                () -> {},
+47 −34
Original line number Diff line number Diff line
@@ -51,7 +51,6 @@ import android.os.ServiceManager;
import android.os.ShellCommand;
import android.os.SystemClock;
import android.text.TextUtils;
import android.tracing.perfetto.DataSourceParams;
import android.tracing.perfetto.InitArguments;
import android.tracing.perfetto.Producer;
import android.tracing.perfetto.TracingContext;
@@ -86,7 +85,6 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Stream;

/**
 * A service for the ProtoLog logging system.
@@ -98,6 +96,8 @@ public abstract class PerfettoProtoLogImpl extends IProtoLogClient.Stub implemen

    @NonNull
    protected final ProtoLogDataSource mDataSource;
    @Nullable
    protected final IProtoLogConfigurationService mConfigurationService;
    @NonNull
    protected final TreeMap<String, IProtoLogGroup> mLogGroups = new TreeMap<>();
    @NonNull
@@ -117,10 +117,10 @@ public abstract class PerfettoProtoLogImpl extends IProtoLogClient.Stub implemen
    private boolean mLogcatReady = false;

    protected PerfettoProtoLogImpl(
            @NonNull ProtoLogDataSource dataSource,
            @NonNull Runnable cacheUpdater,
            @NonNull IProtoLogGroup[] groups) throws ServiceManager.ServiceNotFoundException {
        this(cacheUpdater, groups,
                ProtoLogDataSource::new,
        this(dataSource, cacheUpdater, groups,
                android.tracing.Flags.clientSideProtoLogging() ?
                    IProtoLogConfigurationService.Stub.asInterface(
                        ServiceManager.getServiceOrThrow(PROTOLOG_CONFIGURATION_SERVICE)
@@ -129,47 +129,60 @@ public abstract class PerfettoProtoLogImpl extends IProtoLogClient.Stub implemen
    }

    protected PerfettoProtoLogImpl(
            @NonNull ProtoLogDataSource dataSource,
            @NonNull Runnable cacheUpdater,
            @NonNull IProtoLogGroup[] groups,
            @NonNull ProtoLogDataSourceBuilder dataSourceBuilder,
            @Nullable IProtoLogConfigurationService configurationService) {
        mDataSource = dataSourceBuilder.build(
                this::onTracingInstanceStart,
                this::onTracingFlush,
                this::onTracingInstanceStop);
        Producer.init(InitArguments.DEFAULTS);
        DataSourceParams params =
                new DataSourceParams.Builder()
                        .setBufferExhaustedPolicy(
                                DataSourceParams
                                        .PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_DROP)
                        .build();
        // NOTE: Registering that datasource is an async operation, so there may be no data traced
        // for some messages logged right after the construction of this class.
        mDataSource.register(params);

        this.mCacheUpdater = cacheUpdater;
        mDataSource = dataSource;
        mCacheUpdater = cacheUpdater;
        mConfigurationService = configurationService;

        registerGroupsLocally(groups);
    }

    /**
     * To be called to enable the ProtoLogImpl to start tracing to ProtoLog and register with all
     * the expected ProtoLog components.
     */
    public void enable() {
        Producer.init(InitArguments.DEFAULTS);

        if (android.tracing.Flags.clientSideProtoLogging()) {
            Objects.requireNonNull(configurationService,
            connectToConfigurationService();
        }

        mDataSource.registerOnStartCallback(this::onTracingInstanceStart);
        mDataSource.registerOnFlushCallback(this::onTracingFlush);
        mDataSource.registerOnStopCallback(this::onTracingInstanceStop);
    }

    private void connectToConfigurationService() {
        Objects.requireNonNull(mConfigurationService,
                "A null ProtoLog Configuration Service was provided!");

        try {
            var args = createConfigurationServiceRegisterClientArgs();

                final var groupArgs = Stream.of(groups)
            final var groupArgs = mLogGroups.values().stream()
                    .map(group -> new RegisterClientArgs
                            .GroupConfig(group.name(), group.isLogToLogcat()))
                    .toArray(RegisterClientArgs.GroupConfig[]::new);
            args.setGroups(groupArgs);

                configurationService.registerClient(this, args);
            mConfigurationService.registerClient(this, args);
        } catch (RemoteException e) {
            throw new RuntimeException("Failed to register ProtoLog client");
        }
    }

    /**
     * Should be called when we no longer want to use the ProtoLog logger to unlink ourselves from
     * the datasource and the configuration service to ensure we no longer receive the callback.
     */
    public void disable() {
        mDataSource.unregisterOnStartCallback(this::onTracingInstanceStart);
        mDataSource.unregisterOnFlushCallback(this::onTracingFlush);
        mDataSource.unregisterOnStopCallback(this::onTracingInstanceStop);
    }

    @NonNull
+6 −4
Original line number Diff line number Diff line
@@ -42,10 +42,11 @@ public class ProcessedPerfettoProtoLogImpl extends PerfettoProtoLogImpl {
    private final String mViewerConfigFilePath;

    public ProcessedPerfettoProtoLogImpl(
            @NonNull ProtoLogDataSource datasource,
            @NonNull String viewerConfigFilePath,
            @NonNull Runnable cacheUpdater,
            @NonNull IProtoLogGroup[] groups) throws ServiceManager.ServiceNotFoundException {
        this(viewerConfigFilePath, new ViewerConfigInputStreamProvider() {
        this(datasource, viewerConfigFilePath, new ViewerConfigInputStreamProvider() {
                    @NonNull
                    @Override
                    public AutoClosableProtoInputStream getInputStream() {
@@ -64,11 +65,12 @@ public class ProcessedPerfettoProtoLogImpl extends PerfettoProtoLogImpl {

    @VisibleForTesting
    public ProcessedPerfettoProtoLogImpl(
            @NonNull ProtoLogDataSource datasource,
            @NonNull String viewerConfigFilePath,
            @NonNull ViewerConfigInputStreamProvider viewerConfigInputStreamProvider,
            @NonNull Runnable cacheUpdater,
            @NonNull IProtoLogGroup[] groups) throws ServiceManager.ServiceNotFoundException {
        super(cacheUpdater, groups);
        super(datasource, cacheUpdater, groups);

        this.mViewerConfigFilePath = viewerConfigFilePath;

@@ -80,15 +82,15 @@ public class ProcessedPerfettoProtoLogImpl extends PerfettoProtoLogImpl {

    @VisibleForTesting
    public ProcessedPerfettoProtoLogImpl(
            @NonNull ProtoLogDataSource datasource,
            @NonNull String viewerConfigFilePath,
            @NonNull ViewerConfigInputStreamProvider viewerConfigInputStreamProvider,
            @NonNull ProtoLogViewerConfigReader viewerConfigReader,
            @NonNull Runnable cacheUpdater,
            @NonNull IProtoLogGroup[] groups,
            @NonNull ProtoLogDataSourceBuilder dataSourceBuilder,
            @Nullable IProtoLogConfigurationService configurationService)
            throws ServiceManager.ServiceNotFoundException {
        super(cacheUpdater, groups, dataSourceBuilder, configurationService);
        super(datasource, cacheUpdater, groups, configurationService);

        this.mViewerConfigFilePath = viewerConfigFilePath;

+34 −15
Original line number Diff line number Diff line
@@ -74,25 +74,44 @@ public class ProtoLog {
        // files to extract out the log strings. Otherwise, the trace calls are replaced with calls
        // directly to the generated tracing implementations.
        if (android.tracing.Flags.perfettoProtologTracing()) {
            initializePerfettoProtoLog(groups);
        } else {
            sProtoLogInstance = new LogcatOnlyProtoLogImpl();
        }
    }

    private static void initializePerfettoProtoLog(IProtoLogGroup... groups) {
        var datasource = getSharedSingleInstanceDataSource();

        synchronized (sInitLock) {
            final var allGroups = new HashSet<>(Arrays.stream(groups).toList());
                if (sProtoLogInstance != null) {
            final var previousProtoLogImpl = sProtoLogInstance;
            if (previousProtoLogImpl != null) {
                // The ProtoLog instance has already been initialized in this process
                    final var alreadyRegisteredGroups = sProtoLogInstance.getRegisteredGroups();
                final var alreadyRegisteredGroups = previousProtoLogImpl.getRegisteredGroups();
                allGroups.addAll(alreadyRegisteredGroups);
            }

            sProtoLogInstance = createAndEnableNewPerfettoProtoLogImpl(
                    datasource, allGroups.toArray(new IProtoLogGroup[0]));
            if (previousProtoLogImpl instanceof PerfettoProtoLogImpl) {
                ((PerfettoProtoLogImpl) previousProtoLogImpl).disable();
            }
        }
    }

    private static PerfettoProtoLogImpl createAndEnableNewPerfettoProtoLogImpl(
            ProtoLogDataSource datasource, IProtoLogGroup[] groups) {
        try {
                    sProtoLogInstance = new UnprocessedPerfettoProtoLogImpl(
                            allGroups.toArray(new IProtoLogGroup[0]));
            var unprocessedPerfettoProtoLogImpl =
                    new UnprocessedPerfettoProtoLogImpl(datasource, groups);
            unprocessedPerfettoProtoLogImpl.enable();

            return unprocessedPerfettoProtoLogImpl;
        } catch (ServiceManager.ServiceNotFoundException e) {
            throw new RuntimeException(e);
        }
    }
        } else {
            sProtoLogInstance = new LogcatOnlyProtoLogImpl();
        }
    }

    /**
     * DEBUG level log.
+12 −23
Original line number Diff line number Diff line
@@ -34,9 +34,6 @@ import android.content.Context;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.tracing.perfetto.DataSourceParams;
import android.tracing.perfetto.InitArguments;
import android.tracing.perfetto.Producer;
import android.util.Log;
import android.util.proto.ProtoInputStream;
import android.util.proto.ProtoOutputStream;
@@ -110,39 +107,31 @@ public class ProtoLogConfigurationServiceImpl extends IProtoLogConfigurationServ
    private final ViewerConfigFileTracer mViewerConfigFileTracer;

    public ProtoLogConfigurationServiceImpl() {
        this(ProtoLogDataSource::new, ProtoLogConfigurationServiceImpl::dumpViewerConfig);
        this(ProtoLog.getSharedSingleInstanceDataSource(),
                ProtoLogConfigurationServiceImpl::dumpViewerConfig);
    }

    @VisibleForTesting
    public ProtoLogConfigurationServiceImpl(@NonNull ProtoLogDataSourceBuilder dataSourceBuilder) {
        this(dataSourceBuilder, ProtoLogConfigurationServiceImpl::dumpViewerConfig);
    public ProtoLogConfigurationServiceImpl(@NonNull ProtoLogDataSource datasource) {
        this(datasource, ProtoLogConfigurationServiceImpl::dumpViewerConfig);
    }

    @VisibleForTesting
    public ProtoLogConfigurationServiceImpl(@NonNull ViewerConfigFileTracer tracer) {
        this(ProtoLogDataSource::new, tracer);
        this(ProtoLog.getSharedSingleInstanceDataSource(), tracer);
    }

    @VisibleForTesting
    public ProtoLogConfigurationServiceImpl(
            @NonNull ProtoLogDataSourceBuilder dataSourceBuilder,
            @NonNull ProtoLogDataSource datasource,
            @NonNull ViewerConfigFileTracer tracer) {
        mDataSource = dataSourceBuilder.build(
            this::onTracingInstanceStart,
            this::onTracingInstanceFlush,
            this::onTracingInstanceStop
        );

        // Initialize the Perfetto producer and register the Perfetto ProtoLog datasource to be
        // receive the lifecycle callbacks of the datasource and write the viewer configs if and
        // when required to the datasource.
        Producer.init(InitArguments.DEFAULTS);
        final var params = new DataSourceParams.Builder()
                .setBufferExhaustedPolicy(DataSourceParams.PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_DROP)
                .build();
        mDataSource.register(params);

        mViewerConfigFileTracer = tracer;

        datasource.registerOnStartCallback(this::onTracingInstanceStart);
        datasource.registerOnFlushCallback(this::onTracingInstanceFlush);
        datasource.registerOnStopCallback(this::onTracingInstanceStop);

        mDataSource = datasource;
    }

    public static class RegisterClientArgs extends IRegisterClientArgs.Stub {
Loading