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

Commit fc5ccc5e authored by Pablo Gamito's avatar Pablo Gamito Committed by Android (Google) Code Review
Browse files

Merge changes from topic "protolog-reuse-datasource" into main

* changes:
  Update ProtoLog cache updater to take an ProtoLog instance object
  Refactor PerfettoProtoLogImpl classes to take the datasource object directly and be enabled and disabled on demand
  Add options to register lifecycle callbacks on a ProtoLogDataSource
  Create shared static ProtoLogDataSource
parents 3507614a 49a37555
Loading
Loading
Loading
Loading
+18 −1
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,9 +114,10 @@ public class ProtoLogPerfTest {
        TestProtoLogGroup.TEST_GROUP.setLogToLogcat(mLogToLogcat);

        mProcessedProtoLogger = new ProcessedPerfettoProtoLogImpl(
                sTestDataSource,
                MOCK_TEST_FILE_PATH,
                () -> new AutoClosableProtoInputStream(VIEWER_CONFIG.toByteArray()),
                () -> {},
                (instance) -> {},
                TestProtoLogGroup.values()
        );
    }
+4 −4
Original line number Diff line number Diff line
@@ -70,7 +70,7 @@ public class LegacyProtoLogImpl implements IProtoLog {
    private final TraceBuffer mBuffer;
    private final LegacyProtoLogViewerConfigReader mViewerConfig;
    private final Map<String, IProtoLogGroup> mLogGroups = new TreeMap<>();
    private final Runnable mCacheUpdater;
    private final ProtoLogCacheUpdater mCacheUpdater;
    private final int mPerChunkSize;

    private boolean mProtoLogEnabled;
@@ -78,14 +78,14 @@ public class LegacyProtoLogImpl implements IProtoLog {
    private final Object mProtoLogEnabledLock = new Object();

    public LegacyProtoLogImpl(String outputFile, String viewerConfigFilename,
            Runnable cacheUpdater) {
            ProtoLogCacheUpdater cacheUpdater) {
        this(new File(outputFile), viewerConfigFilename, BUFFER_CAPACITY,
                new LegacyProtoLogViewerConfigReader(), PER_CHUNK_SIZE, cacheUpdater);
    }

    public LegacyProtoLogImpl(File file, String viewerConfigFilename, int bufferCapacity,
            LegacyProtoLogViewerConfigReader viewerConfig, int perChunkSize,
            Runnable cacheUpdater) {
            ProtoLogCacheUpdater cacheUpdater) {
        mLogFile = file;
        mBuffer = new TraceBuffer(bufferCapacity);
        mLegacyViewerConfigFilename = viewerConfigFilename;
@@ -298,7 +298,7 @@ public class LegacyProtoLogImpl implements IProtoLog {
            }
        }

        mCacheUpdater.run();
        mCacheUpdater.update(this);
        return 0;
    }

+53 −40
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,10 +96,12 @@ 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
    private final Runnable mCacheUpdater;
    private final ProtoLogCacheUpdater mCacheUpdater;

    @NonNull
    private final int[] mDefaultLogLevelCounts = new int[LogLevel.values().length];
@@ -117,10 +117,10 @@ public abstract class PerfettoProtoLogImpl extends IProtoLogClient.Stub implemen
    private boolean mLogcatReady = false;

    protected PerfettoProtoLogImpl(
            @NonNull Runnable cacheUpdater,
            @NonNull ProtoLogDataSource dataSource,
            @NonNull ProtoLogCacheUpdater 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 Runnable cacheUpdater,
            @NonNull ProtoLogDataSource dataSource,
            @NonNull ProtoLogCacheUpdater 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
@@ -703,7 +716,7 @@ public abstract class PerfettoProtoLogImpl extends IProtoLogClient.Stub implemen
            }
        }

        mCacheUpdater.run();
        mCacheUpdater.update(this);
        return 0;
    }

@@ -746,7 +759,7 @@ public abstract class PerfettoProtoLogImpl extends IProtoLogClient.Stub implemen
            }
        }

        mCacheUpdater.run();
        mCacheUpdater.update(this);

        this.mTracingInstances.incrementAndGet();

@@ -786,7 +799,7 @@ public abstract class PerfettoProtoLogImpl extends IProtoLogClient.Stub implemen
            }
        }

        mCacheUpdater.run();
        mCacheUpdater.update(this);
        Log.d(LOG_TAG, "Finished onTracingInstanceStop");
    }

+9 −7
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 ProtoLogCacheUpdater 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 ProtoLogCacheUpdater 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 ProtoLogCacheUpdater 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;

+65 −15
Original line number Diff line number Diff line
@@ -17,6 +17,9 @@
package com.android.internal.protolog;

import android.os.ServiceManager;
import android.tracing.perfetto.DataSourceParams;
import android.tracing.perfetto.InitArguments;
import android.tracing.perfetto.Producer;

import com.android.internal.protolog.common.IProtoLog;
import com.android.internal.protolog.common.IProtoLogGroup;
@@ -54,6 +57,8 @@ public class ProtoLog {

    private static IProtoLog sProtoLogInstance;

    private static ProtoLogDataSource sDataSource;

    private static final Object sInitLock = new Object();

    /**
@@ -69,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.
@@ -190,6 +214,32 @@ public class ProtoLog {
        return sProtoLogInstance;
    }

    /**
     * Gets or creates if it doesn't exist yet the protolog datasource to use in this process.
     * We should re-use the same datasource to avoid registering the datasource multiple times in
     * the same process, since there is no way to unregister the datasource after registration.
     *
     * @return The single ProtoLog datasource instance to be shared across all ProtoLog tracing
     *         objects.
     */
    public static synchronized ProtoLogDataSource getSharedSingleInstanceDataSource() {
        if (sDataSource == null) {
            Producer.init(InitArguments.DEFAULTS);
            sDataSource = new ProtoLogDataSource();
            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.
            sDataSource.register(params);
        }

        return sDataSource;
    }

    private static void logStringMessage(LogLevel logLevel, IProtoLogGroup group,
            String stringMessage, Object... args) {
        if (sProtoLogInstance == null) {
Loading