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

Commit 6e15198b authored by Pablo Gamito's avatar Pablo Gamito
Browse files

Turn PerfettoProtoLogImpl class into abstract class to support both a version...

Turn PerfettoProtoLogImpl class into abstract class to support both a version for processed and unprocessed protologs

Bug: 369052956
Flag: EXEMPT minor refactor
Test: atest TracingTests
Change-Id: Ib872eaba62fb0d4d2dc6d64cc0546b3663b3675f
parent 9726edbb
Loading
Loading
Loading
Loading
+21 −179
Original line number Diff line number Diff line
@@ -60,18 +60,16 @@ import android.util.ArraySet;
import android.util.Log;
import android.util.LongArray;
import android.util.Slog;
import android.util.proto.ProtoInputStream;
import android.util.proto.ProtoOutputStream;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.ProtoLogConfigurationServiceImpl.RegisterClientArgs;
import com.android.internal.protolog.common.ILogger;
import com.android.internal.protolog.common.IProtoLog;
import com.android.internal.protolog.common.IProtoLogGroup;
import com.android.internal.protolog.common.LogDataType;
import com.android.internal.protolog.common.LogLevel;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
@@ -93,26 +91,18 @@ import java.util.stream.Stream;
/**
 * A service for the ProtoLog logging system.
 */
public class PerfettoProtoLogImpl extends IProtoLogClient.Stub implements IProtoLog {
public abstract class PerfettoProtoLogImpl extends IProtoLogClient.Stub implements IProtoLog {
    private static final String LOG_TAG = "ProtoLog";
    public static final String NULL_STRING = "null";
    private final AtomicInteger mTracingInstances = new AtomicInteger();

    @NonNull
    private final ProtoLogDataSource mDataSource;
    @Nullable
    private final ProtoLogViewerConfigReader mViewerConfigReader;
    @Deprecated
    @Nullable
    private final ViewerConfigInputStreamProvider mViewerConfigInputStreamProvider;
    protected final ProtoLogDataSource mDataSource;
    @NonNull
    private final TreeMap<String, IProtoLogGroup> mLogGroups = new TreeMap<>();
    protected final TreeMap<String, IProtoLogGroup> mLogGroups = new TreeMap<>();
    @NonNull
    private final Runnable mCacheUpdater;

    @Nullable // null when the flag android.tracing.client_side_proto_logging is not flipped
    private final IProtoLogConfigurationService mProtoLogConfigurationService;

    @NonNull
    private final int[] mDefaultLogLevelCounts = new int[LogLevel.values().length];
    @NonNull
@@ -121,68 +111,12 @@ public class PerfettoProtoLogImpl extends IProtoLogClient.Stub implements IProto
    private final Map<String, Integer> mCollectStackTraceGroupCounts = new ArrayMap<>();

    private final Lock mBackgroundServiceLock = new ReentrantLock();
    private ExecutorService mBackgroundLoggingService = Executors.newSingleThreadExecutor();

    public PerfettoProtoLogImpl(@NonNull IProtoLogGroup[] groups)
            throws ServiceManager.ServiceNotFoundException {
        this(null, null, null, () -> {}, groups);
    }

    public PerfettoProtoLogImpl(@NonNull Runnable cacheUpdater, @NonNull IProtoLogGroup[] groups)
            throws ServiceManager.ServiceNotFoundException {
        this(null, null, null, cacheUpdater, groups);
    }

    public PerfettoProtoLogImpl(
            @NonNull String viewerConfigFilePath,
            @NonNull Runnable cacheUpdater,
            @NonNull IProtoLogGroup[] groups) throws ServiceManager.ServiceNotFoundException {
        this(viewerConfigFilePath,
                null,
                new ProtoLogViewerConfigReader(() -> {
                    try {
                        return new ProtoInputStream(new FileInputStream(viewerConfigFilePath));
                    } catch (FileNotFoundException e) {
                        throw new RuntimeException(
                                "Failed to load viewer config file " + viewerConfigFilePath, e);
                    }
                }),
                cacheUpdater, groups);
    }

    @Deprecated
    @VisibleForTesting
    public PerfettoProtoLogImpl(
            @Nullable ViewerConfigInputStreamProvider viewerConfigInputStreamProvider,
            @Nullable ProtoLogViewerConfigReader viewerConfigReader,
            @NonNull Runnable cacheUpdater,
            @NonNull IProtoLogGroup[] groups,
            @NonNull ProtoLogDataSourceBuilder dataSourceBuilder,
            @NonNull ProtoLogConfigurationService configurationService) {
        this(null, viewerConfigInputStreamProvider, viewerConfigReader, cacheUpdater,
                groups, dataSourceBuilder, configurationService);
    }
    protected ExecutorService mBackgroundLoggingService = Executors.newSingleThreadExecutor();

    @VisibleForTesting
    public PerfettoProtoLogImpl(
            @Nullable String viewerConfigFilePath,
            @Nullable ProtoLogViewerConfigReader viewerConfigReader,
            @NonNull Runnable cacheUpdater,
            @NonNull IProtoLogGroup[] groups,
            @NonNull ProtoLogDataSourceBuilder dataSourceBuilder,
            @NonNull ProtoLogConfigurationService configurationService) {
        this(viewerConfigFilePath, null, viewerConfigReader, cacheUpdater,
                groups, dataSourceBuilder, configurationService);
    }

    private PerfettoProtoLogImpl(
            @Nullable String viewerConfigFilePath,
            @Nullable ViewerConfigInputStreamProvider viewerConfigInputStreamProvider,
            @Nullable ProtoLogViewerConfigReader viewerConfigReader,
    protected PerfettoProtoLogImpl(
            @NonNull Runnable cacheUpdater,
            @NonNull IProtoLogGroup[] groups) throws ServiceManager.ServiceNotFoundException {
        this(viewerConfigFilePath, viewerConfigInputStreamProvider, viewerConfigReader,
                cacheUpdater, groups,
        this(cacheUpdater, groups,
                ProtoLogDataSource::new,
                android.tracing.Flags.clientSideProtoLogging() ?
                    IProtoLogConfigurationService.Stub.asInterface(
@@ -191,19 +125,11 @@ public class PerfettoProtoLogImpl extends IProtoLogClient.Stub implements IProto
        );
    }

    private PerfettoProtoLogImpl(
            @Nullable String viewerConfigFilePath,
            @Nullable ViewerConfigInputStreamProvider viewerConfigInputStreamProvider,
            @Nullable ProtoLogViewerConfigReader viewerConfigReader,
    protected PerfettoProtoLogImpl(
            @NonNull Runnable cacheUpdater,
            @NonNull IProtoLogGroup[] groups,
            @NonNull ProtoLogDataSourceBuilder dataSourceBuilder,
            @Nullable IProtoLogConfigurationService configurationService) {
        if (viewerConfigFilePath != null && viewerConfigInputStreamProvider != null) {
            throw new RuntimeException("Only one of viewerConfigFilePath and "
                    + "viewerConfigInputStreamProvider can be set");
        }

        mDataSource = dataSourceBuilder.build(
                this::onTracingInstanceStart,
                this::onTracingFlush,
@@ -219,55 +145,33 @@ public class PerfettoProtoLogImpl extends IProtoLogClient.Stub implements IProto
        // for some messages logged right after the construction of this class.
        mDataSource.register(params);

        if (viewerConfigInputStreamProvider == null && viewerConfigFilePath != null) {
            viewerConfigInputStreamProvider = new ViewerConfigInputStreamProvider() {
                @NonNull
                @Override
                public ProtoInputStream getInputStream() {
                    try {
                        return new ProtoInputStream(new FileInputStream(viewerConfigFilePath));
                    } catch (FileNotFoundException e) {
                        throw new RuntimeException(
                                "Failed to load viewer config file " + viewerConfigFilePath, e);
                    }
                }
            };
        }

        this.mViewerConfigInputStreamProvider = viewerConfigInputStreamProvider;
        this.mViewerConfigReader = viewerConfigReader;
        this.mCacheUpdater = cacheUpdater;

        registerGroupsLocally(groups);

        if (android.tracing.Flags.clientSideProtoLogging()) {
            mProtoLogConfigurationService = configurationService;
            Objects.requireNonNull(mProtoLogConfigurationService,
            Objects.requireNonNull(configurationService,
                    "A null ProtoLog Configuration Service was provided!");

            try {
                var args = new ProtoLogConfigurationServiceImpl.RegisterClientArgs();

                if (viewerConfigFilePath != null) {
                    args.setViewerConfigFile(viewerConfigFilePath);
                }
                var args = createConfigurationServiceRegisterClientArgs();

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

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

    @NonNull
    protected abstract RegisterClientArgs createConfigurationServiceRegisterClientArgs();

    /**
     * Main log method, do not call directly.
     */
@@ -334,9 +238,6 @@ public class PerfettoProtoLogImpl extends IProtoLogClient.Stub implements IProto
     * @return status code
     */
    public int startLoggingToLogcat(String[] groups, @NonNull ILogger logger) {
        if (mViewerConfigReader != null) {
            mViewerConfigReader.loadViewerConfig(groups, logger);
        }
        return setTextLogging(true, logger, groups);
    }

@@ -347,9 +248,6 @@ public class PerfettoProtoLogImpl extends IProtoLogClient.Stub implements IProto
     * @return status code
     */
    public int stopLoggingToLogcat(String[] groups, @NonNull ILogger logger) {
        if (mViewerConfigReader != null) {
            mViewerConfigReader.unloadViewerConfig(groups, logger);
        }
        return setTextLogging(false, logger, groups);
    }

@@ -372,21 +270,8 @@ public class PerfettoProtoLogImpl extends IProtoLogClient.Stub implements IProto
        // we might want to manually specify an id for the group with a collision
        verifyNoCollisionsOrDuplicates(protoLogGroups);

        final var groupsLoggingToLogcat = new ArrayList<String>();
        for (IProtoLogGroup protoLogGroup : protoLogGroups) {
            mLogGroups.put(protoLogGroup.name(), protoLogGroup);

            if (protoLogGroup.isLogToLogcat()) {
                groupsLoggingToLogcat.add(protoLogGroup.name());
            }
        }

        if (mViewerConfigReader != null) {
            // Load in background to avoid delay in boot process.
            // The caveat is that any log message that is also logged to logcat will not be
            // successfully decoded until this completes.
            mBackgroundLoggingService.execute(() -> mViewerConfigReader
                    .loadViewerConfig(groupsLoggingToLogcat.toArray(new String[0])));
        }
    }

@@ -499,57 +384,14 @@ public class PerfettoProtoLogImpl extends IProtoLogClient.Stub implements IProto
    }

    @Deprecated
    private void dumpViewerConfig() {
        if (mViewerConfigInputStreamProvider == null) {
            // No viewer config available
            return;
        }
    abstract void dumpViewerConfig();

        Log.d(LOG_TAG, "Dumping viewer config to trace");

        Utils.dumpViewerConfig(mDataSource, mViewerConfigInputStreamProvider);

        Log.d(LOG_TAG, "Dumped viewer config to trace");
    }
    @NonNull
    abstract String getLogcatMessageString(@NonNull Message message);

    private void logToLogcat(String tag, LogLevel level, Message message,
    private void logToLogcat(@NonNull String tag, @NonNull LogLevel level, @NonNull Message message,
            @Nullable Object[] args) {
        String messageString;
        if (mViewerConfigReader == null) {
            messageString = message.getMessage();

            if (messageString == null) {
                Log.e(LOG_TAG, "Failed to decode message for logcat. "
                        + "Message not available without ViewerConfig to decode the hash.");
            }
        } else {
            messageString = message.getMessage(mViewerConfigReader);

            if (messageString == null) {
                Log.e(LOG_TAG, "Failed to decode message for logcat. "
                        + "Message hash either not available in viewerConfig file or "
                        + "not loaded into memory from file before decoding.");
            }
        }

        if (messageString == null) {
            StringBuilder builder = new StringBuilder("UNKNOWN MESSAGE");
            if (args != null) {
                builder.append(" args = (");
                builder.append(String.join(", ", Arrays.stream(args)
                        .map(it -> {
                            if (it == null) {
                                return "null";
                            } else {
                                return it.toString();
                            }
                        }).toList()));
                builder.append(")");
            }
            messageString = builder.toString();
            args = new Object[0];
        }

        String messageString = getLogcatMessageString(message);
        logToLogcat(tag, level, messageString, args);
    }