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

Commit 974693e5 authored by Pablo Gamito's avatar Pablo Gamito
Browse files

Support partial viewer config loading and unloading

Most of the time we only enable some groups to be logged to protolog and and not all group. So instead of loading the entire config in memory as soon as any group is being logged to protolog we support more granular control for only loading target groups.

Flag: android.tracing.perfetto_protolog_tracing
Bug: 352290057
Test: atest com.android.internal.protolog.ProtoLogViewerConfigReaderTest
Change-Id: I6d3fb041c5d3947a59a1de4c39660fe65a3a0051
parent f51f3f62
Loading
Loading
Loading
Loading
+3 −3
Original line number Original line Diff line number Diff line
@@ -211,7 +211,7 @@ public class PerfettoProtoLogImpl implements IProtoLog {
     */
     */
    public int startLoggingToLogcat(String[] groups, ILogger logger) {
    public int startLoggingToLogcat(String[] groups, ILogger logger) {
        if (mViewerConfigReader != null) {
        if (mViewerConfigReader != null) {
            mViewerConfigReader.loadViewerConfig(logger);
            mViewerConfigReader.loadViewerConfig(groups, logger);
        }
        }
        return setTextLogging(true, logger, groups);
        return setTextLogging(true, logger, groups);
    }
    }
@@ -224,7 +224,7 @@ public class PerfettoProtoLogImpl implements IProtoLog {
     */
     */
    public int stopLoggingToLogcat(String[] groups, ILogger logger) {
    public int stopLoggingToLogcat(String[] groups, ILogger logger) {
        if (mViewerConfigReader != null) {
        if (mViewerConfigReader != null) {
            mViewerConfigReader.unloadViewerConfig();
            mViewerConfigReader.unloadViewerConfig(groups, logger);
        }
        }
        return setTextLogging(false, logger, groups);
        return setTextLogging(false, logger, groups);
    }
    }
@@ -268,7 +268,7 @@ public class PerfettoProtoLogImpl implements IProtoLog {
            }
            }
            case "enable-text" -> {
            case "enable-text" -> {
                if (mViewerConfigReader != null) {
                if (mViewerConfigReader != null) {
                    mViewerConfigReader.loadViewerConfig(logger);
                    mViewerConfigReader.loadViewerConfig(groups, logger);
                }
                }
                return setTextLogging(true, logger, groups);
                return setTextLogging(true, logger, groups);
            }
            }
+100 −22
Original line number Original line Diff line number Diff line
package com.android.internal.protolog;
package com.android.internal.protolog;


import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.GROUPS;
import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.Group.ID;
import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.Group.NAME;

import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MESSAGES;
import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MESSAGES;
import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.MESSAGE;
import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.MESSAGE;
import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.MESSAGE_ID;
import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.MESSAGE_ID;
import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.GROUP_ID;


import android.util.ArrayMap;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.util.Log;
import android.util.LongSparseArray;
import android.util.proto.ProtoInputStream;
import android.util.proto.ProtoInputStream;


import com.android.internal.protolog.common.ILogger;
import com.android.internal.protolog.common.ILogger;


import java.io.IOException;
import java.io.IOException;
import java.util.Map;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.TreeMap;


public class ProtoLogViewerConfigReader {
public class ProtoLogViewerConfigReader {
    private final ViewerConfigInputStreamProvider mViewerConfigInputStreamProvider;
    private final ViewerConfigInputStreamProvider mViewerConfigInputStreamProvider;
    private Map<Long, String> mLogMessageMap = null;
    private final Map<String, Set<Long>> mGroupHashes = new TreeMap<>();
    private final LongSparseArray<String> mLogMessageMap = new LongSparseArray<>();


    public ProtoLogViewerConfigReader(
    public ProtoLogViewerConfigReader(
            ViewerConfigInputStreamProvider viewerConfigInputStreamProvider) {
            ViewerConfigInputStreamProvider viewerConfigInputStreamProvider) {
@@ -26,39 +38,62 @@ public class ProtoLogViewerConfigReader {
     * or the viewer config is not loaded into memory.
     * or the viewer config is not loaded into memory.
     */
     */
    public synchronized String getViewerString(long messageHash) {
    public synchronized String getViewerString(long messageHash) {
        if (mLogMessageMap != null) {
        return mLogMessageMap.get(messageHash);
        return mLogMessageMap.get(messageHash);
        } else {
            return null;
    }
    }

    public synchronized void loadViewerConfig(String[] groups) {
        loadViewerConfig(groups, (message) -> {});
    }
    }


    /**
    /**
     * Loads the viewer config into memory. No-op if already loaded in memory.
     * Loads the viewer config into memory. No-op if already loaded in memory.
     */
     */
    public synchronized void loadViewerConfig(ILogger logger) {
    public synchronized void loadViewerConfig(String[] groups, @NonNull ILogger logger) {
        if (mLogMessageMap != null) {
        for (String group : groups) {
            return;
            if (mGroupHashes.containsKey(group)) {
                continue;
            }
            }


            try {
            try {
            doLoadViewerConfig();
                Map<Long, String> mappings = loadViewerConfigMappingForGroup(group);
                mGroupHashes.put(group, mappings.keySet());
                for (Long key : mappings.keySet()) {
                    mLogMessageMap.put(key, mappings.get(key));
                }

                logger.log("Loaded " + mLogMessageMap.size() + " log definitions");
                logger.log("Loaded " + mLogMessageMap.size() + " log definitions");
            } catch (IOException e) {
            } catch (IOException e) {
                logger.log("Unable to load log definitions: "
                logger.log("Unable to load log definitions: "
                        + "IOException while processing viewer config" + e);
                        + "IOException while processing viewer config" + e);
            }
            }
        }
        }
    }

    public synchronized void unloadViewerConfig(String[] groups) {
        unloadViewerConfig(groups, (message) -> {});
    }


    /**
    /**
     * Unload the viewer config from memory.
     * Unload the viewer config from memory.
     */
     */
    public synchronized void unloadViewerConfig() {
    public synchronized void unloadViewerConfig(String[] groups, @NonNull ILogger logger) {
        mLogMessageMap = null;
        for (String group : groups) {
            if (!mGroupHashes.containsKey(group)) {
                continue;
            }

            final Set<Long> hashes = mGroupHashes.get(group);
            for (Long hash : hashes) {
                logger.log("Unloading viewer config hash " + hash);
                mLogMessageMap.remove(hash);
            }
            }
        }
    }

    private Map<Long, String> loadViewerConfigMappingForGroup(String group) throws IOException {
        Long targetGroupId = loadGroupId(group);


    private void doLoadViewerConfig() throws IOException {
        final Map<Long, String> hashesForGroup = new TreeMap<>();
        mLogMessageMap = new ArrayMap<>();
        final ProtoInputStream pis = mViewerConfigInputStreamProvider.getInputStream();
        final ProtoInputStream pis = mViewerConfigInputStreamProvider.getInputStream();


        while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
        while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
@@ -67,6 +102,7 @@ public class ProtoLogViewerConfigReader {


                long messageId = 0;
                long messageId = 0;
                String message = null;
                String message = null;
                int groupId = 0;
                while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
                while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
                    switch (pis.getFieldNumber()) {
                    switch (pis.getFieldNumber()) {
                        case (int) MESSAGE_ID:
                        case (int) MESSAGE_ID:
@@ -75,9 +111,16 @@ public class ProtoLogViewerConfigReader {
                        case (int) MESSAGE:
                        case (int) MESSAGE:
                            message = pis.readString(MESSAGE);
                            message = pis.readString(MESSAGE);
                            break;
                            break;
                        case (int) GROUP_ID:
                            groupId = pis.readInt(GROUP_ID);
                            break;
                    }
                    }
                }
                }


                if (groupId == 0) {
                    throw new IOException("Failed to get group id");
                }

                if (messageId == 0) {
                if (messageId == 0) {
                    throw new IOException("Failed to get message id");
                    throw new IOException("Failed to get message id");
                }
                }
@@ -86,10 +129,45 @@ public class ProtoLogViewerConfigReader {
                    throw new IOException("Failed to get message string");
                    throw new IOException("Failed to get message string");
                }
                }


                mLogMessageMap.put(messageId, message);
                if (groupId == targetGroupId) {
                    hashesForGroup.put(messageId, message);
                }

                pis.end(inMessageToken);
            }
        }

        return hashesForGroup;
    }

    private Long loadGroupId(String group) throws IOException {
        final ProtoInputStream pis = mViewerConfigInputStreamProvider.getInputStream();

        while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
            if (pis.getFieldNumber() == (int) GROUPS) {
                final long inMessageToken = pis.start(GROUPS);

                long groupId = 0;
                String groupName = null;
                while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
                    switch (pis.getFieldNumber()) {
                        case (int) ID:
                            groupId = pis.readInt(ID);
                            break;
                        case (int) NAME:
                            groupName = pis.readString(NAME);
                            break;
                    }
                }

                if (Objects.equals(groupName, group)) {
                    return groupId;
                }


                pis.end(inMessageToken);
                pis.end(inMessageToken);
            }
            }
        }
        }

        throw new RuntimeException("Group " + group + "not found in viewer config");
    }
    }
}
}