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

Commit 9beea22a authored by Pablo Gamito's avatar Pablo Gamito
Browse files

Add API to register ProtoLog groups seperately from initialization

This allows us to have more flexibility in add groups to a tracing
instance. Particularly from places where we cannot yet initialize the
tracing instance fully like in static class initializations in Zygote.

Flag: android.tracing.imetracker_protolog
Bug: 410517697
Test: atest TracingTests
Change-Id: I4ffa468c4f7de403cc205743fe826ae7271e73a7
parent 41d630ef
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -47,7 +47,14 @@ interface IProtoLogConfigurationService {
        String viewerConfigFile;
    }

    parcelable RegisterGroupsArgs {
        String[] groups;
        boolean[] groupsDefaultLogcatStatus;
    }

    oneway void registerClient(IProtoLogClient client, in RegisterClientArgs args);

    oneway void registerGroups(IProtoLogClient client, in RegisterGroupsArgs args);

    oneway void unregisterClient(IProtoLogClient  client);
}
+5 −0
Original line number Diff line number Diff line
@@ -95,4 +95,9 @@ public class LogcatOnlyProtoLogImpl implements IProtoLog {
    public List<IProtoLogGroup> getRegisteredGroups() {
        return Collections.emptyList();
    }

    @Override
    public void registerGroups(IProtoLogGroup... groups) {
        // No-op
    }
}
+5 −0
Original line number Diff line number Diff line
@@ -77,6 +77,11 @@ public class NoViewerConfigProtoLogImpl implements IProtoLog {
        return List.of();
    }

    @Override
    public void registerGroups(IProtoLogGroup... groups) {
        // No-op
    }

    private void logMessage(LogLevel logLevel, String tag, String message) {
        switch (logLevel) {
            case VERBOSE -> Log.v(tag, message);
+39 −1
Original line number Diff line number Diff line
@@ -88,6 +88,7 @@ import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.stream.Stream;

/**
 * A service for the ProtoLog logging system.
@@ -175,6 +176,17 @@ public abstract class PerfettoProtoLogImpl extends IProtoLogClient.Stub implemen
        mDataSource.registerOnStopCallback(this);
    }

    @Override
    public void registerGroups(IProtoLogGroup... groups) {
        registerGroupsLocally(groups);

        if (mConfigurationService != null) {
            registerGroupsWithConfigurationServiceAsync(groups);
        } else {
            Log.w(LOG_TAG, "Missing configuration service... Not registering groups with it.");
        }
    }

    private void connectToConfigurationService() {
        Objects.requireNonNull(mConfigurationService,
                "A null ProtoLog Configuration Service was provided!");
@@ -200,6 +212,28 @@ public abstract class PerfettoProtoLogImpl extends IProtoLogClient.Stub implemen
        });
    }

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

        mBackgroundHandler.post(() -> {
            try {
                var args = new IProtoLogConfigurationService.RegisterGroupsArgs();
                args.groups = new String[groups.length];
                args.groupsDefaultLogcatStatus = new boolean[groups.length];
                var i = 0;
                for (var group : groups) {
                    args.groups[i] = group.name();
                    args.groupsDefaultLogcatStatus[i] = group.isLogToLogcat();
                    i++;
                }
                mConfigurationService.registerGroups(this, args);
            } catch (RemoteException e) {
                throw new RuntimeException("Failed to register ProtoLog groups", e);
            }
        });
    }

    /**
     * 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.
@@ -332,7 +366,11 @@ public abstract class PerfettoProtoLogImpl extends IProtoLogClient.Stub implemen
    private void registerGroupsLocally(@NonNull IProtoLogGroup[] protoLogGroups) {
        // Verify we don't have id collisions, if we do we want to know as soon as possible and
        // we might want to manually specify an id for the group with a collision
        verifyNoCollisionsOrDuplicates(protoLogGroups);
        IProtoLogGroup[] allGroups = Stream.concat(
                mLogGroups.values().stream(),
                Arrays.stream(protoLogGroups)
        ).toArray(IProtoLogGroup[]::new);
        verifyNoCollisionsOrDuplicates(allGroups);

        for (IProtoLogGroup protoLogGroup : protoLogGroups) {
            mLogGroups.put(protoLogGroup.name(), protoLogGroup);
+50 −31
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.internal.protolog;

import android.annotation.NonNull;
import android.os.ServiceManager;
import android.ravenwood.annotation.RavenwoodKeepWholeClass;
import android.ravenwood.annotation.RavenwoodReplace;
@@ -23,12 +24,14 @@ import android.tracing.perfetto.DataSourceParams;
import android.tracing.perfetto.InitArguments;
import android.tracing.perfetto.Producer;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.protolog.common.IProtoLog;
import com.android.internal.protolog.common.IProtoLogGroup;
import com.android.internal.protolog.common.LogLevel;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

/**
 * ProtoLog API - exposes static logging methods. Usage of this API is similar
@@ -64,22 +67,58 @@ public class ProtoLog {

    private static final Object sInitLock = new Object();

    @GuardedBy("sInitLock")
    private static final Set<IProtoLogGroup> sGroups = new HashSet<>();

    /**
     * Registers ProtoLog groups in the current process.
     * This method allows for registering {@link IProtoLogGroup} that will be used for logging
     * within the current process.
     *
     * @param groups A varargs array of {@link IProtoLogGroup} instances to be registered.
     */
    public static void registerLogGroupInProcess(@NonNull IProtoLogGroup... groups) {
        synchronized (sInitLock) {
            var newGroups = Arrays.stream(groups)
                    .filter(group -> !sGroups.contains(group))
                    .toArray(IProtoLogGroup[]::new);
            if (newGroups.length == 0) {
                return;
            }

            sGroups.addAll(Arrays.stream(newGroups).toList());

            if (sProtoLogInstance != null) {
                sProtoLogInstance.registerGroups(newGroups);
            }
        }
    }

    /**
     * Initialize ProtoLog in this process.
     * <p>
     * This method MUST be called before any protologging is performed in this process.
     * Ensure that all groups that will be used for protologging are registered.
     *
     * @param groups The ProtoLog groups that will be used in the process.
     */
    public static void init(IProtoLogGroup... groups) {
    public static void init(@NonNull IProtoLogGroup... groups) {
        registerLogGroupInProcess(groups);

        synchronized (sInitLock) {
            if (sProtoLogInstance != null) {
                return;
            }

            // These tracing instances are only used when we cannot or do not preprocess the source
        // files to extract out the log strings. Otherwise, the trace calls are replaced with calls
        // directly to the generated tracing implementations.
            // files to extract out the log strings. Otherwise, the trace calls are replaced with
            // calls directly to the generated tracing implementations.
            if (logOnlyToLogcat()) {
                sProtoLogInstance = new LogcatOnlyProtoLogImpl();
            } else {
            initializePerfettoProtoLog(groups);
                var datasource = getSharedSingleInstanceDataSource();

                sProtoLogInstance = createAndEnableNewPerfettoProtoLogImpl(
                        datasource, sGroups.toArray(new IProtoLogGroup[0]));
            }
        }
    }

@@ -95,28 +134,8 @@ public class ProtoLog {
        return true;
    }

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

        synchronized (sInitLock) {
            final var allGroups = new HashSet<>(Arrays.stream(groups).toList());
            final var previousProtoLogImpl = sProtoLogInstance;
            if (previousProtoLogImpl != null) {
                // The ProtoLog instance has already been initialized in this process
                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) {
            @NonNull ProtoLogDataSource datasource, @NonNull IProtoLogGroup[] groups) {
        try {
            var unprocessedPerfettoProtoLogImpl =
                    new UnprocessedPerfettoProtoLogImpl(datasource, groups);
Loading