WindowManager ProtoLog feature
This CL implements the on-device part of ProtoLog
- the new logging system for WindowManager.
Design doc: go/windowmanager-log2proto
Change-Id: I2c88c97dabb3465ffc0615b8017b335a494bca59
Bug:
Test: atest FrameworksServicesTests:com.android.server.protolog protologtool-tests
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 1643221..80bc1af 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -1,3 +1,60 @@
+java_library {
+ name: "protolog-common",
+ srcs: [
+ "java/com/android/server/protolog/common/**/*.java",
+ ],
+ host_supported: true,
+}
+
+java_library {
+ name: "services.core.wm.protologgroups",
+ srcs: [
+ "java/com/android/server/wm/ProtoLogGroup.java",
+ ],
+ static_libs: ["protolog-common"],
+}
+
+genrule {
+ name: "services.core.protologsrc",
+ srcs: [":services.core.wm.protologgroups", "java/**/*.java"],
+ tools: ["protologtool"],
+ cmd: "$(location protologtool) transform-protolog-calls " +
+ "--protolog-class com.android.server.protolog.common.ProtoLog " +
+ "--protolog-impl-class com.android.server.protolog.ProtoLogImpl " +
+ "--loggroups-class com.android.server.wm.ProtoLogGroup " +
+ "--loggroups-jar $(location :services.core.wm.protologgroups) " +
+ "--output-srcjar $(out) " +
+ "$(locations java/**/*.java)",
+ out: ["services.core.protolog.srcjar"],
+}
+
+genrule {
+ name: "generate-protolog.json",
+ srcs: [":services.core.wm.protologgroups", "java/**/*.java"],
+ tools: ["protologtool"],
+ cmd: "$(location protologtool) generate-viewer-config " +
+ "--protolog-class com.android.server.protolog.common.ProtoLog " +
+ "--loggroups-class com.android.server.wm.ProtoLogGroup " +
+ "--loggroups-jar $(location :services.core.wm.protologgroups) " +
+ "--viewer-conf $(out) " +
+ "$(locations java/**/*.java)",
+ out: ["services.core.protolog.json"],
+}
+
+genrule {
+ name: "checked-protolog.json",
+ srcs: [
+ ":generate-protolog.json",
+ ":services.core.protolog.json",
+ ],
+ cmd: "cp $(location :generate-protolog.json) $(out) && " +
+ "{ diff $(out) $(location :services.core.protolog.json) >/dev/null 2>&1 || " +
+ "{ echo -e '##### ProtoLog viewer config is stale. ### \nRun: \n " +
+ "cp $(location :generate-protolog.json) " +
+ "$(location :services.core.protolog.json)\n' >&2 && false; } }",
+ out: ["services.core.protolog.json"],
+}
+
java_library_static {
name: "services.core.unboosted",
@@ -12,7 +69,7 @@
],
},
srcs: [
- "java/**/*.java",
+ ":services.core.protologsrc",
":dumpstate_aidl",
":idmap2_aidl",
":installd_aidl",
@@ -34,6 +91,7 @@
required: [
"gps_debug.conf",
+ "protolog.conf.json.gz",
],
static_libs: [
@@ -81,3 +139,15 @@
name: "gps_debug.conf",
src: "java/com/android/server/location/gps_debug.conf",
}
+
+genrule {
+ name: "services.core.json.gz",
+ srcs: [":checked-protolog.json"],
+ out: ["services.core.protolog.json.gz"],
+ cmd: "gzip < $(in) > $(out)",
+}
+
+prebuilt_etc {
+ name: "protolog.conf.json.gz",
+ src: ":services.core.json.gz",
+}
diff --git a/services/core/java/com/android/server/protolog/ProtoLogImpl.java b/services/core/java/com/android/server/protolog/ProtoLogImpl.java
new file mode 100644
index 0000000..239a425
--- /dev/null
+++ b/services/core/java/com/android/server/protolog/ProtoLogImpl.java
@@ -0,0 +1,446 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.protolog;
+
+import static com.android.server.protolog.ProtoLogFileProto.LOG;
+import static com.android.server.protolog.ProtoLogFileProto.MAGIC_NUMBER;
+import static com.android.server.protolog.ProtoLogFileProto.MAGIC_NUMBER_H;
+import static com.android.server.protolog.ProtoLogFileProto.MAGIC_NUMBER_L;
+import static com.android.server.protolog.ProtoLogFileProto.REAL_TIME_TO_ELAPSED_TIME_OFFSET_MILLIS;
+import static com.android.server.protolog.ProtoLogFileProto.VERSION;
+import static com.android.server.protolog.ProtoLogMessage.BOOLEAN_PARAMS;
+import static com.android.server.protolog.ProtoLogMessage.DOUBLE_PARAMS;
+import static com.android.server.protolog.ProtoLogMessage.ELAPSED_REALTIME_NANOS;
+import static com.android.server.protolog.ProtoLogMessage.MESSAGE_HASH;
+import static com.android.server.protolog.ProtoLogMessage.SINT64_PARAMS;
+import static com.android.server.protolog.ProtoLogMessage.STR_PARAMS;
+
+import android.annotation.Nullable;
+import android.os.ShellCommand;
+import android.os.SystemClock;
+import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.protolog.common.IProtoLogGroup;
+import com.android.server.protolog.common.LogDataType;
+import com.android.server.utils.TraceBuffer;
+import com.android.server.wm.ProtoLogGroup;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.IllegalFormatConversionException;
+import java.util.TreeMap;
+import java.util.stream.Collectors;
+
+
+/**
+ * A service for the ProtoLog logging system.
+ */
+public class ProtoLogImpl {
+ private static final TreeMap<String, IProtoLogGroup> LOG_GROUPS = new TreeMap<>();
+
+ private static void addLogGroupEnum(IProtoLogGroup[] config) {
+ Arrays.stream(config).forEach(group -> LOG_GROUPS.put(group.name(), group));
+ }
+
+ static {
+ addLogGroupEnum(ProtoLogGroup.values());
+ }
+
+ /** Used by the ProtoLogTool, do not call directly - use {@code ProtoLog} class instead. */
+ public static void d(IProtoLogGroup group, int messageHash, int paramsMask,
+ @Nullable String messageString,
+ Object... args) {
+ getSingleInstance()
+ .log(LogLevel.DEBUG, group, messageHash, paramsMask, messageString, args);
+ }
+
+ /** Used by the ProtoLogTool, do not call directly - use {@code ProtoLog} class instead. */
+ public static void v(IProtoLogGroup group, int messageHash, int paramsMask,
+ @Nullable String messageString,
+ Object... args) {
+ getSingleInstance().log(LogLevel.VERBOSE, group, messageHash, paramsMask, messageString,
+ args);
+ }
+
+ /** Used by the ProtoLogTool, do not call directly - use {@code ProtoLog} class instead. */
+ public static void i(IProtoLogGroup group, int messageHash, int paramsMask,
+ @Nullable String messageString,
+ Object... args) {
+ getSingleInstance().log(LogLevel.INFO, group, messageHash, paramsMask, messageString, args);
+ }
+
+ /** Used by the ProtoLogTool, do not call directly - use {@code ProtoLog} class instead. */
+ public static void w(IProtoLogGroup group, int messageHash, int paramsMask,
+ @Nullable String messageString,
+ Object... args) {
+ getSingleInstance().log(LogLevel.WARN, group, messageHash, paramsMask, messageString, args);
+ }
+
+ /** Used by the ProtoLogTool, do not call directly - use {@code ProtoLog} class instead. */
+ public static void e(IProtoLogGroup group, int messageHash, int paramsMask,
+ @Nullable String messageString,
+ Object... args) {
+ getSingleInstance()
+ .log(LogLevel.ERROR, group, messageHash, paramsMask, messageString, args);
+ }
+
+ /** Used by the ProtoLogTool, do not call directly - use {@code ProtoLog} class instead. */
+ public static void wtf(IProtoLogGroup group, int messageHash, int paramsMask,
+ @Nullable String messageString,
+ Object... args) {
+ getSingleInstance().log(LogLevel.WTF, group, messageHash, paramsMask, messageString, args);
+ }
+
+ private static final int BUFFER_CAPACITY = 1024 * 1024;
+ private static final String LOG_FILENAME = "/data/misc/wmtrace/wm_log.pb";
+ private static final String VIEWER_CONFIG_FILENAME = "/system/etc/protolog.conf.json.gz";
+ private static final String TAG = "ProtoLog";
+ private static final long MAGIC_NUMBER_VALUE = ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L;
+ static final String PROTOLOG_VERSION = "1.0.0";
+
+ private final File mLogFile;
+ private final TraceBuffer mBuffer;
+ private final ProtoLogViewerConfigReader mViewerConfig;
+
+ private boolean mProtoLogEnabled;
+ private boolean mProtoLogEnabledLockFree;
+ private final Object mProtoLogEnabledLock = new Object();
+
+ private static ProtoLogImpl sServiceInstance = null;
+
+ /**
+ * Returns the single instance of the ProtoLogImpl singleton class.
+ */
+ public static synchronized ProtoLogImpl getSingleInstance() {
+ if (sServiceInstance == null) {
+ sServiceInstance = new ProtoLogImpl(new File(LOG_FILENAME), BUFFER_CAPACITY,
+ new ProtoLogViewerConfigReader());
+ }
+ return sServiceInstance;
+ }
+
+ @VisibleForTesting
+ public static synchronized void setSingleInstance(@Nullable ProtoLogImpl instance) {
+ sServiceInstance = instance;
+ }
+
+ @VisibleForTesting
+ public enum LogLevel {
+ DEBUG, VERBOSE, INFO, WARN, ERROR, WTF
+ }
+
+ /**
+ * Main log method, do not call directly.
+ */
+ @VisibleForTesting
+ public void log(LogLevel level, IProtoLogGroup group, int messageHash, int paramsMask,
+ @Nullable String messageString, Object[] args) {
+ if (group.isLogToProto()) {
+ logToProto(messageHash, paramsMask, args);
+ }
+ if (group.isLogToLogcat()) {
+ logToLogcat(group.getTag(), level, messageHash, messageString, args);
+ }
+ }
+
+ private void logToLogcat(String tag, LogLevel level, int messageHash,
+ @Nullable String messageString, Object[] args) {
+ String message = null;
+ if (messageString == null) {
+ messageString = mViewerConfig.getViewerString(messageHash);
+ }
+ if (messageString != null) {
+ try {
+ message = String.format(messageString, args);
+ } catch (IllegalFormatConversionException ex) {
+ Slog.w(TAG, "Invalid ProtoLog format string.", ex);
+ }
+ }
+ if (message == null) {
+ StringBuilder builder = new StringBuilder("UNKNOWN MESSAGE (" + messageHash + ")");
+ for (Object o : args) {
+ builder.append(" ").append(o);
+ }
+ message = builder.toString();
+ }
+ passToLogcat(tag, level, message);
+ }
+
+ /**
+ * SLog wrapper.
+ */
+ @VisibleForTesting
+ public void passToLogcat(String tag, LogLevel level, String message) {
+ switch (level) {
+ case DEBUG:
+ Slog.d(tag, message);
+ break;
+ case VERBOSE:
+ Slog.v(tag, message);
+ break;
+ case INFO:
+ Slog.i(tag, message);
+ break;
+ case WARN:
+ Slog.w(tag, message);
+ break;
+ case ERROR:
+ Slog.e(tag, message);
+ break;
+ case WTF:
+ Slog.wtf(tag, message);
+ break;
+ }
+ }
+
+ private void logToProto(int messageHash, int paramsMask, Object[] args) {
+ if (!isProtoEnabled()) {
+ return;
+ }
+ try {
+ ProtoOutputStream os = new ProtoOutputStream();
+ long token = os.start(LOG);
+ os.write(MESSAGE_HASH, messageHash);
+ os.write(ELAPSED_REALTIME_NANOS, SystemClock.elapsedRealtimeNanos());
+
+ int argIndex = 0;
+ ArrayList<Long> longParams = new ArrayList<>();
+ ArrayList<Double> doubleParams = new ArrayList<>();
+ ArrayList<Boolean> booleanParams = new ArrayList<>();
+ for (Object o : args) {
+ int type = LogDataType.bitmaskToLogDataType(paramsMask, argIndex);
+ try {
+ switch (type) {
+ case LogDataType.STRING:
+ os.write(STR_PARAMS, o.toString());
+ break;
+ case LogDataType.LONG:
+ longParams.add(((Number) o).longValue());
+ break;
+ case LogDataType.DOUBLE:
+ doubleParams.add(((Number) o).doubleValue());
+ break;
+ case LogDataType.BOOLEAN:
+ booleanParams.add((boolean) o);
+ break;
+ }
+ } catch (ClassCastException ex) {
+ // Should not happen unless there is an error in the ProtoLogTool.
+ os.write(STR_PARAMS, "(INVALID PARAMS_MASK) " + o.toString());
+ Slog.e(TAG, "Invalid ProtoLog paramsMask", ex);
+ }
+ argIndex++;
+ }
+ if (longParams.size() > 0) {
+ os.writePackedSInt64(SINT64_PARAMS,
+ longParams.stream().mapToLong(i -> i).toArray());
+ }
+ if (doubleParams.size() > 0) {
+ os.writePackedDouble(DOUBLE_PARAMS,
+ doubleParams.stream().mapToDouble(i -> i).toArray());
+ }
+ if (booleanParams.size() > 0) {
+ boolean[] arr = new boolean[booleanParams.size()];
+ for (int i = 0; i < booleanParams.size(); i++) {
+ arr[i] = booleanParams.get(i);
+ }
+ os.writePackedBool(BOOLEAN_PARAMS, arr);
+ }
+ os.end(token);
+ mBuffer.add(os);
+ } catch (Exception e) {
+ Slog.e(TAG, "Exception while logging to proto", e);
+ }
+ }
+
+
+ @VisibleForTesting
+ ProtoLogImpl(File file, int bufferCapacity, ProtoLogViewerConfigReader viewerConfig) {
+ mLogFile = file;
+ mBuffer = new TraceBuffer(bufferCapacity);
+ mViewerConfig = viewerConfig;
+ }
+
+ /**
+ * Starts the logging a circular proto buffer.
+ *
+ * @param pw Print writer
+ */
+ public void startProtoLog(@Nullable PrintWriter pw) {
+ if (isProtoEnabled()) {
+ return;
+ }
+ synchronized (mProtoLogEnabledLock) {
+ logAndPrintln(pw, "Start logging to " + mLogFile + ".");
+ mBuffer.resetBuffer();
+ mProtoLogEnabled = true;
+ mProtoLogEnabledLockFree = true;
+ }
+ }
+
+ /**
+ * Stops logging to proto.
+ *
+ * @param pw Print writer
+ * @param writeToFile If the current buffer should be written to disk or not
+ */
+ public void stopProtoLog(@Nullable PrintWriter pw, boolean writeToFile) {
+ if (!isProtoEnabled()) {
+ return;
+ }
+ synchronized (mProtoLogEnabledLock) {
+ logAndPrintln(pw, "Stop logging to " + mLogFile + ". Waiting for log to flush.");
+ mProtoLogEnabled = mProtoLogEnabledLockFree = false;
+ if (writeToFile) {
+ writeProtoLogToFileLocked();
+ logAndPrintln(pw, "Log written to " + mLogFile + ".");
+ }
+ if (mProtoLogEnabled) {
+ logAndPrintln(pw, "ERROR: logging was re-enabled while waiting for flush.");
+ throw new IllegalStateException("logging enabled while waiting for flush.");
+ }
+ }
+ }
+
+ /**
+ * Returns {@code true} iff logging to proto is enabled.
+ */
+ public boolean isProtoEnabled() {
+ return mProtoLogEnabledLockFree;
+ }
+
+ private int setLogging(ShellCommand shell, boolean setTextLogging, boolean value) {
+ String group;
+ while ((group = shell.getNextArg()) != null) {
+ IProtoLogGroup g = LOG_GROUPS.get(group);
+ if (g != null) {
+ if (setTextLogging) {
+ g.setLogToLogcat(value);
+ } else {
+ g.setLogToProto(value);
+ }
+ } else {
+ logAndPrintln(shell.getOutPrintWriter(), "No IProtoLogGroup named " + group);
+ return -1;
+ }
+ }
+ return 0;
+ }
+
+ private int unknownCommand(PrintWriter pw) {
+ pw.println("Unknown command");
+ pw.println("Window manager logging options:");
+ pw.println(" start: Start proto logging");
+ pw.println(" stop: Stop proto logging");
+ pw.println(" enable [group...]: Enable proto logging for given groups");
+ pw.println(" disable [group...]: Disable proto logging for given groups");
+ pw.println(" enable-text [group...]: Enable logcat logging for given groups");
+ pw.println(" disable-text [group...]: Disable logcat logging for given groups");
+ return -1;
+ }
+
+ /**
+ * Responds to a shell command.
+ */
+ public int onShellCommand(ShellCommand shell) {
+ PrintWriter pw = shell.getOutPrintWriter();
+ String cmd = shell.getNextArg();
+ if (cmd == null) {
+ return unknownCommand(pw);
+ }
+ switch (cmd) {
+ case "start":
+ startProtoLog(pw);
+ return 0;
+ case "stop":
+ stopProtoLog(pw, true);
+ return 0;
+ case "status":
+ logAndPrintln(pw, getStatus());
+ return 0;
+ case "enable":
+ return setLogging(shell, false, true);
+ case "enable-text":
+ mViewerConfig.loadViewerConfig(pw, VIEWER_CONFIG_FILENAME);
+ return setLogging(shell, true, true);
+ case "disable":
+ return setLogging(shell, false, false);
+ case "disable-text":
+ return setLogging(shell, true, false);
+ default:
+ return unknownCommand(pw);
+ }
+ }
+
+ /**
+ * Returns a human-readable ProtoLog status text.
+ */
+ public String getStatus() {
+ return "ProtoLog status: "
+ + ((isProtoEnabled()) ? "Enabled" : "Disabled")
+ + "\nEnabled log groups: \n Proto: "
+ + LOG_GROUPS.values().stream().filter(
+ it -> it.isEnabled() && it.isLogToProto())
+ .map(IProtoLogGroup::name).collect(Collectors.joining(" "))
+ + "\n Logcat: "
+ + LOG_GROUPS.values().stream().filter(
+ it -> it.isEnabled() && it.isLogToLogcat())
+ .map(IProtoLogGroup::name).collect(Collectors.joining(" "))
+ + "\nLogging definitions loaded: " + mViewerConfig.knownViewerStringsNumber();
+ }
+
+ /**
+ * Writes the log buffer to a new file for the bugreport.
+ *
+ * This method is synchronized with {@code #startProtoLog(PrintWriter)} and
+ * {@link #stopProtoLog(PrintWriter, boolean)}.
+ */
+ public void writeProtoLogToFile() {
+ synchronized (mProtoLogEnabledLock) {
+ writeProtoLogToFileLocked();
+ }
+ }
+
+ private void writeProtoLogToFileLocked() {
+ try {
+ long offset =
+ (System.currentTimeMillis() - (SystemClock.elapsedRealtimeNanos() / 1000000));
+ ProtoOutputStream proto = new ProtoOutputStream();
+ proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE);
+ proto.write(VERSION, PROTOLOG_VERSION);
+ proto.write(REAL_TIME_TO_ELAPSED_TIME_OFFSET_MILLIS, offset);
+ mBuffer.writeTraceToFile(mLogFile, proto);
+ } catch (IOException e) {
+ Slog.e(TAG, "Unable to write buffer to file", e);
+ }
+ }
+
+
+ static void logAndPrintln(@Nullable PrintWriter pw, String msg) {
+ Slog.i(TAG, msg);
+ if (pw != null) {
+ pw.println(msg);
+ pw.flush();
+ }
+ }
+}
+
diff --git a/services/core/java/com/android/server/protolog/ProtoLogViewerConfigReader.java b/services/core/java/com/android/server/protolog/ProtoLogViewerConfigReader.java
new file mode 100644
index 0000000..4944217
--- /dev/null
+++ b/services/core/java/com/android/server/protolog/ProtoLogViewerConfigReader.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.protolog;
+
+import static com.android.server.protolog.ProtoLogImpl.logAndPrintln;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.BufferedReader;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.zip.GZIPInputStream;
+
+/**
+ * Handles loading and parsing of ProtoLog viewer configuration.
+ */
+public class ProtoLogViewerConfigReader {
+ private Map<Integer, String> mLogMessageMap = null;
+
+ /** Returns message format string for its hash or null if unavailable. */
+ public synchronized String getViewerString(int messageHash) {
+ if (mLogMessageMap != null) {
+ return mLogMessageMap.get(messageHash);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Reads the specified viewer configuration file. Does nothing if the config is already loaded.
+ */
+ public synchronized void loadViewerConfig(PrintWriter pw, String viewerConfigFilename) {
+ if (mLogMessageMap != null) {
+ return;
+ }
+ try {
+ InputStreamReader config = new InputStreamReader(
+ new GZIPInputStream(new FileInputStream(viewerConfigFilename)));
+ BufferedReader reader = new BufferedReader(config);
+ StringBuilder builder = new StringBuilder();
+ String line;
+ while ((line = reader.readLine()) != null) {
+ builder.append(line).append('\n');
+ }
+ reader.close();
+ JSONObject json = new JSONObject(builder.toString());
+ JSONObject messages = json.getJSONObject("messages");
+
+ mLogMessageMap = new TreeMap<>();
+ Iterator it = messages.keys();
+ while (it.hasNext()) {
+ String key = (String) it.next();
+ try {
+ int hash = Integer.parseInt(key);
+ JSONObject val = messages.getJSONObject(key);
+ String msg = val.getString("message");
+ mLogMessageMap.put(hash, msg);
+ } catch (NumberFormatException expected) {
+ // Not a messageHash - skip it
+ }
+ }
+ logAndPrintln(pw, "Loaded " + mLogMessageMap.size() + " log definitions from "
+ + viewerConfigFilename);
+ } catch (FileNotFoundException e) {
+ logAndPrintln(pw, "Unable to load log definitions: File "
+ + viewerConfigFilename + " not found." + e);
+ } catch (IOException e) {
+ logAndPrintln(pw, "Unable to load log definitions: IOException while reading "
+ + viewerConfigFilename + ". " + e);
+ } catch (JSONException e) {
+ logAndPrintln(pw,
+ "Unable to load log definitions: JSON parsing exception while reading "
+ + viewerConfigFilename + ". " + e);
+ }
+ }
+
+ /**
+ * Returns the number of loaded log definitions kept in memory.
+ */
+ public synchronized int knownViewerStringsNumber() {
+ if (mLogMessageMap != null) {
+ return mLogMessageMap.size();
+ }
+ return 0;
+ }
+}
diff --git a/services/core/java/com/android/server/protolog/common/BitmaskConversionException.java b/services/core/java/com/android/server/protolog/common/BitmaskConversionException.java
new file mode 100644
index 0000000..7bb27b2
--- /dev/null
+++ b/services/core/java/com/android/server/protolog/common/BitmaskConversionException.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.protolog.common;
+
+/**
+ * Error while converting a bitmask representing a list of LogDataTypes.
+ */
+public class BitmaskConversionException extends RuntimeException {
+ BitmaskConversionException(String msg) {
+ super(msg);
+ }
+}
diff --git a/services/core/java/com/android/server/protolog/common/IProtoLogGroup.java b/services/core/java/com/android/server/protolog/common/IProtoLogGroup.java
new file mode 100644
index 0000000..2c65341
--- /dev/null
+++ b/services/core/java/com/android/server/protolog/common/IProtoLogGroup.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.protolog.common;
+
+/**
+ * Defines a log group configuration object for ProtoLog. Should be implemented as en enum.
+ */
+public interface IProtoLogGroup {
+ /**
+ * if false all log statements for this group are excluded from compilation,
+ */
+ boolean isEnabled();
+
+ /**
+ * is binary logging enabled for the group.
+ */
+ boolean isLogToProto();
+
+ /**
+ * is text logging enabled for the group.
+ */
+ boolean isLogToLogcat();
+
+ /**
+ * returns true is any logging is enabled for this group.
+ */
+ default boolean isLogToAny() {
+ return isLogToLogcat() || isLogToProto();
+ }
+
+ /**
+ * returns the name of the source of the logged message
+ */
+ String getTag();
+
+ /**
+ * set binary logging for this group.
+ */
+ void setLogToProto(boolean logToProto);
+
+ /**
+ * set text logging for this group.
+ */
+ void setLogToLogcat(boolean logToLogcat);
+
+ /**
+ * returns name of the logging group.
+ */
+ String name();
+}
diff --git a/services/core/java/com/android/server/protolog/common/InvalidFormatStringException.java b/services/core/java/com/android/server/protolog/common/InvalidFormatStringException.java
new file mode 100644
index 0000000..947bf98
--- /dev/null
+++ b/services/core/java/com/android/server/protolog/common/InvalidFormatStringException.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.protolog.common;
+
+/**
+ * Unsupported/invalid message format string error.
+ */
+public class InvalidFormatStringException extends RuntimeException {
+ public InvalidFormatStringException(String message) {
+ super(message);
+ }
+
+ public InvalidFormatStringException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/services/core/java/com/android/server/protolog/common/LogDataType.java b/services/core/java/com/android/server/protolog/common/LogDataType.java
new file mode 100644
index 0000000..e73b41a
--- /dev/null
+++ b/services/core/java/com/android/server/protolog/common/LogDataType.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.protolog.common;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Represents a type of logged data encoded in the proto.
+ */
+public class LogDataType {
+ // When updating this list make sure to update bitmask conversion methods accordingly.
+ // STR type should be the first in the enum in order to be the default type.
+ public static final int STRING = 0b00;
+ public static final int LONG = 0b01;
+ public static final int DOUBLE = 0b10;
+ public static final int BOOLEAN = 0b11;
+
+ private static final int TYPE_WIDTH = 2;
+ private static final int TYPE_MASK = 0b11;
+
+ /**
+ * Creates a bitmask representing a list of data types.
+ */
+ public static int logDataTypesToBitMask(List<Integer> types) {
+ if (types.size() > 16) {
+ throw new BitmaskConversionException("Too many log call parameters "
+ + "- max 16 parameters supported");
+ }
+ int mask = 0;
+ for (int i = 0; i < types.size(); i++) {
+ int x = types.get(i);
+ mask = mask | (x << (i * TYPE_WIDTH));
+ }
+ return mask;
+ }
+
+ /**
+ * Decodes a bitmask to a list of LogDataTypes of provided length.
+ */
+ public static int bitmaskToLogDataType(int bitmask, int index) {
+ if (index > 16) {
+ throw new BitmaskConversionException("Max 16 parameters allowed");
+ }
+ return (bitmask >> (index * TYPE_WIDTH)) & TYPE_MASK;
+ }
+
+ /**
+ * Creates a list of LogDataTypes from a message format string.
+ */
+ public static List<Integer> parseFormatString(String messageString) {
+ ArrayList<Integer> types = new ArrayList<>();
+ for (int i = 0; i < messageString.length(); ) {
+ if (messageString.charAt(i) == '%') {
+ if (i + 1 >= messageString.length()) {
+ throw new InvalidFormatStringException("Invalid format string in config");
+ }
+ switch (messageString.charAt(i + 1)) {
+ case 'b':
+ types.add(LogDataType.BOOLEAN);
+ break;
+ case 'd':
+ case 'o':
+ case 'x':
+ types.add(LogDataType.LONG);
+ break;
+ case 'f':
+ case 'e':
+ case 'g':
+ types.add(LogDataType.DOUBLE);
+ break;
+ case 's':
+ types.add(LogDataType.STRING);
+ break;
+ case '%':
+ break;
+ default:
+ throw new InvalidFormatStringException("Invalid format string field"
+ + " %${messageString[i + 1]}");
+ }
+ i += 2;
+ } else {
+ i += 1;
+ }
+ }
+ return types;
+ }
+}
diff --git a/services/core/java/com/android/server/protolog/common/ProtoLog.java b/services/core/java/com/android/server/protolog/common/ProtoLog.java
new file mode 100644
index 0000000..b631bcb
--- /dev/null
+++ b/services/core/java/com/android/server/protolog/common/ProtoLog.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.protolog.common;
+
+/**
+ * ProtoLog API - exposes static logging methods. Usage of this API is similar
+ * to {@code android.utils.Log} class. Instead of plain text log messages each call consists of
+ * a messageString, which is a format string for the log message (has to be a string literal or
+ * a concatenation of string literals) and a vararg array of parameters for the formatter.
+ *
+ * The syntax for the message string is a subset of {@code java.util.Formatter} syntax.
+ * Supported conversions:
+ * %b - boolean
+ * %d, %o and %x - integral type (Short, Integer or Long)
+ * %f, %e and %g - floating point type (Float or Double)
+ * %s - string
+ * %% - a literal percent character
+ * The width and precision modifiers are supported, argument_index and flags are not.
+ *
+ * Methods in this class are stubs, that are replaced by optimised versions by the ProtoLogTool
+ * during build.
+ */
+public class ProtoLog {
+ /**
+ * DEBUG level log.
+ *
+ * @param group {@code IProtoLogGroup} controlling this log call.
+ * @param messageString constant format string for the logged message.
+ * @param args parameters to be used with the format string.
+ */
+ public static void d(IProtoLogGroup group, String messageString, Object... args) {
+ // Stub, replaced by the ProtoLogTool.
+ throw new UnsupportedOperationException(
+ "ProtoLog calls MUST be processed with ProtoLogTool");
+ }
+
+ /**
+ * VERBOSE level log.
+ *
+ * @param group {@code IProtoLogGroup} controlling this log call.
+ * @param messageString constant format string for the logged message.
+ * @param args parameters to be used with the format string.
+ */
+ public static void v(IProtoLogGroup group, String messageString, Object... args) {
+ // Stub, replaced by the ProtoLogTool.
+ throw new UnsupportedOperationException(
+ "ProtoLog calls MUST be processed with ProtoLogTool");
+ }
+
+ /**
+ * INFO level log.
+ *
+ * @param group {@code IProtoLogGroup} controlling this log call.
+ * @param messageString constant format string for the logged message.
+ * @param args parameters to be used with the format string.
+ */
+ public static void i(IProtoLogGroup group, String messageString, Object... args) {
+ // Stub, replaced by the ProtoLogTool.
+ throw new UnsupportedOperationException(
+ "ProtoLog calls MUST be processed with ProtoLogTool");
+ }
+
+ /**
+ * WARNING level log.
+ *
+ * @param group {@code IProtoLogGroup} controlling this log call.
+ * @param messageString constant format string for the logged message.
+ * @param args parameters to be used with the format string.
+ */
+ public static void w(IProtoLogGroup group, String messageString, Object... args) {
+ // Stub, replaced by the ProtoLogTool.
+ throw new UnsupportedOperationException(
+ "ProtoLog calls MUST be processed with ProtoLogTool");
+ }
+
+ /**
+ * ERROR level log.
+ *
+ * @param group {@code IProtoLogGroup} controlling this log call.
+ * @param messageString constant format string for the logged message.
+ * @param args parameters to be used with the format string.
+ */
+ public static void e(IProtoLogGroup group, String messageString, Object... args) {
+ // Stub, replaced by the ProtoLogTool.
+ throw new UnsupportedOperationException(
+ "ProtoLog calls MUST be processed with ProtoLogTool");
+ }
+
+ /**
+ * WHAT A TERRIBLE FAILURE level log.
+ *
+ * @param group {@code IProtoLogGroup} controlling this log call.
+ * @param messageString constant format string for the logged message.
+ * @param args parameters to be used with the format string.
+ */
+ public static void wtf(IProtoLogGroup group, String messageString, Object... args) {
+ // Stub, replaced by the ProtoLogTool.
+ throw new UnsupportedOperationException(
+ "ProtoLog calls MUST be processed with ProtoLogTool");
+ }
+}
diff --git a/services/core/java/com/android/server/wm/WindowTraceBuffer.java b/services/core/java/com/android/server/utils/TraceBuffer.java
similarity index 79%
rename from services/core/java/com/android/server/wm/WindowTraceBuffer.java
rename to services/core/java/com/android/server/utils/TraceBuffer.java
index 8c65884..0567960 100644
--- a/services/core/java/com/android/server/wm/WindowTraceBuffer.java
+++ b/services/core/java/com/android/server/utils/TraceBuffer.java
@@ -14,11 +14,7 @@
* limitations under the License.
*/
-package com.android.server.wm;
-
-import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER;
-import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER_H;
-import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER_L;
+package com.android.server.utils;
import android.util.proto.ProtoOutputStream;
@@ -33,31 +29,32 @@
import java.util.Queue;
/**
- * Buffer used for window tracing.
+ * Buffer used for tracing and logging.
*/
-class WindowTraceBuffer {
- private static final long MAGIC_NUMBER_VALUE = ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L;
-
+public class TraceBuffer {
private final Object mBufferLock = new Object();
private final Queue<ProtoOutputStream> mBuffer = new ArrayDeque<>();
private int mBufferUsedSize;
private int mBufferCapacity;
- WindowTraceBuffer(int bufferCapacity) {
+ public TraceBuffer(int bufferCapacity) {
mBufferCapacity = bufferCapacity;
resetBuffer();
}
- int getAvailableSpace() {
+ public int getAvailableSpace() {
return mBufferCapacity - mBufferUsedSize;
}
- int size() {
+ /**
+ * Returns buffer size.
+ */
+ public int size() {
return mBuffer.size();
}
- void setCapacity(int capacity) {
+ public void setCapacity(int capacity) {
mBufferCapacity = capacity;
}
@@ -68,7 +65,7 @@
* @throws IllegalStateException if the element cannot be added because it is larger
* than the buffer size.
*/
- void add(ProtoOutputStream proto) {
+ public void add(ProtoOutputStream proto) {
int protoLength = proto.getRawSize();
if (protoLength > mBufferCapacity) {
throw new IllegalStateException("Trace object too large for the buffer. Buffer size:"
@@ -88,19 +85,18 @@
}
/**
- * Writes the trace buffer to disk.
+ * Writes the trace buffer to disk inside the encapsulatingProto..
*/
- void writeTraceToFile(File traceFile) throws IOException {
+ public void writeTraceToFile(File traceFile, ProtoOutputStream encapsulatingProto)
+ throws IOException {
synchronized (mBufferLock) {
traceFile.delete();
try (OutputStream os = new FileOutputStream(traceFile)) {
traceFile.setReadable(true /* readable */, false /* ownerOnly */);
- ProtoOutputStream proto = new ProtoOutputStream();
- proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE);
- os.write(proto.getBytes());
+ os.write(encapsulatingProto.getBytes());
for (ProtoOutputStream protoOutputStream : mBuffer) {
- proto = protoOutputStream;
- byte[] protoBytes = proto.getBytes();
+ encapsulatingProto = protoOutputStream;
+ byte[] protoBytes = encapsulatingProto.getBytes();
os.write(protoBytes);
}
os.flush();
@@ -131,7 +127,7 @@
/**
* Removes all elements form the buffer
*/
- void resetBuffer() {
+ public void resetBuffer() {
synchronized (mBufferLock) {
mBuffer.clear();
mBufferUsedSize = 0;
@@ -143,7 +139,10 @@
return mBufferUsedSize;
}
- String getStatus() {
+ /**
+ * Returns the buffer status in human-readable form.
+ */
+ public String getStatus() {
synchronized (mBufferLock) {
return "Buffer size: "
+ mBufferCapacity
diff --git a/services/core/java/com/android/server/wm/ProtoLogGroup.java b/services/core/java/com/android/server/wm/ProtoLogGroup.java
new file mode 100644
index 0000000..313cceb
--- /dev/null
+++ b/services/core/java/com/android/server/wm/ProtoLogGroup.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.protolog.common.IProtoLogGroup;
+import com.android.server.protolog.common.ProtoLog;
+
+/**
+ * Defines logging groups for ProtoLog.
+ *
+ * This file is used by the ProtoLogTool to generate optimized logging code. All of its dependencies
+ * must be included in services.core.wm.protologgroups build target.
+ */
+public enum ProtoLogGroup implements IProtoLogGroup {
+ GENERIC_WM(true, true, false, "WindowManager"),
+
+ TEST_GROUP(true, true, false, "WindowManagetProtoLogTest");
+
+ private final boolean mEnabled;
+ private volatile boolean mLogToProto;
+ private volatile boolean mLogToLogcat;
+ private final String mTag;
+
+ /**
+ * @param enabled set to false to exclude all log statements for this group from
+ * compilation,
+ * they will not be available in runtime.
+ * @param logToProto enable binary logging for the group
+ * @param logToLogcat enable text logging for the group
+ * @param tag name of the source of the logged message
+ */
+ ProtoLogGroup(boolean enabled, boolean logToProto, boolean logToLogcat, String tag) {
+ this.mEnabled = enabled;
+ this.mLogToProto = logToProto;
+ this.mLogToLogcat = logToLogcat;
+ this.mTag = tag;
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return mEnabled;
+ }
+
+ @Override
+ public boolean isLogToProto() {
+ return mLogToProto;
+ }
+
+ @Override
+ public boolean isLogToLogcat() {
+ return mLogToLogcat;
+ }
+
+ @Override
+ public boolean isLogToAny() {
+ return mLogToLogcat || mLogToProto;
+ }
+
+ @Override
+ public String getTag() {
+ return mTag;
+ }
+
+ @Override
+ public void setLogToProto(boolean logToProto) {
+ this.mLogToProto = logToProto;
+ }
+
+ @Override
+ public void setLogToLogcat(boolean logToLogcat) {
+ this.mLogToLogcat = logToLogcat;
+ }
+
+ /**
+ * Test function for automated integration tests. Can be also called manually from adb shell.
+ */
+ @VisibleForTesting
+ public static void testProtoLog() {
+ ProtoLog.e(ProtoLogGroup.TEST_GROUP,
+ "Test completed successfully: %b %d %o %x %e %g %f %% %s.",
+ true, 1, 2, 3, 0.4, 0.5, 0.6, "ok");
+ }
+}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 14214b4..4dd3313 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -265,6 +265,7 @@
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.policy.WindowManagerPolicy.ScreenOffListener;
import com.android.server.power.ShutdownThread;
+import com.android.server.protolog.ProtoLogImpl;
import com.android.server.utils.PriorityDump;
import java.io.BufferedWriter;
@@ -5811,6 +5812,11 @@
pw.print(mWindowTracing.getStatus() + "\n");
}
+ private void dumpLogStatus(PrintWriter pw) {
+ pw.println("WINDOW MANAGER LOGGING (dumpsys window logging)");
+ pw.println(ProtoLogImpl.getSingleInstance().getStatus());
+ }
+
private void dumpSessionsLocked(PrintWriter pw, boolean dumpAll) {
pw.println("WINDOW MANAGER SESSIONS (dumpsys window sessions)");
for (int i=0; i<mSessions.size(); i++) {
@@ -6206,6 +6212,9 @@
} else if ("trace".equals(cmd)) {
dumpTraceStatus(pw);
return;
+ } else if ("logging".equals(cmd)) {
+ dumpLogStatus(pw);
+ return;
} else if ("refresh".equals(cmd)) {
dumpHighRefreshRateBlacklist(pw);
return;
@@ -6267,6 +6276,10 @@
if (dumpAll) {
pw.println(separator);
}
+ dumpLogStatus(pw);
+ if (dumpAll) {
+ pw.println(separator);
+ }
dumpHighRefreshRateBlacklist(pw);
}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index 7384bb7..e01cbf2 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -28,6 +28,8 @@
import android.view.IWindowManager;
import android.view.Surface;
+import com.android.server.protolog.ProtoLogImpl;
+
import java.io.PrintWriter;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -75,6 +77,8 @@
// the output trace file, so the shell gets the correct semantics for where
// trace files can be written.
return mInternal.mWindowTracing.onShellCommand(this);
+ case "logging":
+ return ProtoLogImpl.getSingleInstance().onShellCommand(this);
case "set-user-rotation":
return runSetDisplayUserRotation(pw);
case "set-fix-to-user-rotation":
@@ -389,6 +393,8 @@
if (!IS_USER) {
pw.println(" tracing (start | stop)");
pw.println(" Start or stop window tracing.");
+ pw.println(" logging (start | stop | enable | disable | enable-text | disable-text)");
+ pw.println(" Logging settings.");
}
}
}
diff --git a/services/core/java/com/android/server/wm/WindowTracing.java b/services/core/java/com/android/server/wm/WindowTracing.java
index 765e347..bb66530 100644
--- a/services/core/java/com/android/server/wm/WindowTracing.java
+++ b/services/core/java/com/android/server/wm/WindowTracing.java
@@ -19,6 +19,9 @@
import static android.os.Build.IS_USER;
import static com.android.server.wm.WindowManagerTraceFileProto.ENTRY;
+import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER;
+import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER_H;
+import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER_L;
import static com.android.server.wm.WindowManagerTraceProto.ELAPSED_REALTIME_NANOS;
import static com.android.server.wm.WindowManagerTraceProto.WHERE;
import static com.android.server.wm.WindowManagerTraceProto.WINDOW_MANAGER_SERVICE;
@@ -31,6 +34,9 @@
import android.util.proto.ProtoOutputStream;
import android.view.Choreographer;
+import com.android.server.protolog.ProtoLogImpl;
+import com.android.server.utils.TraceBuffer;
+
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
@@ -50,6 +56,7 @@
private static final int BUFFER_CAPACITY_ALL = 4096 * 1024;
private static final String TRACE_FILENAME = "/data/misc/wmtrace/wm_trace.pb";
private static final String TAG = "WindowTracing";
+ private static final long MAGIC_NUMBER_VALUE = ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L;
private final WindowManagerService mService;
private final Choreographer mChoreographer;
@@ -57,7 +64,7 @@
private final Object mEnabledLock = new Object();
private final File mTraceFile;
- private final WindowTraceBuffer mBuffer;
+ private final com.android.server.utils.TraceBuffer mBuffer;
private final Choreographer.FrameCallback mFrameCallback = (frameTimeNanos) ->
log("onFrame" /* where */);
@@ -84,7 +91,7 @@
mService = service;
mGlobalLock = globalLock;
mTraceFile = file;
- mBuffer = new WindowTraceBuffer(bufferCapacity);
+ mBuffer = new TraceBuffer(bufferCapacity);
setLogLevel(WindowTraceLogLevel.TRIM, null /* pw */);
}
@@ -94,6 +101,7 @@
return;
}
synchronized (mEnabledLock) {
+ ProtoLogImpl.getSingleInstance().startProtoLog(pw);
logAndPrintln(pw, "Start tracing to " + mTraceFile + ".");
mBuffer.resetBuffer();
mEnabled = mEnabledLockFree = true;
@@ -132,6 +140,7 @@
logAndPrintln(pw, "Trace written to " + mTraceFile + ".");
}
}
+ ProtoLogImpl.getSingleInstance().stopProtoLog(pw, writeToFile);
}
private void setLogLevel(@WindowTraceLogLevel int logLevel, PrintWriter pw) {
@@ -317,6 +326,7 @@
synchronized (mEnabledLock) {
writeTraceToFileLocked();
}
+ ProtoLogImpl.getSingleInstance().writeProtoLogToFile();
}
private void logAndPrintln(@Nullable PrintWriter pw, String msg) {
@@ -334,11 +344,13 @@
private void writeTraceToFileLocked() {
try {
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "writeTraceToFileLocked");
- mBuffer.writeTraceToFile(mTraceFile);
+ ProtoOutputStream proto = new ProtoOutputStream();
+ proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE);
+ mBuffer.writeTraceToFile(mTraceFile, proto);
} catch (IOException e) {
Log.e(TAG, "Unable to write buffer to file", e);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
}
}
-}
\ No newline at end of file
+}