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

Commit 37f0674c authored by Pablo Gamito's avatar Pablo Gamito
Browse files

Synchronize ProtoLog configuration service

In particular, synchronize the accesses to

Bug: 419430883
Flag: EXEMPT adding synchronization
Test: atest TracingTests
Change-Id: Ieeab1c54916eac543f733ebc3766f77098dd7774
parent 674ca99d
Loading
Loading
Loading
Loading
+108 −66
Original line number Diff line number Diff line
@@ -40,6 +40,7 @@ import android.util.Log;
import android.util.proto.ProtoInputStream;
import android.util.proto.ProtoOutputStream;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;

import java.io.FileDescriptor;
@@ -76,11 +77,18 @@ public class ProtoLogConfigurationServiceImpl extends IProtoLogConfigurationServ

    private final ProtoLogDataSource mDataSource;

    /**
     * Lock for synchronizing access to {@link #mConfigFileCounts}, {@link #mRegisteredGroups},
     * {@link #mClientRecords}, {@link #mLogGroupToLogcatStatus}, and {@link ClientRecord#groups}.
     */
    private final Object mConfigLock = new Object();

    /**
     * Keeps track of how many of each viewer config file is currently registered.
     * Use to keep track of which viewer config files are actively being used in tracing and might
     * need to be dumped on flush.
     */
    @GuardedBy("mConfigLock")
    private final Map<String, Integer> mConfigFileCounts = new HashMap<>();

    /**
@@ -96,7 +104,10 @@ public class ProtoLogConfigurationServiceImpl extends IProtoLogConfigurationServ
        @Nullable
        public final String configFile;

        /** Mutable set of ProtoLog groups registered for this client to actively trace. */
        /**
         * Mutable set of ProtoLog groups registered for this client to actively trace.
         */
        @GuardedBy("mConfigLock")
        @NonNull
        public final Set<String> groups = new ArraySet<>();

@@ -109,18 +120,21 @@ public class ProtoLogConfigurationServiceImpl extends IProtoLogConfigurationServ
    /**
     * Keeps track of all the clients that are actively tracing.
     */
    @GuardedBy("mConfigLock")
    private final Map<IBinder, ClientRecord> mClientRecords = new HashMap<>();

    /**
     * Keeps track of all the protolog groups that have been registered by clients and are still
     * being actively traced.
     */
    @GuardedBy("mConfigLock")
    private final Set<String> mRegisteredGroups = new HashSet<>();

    /**
     * Keeps track of whether or not a given group should be logged to logcat.
     * True when logging to logcat, false otherwise.
     */
    @GuardedBy("mConfigLock")
    private final Map<String, Boolean> mLogGroupToLogcatStatus = new TreeMap<>();

    /**
@@ -177,12 +191,15 @@ public class ProtoLogConfigurationServiceImpl extends IProtoLogConfigurationServ
        final IBinder clientBinder = client.asBinder();

        final String viewerConfigFile = args.viewerConfigFile;

        synchronized (mConfigLock) {
            mClientRecords.put(clientBinder, new ClientRecord(client, viewerConfigFile));

            if (viewerConfigFile != null) {
                mConfigFileCounts.put(viewerConfigFile,
                        mConfigFileCounts.getOrDefault(viewerConfigFile, 0) + 1);
            }
        }

        registerGroups(client, args.groups, args.groupsDefaultLogcatStatus);

@@ -191,8 +208,6 @@ public class ProtoLogConfigurationServiceImpl extends IProtoLogConfigurationServ

    /**
     * Unregister the {@param client}.
     *
     * TODO: this lacks synchronization.
     */
    @Override
    public void unregisterClient(@Nullable IProtoLogClient client) {
@@ -206,7 +221,10 @@ public class ProtoLogConfigurationServiceImpl extends IProtoLogConfigurationServ
        }

        // Retrieve the client record for cleanup.
        final ClientRecord clientRecord = mClientRecords.remove(clientBinder);
        final ClientRecord clientRecord;
        boolean dumpViewerConfig = false;
        synchronized (mConfigLock) {
            clientRecord = mClientRecords.remove(clientBinder);
            if (clientRecord == null) {
                return;
            }
@@ -215,10 +233,16 @@ public class ProtoLogConfigurationServiceImpl extends IProtoLogConfigurationServ
                final var newCount = mConfigFileCounts.get(clientRecord.configFile) - 1;
                mConfigFileCounts.put(clientRecord.configFile, newCount);

            // Dump the tracing config now if no other client is going to dump the same config file.
                if (newCount == 0) {
                mViewerConfigFileTracer.trace(mDataSource, clientRecord.configFile);
                    mConfigFileCounts.remove(clientRecord.configFile);
                    dumpViewerConfig = true;
                }
            }
        }

        // Dump the tracing config now if no other client is going to dump the same config file.
        if (dumpViewerConfig) {
            mViewerConfigFileTracer.trace(mDataSource, clientRecord.configFile);
        }
    }

@@ -237,8 +261,10 @@ public class ProtoLogConfigurationServiceImpl extends IProtoLogConfigurationServ
    @Override
    @NonNull
    public String[] getGroups() {
        synchronized (mConfigLock) {
            return mRegisteredGroups.toArray(new String[0]);
        }
    }

    /**
     * Enable logging target groups to logcat.
@@ -265,7 +291,10 @@ public class ProtoLogConfigurationServiceImpl extends IProtoLogConfigurationServ
     */
    @Override
    public boolean isLoggingToLogcat(@NonNull String group) {
        final Boolean isLoggingToLogcat = mLogGroupToLogcatStatus.get(group);
        final Boolean isLoggingToLogcat;
        synchronized (mConfigLock) {
            isLoggingToLogcat = mLogGroupToLogcatStatus.get(group);
        }

        if (isLoggingToLogcat == null) {
            throw new RuntimeException(
@@ -301,6 +330,7 @@ public class ProtoLogConfigurationServiceImpl extends IProtoLogConfigurationServ
                        + " and logcatStatuses has length " + logcatStatuses.length);
        }

        synchronized (mConfigLock) {
            final var clientRecord = mClientRecords.get(client.asBinder());
            if (clientRecord == null) {
                throw new RuntimeException("Client " + client + " is not registered");
@@ -310,26 +340,28 @@ public class ProtoLogConfigurationServiceImpl extends IProtoLogConfigurationServ
                String group = groups[i];
                boolean logcatStatus = logcatStatuses[i];

                final boolean requestedLogToLogcat;
                mRegisteredGroups.add(group);
                clientRecord.groups.add(group);

            if (!mLogGroupToLogcatStatus.containsKey(group)) {
                mLogGroupToLogcatStatus.put(group, logcatStatus);
            }
                mLogGroupToLogcatStatus.putIfAbsent(group, logcatStatus);
                requestedLogToLogcat = mLogGroupToLogcatStatus.get(group);

            boolean requestedLogToLogcat = mLogGroupToLogcatStatus.get(group);
                if (requestedLogToLogcat != logcatStatus) {
                    client.toggleLogcat(requestedLogToLogcat, new String[]{group});
                }
            }
        }
    }

    private void toggleProtoLogToLogcat(
            @NonNull PrintWriter pw, boolean enabled, @NonNull String[] groups
    ) {
        // For each client, if its groups intersect the given list, send the command to toggle.
        synchronized (mConfigLock) {
            for (var clientRecord : mClientRecords.values()) {
            final var affectedGroups = new ArraySet<>(clientRecord.groups);
                final ArraySet<String> affectedGroups;
                affectedGroups = new ArraySet<>(clientRecord.groups);
                affectedGroups.retainAll(Arrays.asList(groups));

                if (!affectedGroups.isEmpty()) {
@@ -350,7 +382,7 @@ public class ProtoLogConfigurationServiceImpl extends IProtoLogConfigurationServ

            // Groups that actually have no clients associated indicate some kind of a bug.
            Set<String> noOpGroups = new ArraySet<>(groups);
        mClientRecords.forEach((k, v) -> noOpGroups.removeAll(v.groups));
            mClientRecords.forEach((k, r) -> noOpGroups.removeAll(r.groups));

            // Send out a warning in logcat and the PrintWriter for unrecognized groups.
            for (String group : noOpGroups) {
@@ -365,14 +397,24 @@ public class ProtoLogConfigurationServiceImpl extends IProtoLogConfigurationServ
                mLogGroupToLogcatStatus.put(group, enabled);
            }
        }
    }

    private void onTracingInstanceStart(int instanceIdx, ProtoLogDataSource.ProtoLogConfig config) {
        mRunningInstances.add(instanceIdx);
    }

    private void onTracingInstanceFlush() {
        for (String fileName : mConfigFileCounts.keySet()) {
            mViewerConfigFileTracer.trace(mDataSource, fileName);
        final var configFilesToDump = new HashSet<String>();
        synchronized (mConfigLock) {
            for (var entry : mConfigFileCounts.entrySet()) {
                if (entry.getValue() > 0) {
                    configFilesToDump.add(entry.getKey());
                }
            }
        }

        for (var configFileName : configFilesToDump) {
            mViewerConfigFileTracer.trace(mDataSource, configFileName);
        }
    }