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/Android.bp b/Android.bp
index bef3251..efddc0a 100644
--- a/Android.bp
+++ b/Android.bp
@@ -244,8 +244,9 @@
},
required: [
- // TODO: remove gps_debug when the build system propagates "required" properly.
+ // TODO: remove gps_debug and protolog.conf.json when the build system propagates "required" properly.
"gps_debug.conf",
+ "protolog.conf.json.gz",
],
}
diff --git a/core/proto/Android.bp b/core/proto/Android.bp
index e199dab..6119d71 100644
--- a/core/proto/Android.bp
+++ b/core/proto/Android.bp
@@ -30,9 +30,9 @@
}
java_library_host {
- name: "windowmanager-log-proto",
+ name: "protolog-proto",
srcs: [
- "android/server/windowmanagerlog.proto"
+ "android/server/protolog.proto"
],
proto: {
type: "full",
diff --git a/core/proto/android/server/windowmanagerlog.proto b/core/proto/android/server/protolog.proto
similarity index 88%
rename from core/proto/android/server/windowmanagerlog.proto
rename to core/proto/android/server/protolog.proto
index 5bee1bd..7c98d31 100644
--- a/core/proto/android/server/windowmanagerlog.proto
+++ b/core/proto/android/server/protolog.proto
@@ -16,7 +16,7 @@
syntax = "proto2";
-package com.android.server.wm;
+package com.android.server.protolog;
option java_multiple_files = true;
@@ -36,16 +36,16 @@
repeated bool boolean_params = 6 [packed=true];
}
-/* represents a log file containing window manager log entries.
- Encoded, it should start with 0x9 0x57 0x49 0x4e 0x44 0x4f 0x4c 0x4f 0x47 (.WINDOLOG), such
+/* represents a log file containing ProtoLog log entries.
+ Encoded, it should start with 0x9 0x50 0x52 0x4f 0x54 0x4f 0x4c 0x4f 0x47 (.PROTOLOG), such
that they can be easily identified. */
-message WindowManagerLogFileProto {
+message ProtoLogFileProto {
/* constant; MAGIC_NUMBER = (long) MAGIC_NUMBER_H << 32 | MagicNumber.MAGIC_NUMBER_L
(this is needed because enums have to be 32 bits and there's no nice way to put 64bit
constants into .proto files. */
enum MagicNumber {
INVALID = 0;
- MAGIC_NUMBER_L = 0x444e4957; /* WIND (little-endian ASCII) */
+ MAGIC_NUMBER_L = 0x544f5250; /* PROT (little-endian ASCII) */
MAGIC_NUMBER_H = 0x474f4c4f; /* OLOG (little-endian ASCII) */
}
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index 4493f3a..7e167c9 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -133,3 +133,8 @@
sub_dir: "permissions",
src: "com.android.timezone.updater.xml",
}
+
+filegroup {
+ name: "services.core.protolog.json",
+ srcs: ["services.core.protolog.json"],
+}
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
new file mode 100644
index 0000000..261891f
--- /dev/null
+++ b/data/etc/services.core.protolog.json
@@ -0,0 +1,15 @@
+{
+ "version": "1.0.0",
+ "messages": {
+ "485522692": {
+ "message": "Test completed successfully: %b %d %o %x %e %g %f %% %s.",
+ "level": "ERROR",
+ "group": "TEST_GROUP"
+ }
+ },
+ "groups": {
+ "TEST_GROUP": {
+ "tag": "WindowManagetProtoLogTest"
+ }
+ }
+}
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/tools/protologtool/src/com/android/protologtool/LogGroup.kt b/services/core/java/com/android/server/protolog/common/BitmaskConversionException.java
similarity index 69%
copy from tools/protologtool/src/com/android/protologtool/LogGroup.kt
copy to services/core/java/com/android/server/protolog/common/BitmaskConversionException.java
index 42a37a2..7bb27b2 100644
--- a/tools/protologtool/src/com/android/protologtool/LogGroup.kt
+++ b/services/core/java/com/android/server/protolog/common/BitmaskConversionException.java
@@ -14,11 +14,13 @@
* limitations under the License.
*/
-package com.android.protologtool
+package com.android.server.protolog.common;
-data class LogGroup(
- val name: String,
- val enabled: Boolean,
- val textEnabled: Boolean,
- val tag: String
-)
+/**
+ * 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/tools/protologtool/src/com/android/protologtool/LogGroup.kt b/services/core/java/com/android/server/protolog/common/InvalidFormatStringException.java
similarity index 61%
copy from tools/protologtool/src/com/android/protologtool/LogGroup.kt
copy to services/core/java/com/android/server/protolog/common/InvalidFormatStringException.java
index 42a37a2..947bf98 100644
--- a/tools/protologtool/src/com/android/protologtool/LogGroup.kt
+++ b/services/core/java/com/android/server/protolog/common/InvalidFormatStringException.java
@@ -14,11 +14,17 @@
* limitations under the License.
*/
-package com.android.protologtool
+package com.android.server.protolog.common;
-data class LogGroup(
- val name: String,
- val enabled: Boolean,
- val textEnabled: Boolean,
- val tag: String
-)
+/**
+ * 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
+}
diff --git a/services/tests/servicestests/src/com/android/server/protolog/ProtoLogImplTest.java b/services/tests/servicestests/src/com/android/server/protolog/ProtoLogImplTest.java
new file mode 100644
index 0000000..7aa3d0d
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/protolog/ProtoLogImplTest.java
@@ -0,0 +1,462 @@
+/*
+ * 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 androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static com.android.server.protolog.ProtoLogImpl.PROTOLOG_VERSION;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.os.SystemClock;
+import android.platform.test.annotations.Presubmit;
+import android.util.proto.ProtoInputStream;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.protolog.common.IProtoLogGroup;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.util.LinkedList;
+
+/**
+ * Test class for {@link ProtoLogImpl}.
+ */
+@SuppressWarnings("ConstantConditions")
+@SmallTest
+@Presubmit
+@RunWith(JUnit4.class)
+public class ProtoLogImplTest {
+
+ private static final byte[] MAGIC_HEADER = new byte[]{
+ 0x9, 0x50, 0x52, 0x4f, 0x54, 0x4f, 0x4c, 0x4f, 0x47
+ };
+
+ private ProtoLogImpl mProtoLog;
+ private File mFile;
+
+ @Mock
+ private ProtoLogViewerConfigReader mReader;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ final Context testContext = getInstrumentation().getContext();
+ mFile = testContext.getFileStreamPath("tracing_test.dat");
+ //noinspection ResultOfMethodCallIgnored
+ mFile.delete();
+ mProtoLog = new ProtoLogImpl(mFile, 1024 * 1024, mReader);
+ }
+
+ @After
+ public void tearDown() {
+ if (mFile != null) {
+ //noinspection ResultOfMethodCallIgnored
+ mFile.delete();
+ }
+ ProtoLogImpl.setSingleInstance(null);
+ }
+
+ @Test
+ public void isEnabled_returnsFalseByDefault() {
+ assertFalse(mProtoLog.isProtoEnabled());
+ }
+
+ @Test
+ public void isEnabled_returnsTrueAfterStart() {
+ mProtoLog.startProtoLog(mock(PrintWriter.class));
+ assertTrue(mProtoLog.isProtoEnabled());
+ }
+
+ @Test
+ public void isEnabled_returnsFalseAfterStop() {
+ mProtoLog.startProtoLog(mock(PrintWriter.class));
+ mProtoLog.stopProtoLog(mock(PrintWriter.class), true);
+ assertFalse(mProtoLog.isProtoEnabled());
+ }
+
+ @Test
+ public void logFile_startsWithMagicHeader() throws Exception {
+ mProtoLog.startProtoLog(mock(PrintWriter.class));
+ mProtoLog.stopProtoLog(mock(PrintWriter.class), true);
+
+ assertTrue("Log file should exist", mFile.exists());
+
+ byte[] header = new byte[MAGIC_HEADER.length];
+ try (InputStream is = new FileInputStream(mFile)) {
+ assertEquals(MAGIC_HEADER.length, is.read(header));
+ assertArrayEquals(MAGIC_HEADER, header);
+ }
+ }
+
+ @Test
+ public void getSingleInstance() {
+ ProtoLogImpl mockedProtoLog = mock(ProtoLogImpl.class);
+ ProtoLogImpl.setSingleInstance(mockedProtoLog);
+ assertSame(mockedProtoLog, ProtoLogImpl.getSingleInstance());
+ }
+
+ @Test
+ public void d_logCalled() {
+ ProtoLogImpl mockedProtoLog = mock(ProtoLogImpl.class);
+ ProtoLogImpl.setSingleInstance(mockedProtoLog);
+ ProtoLogImpl.d(TestProtoLogGroup.TEST_GROUP, 1234, 4321, "test %d");
+ verify(mockedProtoLog).log(eq(ProtoLogImpl.LogLevel.DEBUG), eq(
+ TestProtoLogGroup.TEST_GROUP),
+ eq(1234), eq(4321), eq("test %d"), eq(new Object[]{}));
+ }
+
+ @Test
+ public void v_logCalled() {
+ ProtoLogImpl mockedProtoLog = mock(ProtoLogImpl.class);
+ ProtoLogImpl.setSingleInstance(mockedProtoLog);
+ ProtoLogImpl.v(TestProtoLogGroup.TEST_GROUP, 1234, 4321, "test %d");
+ verify(mockedProtoLog).log(eq(ProtoLogImpl.LogLevel.VERBOSE), eq(
+ TestProtoLogGroup.TEST_GROUP),
+ eq(1234), eq(4321), eq("test %d"), eq(new Object[]{}));
+ }
+
+ @Test
+ public void i_logCalled() {
+ ProtoLogImpl mockedProtoLog = mock(ProtoLogImpl.class);
+ ProtoLogImpl.setSingleInstance(mockedProtoLog);
+ ProtoLogImpl.i(TestProtoLogGroup.TEST_GROUP, 1234, 4321, "test %d");
+ verify(mockedProtoLog).log(eq(ProtoLogImpl.LogLevel.INFO), eq(
+ TestProtoLogGroup.TEST_GROUP),
+ eq(1234), eq(4321), eq("test %d"), eq(new Object[]{}));
+ }
+
+ @Test
+ public void w_logCalled() {
+ ProtoLogImpl mockedProtoLog = mock(ProtoLogImpl.class);
+ ProtoLogImpl.setSingleInstance(mockedProtoLog);
+ ProtoLogImpl.w(TestProtoLogGroup.TEST_GROUP, 1234,
+ 4321, "test %d");
+ verify(mockedProtoLog).log(eq(ProtoLogImpl.LogLevel.WARN), eq(
+ TestProtoLogGroup.TEST_GROUP),
+ eq(1234), eq(4321), eq("test %d"), eq(new Object[]{}));
+ }
+
+ @Test
+ public void e_logCalled() {
+ ProtoLogImpl mockedProtoLog = mock(ProtoLogImpl.class);
+ ProtoLogImpl.setSingleInstance(mockedProtoLog);
+ ProtoLogImpl.e(TestProtoLogGroup.TEST_GROUP, 1234, 4321, "test %d");
+ verify(mockedProtoLog).log(eq(ProtoLogImpl.LogLevel.ERROR), eq(
+ TestProtoLogGroup.TEST_GROUP),
+ eq(1234), eq(4321), eq("test %d"), eq(new Object[]{}));
+ }
+
+ @Test
+ public void wtf_logCalled() {
+ ProtoLogImpl mockedProtoLog = mock(ProtoLogImpl.class);
+ ProtoLogImpl.setSingleInstance(mockedProtoLog);
+ ProtoLogImpl.wtf(TestProtoLogGroup.TEST_GROUP,
+ 1234, 4321, "test %d");
+ verify(mockedProtoLog).log(eq(ProtoLogImpl.LogLevel.WTF), eq(
+ TestProtoLogGroup.TEST_GROUP),
+ eq(1234), eq(4321), eq("test %d"), eq(new Object[]{}));
+ }
+
+ @Test
+ public void log_logcatEnabledExternalMessage() {
+ when(mReader.getViewerString(anyInt())).thenReturn("test %b %d %% %o %x %e %g %s %f");
+ ProtoLogImpl implSpy = Mockito.spy(mProtoLog);
+ TestProtoLogGroup.TEST_GROUP.setLogToLogcat(true);
+ TestProtoLogGroup.TEST_GROUP.setLogToProto(false);
+
+ implSpy.log(
+ ProtoLogImpl.LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234, 4321, null,
+ new Object[]{true, 10000, 20000, 30000, 0.0001, 0.00002, "test", 0.000003});
+
+ verify(implSpy).passToLogcat(eq(TestProtoLogGroup.TEST_GROUP.getTag()), eq(
+ ProtoLogImpl.LogLevel.INFO),
+ eq("test true 10000 % 47040 7530 1.000000e-04 2.00000e-05 test 0.000003"));
+ verify(mReader).getViewerString(eq(1234));
+ }
+
+ @Test
+ public void log_logcatEnabledInvalidMessage() {
+ when(mReader.getViewerString(anyInt())).thenReturn("test %b %d %% %o %x %e %g %s %f");
+ ProtoLogImpl implSpy = Mockito.spy(mProtoLog);
+ TestProtoLogGroup.TEST_GROUP.setLogToLogcat(true);
+ TestProtoLogGroup.TEST_GROUP.setLogToProto(false);
+
+ implSpy.log(
+ ProtoLogImpl.LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234, 4321, null,
+ new Object[]{true, 10000, 0.0001, 0.00002, "test"});
+
+ verify(implSpy).passToLogcat(eq(TestProtoLogGroup.TEST_GROUP.getTag()), eq(
+ ProtoLogImpl.LogLevel.INFO),
+ eq("UNKNOWN MESSAGE (1234) true 10000 1.0E-4 2.0E-5 test"));
+ verify(mReader).getViewerString(eq(1234));
+ }
+
+ @Test
+ public void log_logcatEnabledInlineMessage() {
+ when(mReader.getViewerString(anyInt())).thenReturn("test %d");
+ ProtoLogImpl implSpy = Mockito.spy(mProtoLog);
+ TestProtoLogGroup.TEST_GROUP.setLogToLogcat(true);
+ TestProtoLogGroup.TEST_GROUP.setLogToProto(false);
+
+ implSpy.log(
+ ProtoLogImpl.LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234, 4321, "test %d",
+ new Object[]{5});
+
+ verify(implSpy).passToLogcat(eq(TestProtoLogGroup.TEST_GROUP.getTag()), eq(
+ ProtoLogImpl.LogLevel.INFO), eq("test 5"));
+ verify(mReader, never()).getViewerString(anyInt());
+ }
+
+ @Test
+ public void log_logcatEnabledNoMessage() {
+ when(mReader.getViewerString(anyInt())).thenReturn(null);
+ ProtoLogImpl implSpy = Mockito.spy(mProtoLog);
+ TestProtoLogGroup.TEST_GROUP.setLogToLogcat(true);
+ TestProtoLogGroup.TEST_GROUP.setLogToProto(false);
+
+ implSpy.log(
+ ProtoLogImpl.LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234, 4321, null,
+ new Object[]{5});
+
+ verify(implSpy).passToLogcat(eq(TestProtoLogGroup.TEST_GROUP.getTag()), eq(
+ ProtoLogImpl.LogLevel.INFO), eq("UNKNOWN MESSAGE (1234) 5"));
+ verify(mReader).getViewerString(eq(1234));
+ }
+
+ @Test
+ public void log_logcatDisabled() {
+ when(mReader.getViewerString(anyInt())).thenReturn("test %d");
+ ProtoLogImpl implSpy = Mockito.spy(mProtoLog);
+ TestProtoLogGroup.TEST_GROUP.setLogToLogcat(false);
+ TestProtoLogGroup.TEST_GROUP.setLogToProto(false);
+
+ implSpy.log(
+ ProtoLogImpl.LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234, 4321, "test %d",
+ new Object[]{5});
+
+ verify(implSpy, never()).passToLogcat(any(), any(), any());
+ verify(mReader, never()).getViewerString(anyInt());
+ }
+
+ private static class ProtoLogData {
+ Integer mMessageHash = null;
+ Long mElapsedTime = null;
+ LinkedList<String> mStrParams = new LinkedList<>();
+ LinkedList<Long> mSint64Params = new LinkedList<>();
+ LinkedList<Double> mDoubleParams = new LinkedList<>();
+ LinkedList<Boolean> mBooleanParams = new LinkedList<>();
+ }
+
+ private ProtoLogData readProtoLogSingle(ProtoInputStream ip) throws IOException {
+ while (ip.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ if (ip.getFieldNumber() == (int) ProtoLogFileProto.VERSION) {
+ assertEquals(PROTOLOG_VERSION, ip.readString(ProtoLogFileProto.VERSION));
+ continue;
+ }
+ if (ip.getFieldNumber() != (int) ProtoLogFileProto.LOG) {
+ continue;
+ }
+ long token = ip.start(ProtoLogFileProto.LOG);
+ ProtoLogData data = new ProtoLogData();
+ while (ip.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (ip.getFieldNumber()) {
+ case (int) ProtoLogMessage.MESSAGE_HASH: {
+ data.mMessageHash = ip.readInt(ProtoLogMessage.MESSAGE_HASH);
+ break;
+ }
+ case (int) ProtoLogMessage.ELAPSED_REALTIME_NANOS: {
+ data.mElapsedTime = ip.readLong(ProtoLogMessage.ELAPSED_REALTIME_NANOS);
+ break;
+ }
+ case (int) ProtoLogMessage.STR_PARAMS: {
+ data.mStrParams.add(ip.readString(ProtoLogMessage.STR_PARAMS));
+ break;
+ }
+ case (int) ProtoLogMessage.SINT64_PARAMS: {
+ data.mSint64Params.add(ip.readLong(ProtoLogMessage.SINT64_PARAMS));
+ break;
+ }
+ case (int) ProtoLogMessage.DOUBLE_PARAMS: {
+ data.mDoubleParams.add(ip.readDouble(ProtoLogMessage.DOUBLE_PARAMS));
+ break;
+ }
+ case (int) ProtoLogMessage.BOOLEAN_PARAMS: {
+ data.mBooleanParams.add(ip.readBoolean(ProtoLogMessage.BOOLEAN_PARAMS));
+ break;
+ }
+ }
+ }
+ ip.end(token);
+ return data;
+ }
+ return null;
+ }
+
+ @Test
+ public void log_protoEnabled() throws Exception {
+ TestProtoLogGroup.TEST_GROUP.setLogToLogcat(false);
+ TestProtoLogGroup.TEST_GROUP.setLogToProto(true);
+ mProtoLog.startProtoLog(mock(PrintWriter.class));
+ long before = SystemClock.elapsedRealtimeNanos();
+ mProtoLog.log(
+ ProtoLogImpl.LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234,
+ 0b1110101001010100, null,
+ new Object[]{"test", 1, 2, 3, 0.4, 0.5, 0.6, true});
+ long after = SystemClock.elapsedRealtimeNanos();
+ mProtoLog.stopProtoLog(mock(PrintWriter.class), true);
+ try (InputStream is = new FileInputStream(mFile)) {
+ ProtoInputStream ip = new ProtoInputStream(is);
+ ProtoLogData data = readProtoLogSingle(ip);
+ assertNotNull(data);
+ assertEquals(1234, data.mMessageHash.longValue());
+ assertTrue(before < data.mElapsedTime && data.mElapsedTime < after);
+ assertArrayEquals(new String[]{"test"}, data.mStrParams.toArray());
+ assertArrayEquals(new Long[]{1L, 2L, 3L}, data.mSint64Params.toArray());
+ assertArrayEquals(new Double[]{0.4, 0.5, 0.6}, data.mDoubleParams.toArray());
+ assertArrayEquals(new Boolean[]{true}, data.mBooleanParams.toArray());
+ }
+ }
+
+ @Test
+ public void log_invalidParamsMask() throws Exception {
+ TestProtoLogGroup.TEST_GROUP.setLogToLogcat(false);
+ TestProtoLogGroup.TEST_GROUP.setLogToProto(true);
+ mProtoLog.startProtoLog(mock(PrintWriter.class));
+ long before = SystemClock.elapsedRealtimeNanos();
+ mProtoLog.log(
+ ProtoLogImpl.LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234,
+ 0b01100100, null,
+ new Object[]{"test", 1, 0.1, true});
+ long after = SystemClock.elapsedRealtimeNanos();
+ mProtoLog.stopProtoLog(mock(PrintWriter.class), true);
+ try (InputStream is = new FileInputStream(mFile)) {
+ ProtoInputStream ip = new ProtoInputStream(is);
+ ProtoLogData data = readProtoLogSingle(ip);
+ assertNotNull(data);
+ assertEquals(1234, data.mMessageHash.longValue());
+ assertTrue(before < data.mElapsedTime && data.mElapsedTime < after);
+ assertArrayEquals(new String[]{"test", "(INVALID PARAMS_MASK) true"},
+ data.mStrParams.toArray());
+ assertArrayEquals(new Long[]{1L}, data.mSint64Params.toArray());
+ assertArrayEquals(new Double[]{0.1}, data.mDoubleParams.toArray());
+ assertArrayEquals(new Boolean[]{}, data.mBooleanParams.toArray());
+ }
+ }
+
+ @Test
+ public void log_protoDisabled() throws Exception {
+ TestProtoLogGroup.TEST_GROUP.setLogToLogcat(false);
+ TestProtoLogGroup.TEST_GROUP.setLogToProto(false);
+ mProtoLog.startProtoLog(mock(PrintWriter.class));
+ mProtoLog.log(ProtoLogImpl.LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234,
+ 0b11, null, new Object[]{true});
+ mProtoLog.stopProtoLog(mock(PrintWriter.class), true);
+ try (InputStream is = new FileInputStream(mFile)) {
+ ProtoInputStream ip = new ProtoInputStream(is);
+ ProtoLogData data = readProtoLogSingle(ip);
+ assertNull(data);
+ }
+ }
+
+ private enum TestProtoLogGroup implements IProtoLogGroup {
+ 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
+ */
+ TestProtoLogGroup(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;
+ }
+
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/protolog/ProtoLogViewerConfigReaderTest.java b/services/tests/servicestests/src/com/android/server/protolog/ProtoLogViewerConfigReaderTest.java
new file mode 100644
index 0000000..0254055
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/protolog/ProtoLogViewerConfigReaderTest.java
@@ -0,0 +1,114 @@
+/*
+ * 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 org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.util.zip.GZIPOutputStream;
+
+@SmallTest
+@Presubmit
+@RunWith(JUnit4.class)
+public class ProtoLogViewerConfigReaderTest {
+ private static final String TEST_VIEWER_CONFIG = "{\n"
+ + " \"version\": \"1.0.0\",\n"
+ + " \"messages\": {\n"
+ + " \"70933285\": {\n"
+ + " \"message\": \"Test completed successfully: %b\",\n"
+ + " \"level\": \"ERROR\",\n"
+ + " \"group\": \"GENERIC_WM\"\n"
+ + " },\n"
+ + " \"1792430067\": {\n"
+ + " \"message\": \"Attempted to add window to a display that does not exist: %d."
+ + " Aborting.\",\n"
+ + " \"level\": \"WARN\",\n"
+ + " \"group\": \"GENERIC_WM\"\n"
+ + " },\n"
+ + " \"1352021864\": {\n"
+ + " \"message\": \"Test 2\",\n"
+ + " \"level\": \"WARN\",\n"
+ + " \"group\": \"GENERIC_WM\"\n"
+ + " },\n"
+ + " \"409412266\": {\n"
+ + " \"message\": \"Window %s is already added\",\n"
+ + " \"level\": \"WARN\",\n"
+ + " \"group\": \"GENERIC_WM\"\n"
+ + " }\n"
+ + " },\n"
+ + " \"groups\": {\n"
+ + " \"GENERIC_WM\": {\n"
+ + " \"tag\": \"WindowManager\"\n"
+ + " }\n"
+ + " }\n"
+ + "}\n";
+
+
+ private ProtoLogViewerConfigReader
+ mConfig = new ProtoLogViewerConfigReader();
+ private File mTestViewerConfig;
+
+ @Before
+ public void setUp() throws IOException {
+ mTestViewerConfig = File.createTempFile("testConfig", ".json.gz");
+ OutputStreamWriter writer = new OutputStreamWriter(
+ new GZIPOutputStream(new FileOutputStream(mTestViewerConfig)));
+ writer.write(TEST_VIEWER_CONFIG);
+ writer.close();
+ }
+
+ @After
+ public void tearDown() {
+ //noinspection ResultOfMethodCallIgnored
+ mTestViewerConfig.delete();
+ }
+
+ @Test
+ public void getViewerString_notLoaded() {
+ assertNull(mConfig.getViewerString(1));
+ }
+
+ @Test
+ public void loadViewerConfig() {
+ mConfig.loadViewerConfig(null, mTestViewerConfig.getAbsolutePath());
+ assertEquals("Test completed successfully: %b", mConfig.getViewerString(70933285));
+ assertEquals("Test 2", mConfig.getViewerString(1352021864));
+ assertEquals("Window %s is already added", mConfig.getViewerString(409412266));
+ assertNull(mConfig.getViewerString(1));
+ }
+
+ @Test
+ public void loadViewerConfig_invalidFile() {
+ mConfig.loadViewerConfig(null, "/tmp/unknown/file/does/not/exist");
+ // No exception is thrown.
+ assertNull(mConfig.getViewerString(1));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/protolog/common/LogDataTypeTest.java b/services/tests/servicestests/src/com/android/server/protolog/common/LogDataTypeTest.java
new file mode 100644
index 0000000..4c7f5fd
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/protolog/common/LogDataTypeTest.java
@@ -0,0 +1,85 @@
+/*
+ * 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 static org.junit.Assert.assertEquals;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+@SmallTest
+@Presubmit
+@RunWith(JUnit4.class)
+public class LogDataTypeTest {
+ @Test
+ public void parseFormatString() {
+ String str = "%b %d %o %x %f %e %g %s %%";
+ List<Integer> out = LogDataType.parseFormatString(str);
+ assertEquals(Arrays.asList(
+ LogDataType.BOOLEAN,
+ LogDataType.LONG,
+ LogDataType.LONG,
+ LogDataType.LONG,
+ LogDataType.DOUBLE,
+ LogDataType.DOUBLE,
+ LogDataType.DOUBLE,
+ LogDataType.STRING
+ ), out);
+ }
+
+ @Test(expected = InvalidFormatStringException.class)
+ public void parseFormatString_invalid() {
+ String str = "%q";
+ LogDataType.parseFormatString(str);
+ }
+
+ @Test
+ public void logDataTypesToBitMask() {
+ List<Integer> types = Arrays.asList(LogDataType.STRING, LogDataType.DOUBLE,
+ LogDataType.LONG, LogDataType.BOOLEAN);
+ int mask = LogDataType.logDataTypesToBitMask(types);
+ assertEquals(0b11011000, mask);
+ }
+
+ @Test(expected = BitmaskConversionException.class)
+ public void logDataTypesToBitMask_toManyParams() {
+ ArrayList<Integer> types = new ArrayList<>();
+ for (int i = 0; i <= 16; i++) {
+ types.add(LogDataType.STRING);
+ }
+ LogDataType.logDataTypesToBitMask(types);
+ }
+
+ @Test
+ public void bitmaskToLogDataTypes() {
+ int bitmask = 0b11011000;
+ List<Integer> types = Arrays.asList(LogDataType.STRING, LogDataType.DOUBLE,
+ LogDataType.LONG, LogDataType.BOOLEAN);
+ for (int i = 0; i < types.size(); i++) {
+ assertEquals(types.get(i).intValue(), LogDataType.bitmaskToLogDataType(bitmask, i));
+ }
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTraceBufferTest.java b/services/tests/servicestests/src/com/android/server/utils/TraceBufferTest.java
similarity index 95%
rename from services/tests/wmtests/src/com/android/server/wm/WindowTraceBufferTest.java
rename to services/tests/servicestests/src/com/android/server/utils/TraceBufferTest.java
index b299f0d..09b75e7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTraceBufferTest.java
+++ b/services/tests/servicestests/src/com/android/server/utils/TraceBufferTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.wm;
+package com.android.server.utils;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
@@ -39,16 +39,16 @@
/**
- * Test class for {@link WindowTraceBuffer}.
+ * Test class for {@link TraceBuffer}.
*
* Build/Install/Run:
- * atest WmTests:WindowTraceBufferTest
+ * atest WmTests:TraceBufferTest
*/
@SmallTest
@Presubmit
-public class WindowTraceBufferTest {
+public class TraceBufferTest {
private File mFile;
- private WindowTraceBuffer mBuffer;
+ private TraceBuffer mBuffer;
@Before
public void setUp() throws Exception {
@@ -56,7 +56,7 @@
mFile = testContext.getFileStreamPath("tracing_test.dat");
mFile.delete();
- mBuffer = new WindowTraceBuffer(10);
+ mBuffer = new TraceBuffer(10);
}
@After
diff --git a/services/tests/wmtests/src/com/android/server/wm/ProtoLogIntegrationTest.java b/services/tests/wmtests/src/com/android/server/wm/ProtoLogIntegrationTest.java
new file mode 100644
index 0000000..8eecff5
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/ProtoLogIntegrationTest.java
@@ -0,0 +1,56 @@
+/*
+ * 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 static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.protolog.ProtoLogImpl;
+
+import org.junit.After;
+import org.junit.Test;
+
+/**
+ * Check if the ProtoLogTools is used to process the WindowManager source code.
+ */
+@SmallTest
+@Presubmit
+public class ProtoLogIntegrationTest {
+ @After
+ public void tearDown() {
+ ProtoLogImpl.setSingleInstance(null);
+ }
+
+ @Test
+ public void testProtoLogToolIntegration() {
+ ProtoLogImpl mockedProtoLog = mock(ProtoLogImpl.class);
+ ProtoLogImpl.setSingleInstance(mockedProtoLog);
+ ProtoLogGroup.testProtoLog();
+ verify(mockedProtoLog).log(eq(ProtoLogImpl.LogLevel.ERROR), eq(
+ ProtoLogGroup.TEST_GROUP),
+ eq(485522692), eq(0b0010101001010111),
+ eq(ProtoLogGroup.TEST_GROUP.isLogToLogcat()
+ ? "Test completed successfully: %b %d %o %x %e %g %f %% %s"
+ : null),
+ eq(new Object[]{true, 1L, 2L, 3L, 0.4, 0.5, 0.6, "ok"}));
+ }
+}
diff --git a/tools/protologtool/Android.bp b/tools/protologtool/Android.bp
index a86c226..d1a86c2 100644
--- a/tools/protologtool/Android.bp
+++ b/tools/protologtool/Android.bp
@@ -1,13 +1,21 @@
+java_library_host {
+ name: "protologtool-lib",
+ srcs: [
+ "src/com/android/protolog/tool/**/*.kt",
+ ],
+ static_libs: [
+ "protolog-common",
+ "javaparser",
+ "protolog-proto",
+ "jsonlib",
+ ],
+}
+
java_binary_host {
name: "protologtool",
manifest: "manifest.txt",
- srcs: [
- "src/**/*.kt",
- ],
static_libs: [
- "javaparser",
- "windowmanager-log-proto",
- "jsonlib",
+ "protologtool-lib",
],
}
@@ -15,13 +23,10 @@
name: "protologtool-tests",
test_suites: ["general-tests"],
srcs: [
- "src/**/*.kt",
"tests/**/*.kt",
],
static_libs: [
- "javaparser",
- "windowmanager-log-proto",
- "jsonlib",
+ "protologtool-lib",
"junit",
"mockito",
],
diff --git a/tools/protologtool/manifest.txt b/tools/protologtool/manifest.txt
index f5e53c4..cabebd5 100644
--- a/tools/protologtool/manifest.txt
+++ b/tools/protologtool/manifest.txt
@@ -1 +1 @@
-Main-class: com.android.protologtool.ProtoLogTool
+Main-class: com.android.protolog.tool.ProtoLogTool
diff --git a/tools/protologtool/src/com/android/protolog/tool/CodeUtils.kt b/tools/protologtool/src/com/android/protolog/tool/CodeUtils.kt
new file mode 100644
index 0000000..5c92161
--- /dev/null
+++ b/tools/protologtool/src/com/android/protolog/tool/CodeUtils.kt
@@ -0,0 +1,74 @@
+/*
+ * 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.protolog.tool
+
+import com.github.javaparser.ast.CompilationUnit
+import com.github.javaparser.ast.ImportDeclaration
+import com.github.javaparser.ast.expr.BinaryExpr
+import com.github.javaparser.ast.expr.Expression
+import com.github.javaparser.ast.expr.StringLiteralExpr
+
+object CodeUtils {
+ /**
+ * Returns a stable hash of a string.
+ * We reimplement String::hashCode() for readability reasons.
+ */
+ fun hash(str: String, level: LogLevel): Int {
+ return (level.name + str).map { c -> c.toInt() }.reduce { h, c -> h * 31 + c }
+ }
+
+ fun isWildcardStaticImported(code: CompilationUnit, className: String): Boolean {
+ return code.findAll(ImportDeclaration::class.java)
+ .any { im -> im.isStatic && im.isAsterisk && im.name.toString() == className }
+ }
+
+ fun isClassImportedOrSamePackage(code: CompilationUnit, className: String): Boolean {
+ val packageName = className.substringBeforeLast('.')
+ return code.packageDeclaration.isPresent &&
+ code.packageDeclaration.get().nameAsString == packageName ||
+ code.findAll(ImportDeclaration::class.java)
+ .any { im ->
+ !im.isStatic &&
+ ((!im.isAsterisk && im.name.toString() == className) ||
+ (im.isAsterisk && im.name.toString() == packageName))
+ }
+ }
+
+ fun staticallyImportedMethods(code: CompilationUnit, className: String): Set<String> {
+ return code.findAll(ImportDeclaration::class.java)
+ .filter { im ->
+ im.isStatic &&
+ im.name.toString().substringBeforeLast('.') == className
+ }
+ .map { im -> im.name.toString().substringAfterLast('.') }.toSet()
+ }
+
+ fun concatMultilineString(expr: Expression): String {
+ return when (expr) {
+ is StringLiteralExpr -> expr.asString()
+ is BinaryExpr -> when {
+ expr.operator == BinaryExpr.Operator.PLUS ->
+ concatMultilineString(expr.left) + concatMultilineString(expr.right)
+ else -> throw InvalidProtoLogCallException(
+ "messageString must be a string literal " +
+ "or concatenation of string literals.", expr)
+ }
+ else -> throw InvalidProtoLogCallException("messageString must be a string literal " +
+ "or concatenation of string literals.", expr)
+ }
+ }
+}
diff --git a/tools/protologtool/src/com/android/protologtool/CommandOptions.kt b/tools/protologtool/src/com/android/protolog/tool/CommandOptions.kt
similarity index 99%
rename from tools/protologtool/src/com/android/protologtool/CommandOptions.kt
rename to tools/protologtool/src/com/android/protolog/tool/CommandOptions.kt
index df49e15..3dfa4d2 100644
--- a/tools/protologtool/src/com/android/protologtool/CommandOptions.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/CommandOptions.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.protologtool
+package com.android.protolog.tool
import java.util.regex.Pattern
diff --git a/tools/protologtool/src/com/android/protologtool/Constants.kt b/tools/protologtool/src/com/android/protolog/tool/Constants.kt
similarity index 81%
rename from tools/protologtool/src/com/android/protologtool/Constants.kt
rename to tools/protologtool/src/com/android/protolog/tool/Constants.kt
index 2ccfc4d..83b3c00 100644
--- a/tools/protologtool/src/com/android/protologtool/Constants.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/Constants.kt
@@ -14,14 +14,11 @@
* limitations under the License.
*/
-package com.android.protologtool
+package com.android.protolog.tool
object Constants {
const val NAME = "protologtool"
const val VERSION = "1.0.0"
- const val IS_ENABLED_METHOD = "isEnabled"
- const val IS_LOG_TO_LOGCAT_METHOD = "isLogToLogcat"
const val IS_LOG_TO_ANY_METHOD = "isLogToAny"
- const val GET_TAG_METHOD = "getTag"
const val ENUM_VALUES_METHOD = "values"
}
diff --git a/tools/protologtool/src/com/android/protologtool/LogGroup.kt b/tools/protologtool/src/com/android/protolog/tool/LogGroup.kt
similarity index 95%
rename from tools/protologtool/src/com/android/protologtool/LogGroup.kt
rename to tools/protologtool/src/com/android/protolog/tool/LogGroup.kt
index 42a37a2..587f7b9 100644
--- a/tools/protologtool/src/com/android/protologtool/LogGroup.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/LogGroup.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.protologtool
+package com.android.protolog.tool
data class LogGroup(
val name: String,
diff --git a/tools/protologtool/src/com/android/protologtool/LogLevel.kt b/tools/protologtool/src/com/android/protolog/tool/LogLevel.kt
similarity index 96%
rename from tools/protologtool/src/com/android/protologtool/LogLevel.kt
rename to tools/protologtool/src/com/android/protolog/tool/LogLevel.kt
index dc29557..7759f35 100644
--- a/tools/protologtool/src/com/android/protologtool/LogLevel.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/LogLevel.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.protologtool
+package com.android.protolog.tool
import com.github.javaparser.ast.Node
diff --git a/tools/protologtool/src/com/android/protologtool/LogParser.kt b/tools/protologtool/src/com/android/protolog/tool/LogParser.kt
similarity index 81%
rename from tools/protologtool/src/com/android/protologtool/LogParser.kt
rename to tools/protologtool/src/com/android/protolog/tool/LogParser.kt
index 4d0eb0e..a59038f 100644
--- a/tools/protologtool/src/com/android/protologtool/LogParser.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/LogParser.kt
@@ -14,11 +14,13 @@
* limitations under the License.
*/
-package com.android.protologtool
+package com.android.protolog.tool
import com.android.json.stream.JsonReader
-import com.android.server.wm.ProtoLogMessage
-import com.android.server.wm.WindowManagerLogFileProto
+import com.android.server.protolog.common.InvalidFormatStringException
+import com.android.server.protolog.common.LogDataType
+import com.android.server.protolog.ProtoLogMessage
+import com.android.server.protolog.ProtoLogFileProto
import java.io.BufferedReader
import java.io.InputStream
import java.io.InputStreamReader
@@ -36,8 +38,8 @@
companion object {
private val dateFormat = SimpleDateFormat("MM-dd HH:mm:ss.SSS", Locale.US)
private val magicNumber =
- WindowManagerLogFileProto.MagicNumber.MAGIC_NUMBER_H.number.toLong() shl 32 or
- WindowManagerLogFileProto.MagicNumber.MAGIC_NUMBER_L.number.toLong()
+ ProtoLogFileProto.MagicNumber.MAGIC_NUMBER_H.number.toLong() shl 32 or
+ ProtoLogFileProto.MagicNumber.MAGIC_NUMBER_L.number.toLong()
}
private fun printTime(time: Long, offset: Long, ps: PrintStream) {
@@ -55,14 +57,15 @@
val boolParamsIt = protoLogMessage.booleanParamsList.iterator()
val args = mutableListOf<Any>()
val format = configEntry.messageString
- val argTypes = CodeUtils.parseFormatString(format)
+ val argTypes = LogDataType.parseFormatString(format)
try {
argTypes.forEach {
when (it) {
- CodeUtils.LogDataTypes.BOOLEAN -> args.add(boolParamsIt.next())
- CodeUtils.LogDataTypes.LONG -> args.add(longParamsIt.next())
- CodeUtils.LogDataTypes.DOUBLE -> args.add(doubleParamsIt.next())
- CodeUtils.LogDataTypes.STRING -> args.add(strParmIt.next())
+ LogDataType.BOOLEAN -> args.add(boolParamsIt.next())
+ LogDataType.LONG -> args.add(longParamsIt.next())
+ LogDataType.DOUBLE -> args.add(doubleParamsIt.next())
+ LogDataType.STRING -> args.add(strParmIt.next())
+ null -> throw NullPointerException()
}
}
} catch (ex: NoSuchElementException) {
@@ -85,7 +88,7 @@
fun parse(protoLogInput: InputStream, jsonConfigInput: InputStream, ps: PrintStream) {
val jsonReader = JsonReader(BufferedReader(InputStreamReader(jsonConfigInput)))
val config = configParser.parseConfig(jsonReader)
- val protoLog = WindowManagerLogFileProto.parseFrom(protoLogInput)
+ val protoLog = ProtoLogFileProto.parseFrom(protoLogInput)
if (protoLog.magicNumber != magicNumber) {
throw InvalidInputException("ProtoLog file magic number is invalid.")
diff --git a/tools/protologtool/src/com/android/protologtool/ProtoLogCallProcessor.kt b/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallProcessor.kt
similarity index 99%
rename from tools/protologtool/src/com/android/protologtool/ProtoLogCallProcessor.kt
rename to tools/protologtool/src/com/android/protolog/tool/ProtoLogCallProcessor.kt
index 29d8ae5..eae6396 100644
--- a/tools/protologtool/src/com/android/protologtool/ProtoLogCallProcessor.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallProcessor.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.protologtool
+package com.android.protolog.tool
import com.github.javaparser.ast.CompilationUnit
import com.github.javaparser.ast.expr.Expression
diff --git a/tools/protologtool/src/com/android/protologtool/ProtoLogCallVisitor.kt b/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallVisitor.kt
similarity index 95%
rename from tools/protologtool/src/com/android/protologtool/ProtoLogCallVisitor.kt
rename to tools/protologtool/src/com/android/protolog/tool/ProtoLogCallVisitor.kt
index 42a75f8..aa58b69 100644
--- a/tools/protologtool/src/com/android/protologtool/ProtoLogCallVisitor.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallVisitor.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.protologtool
+package com.android.protolog.tool
import com.github.javaparser.ast.expr.MethodCallExpr
diff --git a/tools/protologtool/src/com/android/protologtool/ProtoLogGroupReader.kt b/tools/protologtool/src/com/android/protolog/tool/ProtoLogGroupReader.kt
similarity index 61%
rename from tools/protologtool/src/com/android/protologtool/ProtoLogGroupReader.kt
rename to tools/protologtool/src/com/android/protolog/tool/ProtoLogGroupReader.kt
index 664c8a6..75493b6 100644
--- a/tools/protologtool/src/com/android/protologtool/ProtoLogGroupReader.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/ProtoLogGroupReader.kt
@@ -14,14 +14,11 @@
* limitations under the License.
*/
-package com.android.protologtool
+package com.android.protolog.tool
-import com.android.protologtool.Constants.ENUM_VALUES_METHOD
-import com.android.protologtool.Constants.GET_TAG_METHOD
-import com.android.protologtool.Constants.IS_ENABLED_METHOD
-import com.android.protologtool.Constants.IS_LOG_TO_LOGCAT_METHOD
+import com.android.protolog.tool.Constants.ENUM_VALUES_METHOD
+import com.android.server.protolog.common.IProtoLogGroup
import java.io.File
-import java.lang.RuntimeException
import java.net.URLClassLoader
class ProtoLogGroupReader {
@@ -31,18 +28,10 @@
return URLClassLoader(arrayOf(url), ProtoLogGroupReader::class.java.classLoader)
}
- private fun getEnumValues(clazz: Class<*>): List<Enum<*>> {
+ private fun getEnumValues(clazz: Class<*>): List<IProtoLogGroup> {
val valuesMethod = clazz.getMethod(ENUM_VALUES_METHOD)
@Suppress("UNCHECKED_CAST")
- return (valuesMethod.invoke(null) as Array<Enum<*>>).toList()
- }
-
- private fun getLogGroupFromEnumValue(group: Any, clazz: Class<*>): LogGroup {
- val enabled = clazz.getMethod(IS_ENABLED_METHOD).invoke(group) as Boolean
- val textEnabled = clazz.getMethod(IS_LOG_TO_LOGCAT_METHOD).invoke(group) as Boolean
- val tag = clazz.getMethod(GET_TAG_METHOD).invoke(group) as String
- val name = (group as Enum<*>).name
- return LogGroup(name, enabled, textEnabled, tag)
+ return (valuesMethod.invoke(null) as Array<IProtoLogGroup>).toList()
}
fun loadFromJar(jarPath: String, className: String): Map<String, LogGroup> {
@@ -51,7 +40,8 @@
val clazz = classLoader.loadClass(className)
val values = getEnumValues(clazz)
return values.map { group ->
- group.name to getLogGroupFromEnumValue(group, clazz)
+ group.name() to
+ LogGroup(group.name(), group.isEnabled, group.isLogToLogcat, group.tag)
}.toMap()
} catch (ex: ReflectiveOperationException) {
throw RuntimeException("Unable to load ProtoLogGroup enum class", ex)
diff --git a/tools/protologtool/src/com/android/protologtool/ProtoLogTool.kt b/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt
similarity index 81%
rename from tools/protologtool/src/com/android/protologtool/ProtoLogTool.kt
rename to tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt
index 618e4b14..9678ec3 100644
--- a/tools/protologtool/src/com/android/protologtool/ProtoLogTool.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-package com.android.protologtool
+package com.android.protolog.tool
-import com.android.protologtool.CommandOptions.Companion.USAGE
+import com.android.protolog.tool.CommandOptions.Companion.USAGE
import com.github.javaparser.StaticJavaParser
import java.io.File
import java.io.FileInputStream
@@ -31,6 +31,11 @@
exitProcess(-1)
}
+ private fun containsProtoLogText(source: String, protoLogClassName: String): Boolean {
+ val protoLogSimpleClassName = protoLogClassName.substringAfterLast('.')
+ return source.contains(protoLogSimpleClassName)
+ }
+
private fun processClasses(command: CommandOptions) {
val groups = ProtoLogGroupReader()
.loadFromJar(command.protoLogGroupsJarArg, command.protoLogGroupsClassNameArg)
@@ -44,7 +49,11 @@
val file = File(path)
val text = file.readText()
val code = StaticJavaParser.parse(text)
- val outSrc = transformer.processClass(text, code)
+ val outSrc = when {
+ containsProtoLogText(text, command.protoLogClassNameArg) ->
+ transformer.processClass(text, code)
+ else -> text
+ }
val pack = if (code.packageDeclaration.isPresent) code.packageDeclaration
.get().nameAsString else ""
val newPath = pack.replace('.', '/') + '/' + file.name
@@ -65,14 +74,17 @@
val builder = ViewerConfigBuilder(processor)
command.javaSourceArgs.forEach { path ->
val file = File(path)
- builder.processClass(StaticJavaParser.parse(file))
+ val text = file.readText()
+ if (containsProtoLogText(text, command.protoLogClassNameArg)) {
+ builder.processClass(StaticJavaParser.parse(text))
+ }
}
val out = FileOutputStream(command.viewerConfigJsonArg)
out.write(builder.build().toByteArray())
out.close()
}
- fun read(command: CommandOptions) {
+ private fun read(command: CommandOptions) {
LogParser(ViewerConfigParser())
.parse(FileInputStream(command.logProtofileArg),
FileInputStream(command.viewerConfigJsonArg), System.out)
diff --git a/tools/protologtool/src/com/android/protologtool/SourceTransformer.kt b/tools/protologtool/src/com/android/protolog/tool/SourceTransformer.kt
similarity index 83%
rename from tools/protologtool/src/com/android/protologtool/SourceTransformer.kt
rename to tools/protologtool/src/com/android/protolog/tool/SourceTransformer.kt
index f915ea6..c392078 100644
--- a/tools/protologtool/src/com/android/protologtool/SourceTransformer.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/SourceTransformer.kt
@@ -14,26 +14,32 @@
* limitations under the License.
*/
-package com.android.protologtool
+package com.android.protolog.tool
-import com.android.protologtool.Constants.IS_LOG_TO_ANY_METHOD
+import com.android.protolog.tool.Constants.IS_LOG_TO_ANY_METHOD
+import com.android.server.protolog.common.LogDataType
import com.github.javaparser.StaticJavaParser
import com.github.javaparser.ast.CompilationUnit
import com.github.javaparser.ast.NodeList
import com.github.javaparser.ast.body.VariableDeclarator
import com.github.javaparser.ast.expr.BooleanLiteralExpr
import com.github.javaparser.ast.expr.CastExpr
+import com.github.javaparser.ast.expr.Expression
import com.github.javaparser.ast.expr.FieldAccessExpr
import com.github.javaparser.ast.expr.IntegerLiteralExpr
import com.github.javaparser.ast.expr.MethodCallExpr
import com.github.javaparser.ast.expr.NameExpr
import com.github.javaparser.ast.expr.NullLiteralExpr
import com.github.javaparser.ast.expr.SimpleName
+import com.github.javaparser.ast.expr.TypeExpr
import com.github.javaparser.ast.expr.VariableDeclarationExpr
import com.github.javaparser.ast.stmt.BlockStmt
import com.github.javaparser.ast.stmt.ExpressionStmt
import com.github.javaparser.ast.stmt.IfStmt
import com.github.javaparser.ast.type.ArrayType
+import com.github.javaparser.ast.type.ClassOrInterfaceType
+import com.github.javaparser.ast.type.PrimitiveType
+import com.github.javaparser.ast.type.Type
import com.github.javaparser.printer.PrettyPrinter
import com.github.javaparser.printer.PrettyPrinterConfiguration
import com.github.javaparser.printer.lexicalpreservation.LexicalPreservingPrinter
@@ -77,8 +83,8 @@
// Insert message string hash as a second argument.
// Out: ProtoLog.e(GROUP, 1234, null, arg)
newCall.arguments.add(1, IntegerLiteralExpr(hash))
- val argTypes = CodeUtils.parseFormatString(messageString)
- val typeMask = CodeUtils.logDataTypesToBitMask(argTypes)
+ val argTypes = LogDataType.parseFormatString(messageString)
+ val typeMask = LogDataType.logDataTypesToBitMask(argTypes)
// Insert bitmap representing which Number parameters are to be considered as
// floating point numbers.
// Out: ProtoLog.e(GROUP, 1234, 0, null, arg)
@@ -101,8 +107,8 @@
// Out: long protoLogParam0 = arg
argTypes.forEachIndexed { idx, type ->
val varName = "protoLogParam$idx"
- val declaration = VariableDeclarator(type.type, varName,
- type.toType(newCall.arguments[idx + 4].clone()))
+ val declaration = VariableDeclarator(getASTTypeForDataType(type), varName,
+ getConversionForType(type)(newCall.arguments[idx + 4].clone()))
blockStmt.addStatement(ExpressionStmt(VariableDeclarationExpr(declaration)))
newCall.setArgument(idx + 4, NameExpr(SimpleName(varName)))
}
@@ -174,6 +180,34 @@
inlinePrinter = PrettyPrinter(config)
}
+ companion object {
+ private val stringType: ClassOrInterfaceType =
+ StaticJavaParser.parseClassOrInterfaceType("String")
+
+ fun getASTTypeForDataType(type: Int): Type {
+ return when (type) {
+ LogDataType.STRING -> stringType.clone()
+ LogDataType.LONG -> PrimitiveType.longType()
+ LogDataType.DOUBLE -> PrimitiveType.doubleType()
+ LogDataType.BOOLEAN -> PrimitiveType.booleanType()
+ else -> {
+ // Should never happen.
+ throw RuntimeException("Invalid LogDataType")
+ }
+ }
+ }
+
+ fun getConversionForType(type: Int): (Expression) -> Expression {
+ return when (type) {
+ LogDataType.STRING -> { expr ->
+ MethodCallExpr(TypeExpr(StaticJavaParser.parseClassOrInterfaceType("String")),
+ SimpleName("valueOf"), NodeList(expr))
+ }
+ else -> { expr -> expr }
+ }
+ }
+ }
+
private val protoLogImplClassNode =
StaticJavaParser.parseExpression<FieldAccessExpr>(protoLogImplClassName)
private var processedCode: MutableList<String> = mutableListOf()
diff --git a/tools/protologtool/src/com/android/protologtool/ViewerConfigBuilder.kt b/tools/protologtool/src/com/android/protolog/tool/ViewerConfigBuilder.kt
similarity index 97%
rename from tools/protologtool/src/com/android/protologtool/ViewerConfigBuilder.kt
rename to tools/protologtool/src/com/android/protolog/tool/ViewerConfigBuilder.kt
index 8ce9a49..a75b5c9 100644
--- a/tools/protologtool/src/com/android/protologtool/ViewerConfigBuilder.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/ViewerConfigBuilder.kt
@@ -14,11 +14,11 @@
* limitations under the License.
*/
-package com.android.protologtool
+package com.android.protolog.tool
import com.android.json.stream.JsonWriter
import com.github.javaparser.ast.CompilationUnit
-import com.android.protologtool.Constants.VERSION
+import com.android.protolog.tool.Constants.VERSION
import com.github.javaparser.ast.expr.MethodCallExpr
import java.io.StringWriter
diff --git a/tools/protologtool/src/com/android/protologtool/ViewerConfigParser.kt b/tools/protologtool/src/com/android/protolog/tool/ViewerConfigParser.kt
similarity index 94%
rename from tools/protologtool/src/com/android/protologtool/ViewerConfigParser.kt
rename to tools/protologtool/src/com/android/protolog/tool/ViewerConfigParser.kt
index 69cf92d..7278db0 100644
--- a/tools/protologtool/src/com/android/protologtool/ViewerConfigParser.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/ViewerConfigParser.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.protologtool
+package com.android.protolog.tool
import com.android.json.stream.JsonReader
@@ -31,8 +31,7 @@
var level: String? = null
var groupName: String? = null
while (jsonReader.hasNext()) {
- val key = jsonReader.nextName()
- when (key) {
+ when (jsonReader.nextName()) {
"message" -> message = jsonReader.nextString()
"level" -> level = jsonReader.nextString()
"group" -> groupName = jsonReader.nextString()
@@ -52,8 +51,7 @@
jsonReader.beginObject()
var tag: String? = null
while (jsonReader.hasNext()) {
- val key = jsonReader.nextName()
- when (key) {
+ when (jsonReader.nextName()) {
"tag" -> tag = jsonReader.nextString()
else -> jsonReader.skipValue()
}
@@ -98,8 +96,7 @@
jsonReader.beginObject()
while (jsonReader.hasNext()) {
- val key = jsonReader.nextName()
- when (key) {
+ when (jsonReader.nextName()) {
"messages" -> messages = parseMessages(jsonReader)
"groups" -> groups = parseGroups(jsonReader)
"version" -> version = jsonReader.nextString()
diff --git a/tools/protologtool/src/com/android/protologtool/exceptions.kt b/tools/protologtool/src/com/android/protolog/tool/exceptions.kt
similarity index 74%
rename from tools/protologtool/src/com/android/protologtool/exceptions.kt
rename to tools/protologtool/src/com/android/protolog/tool/exceptions.kt
index 2199785..0401d8f 100644
--- a/tools/protologtool/src/com/android/protologtool/exceptions.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/exceptions.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.protologtool
+package com.android.protolog.tool
import com.github.javaparser.ast.Node
import java.lang.Exception
@@ -27,17 +27,7 @@
class InvalidProtoLogCallException(message: String, node: Node)
: RuntimeException("$message\nAt: $node")
-class InvalidViewerConfigException : Exception {
- constructor(message: String) : super(message)
-
- constructor(message: String, ex: Exception) : super(message, ex)
-}
-
-class InvalidFormatStringException : Exception {
- constructor(message: String) : super(message)
-
- constructor(message: String, ex: Exception) : super(message, ex)
-}
+class InvalidViewerConfigException(message: String) : Exception(message)
class InvalidInputException(message: String) : Exception(message)
diff --git a/tools/protologtool/src/com/android/protologtool/CodeUtils.kt b/tools/protologtool/src/com/android/protologtool/CodeUtils.kt
deleted file mode 100644
index facca62..0000000
--- a/tools/protologtool/src/com/android/protologtool/CodeUtils.kt
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * 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.protologtool
-
-import com.github.javaparser.StaticJavaParser
-import com.github.javaparser.ast.CompilationUnit
-import com.github.javaparser.ast.ImportDeclaration
-import com.github.javaparser.ast.NodeList
-import com.github.javaparser.ast.expr.BinaryExpr
-import com.github.javaparser.ast.expr.Expression
-import com.github.javaparser.ast.expr.MethodCallExpr
-import com.github.javaparser.ast.expr.SimpleName
-import com.github.javaparser.ast.expr.StringLiteralExpr
-import com.github.javaparser.ast.expr.TypeExpr
-import com.github.javaparser.ast.type.PrimitiveType
-import com.github.javaparser.ast.type.Type
-
-object CodeUtils {
- /**
- * Returns a stable hash of a string.
- * We reimplement String::hashCode() for readability reasons.
- */
- fun hash(str: String, level: LogLevel): Int {
- return (level.name + str).map { c -> c.toInt() }.reduce { h, c -> h * 31 + c }
- }
-
- fun isWildcardStaticImported(code: CompilationUnit, className: String): Boolean {
- return code.findAll(ImportDeclaration::class.java)
- .any { im -> im.isStatic && im.isAsterisk && im.name.toString() == className }
- }
-
- fun isClassImportedOrSamePackage(code: CompilationUnit, className: String): Boolean {
- val packageName = className.substringBeforeLast('.')
- return code.packageDeclaration.isPresent &&
- code.packageDeclaration.get().nameAsString == packageName ||
- code.findAll(ImportDeclaration::class.java)
- .any { im ->
- !im.isStatic &&
- ((!im.isAsterisk && im.name.toString() == className) ||
- (im.isAsterisk && im.name.toString() == packageName))
- }
- }
-
- fun staticallyImportedMethods(code: CompilationUnit, className: String): Set<String> {
- return code.findAll(ImportDeclaration::class.java)
- .filter { im ->
- im.isStatic &&
- im.name.toString().substringBeforeLast('.') == className
- }
- .map { im -> im.name.toString().substringAfterLast('.') }.toSet()
- }
-
- fun concatMultilineString(expr: Expression): String {
- return when (expr) {
- is StringLiteralExpr -> expr.asString()
- is BinaryExpr -> when {
- expr.operator == BinaryExpr.Operator.PLUS ->
- concatMultilineString(expr.left) + concatMultilineString(expr.right)
- else -> throw InvalidProtoLogCallException(
- "messageString must be a string literal " +
- "or concatenation of string literals.", expr)
- }
- else -> throw InvalidProtoLogCallException("messageString must be a string literal " +
- "or concatenation of string literals.", expr)
- }
- }
-
- enum class LogDataTypes(
- val type: Type,
- val toType: (Expression) -> Expression = { expr -> expr }
- ) {
- // When adding new LogDataType make sure to update {@code logDataTypesToBitMask} accordingly
- STRING(StaticJavaParser.parseClassOrInterfaceType("String"),
- { expr ->
- MethodCallExpr(TypeExpr(StaticJavaParser.parseClassOrInterfaceType("String")),
- SimpleName("valueOf"), NodeList(expr))
- }),
- LONG(PrimitiveType.longType()),
- DOUBLE(PrimitiveType.doubleType()),
- BOOLEAN(PrimitiveType.booleanType());
- }
-
- fun parseFormatString(messageString: String): List<LogDataTypes> {
- val types = mutableListOf<LogDataTypes>()
- var i = 0
- while (i < messageString.length) {
- if (messageString[i] == '%') {
- if (i + 1 >= messageString.length) {
- throw InvalidFormatStringException("Invalid format string in config")
- }
- when (messageString[i + 1]) {
- 'b' -> types.add(CodeUtils.LogDataTypes.BOOLEAN)
- 'd', 'o', 'x' -> types.add(CodeUtils.LogDataTypes.LONG)
- 'f', 'e', 'g' -> types.add(CodeUtils.LogDataTypes.DOUBLE)
- 's' -> types.add(CodeUtils.LogDataTypes.STRING)
- '%' -> {
- }
- else -> throw InvalidFormatStringException("Invalid format string field" +
- " %${messageString[i + 1]}")
- }
- i += 2
- } else {
- i += 1
- }
- }
- return types
- }
-
- fun logDataTypesToBitMask(types: List<LogDataTypes>): Int {
- if (types.size > 16) {
- throw InvalidFormatStringException("Too many log call parameters " +
- "- max 16 parameters supported")
- }
- var mask = 0
- types.forEachIndexed { idx, type ->
- val x = LogDataTypes.values().indexOf(type)
- mask = mask or (x shl (idx * 2))
- }
- return mask
- }
-}
diff --git a/tools/protologtool/tests/com/android/protologtool/CodeUtilsTest.kt b/tools/protologtool/tests/com/android/protolog/tool/CodeUtilsTest.kt
similarity index 79%
rename from tools/protologtool/tests/com/android/protologtool/CodeUtilsTest.kt
rename to tools/protologtool/tests/com/android/protolog/tool/CodeUtilsTest.kt
index 82daa73..337ed99 100644
--- a/tools/protologtool/tests/com/android/protologtool/CodeUtilsTest.kt
+++ b/tools/protologtool/tests/com/android/protolog/tool/CodeUtilsTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.protologtool
+package com.android.protolog.tool
import com.github.javaparser.StaticJavaParser
import com.github.javaparser.ast.expr.BinaryExpr
@@ -164,43 +164,4 @@
val out = CodeUtils.concatMultilineString(code)
assertEquals("testabc1234test", out)
}
-
- @Test
- fun parseFormatString() {
- val str = "%b %d %o %x %f %e %g %s %%"
- val out = CodeUtils.parseFormatString(str)
- assertEquals(listOf(
- CodeUtils.LogDataTypes.BOOLEAN,
- CodeUtils.LogDataTypes.LONG,
- CodeUtils.LogDataTypes.LONG,
- CodeUtils.LogDataTypes.LONG,
- CodeUtils.LogDataTypes.DOUBLE,
- CodeUtils.LogDataTypes.DOUBLE,
- CodeUtils.LogDataTypes.DOUBLE,
- CodeUtils.LogDataTypes.STRING
- ), out)
- }
-
- @Test(expected = InvalidFormatStringException::class)
- fun parseFormatString_invalid() {
- val str = "%q"
- CodeUtils.parseFormatString(str)
- }
-
- @Test
- fun logDataTypesToBitMask() {
- val types = listOf(CodeUtils.LogDataTypes.STRING, CodeUtils.LogDataTypes.DOUBLE,
- CodeUtils.LogDataTypes.LONG, CodeUtils.LogDataTypes.BOOLEAN)
- val mask = CodeUtils.logDataTypesToBitMask(types)
- assertEquals(0b11011000, mask)
- }
-
- @Test(expected = InvalidFormatStringException::class)
- fun logDataTypesToBitMask_toManyParams() {
- val types = mutableListOf<CodeUtils.LogDataTypes>()
- for (i in 0..16) {
- types.add(CodeUtils.LogDataTypes.STRING)
- }
- CodeUtils.logDataTypesToBitMask(types)
- }
}
diff --git a/tools/protologtool/tests/com/android/protologtool/CommandOptionsTest.kt b/tools/protologtool/tests/com/android/protolog/tool/CommandOptionsTest.kt
similarity index 99%
rename from tools/protologtool/tests/com/android/protologtool/CommandOptionsTest.kt
rename to tools/protologtool/tests/com/android/protolog/tool/CommandOptionsTest.kt
index c1cd473..615712e 100644
--- a/tools/protologtool/tests/com/android/protologtool/CommandOptionsTest.kt
+++ b/tools/protologtool/tests/com/android/protolog/tool/CommandOptionsTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.protologtool
+package com.android.protolog.tool
import org.junit.Assert.assertEquals
import org.junit.Test
diff --git a/tools/protologtool/tests/com/android/protologtool/LogParserTest.kt b/tools/protologtool/tests/com/android/protolog/tool/LogParserTest.kt
similarity index 85%
rename from tools/protologtool/tests/com/android/protologtool/LogParserTest.kt
rename to tools/protologtool/tests/com/android/protolog/tool/LogParserTest.kt
index 7106ea6..04a3bfa 100644
--- a/tools/protologtool/tests/com/android/protologtool/LogParserTest.kt
+++ b/tools/protologtool/tests/com/android/protolog/tool/LogParserTest.kt
@@ -14,11 +14,11 @@
* limitations under the License.
*/
-package com.android.protologtool
+package com.android.protolog.tool
import com.android.json.stream.JsonReader
-import com.android.server.wm.ProtoLogMessage
-import com.android.server.wm.WindowManagerLogFileProto
+import com.android.server.protolog.ProtoLogMessage
+import com.android.server.protolog.ProtoLogFileProto
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
@@ -51,11 +51,11 @@
return "".byteInputStream()
}
- private fun buildProtoInput(logBuilder: WindowManagerLogFileProto.Builder): InputStream {
+ private fun buildProtoInput(logBuilder: ProtoLogFileProto.Builder): InputStream {
logBuilder.setVersion(Constants.VERSION)
logBuilder.magicNumber =
- WindowManagerLogFileProto.MagicNumber.MAGIC_NUMBER_H.number.toLong() shl 32 or
- WindowManagerLogFileProto.MagicNumber.MAGIC_NUMBER_L.number.toLong()
+ ProtoLogFileProto.MagicNumber.MAGIC_NUMBER_H.number.toLong() shl 32 or
+ ProtoLogFileProto.MagicNumber.MAGIC_NUMBER_L.number.toLong()
return logBuilder.build().toByteArray().inputStream()
}
@@ -68,7 +68,7 @@
config[70933285] = ViewerConfigParser.ConfigEntry("Test completed successfully: %b",
"ERROR", "WindowManager")
- val logBuilder = WindowManagerLogFileProto.newBuilder()
+ val logBuilder = ProtoLogFileProto.newBuilder()
val logMessageBuilder = ProtoLogMessage.newBuilder()
logMessageBuilder
.setMessageHash(70933285)
@@ -87,7 +87,7 @@
config[123] = ViewerConfigParser.ConfigEntry("Test completed successfully: %b %d %% %o" +
" %x %e %g %s %f", "ERROR", "WindowManager")
- val logBuilder = WindowManagerLogFileProto.newBuilder()
+ val logBuilder = ProtoLogFileProto.newBuilder()
val logMessageBuilder = ProtoLogMessage.newBuilder()
logMessageBuilder
.setMessageHash(123)
@@ -110,7 +110,7 @@
config[123] = ViewerConfigParser.ConfigEntry("Test completed successfully: %b %d %% %o",
"ERROR", "WindowManager")
- val logBuilder = WindowManagerLogFileProto.newBuilder()
+ val logBuilder = ProtoLogFileProto.newBuilder()
val logMessageBuilder = ProtoLogMessage.newBuilder()
logMessageBuilder
.setMessageHash(123)
@@ -132,7 +132,7 @@
config[123] = ViewerConfigParser.ConfigEntry("Test completed successfully: %b %d %% %o" +
" %x %e %g %s %f", "ERROR", "WindowManager")
- val logBuilder = WindowManagerLogFileProto.newBuilder()
+ val logBuilder = ProtoLogFileProto.newBuilder()
val logMessageBuilder = ProtoLogMessage.newBuilder()
logMessageBuilder
.setMessageHash(123)
@@ -149,7 +149,7 @@
@Test(expected = InvalidInputException::class)
fun parse_invalidMagicNumber() {
- val logBuilder = WindowManagerLogFileProto.newBuilder()
+ val logBuilder = ProtoLogFileProto.newBuilder()
logBuilder.setVersion(Constants.VERSION)
logBuilder.magicNumber = 0
val stream = logBuilder.build().toByteArray().inputStream()
@@ -159,11 +159,11 @@
@Test(expected = InvalidInputException::class)
fun parse_invalidVersion() {
- val logBuilder = WindowManagerLogFileProto.newBuilder()
+ val logBuilder = ProtoLogFileProto.newBuilder()
logBuilder.setVersion("invalid")
logBuilder.magicNumber =
- WindowManagerLogFileProto.MagicNumber.MAGIC_NUMBER_H.number.toLong() shl 32 or
- WindowManagerLogFileProto.MagicNumber.MAGIC_NUMBER_L.number.toLong()
+ ProtoLogFileProto.MagicNumber.MAGIC_NUMBER_H.number.toLong() shl 32 or
+ ProtoLogFileProto.MagicNumber.MAGIC_NUMBER_L.number.toLong()
val stream = logBuilder.build().toByteArray().inputStream()
parser.parse(stream, getConfigDummyStream(), printStream)
@@ -171,7 +171,7 @@
@Test
fun parse_noConfig() {
- val logBuilder = WindowManagerLogFileProto.newBuilder()
+ val logBuilder = ProtoLogFileProto.newBuilder()
val logMessageBuilder = ProtoLogMessage.newBuilder()
logMessageBuilder
.setMessageHash(70933285)
diff --git a/tools/protologtool/tests/com/android/protologtool/ProtoLogCallProcessorTest.kt b/tools/protologtool/tests/com/android/protolog/tool/ProtoLogCallProcessorTest.kt
similarity index 99%
rename from tools/protologtool/tests/com/android/protologtool/ProtoLogCallProcessorTest.kt
rename to tools/protologtool/tests/com/android/protolog/tool/ProtoLogCallProcessorTest.kt
index dcb1f7f..d20ce7e 100644
--- a/tools/protologtool/tests/com/android/protologtool/ProtoLogCallProcessorTest.kt
+++ b/tools/protologtool/tests/com/android/protolog/tool/ProtoLogCallProcessorTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.protologtool
+package com.android.protolog.tool
import com.github.javaparser.StaticJavaParser
import com.github.javaparser.ast.expr.MethodCallExpr
diff --git a/tools/protologtool/tests/com/android/protologtool/SourceTransformerTest.kt b/tools/protologtool/tests/com/android/protolog/tool/SourceTransformerTest.kt
similarity index 99%
rename from tools/protologtool/tests/com/android/protologtool/SourceTransformerTest.kt
rename to tools/protologtool/tests/com/android/protolog/tool/SourceTransformerTest.kt
index 2cd8562..d6e4a36 100644
--- a/tools/protologtool/tests/com/android/protologtool/SourceTransformerTest.kt
+++ b/tools/protologtool/tests/com/android/protolog/tool/SourceTransformerTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.protologtool
+package com.android.protolog.tool
import com.github.javaparser.StaticJavaParser
import com.github.javaparser.ast.CompilationUnit
diff --git a/tools/protologtool/tests/com/android/protologtool/ViewerConfigBuilderTest.kt b/tools/protologtool/tests/com/android/protolog/tool/ViewerConfigBuilderTest.kt
similarity index 99%
rename from tools/protologtool/tests/com/android/protologtool/ViewerConfigBuilderTest.kt
rename to tools/protologtool/tests/com/android/protolog/tool/ViewerConfigBuilderTest.kt
index 53d2e8b..f435d40 100644
--- a/tools/protologtool/tests/com/android/protologtool/ViewerConfigBuilderTest.kt
+++ b/tools/protologtool/tests/com/android/protolog/tool/ViewerConfigBuilderTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.protologtool
+package com.android.protolog.tool
import com.android.json.stream.JsonReader
import com.github.javaparser.ast.CompilationUnit
diff --git a/tools/protologtool/tests/com/android/protologtool/ViewerConfigParserTest.kt b/tools/protologtool/tests/com/android/protolog/tool/ViewerConfigParserTest.kt
similarity index 98%
rename from tools/protologtool/tests/com/android/protologtool/ViewerConfigParserTest.kt
rename to tools/protologtool/tests/com/android/protolog/tool/ViewerConfigParserTest.kt
index c0cea73..dc3ef7c 100644
--- a/tools/protologtool/tests/com/android/protologtool/ViewerConfigParserTest.kt
+++ b/tools/protologtool/tests/com/android/protolog/tool/ViewerConfigParserTest.kt
@@ -14,12 +14,12 @@
* limitations under the License.
*/
-package com.android.protologtool
+package com.android.protolog.tool
import com.android.json.stream.JsonReader
+import org.junit.Assert.assertEquals
import org.junit.Test
import java.io.StringReader
-import org.junit.Assert.assertEquals
class ViewerConfigParserTest {
private val parser = ViewerConfigParser()
@@ -322,6 +322,6 @@
}
}
"""
- val config = parser.parseConfig(getJSONReader(json))
+ parser.parseConfig(getJSONReader(json))
}
}