(bgn, this) {
+ @Override
+ protected CpuStateSnapshot computeDelta() {
+ CpuStateSnapshot delta = new CpuStateSnapshot();
+ if (bgn.cpuCoreStates.size() != end.cpuCoreStates.size()) {
+ delta.setValid(false);
+ } else {
+ delta.cpuCoreStates = new ArrayList<>();
+ for (int i = 0; i < end.cpuCoreStates.size(); i++) {
+ delta.cpuCoreStates.add(Differ.ListDiffer.globalDiff(bgn.cpuCoreStates.get(i), end.cpuCoreStates.get(i)));
+ }
+ delta.procCpuCoreStates = new ArrayList<>();
+ for (int i = 0; i < end.procCpuCoreStates.size(); i++) {
+ delta.procCpuCoreStates.add(Differ.ListDiffer.globalDiff(bgn.procCpuCoreStates.get(i), end.procCpuCoreStates.get(i)));
+ }
+ }
+ return delta;
+ }
+ };
+ }
+ }
+}
diff --git a/matrix/matrix-android/matrix-battery-canary/src/main/java/com/tencent/matrix/batterycanary/utils/BatteryCanaryUtil.java b/matrix/matrix-android/matrix-battery-canary/src/main/java/com/tencent/matrix/batterycanary/utils/BatteryCanaryUtil.java
index bd6a8d031..d7110058f 100644
--- a/matrix/matrix-android/matrix-battery-canary/src/main/java/com/tencent/matrix/batterycanary/utils/BatteryCanaryUtil.java
+++ b/matrix/matrix-android/matrix-battery-canary/src/main/java/com/tencent/matrix/batterycanary/utils/BatteryCanaryUtil.java
@@ -62,7 +62,11 @@ public final class BatteryCanaryUtil {
private static final String TAG = "Matrix.battery.Utils";
private static final int DEFAULT_MAX_STACK_LAYER = 10;
private static final int DEFAULT_AMS_CACHE_MILLIS = 5 * 1000;
- public static final int ONE_MIN = 60 * 1000;
+
+ public static final int ONE_MIN = 60 * 1000;
+ public static final int ONE_HOR = 60 * 60 * 1000;
+ public static final int JIFFY_HZ = 100; // @Os.sysconf(OsConstants._SC_CLK_TCK)
+ public static final int JIFFY_MILLIS = 1000 / JIFFY_HZ;
public interface Proxy {
String getProcessName();
@@ -251,8 +255,8 @@ public static String getAlarmTypeString(final int type) {
}
public static int[] getCpuCurrentFreq() {
- int[] output = new int[getNumCores()];
- for (int i = 0; i < getNumCores(); i++) {
+ int[] output = new int[getCpuCoreNum()];
+ for (int i = 0; i < getCpuCoreNum(); i++) {
output[i] = 0;
String path = "/sys/devices/system/cpu/cpu" + i + "/cpufreq/scaling_cur_freq";
String cat = cat(path);
@@ -267,7 +271,7 @@ public static int[] getCpuCurrentFreq() {
return output;
}
- private static int getNumCores() {
+ public static int getCpuCoreNum() {
try {
// Get directory containing CPU info
File dir = new File("/sys/devices/system/cpu/");
diff --git a/matrix/matrix-android/matrix-battery-canary/src/main/java/com/tencent/matrix/batterycanary/utils/KernelCpuSpeedReader.java b/matrix/matrix-android/matrix-battery-canary/src/main/java/com/tencent/matrix/batterycanary/utils/KernelCpuSpeedReader.java
new file mode 100644
index 000000000..cd927965a
--- /dev/null
+++ b/matrix/matrix-android/matrix-battery-canary/src/main/java/com/tencent/matrix/batterycanary/utils/KernelCpuSpeedReader.java
@@ -0,0 +1,71 @@
+package com.tencent.matrix.batterycanary.utils;
+
+
+import android.text.TextUtils;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+
+import androidx.annotation.RestrictTo;
+
+/**
+ * Reads CPU time of a specific core spent at various frequencies and provides a delta from the
+ * last call to {@link #readDelta}. Each line in the proc file has the format:
+ *
+ * freq time
+ *
+ * where time is measured in jiffies.
+ *
+ * @see com.android.internal.os.KernelCpuSpeedReader
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+@SuppressWarnings({"SpellCheckingInspection", "JavadocReference"})
+public class KernelCpuSpeedReader {
+ private static final String TAG = "KernelCpuSpeedReader";
+
+ private final String mProcFile;
+ private final int mNumSpeedSteps;
+
+ public KernelCpuSpeedReader(int cpuNumber, int numSpeedSteps) {
+ mProcFile = "/sys/devices/system/cpu/cpu" + cpuNumber + "/cpufreq/stats/time_in_state";
+ mNumSpeedSteps = numSpeedSteps;
+ }
+
+ public void smoke() throws IOException {
+ long[] stepJiffies = readAbsolute();
+ if (stepJiffies.length != mNumSpeedSteps) {
+ throw new IOException("CpuCore Step unmatched, expect = " + mNumSpeedSteps + ", actual = " + stepJiffies.length + ", path = " + mProcFile);
+ }
+ }
+
+ public long readTotoal() throws IOException {
+ long sum = 0;
+ for (long item : readAbsolute()) {
+ sum += item;
+ }
+ return sum;
+ }
+
+ /**
+ * @return The time (in jiffies) spent at different cpu speeds. The values should be
+ * monotonically increasing, unless the cpu was hotplugged.
+ */
+ public long[] readAbsolute() throws IOException {
+ long[] speedTimeJiffies = new long[mNumSpeedSteps];
+ try (BufferedReader reader = new BufferedReader(new FileReader(mProcFile))) {
+ TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter(' ');
+ String line;
+ int speedIndex = 0;
+ while (speedIndex < mNumSpeedSteps && (line = reader.readLine()) != null) {
+ splitter.setString(line);
+ splitter.next();
+ speedTimeJiffies[speedIndex] = Long.parseLong(splitter.next());
+ speedIndex++;
+ }
+ } catch (Throwable e) {
+ throw new IOException("Failed to read cpu-freq: " + e.getMessage(), e);
+ }
+ return speedTimeJiffies;
+ }
+}
\ No newline at end of file
diff --git a/matrix/matrix-android/matrix-battery-canary/src/main/java/com/tencent/matrix/batterycanary/utils/KernelCpuUidFreqTimeReader.java b/matrix/matrix-android/matrix-battery-canary/src/main/java/com/tencent/matrix/batterycanary/utils/KernelCpuUidFreqTimeReader.java
new file mode 100644
index 000000000..248afed47
--- /dev/null
+++ b/matrix/matrix-android/matrix-battery-canary/src/main/java/com/tencent/matrix/batterycanary/utils/KernelCpuUidFreqTimeReader.java
@@ -0,0 +1,102 @@
+package com.tencent.matrix.batterycanary.utils;
+
+
+import android.text.TextUtils;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import androidx.annotation.RestrictTo;
+
+/**
+ * Reads /proc/uid_time_in_state which has the format:
+ *
+ * uid: [freq1] [freq2] [freq3] ...
+ * [uid1]: [time in freq1] [time in freq2] [time in freq3] ...
+ * [uid2]: [time in freq1] [time in freq2] [time in freq3] ...
+ * ...
+ *
+ * This provides the times a UID's processes spent executing at each different cpu frequency.
+ * The file contains a monotonically increasing count of time for a single boot. This class
+ * maintains the previous results of a call to {@link #readDelta} in order to provide a proper
+ * delta.
+ *
+ * where time is measured in jiffies.
+ *
+ * @see com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+@SuppressWarnings({"SpellCheckingInspection", "JavadocReference"})
+public class KernelCpuUidFreqTimeReader {
+ private static final String TAG = "KernelCpuUidFreqTimeReader";
+
+ private final String mProcFile;
+ private final int[] mClusterSteps;
+
+ public KernelCpuUidFreqTimeReader(int pid, int[] clusterSteps) {
+ mProcFile = "/proc/" + pid + "/time_in_state";
+ mClusterSteps = clusterSteps;
+ }
+
+ public void smoke() throws IOException {
+ List cpuCoreStepJiffies = readAbsolute();
+ if (mClusterSteps.length != cpuCoreStepJiffies.size()) {
+ throw new IOException("Cpu clusterNum unmatched, expect = " + mClusterSteps.length + ", actual = " + cpuCoreStepJiffies.size());
+ }
+ for (int i = 0; i < cpuCoreStepJiffies.size(); i++) {
+ long[] clusterStepJiffies = cpuCoreStepJiffies.get(i);
+ if (mClusterSteps[i] != clusterStepJiffies.length) {
+ throw new IOException("Cpu clusterStepNum unmatched, expect = " + mClusterSteps[i] + ", actual = " + clusterStepJiffies.length + ", cluster = " + i);
+ }
+ }
+ }
+
+ public List readTotoal() throws IOException {
+ List cpuCoreStepJiffies = readAbsolute();
+ List cpuCoreJiffies = new ArrayList<>(cpuCoreStepJiffies.size());
+ for (long[] stepJiffies : cpuCoreStepJiffies) {
+ long sum = 0;
+ for (long item : stepJiffies) {
+ sum += item;
+ }
+ cpuCoreJiffies.add(sum);
+ }
+
+ return cpuCoreJiffies;
+ }
+
+ public List readAbsolute() throws IOException {
+ List cpuCoreJiffies = new ArrayList<>();
+ long[] speedJiffies = null;
+ try (BufferedReader reader = new BufferedReader(new FileReader(mProcFile))) {
+ TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter(' ');
+ String line;
+ int cluster = -1;
+ int speedIndex = 0;
+ while ((line = reader.readLine()) != null) {
+ if (line.startsWith("cpu")) {
+ if (cluster >= 0) {
+ cpuCoreJiffies.add(speedJiffies);
+ }
+ cluster++;
+ speedIndex = 0;
+ speedJiffies = new long[mClusterSteps[cluster]];
+ continue;
+ }
+ if (speedIndex < mClusterSteps[cluster]) {
+ splitter.setString(line);
+ splitter.next();
+ speedJiffies[speedIndex] = Long.parseLong(splitter.next());
+ speedIndex++;
+ }
+ }
+ cpuCoreJiffies.add(speedJiffies);
+ } catch (Throwable e) {
+ throw new IOException("Failed to read cpu-freq: " + e.getMessage(), e);
+ }
+ return cpuCoreJiffies;
+ }
+}
\ No newline at end of file
diff --git a/matrix/matrix-android/matrix-battery-canary/src/main/java/com/tencent/matrix/batterycanary/utils/PowerProfile.java b/matrix/matrix-android/matrix-battery-canary/src/main/java/com/tencent/matrix/batterycanary/utils/PowerProfile.java
new file mode 100644
index 000000000..09f6881b2
--- /dev/null
+++ b/matrix/matrix-android/matrix-battery-canary/src/main/java/com/tencent/matrix/batterycanary/utils/PowerProfile.java
@@ -0,0 +1,504 @@
+package com.tencent.matrix.batterycanary.utils;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.XmlResourceParser;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import androidx.annotation.Nullable;
+import androidx.annotation.RestrictTo;
+
+/**
+ * @see com.android.internal.os.PowerProfile
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+@SuppressWarnings({"JavadocReference", "ConstantConditions", "TryFinallyCanBeTryWithResources"})
+public class PowerProfile {
+ private static PowerProfile sInstance = null;
+
+ @Nullable
+ public static PowerProfile getInstance() {
+ return sInstance;
+ }
+
+ public static PowerProfile init(Context context) throws IOException {
+ synchronized (sLock) {
+ try {
+ sInstance = new PowerProfile(context).smoke();
+ return sInstance;
+ } catch (Throwable e) {
+ throw new IOException(e);
+ }
+ }
+ }
+
+ public PowerProfile smoke() throws IOException {
+ if (getNumCpuClusters() <= 0) {
+ throw new IOException("Invalid cpu clusters: " + getNumCpuClusters());
+ }
+ for (int i = 0; i < getNumCpuClusters(); i++) {
+ if (getNumSpeedStepsInCpuCluster(i) <= 0) {
+ throw new IOException("Invalid cpu cluster speed-steps: cluster = " + i
+ + ", steps = " + getNumSpeedStepsInCpuCluster(i));
+ }
+ }
+ int cpuCoreNum = BatteryCanaryUtil.getCpuCoreNum();
+ int cpuCoreNumInProfile = getCpuCoreNum();
+ if (cpuCoreNum != cpuCoreNumInProfile) {
+ throw new IOException("Unmatched cpu core num, sys = " + cpuCoreNum
+ + ", profile = " + cpuCoreNumInProfile);
+ }
+ return this;
+ }
+
+ public boolean isSupported() {
+ try {
+ smoke();
+ return true;
+ } catch (IOException ignored) {
+ return false;
+ }
+ }
+
+ public int getCpuCoreNum() {
+ int cpuCoreNumInProfile = 0;
+ for (int i = 0; i < getNumCpuClusters(); i++) {
+ cpuCoreNumInProfile += getNumCoresInCpuCluster(i);
+ }
+ return cpuCoreNumInProfile;
+ }
+
+ public int getClusterByCpuNum(int cpuCoreNum) {
+ int idx = -1;
+ if (cpuCoreNum < 0) {
+ return idx; // index out of bound
+ }
+ int delta = 0;
+ for (int i = 0; i < mCpuClusters.length; i++) {
+ CpuClusterKey cpuCluster = mCpuClusters[i];
+ if (cpuCluster.numCpus + delta >= cpuCoreNum + 1) {
+ return i;
+ }
+ delta += cpuCluster.numCpus;
+ }
+ return -2;
+ }
+
+ public static final String POWER_CPU_SUSPEND = "cpu.suspend";
+ public static final String POWER_CPU_IDLE = "cpu.idle";
+ public static final String POWER_CPU_ACTIVE = "cpu.active";
+
+ /**
+ * Power consumption when WiFi driver is scanning for networks.
+ */
+ public static final String POWER_WIFI_SCAN = "wifi.scan";
+
+ /**
+ * Power consumption when WiFi driver is on.
+ */
+ public static final String POWER_WIFI_ON = "wifi.on";
+
+ /**
+ * Power consumption when WiFi driver is transmitting/receiving.
+ */
+ public static final String POWER_WIFI_ACTIVE = "wifi.active";
+
+ //
+ // Updated power constants. These are not estimated, they are real world
+ // currents and voltages for the underlying bluetooth and wifi controllers.
+ //
+ public static final String POWER_WIFI_CONTROLLER_IDLE = "wifi.controller.idle";
+ public static final String POWER_WIFI_CONTROLLER_RX = "wifi.controller.rx";
+ public static final String POWER_WIFI_CONTROLLER_TX = "wifi.controller.tx";
+ public static final String POWER_WIFI_CONTROLLER_TX_LEVELS = "wifi.controller.tx_levels";
+ public static final String POWER_WIFI_CONTROLLER_OPERATING_VOLTAGE = "wifi.controller.voltage";
+
+ public static final String POWER_BLUETOOTH_CONTROLLER_IDLE = "bluetooth.controller.idle";
+ public static final String POWER_BLUETOOTH_CONTROLLER_RX = "bluetooth.controller.rx";
+ public static final String POWER_BLUETOOTH_CONTROLLER_TX = "bluetooth.controller.tx";
+ public static final String POWER_BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE =
+ "bluetooth.controller.voltage";
+
+ public static final String POWER_MODEM_CONTROLLER_SLEEP = "modem.controller.sleep";
+ public static final String POWER_MODEM_CONTROLLER_IDLE = "modem.controller.idle";
+ public static final String POWER_MODEM_CONTROLLER_RX = "modem.controller.rx";
+ public static final String POWER_MODEM_CONTROLLER_TX = "modem.controller.tx";
+ public static final String POWER_MODEM_CONTROLLER_OPERATING_VOLTAGE =
+ "modem.controller.voltage";
+
+ /**
+ * Power consumption when GPS is on.
+ */
+ public static final String POWER_GPS_ON = "gps.on";
+
+ /**
+ * GPS power parameters based on signal quality
+ */
+ public static final String POWER_GPS_SIGNAL_QUALITY_BASED = "gps.signalqualitybased";
+ public static final String POWER_GPS_OPERATING_VOLTAGE = "gps.voltage";
+
+ /**
+ * Power consumption when Bluetooth driver is on.
+ *
+ * @deprecated
+ */
+ @Deprecated
+ public static final String POWER_BLUETOOTH_ON = "bluetooth.on";
+
+ /**
+ * Power consumption when Bluetooth driver is transmitting/receiving.
+ *
+ * @deprecated
+ */
+ @Deprecated
+ public static final String POWER_BLUETOOTH_ACTIVE = "bluetooth.active";
+
+ /**
+ * Power consumption when Bluetooth driver gets an AT command.
+ *
+ * @deprecated
+ */
+ @Deprecated
+ public static final String POWER_BLUETOOTH_AT_CMD = "bluetooth.at";
+
+ /**
+ * Power consumption when screen is in doze/ambient/always-on mode, including backlight power.
+ */
+ public static final String POWER_AMBIENT_DISPLAY = "ambient.on";
+
+ /**
+ * Power consumption when screen is on, not including the backlight power.
+ */
+ public static final String POWER_SCREEN_ON = "screen.on";
+
+ /**
+ * Power consumption when cell radio is on but not on a call.
+ */
+ public static final String POWER_RADIO_ON = "radio.on";
+
+ /**
+ * Power consumption when cell radio is hunting for a signal.
+ */
+ public static final String POWER_RADIO_SCANNING = "radio.scanning";
+
+ /**
+ * Power consumption when talking on the phone.
+ */
+ public static final String POWER_RADIO_ACTIVE = "radio.active";
+
+ /**
+ * Power consumption at full backlight brightness. If the backlight is at
+ * 50% brightness, then this should be multiplied by 0.5
+ */
+ public static final String POWER_SCREEN_FULL = "screen.full";
+
+ /**
+ * Power consumed by the audio hardware when playing back audio content. This is in addition
+ * to the CPU power, probably due to a DSP and / or amplifier.
+ */
+ public static final String POWER_AUDIO = "audio";
+
+ /**
+ * Power consumed by any media hardware when playing back video content. This is in addition
+ * to the CPU power, probably due to a DSP.
+ */
+ public static final String POWER_VIDEO = "video";
+
+ /**
+ * Average power consumption when camera flashlight is on.
+ */
+ public static final String POWER_FLASHLIGHT = "camera.flashlight";
+
+ /**
+ * Power consumption when DDR is being used.
+ */
+ public static final String POWER_MEMORY = "memory.bandwidths";
+
+ /**
+ * Average power consumption when the camera is on over all standard use cases.
+ *
+ * TODO: Add more fine-grained camera power metrics.
+ */
+ public static final String POWER_CAMERA = "camera.avg";
+
+ /**
+ * Power consumed by wif batched scaning. Broken down into bins by
+ * Channels Scanned per Hour. May do 1-720 scans per hour of 1-100 channels
+ * for a range of 1-72,000. Going logrithmic (1-8, 9-64, 65-512, 513-4096, 4097-)!
+ */
+ public static final String POWER_WIFI_BATCHED_SCAN = "wifi.batchedscan";
+
+ /**
+ * Battery capacity in milliAmpHour (mAh).
+ */
+ public static final String POWER_BATTERY_CAPACITY = "battery.capacity";
+
+ /**
+ * A map from Power Use Item to its power consumption.
+ */
+ static final HashMap sPowerItemMap = new HashMap<>();
+ /**
+ * A map from Power Use Item to an array of its power consumption
+ * (for items with variable power e.g. CPU).
+ */
+ static final HashMap sPowerArrayMap = new HashMap<>();
+
+ private static final String TAG_DEVICE = "device";
+ private static final String TAG_ITEM = "item";
+ private static final String TAG_ARRAY = "array";
+ private static final String TAG_ARRAYITEM = "value";
+ private static final String ATTR_NAME = "name";
+
+ private static final Object sLock = new Object();
+
+
+ PowerProfile(Context context) {
+ // Read the XML file for the given profile (normally only one per device)
+ synchronized (sLock) {
+ if (sPowerItemMap.size() == 0 && sPowerArrayMap.size() == 0) {
+ readPowerValuesFromXml(context);
+ }
+ initCpuClusters();
+ }
+ }
+
+ @SuppressWarnings({"ToArrayCallWithZeroLengthArrayArgument", "UnnecessaryBoxing", "CatchMayIgnoreException", "TryWithIdenticalCatches"})
+ private void readPowerValuesFromXml(Context context) {
+ final int id = context.getResources().getIdentifier("power_profile", "xml", "android");
+ final Resources resources = context.getResources();
+ XmlResourceParser parser = resources.getXml(id);
+ boolean parsingArray = false;
+ ArrayList array = new ArrayList<>();
+ String arrayName = null;
+
+ try {
+ XmlUtils.beginDocument(parser, TAG_DEVICE);
+
+ while (true) {
+ XmlUtils.nextElement(parser);
+
+ String element = parser.getName();
+ if (element == null) break;
+
+ if (parsingArray && !element.equals(TAG_ARRAYITEM)) {
+ // Finish array
+ sPowerArrayMap.put(arrayName, array.toArray(new Double[array.size()]));
+ parsingArray = false;
+ }
+ if (element.equals(TAG_ARRAY)) {
+ parsingArray = true;
+ array.clear();
+ arrayName = parser.getAttributeValue(null, ATTR_NAME);
+ } else if (element.equals(TAG_ITEM) || element.equals(TAG_ARRAYITEM)) {
+ String name = null;
+ if (!parsingArray) name = parser.getAttributeValue(null, ATTR_NAME);
+ if (parser.next() == XmlPullParser.TEXT) {
+ String power = parser.getText();
+ double value = 0;
+ try {
+ value = Double.valueOf(power);
+ } catch (NumberFormatException nfe) {
+ }
+ if (element.equals(TAG_ITEM)) {
+ sPowerItemMap.put(name, value);
+ } else if (parsingArray) {
+ array.add(value);
+ }
+ }
+ }
+ }
+ if (parsingArray) {
+ sPowerArrayMap.put(arrayName, array.toArray(new Double[array.size()]));
+ }
+ } catch (XmlPullParserException e) {
+ throw new RuntimeException(e);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ } finally {
+ parser.close();
+ }
+
+ // Now collect other config variables.
+ int[] configResIds = new int[]{
+ context.getResources().getIdentifier("config_bluetooth_idle_cur_ma", "integer", "android"),
+ context.getResources().getIdentifier("config_bluetooth_rx_cur_ma", "integer", "android"),
+ context.getResources().getIdentifier("config_bluetooth_tx_cur_ma", "integer", "android"),
+ context.getResources().getIdentifier("config_bluetooth_operating_voltage_mv", "integer", "android"),
+ };
+
+ String[] configResIdKeys = new String[]{
+ POWER_BLUETOOTH_CONTROLLER_IDLE,
+ POWER_BLUETOOTH_CONTROLLER_RX,
+ POWER_BLUETOOTH_CONTROLLER_TX,
+ POWER_BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE,
+ };
+
+ for (int i = 0; i < configResIds.length; i++) {
+ String key = configResIdKeys[i];
+ // if we already have some of these parameters in power_profile.xml, ignore the
+ // value in config.xml
+ if ((sPowerItemMap.containsKey(key) && sPowerItemMap.get(key) > 0)) {
+ continue;
+ }
+ int value = resources.getInteger(configResIds[i]);
+ if (value > 0) {
+ sPowerItemMap.put(key, (double) value);
+ }
+ }
+ }
+
+ private CpuClusterKey[] mCpuClusters;
+
+ private static final String CPU_PER_CLUSTER_CORE_COUNT = "cpu.clusters.cores";
+ private static final String CPU_CLUSTER_POWER_COUNT = "cpu.cluster_power.cluster";
+ private static final String CPU_CORE_SPEED_PREFIX = "cpu.core_speeds.cluster";
+ private static final String CPU_CORE_POWER_PREFIX = "cpu.core_power.cluster";
+
+ private void initCpuClusters() {
+ if (sPowerArrayMap.containsKey(CPU_PER_CLUSTER_CORE_COUNT)) {
+ final Double[] data = sPowerArrayMap.get(CPU_PER_CLUSTER_CORE_COUNT);
+ mCpuClusters = new CpuClusterKey[data.length];
+ for (int cluster = 0; cluster < data.length; cluster++) {
+ int numCpusInCluster = (int) Math.round(data[cluster]);
+ mCpuClusters[cluster] = new CpuClusterKey(
+ CPU_CORE_SPEED_PREFIX + cluster, CPU_CLUSTER_POWER_COUNT + cluster,
+ CPU_CORE_POWER_PREFIX + cluster, numCpusInCluster);
+ }
+ } else {
+ // Default to single.
+ mCpuClusters = new CpuClusterKey[1];
+ int numCpus = 1;
+ if (sPowerItemMap.containsKey(CPU_PER_CLUSTER_CORE_COUNT)) {
+ numCpus = (int) Math.round(sPowerItemMap.get(CPU_PER_CLUSTER_CORE_COUNT));
+ }
+ mCpuClusters[0] = new CpuClusterKey(CPU_CORE_SPEED_PREFIX + 0,
+ CPU_CLUSTER_POWER_COUNT + 0, CPU_CORE_POWER_PREFIX + 0, numCpus);
+ }
+ }
+
+ public static class CpuClusterKey {
+ private final String freqKey;
+ private final String clusterPowerKey;
+ private final String corePowerKey;
+ private final int numCpus;
+
+ private CpuClusterKey(String freqKey, String clusterPowerKey,
+ String corePowerKey, int numCpus) {
+ this.freqKey = freqKey;
+ this.clusterPowerKey = clusterPowerKey;
+ this.corePowerKey = corePowerKey;
+ this.numCpus = numCpus;
+ }
+ }
+
+ public int getNumCpuClusters() {
+ return mCpuClusters.length;
+ }
+
+ public int getNumCoresInCpuCluster(int cluster) {
+ return mCpuClusters[cluster].numCpus;
+ }
+
+ public int getNumSpeedStepsInCpuCluster(int cluster) {
+ if (cluster < 0 || cluster >= mCpuClusters.length) {
+ return 0; // index out of bound
+ }
+ if (sPowerArrayMap.containsKey(mCpuClusters[cluster].freqKey)) {
+ return sPowerArrayMap.get(mCpuClusters[cluster].freqKey).length;
+ }
+ return 1; // Only one speed
+ }
+
+ public double getAveragePowerForCpuCluster(int cluster) {
+ if (cluster >= 0 && cluster < mCpuClusters.length) {
+ return getAveragePower(mCpuClusters[cluster].clusterPowerKey);
+ }
+ return 0;
+ }
+
+ public double getAveragePowerForCpuCore(int cluster, int step) {
+ if (cluster >= 0 && cluster < mCpuClusters.length) {
+ return getAveragePower(mCpuClusters[cluster].corePowerKey, step);
+ }
+ return 0;
+ }
+
+ public int getNumElements(String key) {
+ if (sPowerItemMap.containsKey(key)) {
+ return 1;
+ } else if (sPowerArrayMap.containsKey(key)) {
+ return sPowerArrayMap.get(key).length;
+ }
+ return 0;
+ }
+
+
+ public double getAveragePowerOrDefault(String type, double defaultValue) {
+ if (sPowerItemMap.containsKey(type)) {
+ return sPowerItemMap.get(type);
+ } else if (sPowerArrayMap.containsKey(type)) {
+ return sPowerArrayMap.get(type)[0];
+ } else {
+ return defaultValue;
+ }
+ }
+
+
+ public double getAveragePower(String type) {
+ return getAveragePowerOrDefault(type, 0);
+ }
+
+
+ public double getAveragePower(String type, int level) {
+ if (sPowerItemMap.containsKey(type)) {
+ return sPowerItemMap.get(type);
+ } else if (sPowerArrayMap.containsKey(type)) {
+ final Double[] values = sPowerArrayMap.get(type);
+ if (values.length > level && level >= 0) {
+ return values[level];
+ } else if (level < 0 || values.length == 0) {
+ return 0;
+ } else {
+ return values[values.length - 1];
+ }
+ } else {
+ return 0;
+ }
+ }
+
+ public double getBatteryCapacity() {
+ return getAveragePower(POWER_BATTERY_CAPACITY);
+ }
+
+ @SuppressWarnings("StatementWithEmptyBody")
+ static class XmlUtils {
+ public static void beginDocument(XmlPullParser parser, String firstElementName) throws XmlPullParserException, IOException {
+ int type;
+ while ((type = parser.next()) != parser.START_TAG
+ && type != parser.END_DOCUMENT) {
+ }
+
+ if (type != parser.START_TAG) {
+ throw new XmlPullParserException("No start tag found");
+ }
+
+ if (!parser.getName().equals(firstElementName)) {
+ throw new XmlPullParserException("Unexpected start tag: found " + parser.getName() +
+ ", expected " + firstElementName);
+ }
+ }
+
+ public static void nextElement(XmlPullParser parser) throws XmlPullParserException, IOException {
+ int type;
+ while ((type = parser.next()) != parser.START_TAG
+ && type != parser.END_DOCUMENT) {
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/matrix/matrix-android/matrix-commons/src/main/java/com/android/dexdeps/DexData.java b/matrix/matrix-android/matrix-commons/src/main/java/com/android/dexdeps/DexData.java
index 9ca5f9daf..d3dbbd7fa 100644
--- a/matrix/matrix-android/matrix-commons/src/main/java/com/android/dexdeps/DexData.java
+++ b/matrix/matrix-android/matrix-commons/src/main/java/com/android/dexdeps/DexData.java
@@ -70,7 +70,9 @@ public void load() throws IOException {
private static boolean verifyMagic(byte[] magic) {
return Arrays.equals(magic, HeaderItem.DEX_FILE_MAGIC_v035)
|| Arrays.equals(magic, HeaderItem.DEX_FILE_MAGIC_v037)
- || Arrays.equals(magic, HeaderItem.DEX_FILE_MAGIC_v038);
+ || Arrays.equals(magic, HeaderItem.DEX_FILE_MAGIC_v038)
+ || Arrays.equals(magic, HeaderItem.DEX_FILE_MAGIC_v039)
+ || Arrays.equals(magic, HeaderItem.DEX_FILE_MAGIC_v040);
}
/**
@@ -663,6 +665,14 @@ static class HeaderItem {
public static final byte[] DEX_FILE_MAGIC_v038 =
"dex\n038\0".getBytes(StandardCharsets.US_ASCII);
+ // Dex version 039: Android "P" and beyond.
+ public static final byte[] DEX_FILE_MAGIC_v039 =
+ "dex\n039\0".getBytes(StandardCharsets.US_ASCII);
+
+ // Dex version 040: beyond Android "10" (previously known as Android "Q").
+ public static final byte[] DEX_FILE_MAGIC_v040 =
+ "dex\n040\0".getBytes(StandardCharsets.US_ASCII);
+
public static final int ENDIAN_CONSTANT = 0x12345678;
public static final int REVERSE_ENDIAN_CONSTANT = 0x78563412;
}
diff --git a/matrix/matrix-android/matrix-commons/src/main/java/com/tencent/matrix/javalib/util/FileUtil.java b/matrix/matrix-android/matrix-commons/src/main/java/com/tencent/matrix/javalib/util/FileUtil.java
index 648a71cef..0358b8908 100644
--- a/matrix/matrix-android/matrix-commons/src/main/java/com/tencent/matrix/javalib/util/FileUtil.java
+++ b/matrix/matrix-android/matrix-commons/src/main/java/com/tencent/matrix/javalib/util/FileUtil.java
@@ -252,6 +252,13 @@ public static void unzip(String filePath, String destFolder) {
Enumeration emu = zipFile.entries();
while (emu.hasMoreElements()) {
ZipEntry entry = (ZipEntry) emu.nextElement();
+ String entryName = entry.getName();
+
+ if (Util.preventZipSlip(new File(destFolder), entryName)) {
+ Log.e(TAG, "writeEntry entry %s failed!", entryName);
+ continue;
+ }
+
if (entry.isDirectory()) {
new File(destFolder, entry.getName()).mkdirs();
continue;
diff --git a/matrix/matrix-android/matrix-commons/src/main/java/com/tencent/matrix/javalib/util/Util.java b/matrix/matrix-android/matrix-commons/src/main/java/com/tencent/matrix/javalib/util/Util.java
index e27dc7e0d..98fbddca9 100644
--- a/matrix/matrix-android/matrix-commons/src/main/java/com/tencent/matrix/javalib/util/Util.java
+++ b/matrix/matrix-android/matrix-commons/src/main/java/com/tencent/matrix/javalib/util/Util.java
@@ -16,6 +16,8 @@
package com.tencent.matrix.javalib.util;
+import java.io.File;
+import java.io.IOException;
import java.util.regex.Pattern;
/**
@@ -35,6 +37,19 @@ public static String nullAsNil(String str) {
return str == null ? "" : str;
}
+ public static boolean preventZipSlip(java.io.File output, String zipEntryName) {
+
+ try {
+ if (zipEntryName.contains("..") && new File(output, zipEntryName).getCanonicalPath().startsWith(output.getCanonicalPath() + File.separator)) {
+ return true;
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ return true;
+ }
+ return false;
+ }
+
public static boolean isNumber(String str) {
Pattern pattern = Pattern.compile("\\d+");
return pattern.matcher(str).matches();
diff --git a/matrix/matrix-android/matrix-gradle-plugin/build.gradle b/matrix/matrix-android/matrix-gradle-plugin/build.gradle
index 7807c7d83..76a401aa8 100644
--- a/matrix/matrix-android/matrix-gradle-plugin/build.gradle
+++ b/matrix/matrix-android/matrix-gradle-plugin/build.gradle
@@ -5,8 +5,8 @@ dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation gradleApi()
implementation project(':matrix-commons')
- implementation group: 'org.ow2.asm', name: 'asm', version: '5.1'
- implementation group: 'org.ow2.asm', name: 'asm-commons', version: '5.1'
+ implementation group: 'org.ow2.asm', name: 'asm', version: '7.0'
+ implementation group: 'org.ow2.asm', name: 'asm-commons', version: '7.0'
implementation 'com.android.tools.build:gradle:4.0.0'
implementation project(':matrix-arscutil')
implementation "org.jetbrains.kotlin:kotlin-stdlib:${gradle.KOTLIN_VERSION}"
diff --git a/matrix/matrix-android/matrix-gradle-plugin/src/main/java/com/tencent/matrix/trace/Configuration.java b/matrix/matrix-android/matrix-gradle-plugin/src/main/java/com/tencent/matrix/trace/Configuration.java
index c4819f6c8..8fb16b8d4 100644
--- a/matrix/matrix-android/matrix-gradle-plugin/src/main/java/com/tencent/matrix/trace/Configuration.java
+++ b/matrix/matrix-android/matrix-gradle-plugin/src/main/java/com/tencent/matrix/trace/Configuration.java
@@ -15,13 +15,14 @@ public class Configuration {
public String ignoreMethodMapFilePath;
public String blockListFilePath;
public String traceClassOut;
+ public boolean skipCheckClass;
public HashSet blockSet = new HashSet<>();
public Configuration() {
}
Configuration(String packageName, String mappingDir, String baseMethodMapPath, String methodMapFilePath,
- String ignoreMethodMapFilePath, String blockListFilePath, String traceClassOut) {
+ String ignoreMethodMapFilePath, String blockListFilePath, String traceClassOut, boolean skipCheckClass) {
this.packageName = packageName;
this.mappingDir = Util.nullAsNil(mappingDir);
this.baseMethodMapPath = Util.nullAsNil(baseMethodMapPath);
@@ -29,6 +30,7 @@ public Configuration() {
this.ignoreMethodMapFilePath = Util.nullAsNil(ignoreMethodMapFilePath);
this.blockListFilePath = Util.nullAsNil(blockListFilePath);
this.traceClassOut = Util.nullAsNil(traceClassOut);
+ this.skipCheckClass = skipCheckClass;
}
public int parseBlockFile(MappingCollector processor) {
@@ -82,6 +84,7 @@ public static class Builder {
public String ignoreMethodMapFile;
public String blockListFile;
public String traceClassOut;
+ public boolean skipCheckClass = false;
public Builder setPackageName(String packageName) {
this.packageName = packageName;
@@ -118,8 +121,13 @@ public Builder setBlockListFile(String blockListFile) {
return this;
}
+ public Builder setSkipCheckClass(boolean skipCheckClass) {
+ this.skipCheckClass = skipCheckClass;
+ return this;
+ }
+
public Configuration build() {
- return new Configuration(packageName, mappingPath, baseMethodMap, methodMapFile, ignoreMethodMapFile, blockListFile, traceClassOut);
+ return new Configuration(packageName, mappingPath, baseMethodMap, methodMapFile, ignoreMethodMapFile, blockListFile, traceClassOut, skipCheckClass);
}
}
diff --git a/matrix/matrix-android/matrix-gradle-plugin/src/main/java/com/tencent/matrix/trace/MethodCollector.java b/matrix/matrix-android/matrix-gradle-plugin/src/main/java/com/tencent/matrix/trace/MethodCollector.java
index 00d6c9ccd..66bca5f42 100644
--- a/matrix/matrix-android/matrix-gradle-plugin/src/main/java/com/tencent/matrix/trace/MethodCollector.java
+++ b/matrix/matrix-android/matrix-gradle-plugin/src/main/java/com/tencent/matrix/trace/MethodCollector.java
@@ -1,6 +1,7 @@
package com.tencent.matrix.trace;
import com.tencent.matrix.javalib.util.Log;
+import com.tencent.matrix.plugin.compat.AgpCompat;
import com.tencent.matrix.trace.item.TraceMethod;
import com.tencent.matrix.trace.retrace.MappingCollector;
@@ -131,7 +132,7 @@ public void run() {
is = new FileInputStream(classFile);
ClassReader classReader = new ClassReader(is);
ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS);
- ClassVisitor visitor = new TraceClassAdapter(Opcodes.ASM5, classWriter);
+ ClassVisitor visitor = new TraceClassAdapter(AgpCompat.getAsmApi(), classWriter);
classReader.accept(visitor, 0);
} catch (Exception e) {
@@ -167,7 +168,7 @@ public void run() {
InputStream inputStream = zipFile.getInputStream(zipEntry);
ClassReader classReader = new ClassReader(inputStream);
ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS);
- ClassVisitor visitor = new TraceClassAdapter(Opcodes.ASM5, classWriter);
+ ClassVisitor visitor = new TraceClassAdapter(AgpCompat.getAsmApi(), classWriter);
classReader.accept(visitor, 0);
}
}
@@ -305,7 +306,7 @@ private class CollectMethodNode extends MethodNode {
CollectMethodNode(String className, int access, String name, String desc,
String signature, String[] exceptions) {
- super(Opcodes.ASM5, access, name, desc, signature, exceptions);
+ super(AgpCompat.getAsmApi(), access, name, desc, signature, exceptions);
this.className = className;
}
diff --git a/matrix/matrix-android/matrix-gradle-plugin/src/main/java/com/tencent/matrix/trace/MethodTracer.java b/matrix/matrix-android/matrix-gradle-plugin/src/main/java/com/tencent/matrix/trace/MethodTracer.java
index 1084560dd..8c43c72bd 100644
--- a/matrix/matrix-android/matrix-gradle-plugin/src/main/java/com/tencent/matrix/trace/MethodTracer.java
+++ b/matrix/matrix-android/matrix-gradle-plugin/src/main/java/com/tencent/matrix/trace/MethodTracer.java
@@ -18,6 +18,8 @@
import com.tencent.matrix.javalib.util.FileUtil;
import com.tencent.matrix.javalib.util.Log;
+import com.tencent.matrix.javalib.util.Util;
+import com.tencent.matrix.plugin.compat.AgpCompat;
import com.tencent.matrix.trace.item.TraceMethod;
import com.tencent.matrix.trace.retrace.MappingCollector;
@@ -27,6 +29,7 @@
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.commons.AdviceAdapter;
+import org.objectweb.asm.util.CheckClassAdapter;
import java.io.ByteArrayInputStream;
import java.io.File;
@@ -67,6 +70,8 @@ public class MethodTracer {
private final ExecutorService executor;
private MappingCollector mappingCollector;
+ private volatile boolean traceError = false;
+
public MethodTracer(ExecutorService executor, MappingCollector mappingCollector, Configuration config, ConcurrentHashMap collectedMap, ConcurrentHashMap collectedClassExtendMap) {
this.configuration = config;
this.mappingCollector = mappingCollector;
@@ -75,44 +80,46 @@ public MethodTracer(ExecutorService executor, MappingCollector mappingCollector,
this.collectedMethodMap = collectedMap;
}
-
- public void trace(Map srcFolderList, Map dependencyJarList) throws ExecutionException, InterruptedException {
+ public void trace(Map srcFolderList, Map dependencyJarList, ClassLoader classLoader, boolean ignoreCheckClass) throws ExecutionException, InterruptedException {
List futures = new LinkedList<>();
- traceMethodFromSrc(srcFolderList, futures);
- traceMethodFromJar(dependencyJarList, futures);
+ traceMethodFromSrc(srcFolderList, futures, classLoader, ignoreCheckClass);
+ traceMethodFromJar(dependencyJarList, futures, classLoader, ignoreCheckClass);
for (Future future : futures) {
future.get();
}
+ if (traceError) {
+ throw new IllegalArgumentException("something wrong with trace, see detail log before");
+ }
futures.clear();
}
- private void traceMethodFromSrc(Map srcMap, List futures) {
+ private void traceMethodFromSrc(Map srcMap, List futures, final ClassLoader classLoader, final boolean skipCheckClass) {
if (null != srcMap) {
for (Map.Entry entry : srcMap.entrySet()) {
futures.add(executor.submit(new Runnable() {
@Override
public void run() {
- innerTraceMethodFromSrc(entry.getKey(), entry.getValue());
+ innerTraceMethodFromSrc(entry.getKey(), entry.getValue(), classLoader, skipCheckClass);
}
}));
}
}
}
- private void traceMethodFromJar(Map dependencyMap, List futures) {
+ private void traceMethodFromJar(Map dependencyMap, List futures, final ClassLoader classLoader, final boolean skipCheckClass) {
if (null != dependencyMap) {
for (Map.Entry entry : dependencyMap.entrySet()) {
futures.add(executor.submit(new Runnable() {
@Override
public void run() {
- innerTraceMethodFromJar(entry.getKey(), entry.getValue());
+ innerTraceMethodFromJar(entry.getKey(), entry.getValue(), classLoader, skipCheckClass);
}
}));
}
}
}
- private void innerTraceMethodFromSrc(File input, File output) {
+ private void innerTraceMethodFromSrc(File input, File output, ClassLoader classLoader, boolean ignoreCheckClass) {
ArrayList classFileList = new ArrayList<>();
if (input.isDirectory()) {
@@ -133,25 +140,40 @@ private void innerTraceMethodFromSrc(File input, File output) {
changedFileOutput.createNewFile();
if (MethodCollector.isNeedTraceFile(classFile.getName())) {
+
is = new FileInputStream(classFile);
ClassReader classReader = new ClassReader(is);
- ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS);
- ClassVisitor classVisitor = new TraceClassAdapter(Opcodes.ASM5, classWriter);
+ ClassWriter classWriter = new TraceClassWriter(ClassWriter.COMPUTE_FRAMES, classLoader);
+ ClassVisitor classVisitor = new TraceClassAdapter(AgpCompat.getAsmApi(), classWriter);
classReader.accept(classVisitor, ClassReader.EXPAND_FRAMES);
is.close();
+ byte[] data = classWriter.toByteArray();
+
+ if (!ignoreCheckClass) {
+ try {
+ ClassReader cr = new ClassReader(data);
+ ClassWriter cw = new ClassWriter(0);
+ ClassVisitor check = new CheckClassAdapter(cw);
+ cr.accept(check, ClassReader.EXPAND_FRAMES);
+ } catch (Throwable e) {
+ System.err.println("trace output ERROR : " + e.getMessage() + ", " + classFile);
+ traceError = true;
+ }
+ }
+
if (output.isDirectory()) {
os = new FileOutputStream(changedFileOutput);
} else {
os = new FileOutputStream(output);
}
- os.write(classWriter.toByteArray());
+ os.write(data);
os.close();
} else {
FileUtil.copyFileUsingStream(classFile, changedFileOutput);
}
} catch (Exception e) {
- Log.e(TAG, "[innerTraceMethodFromSrc] input:%s e:%s", input.getName(), e);
+ Log.e(TAG, "[innerTraceMethodFromSrc] input:%s e:%s", input.getName(), e.getMessage());
try {
Files.copy(input.toPath(), output.toPath(), StandardCopyOption.REPLACE_EXISTING);
} catch (Exception e1) {
@@ -168,7 +190,7 @@ private void innerTraceMethodFromSrc(File input, File output) {
}
}
- private void innerTraceMethodFromJar(File input, File output) {
+ private void innerTraceMethodFromJar(File input, File output, final ClassLoader classLoader, boolean skipCheckClass) {
ZipOutputStream zipOutputStream = null;
ZipFile zipFile = null;
try {
@@ -178,13 +200,34 @@ private void innerTraceMethodFromJar(File input, File output) {
while (enumeration.hasMoreElements()) {
ZipEntry zipEntry = enumeration.nextElement();
String zipEntryName = zipEntry.getName();
+
+ if (Util.preventZipSlip(output, zipEntryName)) {
+ Log.e(TAG, "Unzip entry %s failed!", zipEntryName);
+ continue;
+ }
+
if (MethodCollector.isNeedTraceFile(zipEntryName)) {
+
InputStream inputStream = zipFile.getInputStream(zipEntry);
ClassReader classReader = new ClassReader(inputStream);
- ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS);
- ClassVisitor classVisitor = new TraceClassAdapter(Opcodes.ASM5, classWriter);
+ ClassWriter classWriter = new TraceClassWriter(ClassWriter.COMPUTE_FRAMES, classLoader);
+ ClassVisitor classVisitor = new TraceClassAdapter(AgpCompat.getAsmApi(), classWriter);
classReader.accept(classVisitor, ClassReader.EXPAND_FRAMES);
byte[] data = classWriter.toByteArray();
+//
+ if (!skipCheckClass) {
+ try {
+ ClassReader r = new ClassReader(data);
+ ClassWriter w = new ClassWriter(0);
+ ClassVisitor v = new CheckClassAdapter(w);
+ r.accept(v, ClassReader.EXPAND_FRAMES);
+ } catch (Throwable e) {
+ System.err.println("trace jar output ERROR: " + e.getMessage() + ", " + zipEntryName);
+// e.printStackTrace();
+ traceError = true;
+ }
+ }
+
InputStream byteArrayInputStream = new ByteArrayInputStream(data);
ZipEntry newZipEntry = new ZipEntry(zipEntryName);
FileUtil.addZipEntry(zipOutputStream, newZipEntry, byteArrayInputStream);
@@ -195,7 +238,7 @@ private void innerTraceMethodFromJar(File input, File output) {
}
}
} catch (Exception e) {
- Log.e(TAG, "[innerTraceMethodFromJar] input:%s output:%s e:%s", input, output, e);
+ Log.e(TAG, "[innerTraceMethodFromJar] input:%s output:%s e:%s", input, output, e.getMessage());
if (e instanceof ZipException) {
e.printStackTrace();
}
diff --git a/matrix/matrix-android/matrix-gradle-plugin/src/main/java/com/tencent/matrix/trace/TraceClassLoader.java b/matrix/matrix-android/matrix-gradle-plugin/src/main/java/com/tencent/matrix/trace/TraceClassLoader.java
new file mode 100644
index 000000000..64bde9ed6
--- /dev/null
+++ b/matrix/matrix-android/matrix-gradle-plugin/src/main/java/com/tencent/matrix/trace/TraceClassLoader.java
@@ -0,0 +1,70 @@
+package com.tencent.matrix.trace;
+
+import com.android.build.gradle.AppExtension;
+import com.android.build.gradle.BaseExtension;
+import com.android.build.gradle.LibraryExtension;
+import com.google.common.collect.ImmutableList;
+
+import org.gradle.api.Project;
+
+import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.Collection;
+
+/**
+ * Created by habbyge on 2019/4/24.
+ */
+public class TraceClassLoader {
+
+ public static URLClassLoader getClassLoader(Project project, Collection inputFiles)
+ throws MalformedURLException {
+
+ ImmutableList.Builder urls = new ImmutableList.Builder<>();
+ File androidJar = getAndroidJar(project);
+ if (androidJar != null) {
+ urls.add(androidJar.toURI().toURL());
+ }
+
+ for (File inputFile : inputFiles) {
+ urls.add(inputFile.toURI().toURL());
+ }
+
+// for (TransformInput inputs : Iterables.concat(invocation.getInputs(), invocation.getReferencedInputs())) {
+// for (DirectoryInput directoryInput : inputs.getDirectoryInputs()) {
+// if (directoryInput.getFile().isDirectory()) {
+// urls.add(directoryInput.getFile().toURI().toURL());
+// }
+// }
+// for (JarInput jarInput : inputs.getJarInputs()) {
+// if (jarInput.getFile().isFile()) {
+// urls.add(jarInput.getFile().toURI().toURL());
+// }
+// }
+// }
+
+ ImmutableList urlImmutableList = urls.build();
+ URL[] classLoaderUrls = urlImmutableList.toArray(new URL[urlImmutableList.size()]);
+ return new URLClassLoader(classLoaderUrls);
+ }
+
+ private static File getAndroidJar(Project project) {
+ BaseExtension extension = null;
+ if (project.getPlugins().hasPlugin("com.android.application")) {
+ extension = project.getExtensions().findByType(AppExtension.class);
+ } else if (project.getPlugins().hasPlugin("com.android.library")) {
+ extension = project.getExtensions().findByType(LibraryExtension.class);
+ }
+ if (extension == null) {
+ return null;
+ }
+
+ String sdkDirectory = extension.getSdkDirectory().getAbsolutePath();
+ String compileSdkVersion = extension.getCompileSdkVersion();
+ sdkDirectory = sdkDirectory + File.separator + "platforms" + File.separator;
+ String androidJarPath = sdkDirectory + compileSdkVersion + File.separator + "android.jar";
+ File androidJar = new File(androidJarPath);
+ return androidJar.exists() ? androidJar : null;
+ }
+}
diff --git a/matrix/matrix-android/matrix-gradle-plugin/src/main/java/com/tencent/matrix/trace/TraceClassWriter.java b/matrix/matrix-android/matrix-gradle-plugin/src/main/java/com/tencent/matrix/trace/TraceClassWriter.java
new file mode 100644
index 000000000..38d12cb1e
--- /dev/null
+++ b/matrix/matrix-android/matrix-gradle-plugin/src/main/java/com/tencent/matrix/trace/TraceClassWriter.java
@@ -0,0 +1,152 @@
+package com.tencent.matrix.trace;
+
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassWriter;
+
+/**
+ * Created by habbyge on 2019/4/25.
+ *
+ * fix:
+ * java.lang.TypeNotPresentException: Type android/content/res/TypedArray not present
+ * at org.objectweb.asm.ClassWriter.getCommonSuperClass(ClassWriter.java:1025)
+ * at org.objectweb.asm.SymbolTable.addMergedType(SymbolTable.java:1202)
+ * at org.objectweb.asm.Frame.merge(Frame.java:1299)
+ * at org.objectweb.asm.Frame.merge(Frame.java:1197)
+ * at org.objectweb.asm.MethodWriter.computeAllFrames(MethodWriter.java:1610)
+ * at org.objectweb.asm.MethodWriter.visitMaxs(MethodWriter.java:1546)
+ * at org.objectweb.asm.tree.MethodNode.accept(MethodNode.java:769)
+ * at org.objectweb.asm.util.CheckMethodAdapter$1.visitEnd(CheckMethodAdapter.java:465)
+ * at org.objectweb.asm.MethodVisitor.visitEnd(MethodVisitor.java:783)
+ * at org.objectweb.asm.util.CheckMethodAdapter.visitEnd(CheckMethodAdapter.java:1036)
+ * at org.objectweb.asm.ClassReader.readMethod(ClassReader.java:1495)
+ * at org.objectweb.asm.ClassReader.accept(ClassReader.java:721)
+ *
+ */
+class TraceClassWriter extends ClassWriter {
+ private ClassLoader mClassLoader;
+ TraceClassWriter(int flags, ClassLoader classLoader) {
+ super(flags);
+ mClassLoader = classLoader;
+ }
+
+ TraceClassWriter(ClassReader classReader, int flags, ClassLoader classLoader) {
+ super(classReader, flags);
+ mClassLoader = classLoader;
+ }
+
+ @Override
+ protected String getCommonSuperClass(String type1, String type2) {
+ try {
+ return super.getCommonSuperClass(type1, type2);
+ } catch (Exception e) {
+ try {
+ return getCommonSuperClassV1(type1, type2);
+ } catch (Exception e1) {
+ try {
+ return getCommonSuperClassV2(type1, type2);
+ } catch (Exception e2) {
+ return getCommonSuperClassV3(type1, type2);
+ }
+ }
+ }
+ }
+
+ private String getCommonSuperClassV1(String type1, String type2) {
+ ClassLoader curClassLoader = getClass().getClassLoader();
+
+ Class clazz1, clazz2;
+ try {
+ clazz1 = Class.forName(type1.replace('/', '.'), false, mClassLoader);
+ clazz2 = Class.forName(type2.replace('/', '.'), false, mClassLoader);
+ } catch (Exception e) {
+ /*throw new RuntimeException(e.toString());*/
+ /*e.printStackTrace();*/
+ return "java/lang/Object";
+ } catch (LinkageError error) {
+ return "java/lang/Object";
+ }
+ //noinspection unchecked
+ if (clazz1.isAssignableFrom(clazz2)) {
+ return type1;
+ }
+ //noinspection unchecked
+ if (clazz2.isAssignableFrom(clazz1)) {
+ return type2;
+ }
+ if (clazz1.isInterface() || clazz2.isInterface()) {
+ return "java/lang/Object";
+ } else {
+ //noinspection unchecked
+ do {
+ clazz1 = clazz1.getSuperclass();
+ } while (!clazz1.isAssignableFrom(clazz2));
+ return clazz1.getName().replace('.', '/');
+ }
+ }
+
+ private String getCommonSuperClassV2(String type1, String type2) {
+ ClassLoader curClassLoader = getClass().getClassLoader();
+
+ Class clazz1, clazz2;
+ try {
+ clazz1 = Class.forName(type1.replace('/', '.'), false, curClassLoader);
+ clazz2 = Class.forName(type2.replace('/', '.'), false, mClassLoader);
+ } catch (Exception e) {
+ /*throw new RuntimeException(e.toString());*/
+ /*e.printStackTrace();*/
+ return "java/lang/Object";
+ } catch (LinkageError error) {
+ return "java/lang/Object";
+ }
+ //noinspection unchecked
+ if (clazz1.isAssignableFrom(clazz2)) {
+ return type1;
+ }
+ //noinspection unchecked
+ if (clazz2.isAssignableFrom(clazz1)) {
+ return type2;
+ }
+ if (clazz1.isInterface() || clazz2.isInterface()) {
+ return "java/lang/Object";
+ } else {
+ //noinspection unchecked
+ do {
+ clazz1 = clazz1.getSuperclass();
+ } while (!clazz1.isAssignableFrom(clazz2));
+ return clazz1.getName().replace('.', '/');
+ }
+ }
+
+ private String getCommonSuperClassV3(String type1, String type2) {
+ ClassLoader curClassLoader = getClass().getClassLoader();
+
+ Class clazz1, clazz2;
+ try {
+ clazz1 = Class.forName(type1.replace('/', '.'), false, mClassLoader);
+ clazz2 = Class.forName(type2.replace('/', '.'), false, curClassLoader);
+ } catch (Exception e) {
+ /*e.printStackTrace();*/
+ /*throw new RuntimeException(e.toString());*/
+ return "java/lang/Object";
+ } catch (LinkageError error) {
+ return "java/lang/Object";
+ }
+ //noinspection unchecked
+ if (clazz1.isAssignableFrom(clazz2)) {
+ return type1;
+ }
+ //noinspection unchecked
+ if (clazz2.isAssignableFrom(clazz1)) {
+ return type2;
+ }
+ if (clazz1.isInterface() || clazz2.isInterface()) {
+ return "java/lang/Object";
+ } else {
+ //noinspection unchecked
+ do {
+ clazz1 = clazz1.getSuperclass();
+ } while (!clazz1.isAssignableFrom(clazz2));
+ return clazz1.getName().replace('.', '/');
+ }
+ }
+}
diff --git a/matrix/matrix-android/matrix-gradle-plugin/src/main/java/com/tencent/matrix/trace/extension/MatrixTraceExtension.java b/matrix/matrix-android/matrix-gradle-plugin/src/main/java/com/tencent/matrix/trace/extension/MatrixTraceExtension.java
index 95a42b37b..8261d741e 100644
--- a/matrix/matrix-android/matrix-gradle-plugin/src/main/java/com/tencent/matrix/trace/extension/MatrixTraceExtension.java
+++ b/matrix/matrix-android/matrix-gradle-plugin/src/main/java/com/tencent/matrix/trace/extension/MatrixTraceExtension.java
@@ -5,6 +5,7 @@ public class MatrixTraceExtension {
String baseMethodMapFile;
String blackListFile;
String customDexTransformName;
+ boolean skipCheckClass = true; // skip by default
boolean enable;
@@ -32,4 +33,8 @@ public boolean isTransformInjectionForced() {
public boolean isEnable() {
return enable;
}
+
+ public boolean isSkipCheckClass() {
+ return skipCheckClass;
+ }
}
diff --git a/matrix/matrix-android/matrix-gradle-plugin/src/main/kotlin/com/tencent/matrix/plugin/compat/AgpCompat.kt b/matrix/matrix-android/matrix-gradle-plugin/src/main/kotlin/com/tencent/matrix/plugin/compat/AgpCompat.kt
index 71ed7f270..a5a962181 100644
--- a/matrix/matrix-android/matrix-gradle-plugin/src/main/kotlin/com/tencent/matrix/plugin/compat/AgpCompat.kt
+++ b/matrix/matrix-android/matrix-gradle-plugin/src/main/kotlin/com/tencent/matrix/plugin/compat/AgpCompat.kt
@@ -17,7 +17,9 @@
package com.tencent.matrix.plugin.compat
import com.android.build.gradle.api.BaseVariant
+import com.android.build.gradle.internal.api.ReadOnlyBuildType
import com.android.builder.model.SigningConfig
+import org.objectweb.asm.Opcodes
class AgpCompat {
@@ -32,8 +34,15 @@ class AgpCompat {
}
fun getSigningConfig(variant: BaseVariant): SigningConfig? {
- return variant.buildType.signingConfig
+ return (variant.buildType as ReadOnlyBuildType).signingConfig
}
+
+ @JvmStatic
+ val asmApi: Int
+ get() = when {
+ VersionsCompat.greatThanOrEqual(AGPVersion.AGP_7_0_0) -> Opcodes.ASM6
+ else -> Opcodes.ASM5
+ }
}
}
\ No newline at end of file
diff --git a/matrix/matrix-android/matrix-gradle-plugin/src/main/kotlin/com/tencent/matrix/plugin/compat/VersionsCompat.kt b/matrix/matrix-android/matrix-gradle-plugin/src/main/kotlin/com/tencent/matrix/plugin/compat/VersionsCompat.kt
index b1c95cb23..9d1ca9530 100644
--- a/matrix/matrix-android/matrix-gradle-plugin/src/main/kotlin/com/tencent/matrix/plugin/compat/VersionsCompat.kt
+++ b/matrix/matrix-android/matrix-gradle-plugin/src/main/kotlin/com/tencent/matrix/plugin/compat/VersionsCompat.kt
@@ -24,6 +24,7 @@ enum class AGPVersion(
AGP_3_6_0("3.6.0"),
AGP_4_0_0("4.0.0"),
AGP_4_1_0("4.1.0"),
+ AGP_7_0_0("7.0.0")
}
class VersionsCompat {
diff --git a/matrix/matrix-android/matrix-gradle-plugin/src/main/kotlin/com/tencent/matrix/plugin/task/MatrixTraceTask.kt b/matrix/matrix-android/matrix-gradle-plugin/src/main/kotlin/com/tencent/matrix/plugin/task/MatrixTraceTask.kt
index ad72706fc..e7c52434f 100644
--- a/matrix/matrix-android/matrix-gradle-plugin/src/main/kotlin/com/tencent/matrix/plugin/task/MatrixTraceTask.kt
+++ b/matrix/matrix-android/matrix-gradle-plugin/src/main/kotlin/com/tencent/matrix/plugin/task/MatrixTraceTask.kt
@@ -78,6 +78,9 @@ abstract class MatrixTraceTask : DefaultTask() {
@get:Optional
abstract val methodMapFileOutput: RegularFileProperty
+ @get:Optional
+ abstract val skipCheckClass: Property
+
@TaskAction
fun execute(inputChanges: InputChanges) {
@@ -107,11 +110,13 @@ abstract class MatrixTraceTask : DefaultTask() {
methodMapFilePath = methodMapFileOutput.asFile.get().absolutePath,
baseMethodMapPath = baseMethodMapFile.asFile.orNull?.absolutePath,
blockListFilePath = blockListFile.asFile.orNull?.absolutePath,
- mappingDir = mappingDir.get()
+ mappingDir = mappingDir.get(),
+ project = project
).doTransform(
classInputs = classInputs.files,
changedFiles = changedFiles,
isIncremental = incremental,
+ skipCheckClass = this.skipCheckClass.get(),
traceClassDirectoryOutput = outputDirectory,
inputToOutput = ConcurrentHashMap(),
legacyReplaceChangedFile = null,
@@ -173,6 +178,7 @@ abstract class MatrixTraceTask : DefaultTask() {
}
task.mappingDir.set(mappingOut)
task.traceClassOutputDirectory.set(traceClassOut)
+ task.skipCheckClass.set(extension.isSkipCheckClass)
task.classOutputs.from(project.files(Callable> {
val outputDirectory = File(task.traceClassOutputDirectory.get())
diff --git a/matrix/matrix-android/matrix-gradle-plugin/src/main/kotlin/com/tencent/matrix/plugin/trace/MatrixTrace.kt b/matrix/matrix-android/matrix-gradle-plugin/src/main/kotlin/com/tencent/matrix/plugin/trace/MatrixTrace.kt
index 3c6ab5c11..27a3ebc80 100644
--- a/matrix/matrix-android/matrix-gradle-plugin/src/main/kotlin/com/tencent/matrix/plugin/trace/MatrixTrace.kt
+++ b/matrix/matrix-android/matrix-gradle-plugin/src/main/kotlin/com/tencent/matrix/plugin/trace/MatrixTrace.kt
@@ -22,13 +22,11 @@ import com.google.common.hash.Hashing
import com.tencent.matrix.javalib.util.IOUtil
import com.tencent.matrix.javalib.util.Log
import com.tencent.matrix.javalib.util.Util
-import com.tencent.matrix.trace.Configuration
-import com.tencent.matrix.trace.MethodCollector
-import com.tencent.matrix.trace.MethodTracer
-import com.tencent.matrix.trace.TraceBuildConstants
+import com.tencent.matrix.trace.*
import com.tencent.matrix.trace.item.TraceMethod
import com.tencent.matrix.trace.retrace.MappingCollector
import com.tencent.matrix.trace.retrace.MappingReader
+import org.gradle.api.Project
import java.io.File
import java.util.*
import java.util.concurrent.ConcurrentHashMap
@@ -36,6 +34,7 @@ import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import java.util.concurrent.Future
import java.util.concurrent.atomic.AtomicInteger
+import kotlin.collections.ArrayList
import kotlin.collections.HashMap
class MatrixTrace(
@@ -43,7 +42,8 @@ class MatrixTrace(
private val methodMapFilePath: String,
private val baseMethodMapPath: String?,
private val blockListFilePath: String?,
- private val mappingDir: String
+ private val mappingDir: String,
+ private val project: Project
) {
companion object {
private const val TAG: String = "Matrix.Trace"
@@ -68,6 +68,7 @@ class MatrixTrace(
changedFiles: Map,
inputToOutput: Map,
isIncremental: Boolean,
+ skipCheckClass: Boolean,
traceClassDirectoryOutput: File,
legacyReplaceChangedFile: ((File, Map) -> Object)?,
legacyReplaceFile: ((File, File) -> (Object))?
@@ -81,6 +82,7 @@ class MatrixTrace(
.setBaseMethodMap(baseMethodMapPath)
.setBlockListFile(blockListFilePath)
.setMappingPath(mappingDir)
+ .setSkipCheckClass(skipCheckClass)
.build()
/**
@@ -152,7 +154,13 @@ class MatrixTrace(
*/
start = System.currentTimeMillis()
val methodTracer = MethodTracer(executor, mappingCollector, config, methodCollector.collectedMethodMap, methodCollector.collectedClassExtendMap)
- methodTracer.trace(dirInputOutMap, jarInputOutMap)
+ val allInputs = ArrayList().also {
+ it.addAll(dirInputOutMap.keys)
+ it.addAll(jarInputOutMap.keys)
+ }
+ val traceClassLoader = TraceClassLoader.getClassLoader(project, allInputs)
+ methodTracer.trace(dirInputOutMap, jarInputOutMap, traceClassLoader, skipCheckClass)
+
Log.i(TAG, "[doTransform] Step(3)[Trace]... cost:%sms", System.currentTimeMillis() - start)
}
diff --git a/matrix/matrix-android/matrix-gradle-plugin/src/main/kotlin/com/tencent/matrix/plugin/transform/MatrixTraceLegacyTransform.kt b/matrix/matrix-android/matrix-gradle-plugin/src/main/kotlin/com/tencent/matrix/plugin/transform/MatrixTraceLegacyTransform.kt
index 5e41d4a01..02fe36c98 100644
--- a/matrix/matrix-android/matrix-gradle-plugin/src/main/kotlin/com/tencent/matrix/plugin/transform/MatrixTraceLegacyTransform.kt
+++ b/matrix/matrix-android/matrix-gradle-plugin/src/main/kotlin/com/tencent/matrix/plugin/transform/MatrixTraceLegacyTransform.kt
@@ -34,6 +34,7 @@ import java.util.concurrent.ConcurrentHashMap
// For Android Gradle Plugin 3.5.0
class MatrixTraceLegacyTransform(
+ private val project: Project,
private val config: Configuration,
private val origTransform: Transform
) : Transform() {
@@ -63,6 +64,7 @@ class MatrixTraceLegacyTransform(
.setIgnoreMethodMapFilePath("$mappingOut/ignoreMethodMapping.txt")
.setMappingPath(mappingOut)
.setTraceClassOut(traceClassOut)
+ .setSkipCheckClass(extension.isSkipCheckClass)
.build()
val hardTask = getTransformTaskName(extension.customDexTransformName, variant.name)
@@ -72,7 +74,7 @@ class MatrixTraceLegacyTransform(
Log.i(TAG, "successfully inject task:" + task.name)
val field = TransformTask::class.java.getDeclaredField("transform")
field.isAccessible = true
- field.set(task, MatrixTraceLegacyTransform(config, task.transform))
+ field.set(task, MatrixTraceLegacyTransform(project, config, task.transform))
break
}
}
@@ -170,11 +172,13 @@ class MatrixTraceLegacyTransform(
methodMapFilePath = config.methodMapFilePath,
baseMethodMapPath = config.baseMethodMapPath,
blockListFilePath = config.blockListFilePath,
- mappingDir = config.mappingDir
+ mappingDir = config.mappingDir,
+ project = project
).doTransform(
classInputs = inputFiles,
changedFiles = changedFiles,
isIncremental = isIncremental,
+ skipCheckClass = config.skipCheckClass,
traceClassDirectoryOutput = File(config.traceClassOut),
inputToOutput = ConcurrentHashMap(),
legacyReplaceChangedFile = legacyReplaceChangedFile,
diff --git a/matrix/matrix-android/matrix-gradle-plugin/src/main/kotlin/com/tencent/matrix/plugin/transform/MatrixTraceTransform.kt b/matrix/matrix-android/matrix-gradle-plugin/src/main/kotlin/com/tencent/matrix/plugin/transform/MatrixTraceTransform.kt
index 1d626b471..4c6d43907 100644
--- a/matrix/matrix-android/matrix-gradle-plugin/src/main/kotlin/com/tencent/matrix/plugin/transform/MatrixTraceTransform.kt
+++ b/matrix/matrix-android/matrix-gradle-plugin/src/main/kotlin/com/tencent/matrix/plugin/transform/MatrixTraceTransform.kt
@@ -171,6 +171,7 @@ class MatrixTraceTransform(
.setMethodMapFilePath("$mappingOut/methodMapping.txt")
.setIgnoreMethodMapFilePath("$mappingOut/ignoreMethodMapping.txt")
.setMappingPath(mappingOut)
+ .setSkipCheckClass(extension.isSkipCheckClass)
.build()
}
@@ -236,11 +237,13 @@ class MatrixTraceTransform(
methodMapFilePath = config.methodMapFilePath,
baseMethodMapPath = config.baseMethodMapPath,
blockListFilePath = config.blockListFilePath,
- mappingDir = config.mappingDir
+ mappingDir = config.mappingDir,
+ project = project
).doTransform(
classInputs = inputFiles,
changedFiles = changedFiles,
isIncremental = isIncremental,
+ skipCheckClass = config.skipCheckClass,
traceClassDirectoryOutput = outputDirectory,
inputToOutput = inputToOutput,
legacyReplaceChangedFile = null,
diff --git a/matrix/matrix-android/matrix-hooks/CMakeLists.txt b/matrix/matrix-android/matrix-hooks/CMakeLists.txt
index 5fd216c4e..0d4509572 100644
--- a/matrix/matrix-android/matrix-hooks/CMakeLists.txt
+++ b/matrix/matrix-android/matrix-hooks/CMakeLists.txt
@@ -10,6 +10,7 @@ set(SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src/main/cpp)
find_library(log-lib, log)
add_subdirectory(${SOURCE_DIR}/external/libcJSON)
+add_subdirectory(${SOURCE_DIR}/external/fastunwind)
################################## Common Part ##################################
set(TARGET matrix-hookcommon)
@@ -94,3 +95,92 @@ target_link_libraries(
PRIVATE -Wl,--version-script=${SOURCE_DIR}/pthread/pthread.ver
)
#################################################################################
+
+################################### MemGuard ####################################
+set(TARGET memguard_base)
+
+add_library(
+ ${TARGET}
+ STATIC
+ ${SOURCE_DIR}/memguard/port/Hook.cpp
+ ${SOURCE_DIR}/memguard/port/Log.cpp
+ ${SOURCE_DIR}/memguard/port/Memory.cpp
+ ${SOURCE_DIR}/memguard/port/Mutex.cpp
+ ${SOURCE_DIR}/memguard/port/Paths.cpp
+ ${SOURCE_DIR}/memguard/port/Random.cpp
+ ${SOURCE_DIR}/memguard/port/Unwind.cpp
+ ${SOURCE_DIR}/memguard/port/FdSanWrapper.cpp
+ ${SOURCE_DIR}/memguard/util/SignalHandler.cpp
+ ${SOURCE_DIR}/memguard/util/Interception.cpp
+ ${SOURCE_DIR}/memguard/util/PagePool.cpp
+ ${SOURCE_DIR}/memguard/util/Allocation.cpp
+ ${SOURCE_DIR}/memguard/util/Thread.cpp
+ ${SOURCE_DIR}/memguard/util/Issue.cpp
+ ${SOURCE_DIR}/memguard/MemGuard.cpp
+)
+
+target_include_directories(
+ ${TARGET}
+ PRIVATE ${SOURCE_DIR}/memguard
+ PUBLIC ${SOURCE_DIR}
+ PUBLIC ${EXT_DEP}/include
+ PUBLIC ${EXT_DEP}/include/backtrace
+ PUBLIC ${EXT_DEP}/include/backtrace/common
+)
+
+target_compile_options(
+ ${TARGET}
+ PRIVATE -Wall -Wextra -Werror -Wno-unused-function
+ PRIVATE $<$:-std=c17>
+ PRIVATE $<$:-std=c++17>
+ PUBLIC -fvisibility=hidden -fno-exceptions -fno-rtti -fdata-sections -ffunction-sections
+ PUBLIC -DMEMGUARD_LOG_LEVEL=4
+)
+
+target_link_libraries(
+ ${TARGET}
+ PUBLIC -Wl,--gc-sections
+ PRIVATE ${log-lib}
+ PUBLIC matrix-hookcommon
+ PUBLIC wechatbacktrace
+ PUBLIC ${EXT_DEP}/lib/${ANDROID_ABI}/libunwindstack.a
+ PUBLIC fastunwind
+)
+
+
+set(TARGET matrix-memguard)
+
+add_library(
+ ${TARGET}
+ SHARED
+ ${SOURCE_DIR}/memguard/jni/com_tencent_mm_tools_memguard_MemGuard.cpp
+ ${SOURCE_DIR}/memguard/jni/com_tencent_mm_tools_memguard_MemGuard_00024Options.cpp
+ ${SOURCE_DIR}/memguard/jni/JNIAux.cpp
+ ${SOURCE_DIR}/memguard/jni/C2Java.cpp
+)
+
+target_include_directories(
+ ${TARGET}
+ PRIVATE ${SOURCE_DIR}/memguard
+ PRIVATE ${EXT_DEP}/include
+ PRIVATE ${EXT_DEP}/include/backtrace
+ PRIVATE ${EXT_DEP}/include/backtrace/common
+)
+
+target_compile_options(
+ ${TARGET}
+ PRIVATE -Wall -Wextra -Werror -Wno-unused-function
+ PRIVATE -fvisibility=hidden -fno-exceptions -fno-rtti -fdata-sections -ffunction-sections
+ PRIVATE $<$:-std=c17>
+ PRIVATE $<$:-std=c++17>
+)
+
+target_link_libraries(
+ ${TARGET}
+ PRIVATE -Wl,--gc-sections
+ PRIVATE -Wl,--version-script=${SOURCE_DIR}/memguard/memguard.map
+ PRIVATE ${log-lib}
+ PRIVATE matrix-hookcommon
+ PRIVATE memguard_base
+)
+#################################################################################
\ No newline at end of file
diff --git a/matrix/matrix-android/matrix-hooks/src/main/cpp/common/HookCommon.h b/matrix/matrix-android/matrix-hooks/src/main/cpp/common/HookCommon.h
index 01e3da96e..51455c0c4 100644
--- a/matrix/matrix-android/matrix-hooks/src/main/cpp/common/HookCommon.h
+++ b/matrix/matrix-android/matrix-hooks/src/main/cpp/common/HookCommon.h
@@ -32,6 +32,7 @@
#define HOOK_REQUEST_GROUPID_DLOPEN_MON 0x02
#define HOOK_REQUEST_GROUPID_MEMORY 0x03
#define HOOK_REQUEST_GROUPID_PTHREAD 0x04
+#define HOOK_REQUEST_GROUPID_MEMGUARD 0x05
#define GET_CALLER_ADDR(__caller_addr) \
void * __caller_addr = __builtin_return_address(0)
@@ -96,6 +97,7 @@
xhook_grouped_ignore(group_id, ".*/libmatrix-memoryhook\\.so$", NULL); \
xhook_grouped_ignore(group_id, ".*/libmatrix-pthreadhook\\.so$", NULL); \
xhook_grouped_ignore(group_id, ".*/libmatrix-opengl-leak\\.so$", NULL); \
+ xhook_grouped_ignore(group_id, ".*/libmatrix-memguard\\.so$", NULL); \
} while (0)
#include
diff --git a/matrix/matrix-android/matrix-hooks/src/main/cpp/common/Macros.h b/matrix/matrix-android/matrix-hooks/src/main/cpp/common/Macros.h
index 4c10331f3..16867ee4a 100644
--- a/matrix/matrix-android/matrix-hooks/src/main/cpp/common/Macros.h
+++ b/matrix/matrix-android/matrix-hooks/src/main/cpp/common/Macros.h
@@ -25,15 +25,11 @@
#define HOOK_LOG_ERROR(fmt, ...) //__android_log_print(ANDROID_LOG_ERROR, "TestHook", fmt, ##__VA_ARGS__)
#define USE_CRITICAL_CHECK true
-#define USE_MEMORY_MESSAGE_QUEUE true
-#define USE_SPLAY_MAP_SAVE_STACK true
-#define USE_STACK_HASH_NO_COLLISION true
-
-/* Incubating - currently no feasible performance(40% slower). */
-#define USE_MEMORY_MESSAGE_QUEUE_LOCK_FREE false
+#define USE_ALLOC_COUNTER true
/* For testing */
-#define USE_FAKE_BACKTRACE_DATA false
+#define ENABLE_FAKE_BACKTRACE_DATA false
+#define ENABLE_CHECK_MESSAGE_OVERFLOW false
#if USE_CRITICAL_CHECK == true
#define CRITICAL_CHECK(assertion) matrix::_hook_check(assertion)
@@ -42,13 +38,14 @@
#endif
#define SIZE_AUGMENT 192
+
+#define MEMORY_OVER_LIMIT (1024 * 1024 * 150L) // 150M
+
#define PROCESS_BUSY_INTERVAL 40 * 1000L
#define PROCESS_NORMAL_INTERVAL 150 * 1000L
#define PROCESS_LESS_NORMAL_INTERVAL 300 * 1000L
#define PROCESS_IDLE_INTERVAL 800 * 1000L
-#define MEMORY_OVER_LIMIT 1024 * 1024 * 200L // 200M
-
#define PTR_SPLAY_MAP_CAPACITY 10240
#define STACK_SPLAY_MAP_CAPACITY 1024
@@ -56,9 +53,13 @@
#define POINTER_MASK 48
+#ifndef LIKELY
#define LIKELY(cond) (__builtin_expect(!!(cond), 1))
+#endif
+#ifndef UNLIKELY
#define UNLIKELY(cond) (__builtin_expect(!!(cond), 0))
+#endif
#define EXPORT extern __attribute__ ((visibility ("default")))
@@ -71,5 +72,12 @@
abort(); \
}
+#if ENABLE_CHECK_MESSAGE_OVERFLOW == true
+#define CHECK_MESSAGE_OVERFLOW(assertion) HOOK_CHECK(assertion)
+#else
+#define CHECK_MESSAGE_OVERFLOW(assertion)
+#endif
+
+#define ReservedSize(AugmentExp) ((1 << AugmentExp))
#endif //LIBMATRIX_JNI_MACROS_H
diff --git a/matrix/matrix-android/matrix-hooks/src/main/cpp/common/SoLoadMonitor.h b/matrix/matrix-android/matrix-hooks/src/main/cpp/common/SoLoadMonitor.h
index 1dacd19ac..4f5bf76dd 100644
--- a/matrix/matrix-android/matrix-hooks/src/main/cpp/common/SoLoadMonitor.h
+++ b/matrix/matrix-android/matrix-hooks/src/main/cpp/common/SoLoadMonitor.h
@@ -6,10 +6,12 @@
#define MATRIX_ANDROID_SOLOADMONITOR_H
+#include "Macros.h"
+
namespace matrix {
typedef void (*so_load_callback_t)(const char *__file_name);
- extern bool InstallSoLoadMonitor();
+ EXPORT bool InstallSoLoadMonitor();
EXPORT void AddOnSoLoadCallback(so_load_callback_t cb);
EXPORT void PauseLoadSo();
EXPORT void ResumeLoadSo();
diff --git a/matrix/matrix-android/matrix-hooks/src/main/cpp/common/struct/buffer_source.h b/matrix/matrix-android/matrix-hooks/src/main/cpp/common/struct/buffer_source.h
index 1d55029b2..3e3bb2841 100644
--- a/matrix/matrix-android/matrix-hooks/src/main/cpp/common/struct/buffer_source.h
+++ b/matrix/matrix-android/matrix-hooks/src/main/cpp/common/struct/buffer_source.h
@@ -54,11 +54,15 @@ class buffer_source_memory : public buffer_source {
static std::atomic g_realloc_memory_counter;
virtual void *realloc(size_t new_size) {
- if (_buffer) g_realloc_counter.fetch_add(1);
+
+ if (_buffer) g_realloc_counter.fetch_add(1, std::memory_order_relaxed);
+
void *ptr = ::realloc(_buffer, new_size);
if (ptr != NULL) {
- g_realloc_memory_counter.fetch_sub(_buffer_size);
- g_realloc_memory_counter.fetch_add(new_size);
+
+ g_realloc_memory_counter.fetch_sub(_buffer_size, std::memory_order_relaxed);
+ g_realloc_memory_counter.fetch_add(new_size, std::memory_order_relaxed);
+
_buffer = ptr;
_buffer_size = new_size;
}
@@ -68,7 +72,9 @@ class buffer_source_memory : public buffer_source {
virtual void free() {
if (_buffer) {
- g_realloc_memory_counter.fetch_sub(_buffer_size);
+
+ g_realloc_memory_counter.fetch_sub(_buffer_size, std::memory_order_relaxed);
+
::free(_buffer);
_buffer = NULL;
_buffer_size = 0;
diff --git a/matrix/matrix-android/matrix-hooks/src/main/cpp/common/struct/lock_free_array_queue.h b/matrix/matrix-android/matrix-hooks/src/main/cpp/common/struct/lock_free_array_queue.h
new file mode 100644
index 000000000..bf791e201
--- /dev/null
+++ b/matrix/matrix-android/matrix-hooks/src/main/cpp/common/struct/lock_free_array_queue.h
@@ -0,0 +1,202 @@
+/*
+ * Tencent is pleased to support the open source community by making wechat-matrix available.
+ * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
+ * Licensed under the BSD 3-Clause License (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ *
+ * 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.
+ */
+
+#ifndef lock_free_array_queue_h
+#define lock_free_array_queue_h
+
+#include
+#include
+
+#include
+
+namespace matrix {
+
+#define CACHE_LINE_SIZE 64
+
+ template
+ class ObjectStorage {
+ public:
+ ObjectStorage() = default;
+ virtual ~ObjectStorage() = default;
+ virtual size_t provide() = 0;
+ virtual T *get(size_t idx) = 0;
+ };
+
+ template
+ class FixedObjectStorage : public ObjectStorage {
+ public:
+ FixedObjectStorage() = default;
+
+ virtual ~FixedObjectStorage() = default;
+
+ inline size_t provide() override {
+ size_t idx = available_.fetch_add(1);
+ if (UNLIKELY(idx >= Reserved)) {
+ available_.store(Reserved);
+ return SIZE_MAX;
+ }
+ return idx;
+ }
+
+ inline T *get(size_t idx) override {
+ return &objects[idx];
+ }
+
+ std::atomic available_ = 0;
+
+ private:
+ T objects[Reserved];
+ };
+
+ template
+ class SPMC_FixedFreeList {
+ public:
+ SPMC_FixedFreeList(): mask_(Reserved - 1), head_(Reserved), tail_(0) {
+ for (size_t i = 0; i < Reserved; i++) {
+ free_[i] = i;
+ }
+ };
+
+ virtual ~SPMC_FixedFreeList() = default;
+
+ uint32_t allocate_spmc() {
+
+ size_t idx;
+ for (;;) {
+ size_t head = head_.load(std::memory_order_acquire);
+ size_t tail = tail_.load(std::memory_order_acquire);
+ if (UNLIKELY(tail == head)) {
+ return INT_MAX;
+ }
+
+ idx = free_[tail & mask_];
+ if (tail_.compare_exchange_weak(tail, tail + 1)) {
+ break;
+ }
+ }
+ return idx;
+ }
+
+ void deallocate_spmc(uint32_t idx) {
+
+ size_t head = head_.load(std::memory_order_acquire);
+ free_[head & mask_] = idx;
+ bool ok = head_.compare_exchange_strong(head, head + 1);
+ HOOK_CHECK(ok);
+ return;
+ }
+
+ T * get(size_t idx) {
+ return storage_.get(idx);
+ }
+
+ private:
+
+ const size_t mask_;
+
+ FixedObjectStorage storage_;
+
+ char pad1[CACHE_LINE_SIZE - sizeof(std::atomic)];
+ std::atomic head_;
+ char pad2[CACHE_LINE_SIZE - sizeof(std::atomic)];
+ std::atomic tail_;
+ char pad3[CACHE_LINE_SIZE - sizeof(std::atomic)];
+
+ uint32_t free_[Reserved];
+ };
+
+ template
+ class LockFreeArrayQueue {
+ public:
+ LockFreeArrayQueue() : mask_(Reserved - 1), size_(Reserved), head_first_(0), head_second_(0), tail_first_(0) {
+ }
+
+ ~LockFreeArrayQueue() = default;
+
+ bool offer_mpsc(const T & t) {
+
+ uint32_t head, tail, next;
+
+ for (;;) {
+ head = head_first_.load(std::memory_order_acquire);
+ tail = tail_first_.load(std::memory_order_acquire);
+ if (UNLIKELY((head - tail) > mask_)) {
+ return false;
+ }
+ next = head + 1;
+ if (head_first_.compare_exchange_weak(head, next)) {
+ break;
+ }
+ }
+
+ messages_[head & mask_] = t;
+
+ for (;;) {
+ if (head_second_.compare_exchange_weak(head, next)) {
+ break;
+ } else {
+ pause();
+ }
+ }
+
+ return true;
+ }
+
+ bool poll_mpsc(T & ret) {
+
+ uint32_t tail, head;
+ int ok;
+
+ tail = tail_first_.load(std::memory_order_acquire);
+ head = head_second_.load(std::memory_order_acquire);
+
+ if (tail == head)
+ return false;
+
+ HOOK_CHECK(!(tail > head && (head - tail) > mask_));
+
+ ret = messages_[tail & mask_];
+ ok = tail_first_.compare_exchange_strong(tail, tail + 1);
+ HOOK_CHECK(ok)
+
+ return true;
+ }
+
+ void pause() {
+// sched_yield();
+// sleep(0);
+ };
+
+ private:
+ const uint32_t mask_;
+ const uint32_t size_;
+
+ char pad0[CACHE_LINE_SIZE - 2 * sizeof(uint32_t)];
+
+ std::atomic head_first_;
+ char pad1[CACHE_LINE_SIZE - sizeof(std::atomic)];
+ std::atomic head_second_;
+ char pad2[CACHE_LINE_SIZE - sizeof(std::atomic)];
+ std::atomic tail_first_;
+ char pad3[CACHE_LINE_SIZE - sizeof(std::atomic)];
+
+ T messages_[Reserved];
+ };
+
+
+
+}
+#endif /* lock_free_array_queue_h */
diff --git a/matrix/matrix-android/matrix-hooks/src/main/cpp/common/struct/lock_free_queue.h b/matrix/matrix-android/matrix-hooks/src/main/cpp/common/struct/lock_free_queue.h
index 2128eba26..2f8dbe8cb 100644
--- a/matrix/matrix-android/matrix-hooks/src/main/cpp/common/struct/lock_free_queue.h
+++ b/matrix/matrix-android/matrix-hooks/src/main/cpp/common/struct/lock_free_queue.h
@@ -341,36 +341,6 @@ namespace matrix {
}
}
- /*
- bool poll_mpsc(T & ret) {
-
- auto head = head_.load(std::memory_order_acquire);
- auto head_node = head.ptr_;
-
- for (;;) {
-
- auto tail = tail_.load(std::memory_order_acquire);
-
- auto next = head_node->next_.load(std::memory_order_acquire);
- auto next_node = next.ptr_;
-
- if (head == tail) {
- if (next_node == nullptr)
- return false;
- TaggedPointer> new_tail(next_node, tail.tag_ + 1);
- tail_.compare_exchange_strong(tail, new_tail);
- } else {
- ret = next_node->t_;
- TaggedPointer> new_head(next_node, head.tag_ + 1);
- head_.store(new_head);
- free_list_->deallocate(head_node);
- return true;
- }
-
- }
- }
- */
-
private:
std::atomic>> head_;
diff --git a/matrix/matrix-android/matrix-hooks/src/main/cpp/external/fastunwind/CMakeLists.txt b/matrix/matrix-android/matrix-hooks/src/main/cpp/external/fastunwind/CMakeLists.txt
new file mode 100644
index 000000000..5861a8539
--- /dev/null
+++ b/matrix/matrix-android/matrix-hooks/src/main/cpp/external/fastunwind/CMakeLists.txt
@@ -0,0 +1,17 @@
+cmake_minimum_required(VERSION 3.6)
+
+add_library(
+ fastunwind
+ STATIC
+ ${CMAKE_CURRENT_SOURCE_DIR}/FastUnwind.cpp
+)
+
+target_compile_options(
+ fastunwind
+ PRIVATE -Wall -Wextra -Werror -fvisibility=hidden
+)
+
+target_include_directories(
+ fastunwind
+ PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}
+)
\ No newline at end of file
diff --git a/matrix/matrix-android/matrix-hooks/src/main/cpp/external/fastunwind/FastUnwind.cpp b/matrix/matrix-android/matrix-hooks/src/main/cpp/external/fastunwind/FastUnwind.cpp
new file mode 100644
index 000000000..a1481b8e7
--- /dev/null
+++ b/matrix/matrix-android/matrix-hooks/src/main/cpp/external/fastunwind/FastUnwind.cpp
@@ -0,0 +1,130 @@
+//
+// Created by tomystang on 2020/12/15.
+//
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "FastUnwind.h"
+
+using namespace fastunwind;
+
+#define LIKELY(cond) __builtin_expect(!!(cond), 1)
+#define UNLIKELY(cond) __builtin_expect(!!(cond), 0)
+#define TLSVAR __thread __attribute__((tls_model("initial-exec")))
+
+#if 0 // defined(__aarch64__) arm 8.1a feature, disable so far to avoid compatibility issues in ndk r21.
+#define HWCAP2_MTE (1 << 18)
+
+struct ScopedDisableMTE {
+ size_t prevTCO = {};
+
+ ScopedDisableMTE() {
+ if (IsMTESupported()) {
+ __asm__ __volatile__(".arch_extension mte; mrs %0, tco; msr tco, #1" : "=r"(prevTCO));
+ }
+ }
+
+ ~ScopedDisableMTE() {
+ if (IsMTESupported()) {
+ __asm__ __volatile__(".arch_extension mte; msr tco, %0" : : "r"(prevTCO));
+ }
+ }
+
+ static inline bool IsMTESupported() {
+ return (::getauxval(AT_HWCAP2) & HWCAP2_MTE) != 0;
+ }
+};
+#else
+struct ScopedDisableMTE {
+ ScopedDisableMTE() = default;
+};
+#endif
+
+static uintptr_t TLSVAR sThreadStackTop = 0;
+
+static inline __attribute__((unused)) uintptr_t GetThreadStackTop() {
+ uintptr_t threadStackTop = sThreadStackTop;
+ if (UNLIKELY(threadStackTop == 0)) {
+ pthread_attr_t attr = {};
+ if (pthread_getattr_np(pthread_self(), &attr) != 0) {
+ return 0;
+ }
+ void* stackBase = nullptr;
+ size_t stackSize = 0;
+ pthread_attr_getstack(&attr, &stackBase, &stackSize);
+ threadStackTop = (uintptr_t) stackBase + stackSize;
+ sThreadStackTop = threadStackTop;
+ }
+ return threadStackTop;
+}
+
+static inline __attribute__((unused)) uintptr_t ClearPACBits(uintptr_t ptr) {
+#if defined(__aarch64__)
+ register uintptr_t x30 __asm("x30") = ptr;
+ // This is a NOP on pre-Armv8.3-A architectures.
+ asm("xpaclri" : "+r"(x30));
+ return x30;
+#else
+ return ptr;
+#endif
+}
+
+static int UnwindImpl(const void* begin_fp, void** pcs, size_t max_count) {
+ ScopedDisableMTE __attribute__((unused)) x;
+
+ auto begin = reinterpret_cast(begin_fp);
+ uintptr_t end = GetThreadStackTop();
+
+ stack_t ss = {};
+ if (UNLIKELY(::sigaltstack(nullptr, &ss) == 0 && (ss.ss_flags & SS_ONSTACK) != 0)) {
+ end = (uintptr_t) ss.ss_sp + ss.ss_size;
+ }
+
+ struct FrameRecord {
+ uintptr_t nextFrame;
+ uintptr_t returnAddr;
+ };
+
+ size_t numFrames = 0;
+ while (true) {
+ auto* frame = (FrameRecord*) begin;
+ if (frame->nextFrame < begin + sizeof(FrameRecord)
+ || frame->nextFrame >= end
+ || frame->nextFrame % sizeof(void*) != 0) {
+ break;
+ }
+ if (numFrames < max_count) {
+ pcs[numFrames++] = (void*) ClearPACBits(frame->returnAddr);
+ } else {
+ break;
+ }
+ begin = frame->nextFrame;
+ }
+ return numFrames;
+}
+
+int fastunwind::Unwind(void** pcs, size_t max_count) {
+ return UnwindImpl(__builtin_frame_address(0), pcs, max_count);
+}
+
+int fastunwind::Unwind(void* ucontext, void** pcs, size_t max_count) {
+#if defined (__aarch64__)
+ const void* begin_fp = *reinterpret_cast(reinterpret_cast(ucontext) + 432);
+ return UnwindImpl(begin_fp, pcs, max_count);
+#elif defined (__i386__)
+ const void* begin_fp = *reinterpret_cast(reinterpret_cast(ucontext) + 48);
+ return UnwindImpl(begin_fp, pcs, max_count);
+#else
+ (void) (ucontext);
+ (void) (pcs);
+ (void) (max_count);
+ return 0;
+#endif
+}
\ No newline at end of file
diff --git a/matrix/matrix-android/matrix-hooks/src/main/cpp/external/fastunwind/FastUnwind.h b/matrix/matrix-android/matrix-hooks/src/main/cpp/external/fastunwind/FastUnwind.h
new file mode 100644
index 000000000..463afa87f
--- /dev/null
+++ b/matrix/matrix-android/matrix-hooks/src/main/cpp/external/fastunwind/FastUnwind.h
@@ -0,0 +1,16 @@
+//
+// Created by tomystang on 2020/12/15.
+//
+
+#ifndef __MEMGUARD_FASTUNWIND_H__
+#define __MEMGUARD_FASTUNWIND_H__
+
+#include
+
+namespace fastunwind {
+ extern __attribute__((no_sanitize("address", "hwaddress"))) int Unwind(void** pcs, size_t max_count);
+ extern __attribute__((no_sanitize("address", "hwaddress"))) int Unwind(void* ucontext, void** pcs, size_t max_count);
+}
+
+
+#endif //__MEMGUARD_FASTUNWIND_H__
diff --git a/matrix/matrix-android/matrix-hooks/src/main/cpp/memguard/MemGuard.cpp b/matrix/matrix-android/matrix-hooks/src/main/cpp/memguard/MemGuard.cpp
new file mode 100644
index 000000000..e3c85f0d5
--- /dev/null
+++ b/matrix/matrix-android/matrix-hooks/src/main/cpp/memguard/MemGuard.cpp
@@ -0,0 +1,76 @@
+//
+// Created by tomystang on 2020/11/6.
+//
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "MemGuard.h"
+
+using namespace memguard;
+
+#define LOG_TAG "MemGuard.Native"
+
+memguard::Options memguard::gOpts;
+static std::atomic_flag sInstalled(false);
+
+bool memguard::Install(const Options* opts) {
+ if (opts == nullptr) {
+ LOGE(LOG_TAG, "opts == nullptr");
+ return false;
+ }
+
+ if (sInstalled.test_and_set()) {
+ LOGW(LOG_TAG, "Already installed.");
+ return true;
+ }
+
+ gOpts = *opts;
+
+ if (!paths::Exists(gOpts.issueDumpFilePath)) {
+ if (!paths::MakeDirs(paths::GetParent(gOpts.issueDumpFilePath))) {
+ LOGE(LOG_TAG, "Fail to create directory for issue dump file: %s", gOpts.issueDumpFilePath.c_str());
+ return false;
+ }
+ }
+
+ if (!matrix::InstallSoLoadMonitor()) {
+ LOGE(LOG_TAG, "Fail to install SoLoadMonitor.");
+ return false;
+ }
+
+ if (!pagepool::Prepare()) {
+ LOGE(LOG_TAG, "Fail to prepare page pool.");
+ return false;
+ }
+
+ if (!unwind::Initialize()) {
+ LOGE(LOG_TAG, "Fail to initialize stack unwinder.");
+ return false;
+ }
+
+ if (!signalhandler::Install()) {
+ LOGE(LOG_TAG, "Fail to install signal handler.");
+ return false;
+ }
+
+ if (!allocation::Prepare()) {
+ LOGE(LOG_TAG, "Fail to prepare allocation logic.");
+ return false;
+ }
+
+ if (!interception::Install()) {
+ LOGE(LOG_TAG, "Fail to install interceptors.");
+ return false;
+ }
+
+ LOGI(LOG_TAG, "MemGuard was installed.");
+ return true;
+}
diff --git a/matrix/matrix-android/matrix-hooks/src/main/cpp/memguard/MemGuard.h b/matrix/matrix-android/matrix-hooks/src/main/cpp/memguard/MemGuard.h
new file mode 100644
index 000000000..cb9ab7e49
--- /dev/null
+++ b/matrix/matrix-android/matrix-hooks/src/main/cpp/memguard/MemGuard.h
@@ -0,0 +1,18 @@
+//
+// Created by tomystang on 2020/11/6.
+//
+
+#ifndef __MEMGUARD_MEMGUARD_H__
+#define __MEMGUARD_MEMGUARD_H__
+
+
+#include
+#include
+#include "Options.h"
+
+namespace memguard {
+ extern bool Install(const Options* opts);
+}
+
+
+#endif //__MEMGUARD_MEMGUARD_H__
diff --git a/matrix/matrix-android/matrix-hooks/src/main/cpp/memguard/Options.h b/matrix/matrix-android/matrix-hooks/src/main/cpp/memguard/Options.h
new file mode 100644
index 000000000..809b089c2
--- /dev/null
+++ b/matrix/matrix-android/matrix-hooks/src/main/cpp/memguard/Options.h
@@ -0,0 +1,59 @@
+//
+// Created by tomystang on 2020/10/16.
+//
+
+#ifndef __MEMGUARD_OPTIONS_H__
+#define __MEMGUARD_OPTIONS_H__
+
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace memguard {
+ struct Options {
+ #define OPTION_ITEM(type, name, default_value, description) type name;
+ #include "Options.inc"
+ #undef OPTION_ITEM
+
+ Options() {
+ #define OPTION_ITEM(type, name, default_value, description) name = default_value;
+ #include "Options.inc"
+ #undef OPTION_ITEM
+ }
+
+ Options(const Options& other) {
+ #define OPTION_ITEM(type, name, default_value, description) this->name = other.name;
+ #include "Options.inc"
+ #undef OPTION_ITEM
+ }
+
+ Options(Options&& other) {
+ #define OPTION_ITEM(type, name, default_value, description) this->name = std::move(other.name);
+ #include "Options.inc"
+ #undef OPTION_ITEM
+ }
+
+ Options& operator =(const Options& rhs) {
+ #define OPTION_ITEM(type, name, default_value, description) this->name = rhs.name;
+ #include "Options.inc"
+ #undef OPTION_ITEM
+ return *this;
+ }
+
+ Options& operator =(Options&& rhs) {
+ #define OPTION_ITEM(type, name, default_value, description) this->name = std::move(rhs.name);
+ #include "Options.inc"
+ #undef OPTION_ITEM
+ return *this;
+ }
+ };
+
+ extern Options gOpts;
+}
+
+
+#endif //__MEMGUARD_OPTIONS_H__
diff --git a/matrix/matrix-android/matrix-hooks/src/main/cpp/memguard/Options.inc b/matrix/matrix-android/matrix-hooks/src/main/cpp/memguard/Options.inc
new file mode 100644
index 000000000..6f5b0233f
--- /dev/null
+++ b/matrix/matrix-android/matrix-hooks/src/main/cpp/memguard/Options.inc
@@ -0,0 +1,42 @@
+//
+// Created by tomystang on 2020/10/18.
+//
+
+OPTION_ITEM(size_t, maxAllocationSize, 64 * 1024,
+ "Max allocation size MemGuard can detect its under/overflow "
+ "issues.")
+
+OPTION_ITEM(size_t, maxDetectableAllocationCount, 16384,
+ "Max allocation count MemGuard can detect its under/overflow "
+ "issues.")
+
+OPTION_ITEM(size_t, maxSkippedAllocationCount, 5,
+ "Max skipped allocation count between two guarded allocations. "
+ "For example, if 5 was set to this option, MemGuard will generate a "
+ "random number 'k' in range [0,5] and the first k-th allocations "
+ "will be ignored.")
+
+OPTION_ITEM(size_t, percentageOfLeftSideGuard, 30,
+ "Probability of putting guard page on the left side of specific pointer. "
+ "For example, if 30 was set to this option, the probability of a pointer being "
+ "guarded on the left side will be 30%, and the probability of a pointer being guarded "
+ "on the right side will be 70%.")
+
+OPTION_ITEM(bool, perfectRightSideGuard, false,
+ "Whether MemGuard should return a pointer with guard page on right side without "
+ "gaps. If true was set to this option, overflow issue will be easier to be detected "
+ "but the returned pointer may not be aligned properly. Sometimes these not aligned "
+ "pointers can crash your app.")
+
+OPTION_ITEM(bool, ignoreOverlappedReading, false,
+ "Whether MemGuard should regard overlapped reading as an issue.")
+
+OPTION_ITEM(std::string, issueDumpFilePath, "",
+ "Path to write dump file when memory issue was detected. Leave it empty "
+ "will make MemGuard not dump issue info into file.")
+
+OPTION_ITEM(std::vector, targetSOPatterns, std::vector(),
+ "Patterns described by RegEx of target libs that we want to detect any memory issues.")
+
+OPTION_ITEM(std::vector, ignoredSOPatterns, std::vector(),
+ "Patterns described by RegEx of target libs that we want to skip for detecting any memory issues.")
diff --git a/matrix/matrix-android/matrix-hooks/src/main/cpp/memguard/jni/C2Java.cpp b/matrix/matrix-android/matrix-hooks/src/main/cpp/memguard/jni/C2Java.cpp
new file mode 100644
index 000000000..895b468b1
--- /dev/null
+++ b/matrix/matrix-android/matrix-hooks/src/main/cpp/memguard/jni/C2Java.cpp
@@ -0,0 +1,47 @@
+//
+// Created by tomystang on 2021/2/1.
+//
+
+#include
+#include
+#include
+#include "JNIAux.h"
+#include "C2Java.h"
+
+using namespace memguard;
+
+#define LOG_TAG "MemGuard.C2JAVA"
+
+static jclass sMemGuardClazz = nullptr;
+static jmethodID sMethodID_MemGuard_c2jNotifyOnIssueDumpped = nullptr;
+static std::atomic_flag sInstalled(false);
+
+#define PREPARE_METHODID(env, var, clazz, name, sig, is_static, failure_ret_val) \
+ do { \
+ if (!(var)) { \
+ if (is_static) { \
+ (var) = (env)->GetStaticMethodID(clazz, name, sig); \
+ } else { \
+ (var) = (env)->GetMethodID(clazz, name, sig); \
+ } \
+ RETURN_ON_EXCEPTION(env, failure_ret_val); \
+ } \
+ } while (false)
+
+bool memguard::c2j::Prepare(jclass memguard_clazz) {
+ if (sInstalled.test_and_set()) {
+ LOGW(LOG_TAG, "Already prepared.");
+ return true;
+ }
+ auto env = jni::GetEnv();
+ sMemGuardClazz = (jclass) env->NewGlobalRef(memguard_clazz);
+ PREPARE_METHODID(env, sMethodID_MemGuard_c2jNotifyOnIssueDumpped, memguard_clazz,
+ "c2jNotifyOnIssueDumped", "(Ljava/lang/String;)V", true, false);
+ return true;
+}
+
+void memguard::c2j::NotifyOnIssueDumpped(const char *dump_file) {
+ auto env = jni::GetEnv();
+ auto jDumpFile = env->NewStringUTF(dump_file);
+ env->CallStaticVoidMethod(sMemGuardClazz, sMethodID_MemGuard_c2jNotifyOnIssueDumpped, jDumpFile);
+}
\ No newline at end of file
diff --git a/matrix/matrix-android/matrix-hooks/src/main/cpp/memguard/jni/C2Java.h b/matrix/matrix-android/matrix-hooks/src/main/cpp/memguard/jni/C2Java.h
new file mode 100644
index 000000000..825d369c7
--- /dev/null
+++ b/matrix/matrix-android/matrix-hooks/src/main/cpp/memguard/jni/C2Java.h
@@ -0,0 +1,19 @@
+//
+// Created by tomystang on 2021/2/1.
+//
+
+#ifndef __MEMGUARD_C2JAVA_H__
+#define __MEMGUARD_C2JAVA_H__
+
+
+#include
+
+namespace memguard {
+ namespace c2j {
+ extern bool Prepare(jclass memguard_clazz);
+ extern void NotifyOnIssueDumpped(const char* dump_file);
+ }
+}
+
+
+#endif //__MEMGUARD_C2JAVA_H__
diff --git a/matrix/matrix-android/matrix-hooks/src/main/cpp/memguard/jni/JNIAux.cpp b/matrix/matrix-android/matrix-hooks/src/main/cpp/memguard/jni/JNIAux.cpp
new file mode 100644
index 000000000..c0036f83e
--- /dev/null
+++ b/matrix/matrix-android/matrix-hooks/src/main/cpp/memguard/jni/JNIAux.cpp
@@ -0,0 +1,60 @@
+//
+// Created by tomystang on 2020/11/18.
+//
+
+#include
+#include
+#include
+#include
+#include "JNIAux.h"
+
+using namespace memguard;
+
+#define LOG_TAG "MemGuard.JNIAux"
+
+static JavaVM* sVM;
+static pthread_once_t sInitOnceKey;
+static pthread_key_t sAttachFlagTLSKey;
+
+static jclass sClass_Throwable = nullptr;
+static jmethodID sMethodID_printStackTrace = nullptr;
+
+bool memguard::jni::InitializeEnv(JavaVM* vm) {
+ sVM = vm;
+
+ auto env = GetEnv();
+ sClass_Throwable = (jclass) env->NewGlobalRef(env->FindClass("java/lang/Throwable"));
+ RETURN_ON_EXCEPTION(env, false);
+ sMethodID_printStackTrace = env->GetMethodID(sClass_Throwable, "printStackTrace", "()V");
+ RETURN_ON_EXCEPTION(env, false);
+
+ LOGD(LOG_TAG, "InitializeEnv done.");
+ return true;
+}
+
+JNIEnv* memguard::jni::GetEnv() {
+ ASSERT(sVM != nullptr, "Not initialized.");
+ JNIEnv* result = nullptr;
+ auto ret = sVM->GetEnv((void**) &result, JNI_VERSION_1_6);
+ ASSERT(ret != JNI_EVERSION, "System is too old to be supported.");
+ if (ret != JNI_OK) {
+ pthread_once(&sInitOnceKey, []() {
+ pthread_key_create(&sAttachFlagTLSKey, [](void* flag) {
+ if (flag) {
+ sVM->DetachCurrentThread();
+ }
+ });
+ });
+ if (sVM->AttachCurrentThread(&result, nullptr) == JNI_OK) {
+ pthread_setspecific(sAttachFlagTLSKey, (void*) 1);
+ } else {
+ result = nullptr;
+ }
+ }
+ ASSERT(result != nullptr, "Fail to get JNIEnv on current thread (%d).", gettid());
+ return result;
+}
+
+void memguard::jni::PrintStackTrace(JNIEnv *env, jthrowable thr) {
+ env->CallVoidMethod(thr, sMethodID_printStackTrace);
+}
\ No newline at end of file
diff --git a/matrix/matrix-android/matrix-hooks/src/main/cpp/memguard/jni/JNIAux.h b/matrix/matrix-android/matrix-hooks/src/main/cpp/memguard/jni/JNIAux.h
new file mode 100644
index 000000000..7eeab7e58
--- /dev/null
+++ b/matrix/matrix-android/matrix-hooks/src/main/cpp/memguard/jni/JNIAux.h
@@ -0,0 +1,48 @@
+//
+// Created by tomystang on 2020/11/18.
+//
+
+#ifndef __MEMGUARD_JNIAUX_H__
+#define __MEMGUARD_JNIAUX_H__
+
+
+#include
+#include
+#include
+#include
+#include
+
+namespace memguard {
+ namespace jni {
+ extern bool InitializeEnv(JavaVM* vm);
+ extern JNIEnv* GetEnv();
+
+ template ::value, void>::type>
+ ScopedObject> MakeScopedLocalRef(TRef ref) {
+ auto dtor_lambda = [](TRef ref) {
+ auto env = GetEnv();
+ if (ref != nullptr && env->GetObjectRefType(ref) == JNILocalRefType) {
+ env->DeleteLocalRef(ref);
+ }
+ };
+ return MakeScopedObject(std::forward(ref), std::function(dtor_lambda));
+ }
+
+ extern void PrintStackTrace(JNIEnv* env, jthrowable thr);
+ }
+}
+
+#define RETURN_ON_EXCEPTION(env, ret_value) \
+ do { \
+ if ((env)->ExceptionCheck()) { \
+ return (ret_value); \
+ } \
+ } while (false)
+
+#define JNI_METHOD(name) __JNI_METHOD_1(JNI_CLASS_PREFIX, name)
+#define __JNI_METHOD_1(prefix, name) __JNI_METHOD_2(prefix, name)
+#define __JNI_METHOD_2(prefix, name) JNIEXPORT Java_##prefix##_##name
+
+
+#endif //__MEMGUARD_JNIAUX_H__
diff --git a/matrix/matrix-android/matrix-hooks/src/main/cpp/memguard/jni/com_tencent_mm_tools_memguard_MemGuard.cpp b/matrix/matrix-android/matrix-hooks/src/main/cpp/memguard/jni/com_tencent_mm_tools_memguard_MemGuard.cpp
new file mode 100644
index 000000000..ae86ecdde
--- /dev/null
+++ b/matrix/matrix-android/matrix-hooks/src/main/cpp/memguard/jni/com_tencent_mm_tools_memguard_MemGuard.cpp
@@ -0,0 +1,48 @@
+//
+// Created by tomystang on 2020/10/15.
+//
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "JNIAux.h"
+#include "com_tencent_mm_tools_memguard_MemGuard_00024Options.h"
+#include "C2Java.h"
+
+using namespace memguard;
+
+#define LOG_TAG "MemGuard.JNI"
+
+#define JNI_CLASS_PREFIX com_tencent_matrix_memguard_MemGuard
+
+extern "C" jint JNI_OnLoad(JavaVM* vm, void*) {
+ if (!jni::InitializeEnv(vm)) {
+ LOGE(LOG_TAG, "Fail to initialize jni environment.");
+ return JNI_ERR;
+ }
+ LOGI(LOG_TAG, "Libmemguard was loaded.");
+ return JNI_VERSION_1_6;
+}
+
+extern "C" jboolean JNI_METHOD(nativeInstall)(JNIEnv* env, jclass memguard_clazz, jobject opts) {
+ Options raw_opts;
+ jni::FillOptWithJavaOptions(env, opts, &raw_opts);
+ c2j::Prepare(memguard_clazz);
+ if (Install(&raw_opts)) {
+ return JNI_TRUE;
+ } else {
+ return JNI_FALSE;
+ }
+}
+
+extern "C" jstring JNI_METHOD(nativeGetIssueDumpFilePath)(JNIEnv* env, jclass) {
+ if (gOpts.issueDumpFilePath.empty()) {
+ return nullptr;
+ }
+ return env->NewStringUTF(gOpts.issueDumpFilePath.c_str());
+}
\ No newline at end of file
diff --git a/matrix/matrix-android/matrix-hooks/src/main/cpp/memguard/jni/com_tencent_mm_tools_memguard_MemGuard_00024Options.cpp b/matrix/matrix-android/matrix-hooks/src/main/cpp/memguard/jni/com_tencent_mm_tools_memguard_MemGuard_00024Options.cpp
new file mode 100644
index 000000000..12f46e049
--- /dev/null
+++ b/matrix/matrix-android/matrix-hooks/src/main/cpp/memguard/jni/com_tencent_mm_tools_memguard_MemGuard_00024Options.cpp
@@ -0,0 +1,126 @@
+//
+// Created by tomystang on 2020/10/15.
+//
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include "com_tencent_mm_tools_memguard_MemGuard_00024Options.h"
+
+using namespace memguard;
+
+static jfieldID sFieldID_MemGuard$Options_maxAllocationSize = nullptr;
+static jfieldID sFieldID_MemGuard$Options_maxDetectableAllocationCount = nullptr;
+static jfieldID sFieldID_MemGuard$Options_maxSkippedAllocationCount = nullptr;
+static jfieldID sFieldID_MemGuard$Options_percentageOfLeftSideGuard = nullptr;
+static jfieldID sFieldID_MemGuard$Options_perfectRightSideGuard = nullptr;
+static jfieldID sFieldID_MemGuard$Options_ignoreOverlappedReading = nullptr;
+static jfieldID sFieldID_MemGuard$Options_issueDumpFilePath = nullptr;
+static jfieldID sFieldID_MemGuard$Options_targetSOPatterns = nullptr;
+static jfieldID sFieldID_MemGuard$Options_ignoredSOPatterns = nullptr;
+
+#define PREPARE_FIELDID(env, var, clazz, name, sig, failure_ret_val) \
+ do { \
+ if (!(var)) { \
+ (var) = (env)->GetFieldID(clazz, name, sig); \
+ RETURN_ON_EXCEPTION(env, failure_ret_val); \
+ } \
+ } while (false)
+
+bool memguard::jni::FillOptWithJavaOptions(JNIEnv *env, jobject jopts, Options* opts) {
+ ASSERT(jopts != nullptr, "jopts is null.");
+ ASSERT(opts != nullptr, "opts is null.");
+
+ auto jopts_class = MakeScopedLocalRef(env->GetObjectClass(jopts));
+ RETURN_ON_EXCEPTION(env, false);
+
+ PREPARE_FIELDID(env, sFieldID_MemGuard$Options_maxAllocationSize, jopts_class.get(),
+ "maxAllocationSize", "I", false);
+ opts->maxAllocationSize = env->GetIntField(jopts, sFieldID_MemGuard$Options_maxAllocationSize);
+ RETURN_ON_EXCEPTION(env, false);
+
+ PREPARE_FIELDID(env, sFieldID_MemGuard$Options_maxDetectableAllocationCount, jopts_class.get(),
+ "maxDetectableAllocationCount", "I", false);
+ opts->maxDetectableAllocationCount = env->GetIntField(jopts, sFieldID_MemGuard$Options_maxDetectableAllocationCount);
+ RETURN_ON_EXCEPTION(env, false);
+
+ PREPARE_FIELDID(env, sFieldID_MemGuard$Options_maxSkippedAllocationCount, jopts_class.get(),
+ "maxSkippedAllocationCount", "I", false);
+ opts->maxSkippedAllocationCount = env->GetIntField(jopts, sFieldID_MemGuard$Options_maxSkippedAllocationCount);
+ RETURN_ON_EXCEPTION(env, false);
+
+ PREPARE_FIELDID(env, sFieldID_MemGuard$Options_percentageOfLeftSideGuard, jopts_class.get(),
+ "percentageOfLeftSideGuard", "I", false);
+ opts->percentageOfLeftSideGuard = env->GetIntField(jopts, sFieldID_MemGuard$Options_percentageOfLeftSideGuard);
+ RETURN_ON_EXCEPTION(env, false);
+
+ PREPARE_FIELDID(env, sFieldID_MemGuard$Options_perfectRightSideGuard, jopts_class.get(),
+ "perfectRightSideGuard", "Z", false);
+ opts->perfectRightSideGuard = env->GetBooleanField(jopts, sFieldID_MemGuard$Options_perfectRightSideGuard);
+ RETURN_ON_EXCEPTION(env, false);
+
+ PREPARE_FIELDID(env, sFieldID_MemGuard$Options_ignoreOverlappedReading, jopts_class.get(),
+ "ignoreOverlappedReading", "Z", false);
+ opts->ignoreOverlappedReading = env->GetBooleanField(jopts, sFieldID_MemGuard$Options_ignoreOverlappedReading);
+ RETURN_ON_EXCEPTION(env, false);
+
+ PREPARE_FIELDID(env, sFieldID_MemGuard$Options_issueDumpFilePath, jopts_class.get(),
+ "issueDumpFilePath", "Ljava/lang/String;", false);
+ auto jIssueDumpFilePath = MakeScopedLocalRef(
+ (jstring) env->GetObjectField(jopts, sFieldID_MemGuard$Options_issueDumpFilePath));
+ RETURN_ON_EXCEPTION(env, false);
+ if (jIssueDumpFilePath.get() != nullptr) {
+ auto cIssueDumpFilePath = env->GetStringUTFChars(jIssueDumpFilePath.get(), nullptr);
+ if (cIssueDumpFilePath != nullptr) {
+ opts->issueDumpFilePath = cIssueDumpFilePath;
+ }
+ env->ReleaseStringUTFChars(jIssueDumpFilePath.get(), cIssueDumpFilePath);
+ }
+
+ PREPARE_FIELDID(env, sFieldID_MemGuard$Options_targetSOPatterns, jopts_class.get(),
+ "targetSOPatterns", "[Ljava/lang/String;", false);
+ auto j_target_so_patterns_array = MakeScopedLocalRef(
+ (jobjectArray) env->GetObjectField(jopts, sFieldID_MemGuard$Options_targetSOPatterns));
+ RETURN_ON_EXCEPTION(env, false);
+ if (j_target_so_patterns_array.get() != nullptr) {
+ jint pattern_count = env->GetArrayLength(j_target_so_patterns_array.get());
+ for (int i = 0; i < pattern_count; ++i) {
+ auto j_pattern = MakeScopedLocalRef((jstring) env->GetObjectArrayElement(
+ j_target_so_patterns_array.get(), i));
+ RETURN_ON_EXCEPTION(env, false);
+ if (j_pattern.get() == nullptr) {
+ continue;
+ }
+ jsize mutf_len = env->GetStringUTFLength(j_pattern.get());
+ const char* mutf_pattern = env->GetStringUTFChars(j_pattern.get(), nullptr);
+ opts->targetSOPatterns.emplace_back(std::string(mutf_pattern, mutf_len));
+ env->ReleaseStringUTFChars(j_pattern.get(), mutf_pattern);
+ }
+ }
+
+ PREPARE_FIELDID(env, sFieldID_MemGuard$Options_ignoredSOPatterns, jopts_class.get(),
+ "ignoredSOPatterns", "[Ljava/lang/String;", false);
+ auto j_ignored_so_patterns_array = MakeScopedLocalRef(
+ (jobjectArray) env->GetObjectField(jopts, sFieldID_MemGuard$Options_ignoredSOPatterns));
+ RETURN_ON_EXCEPTION(env, false);
+ if (j_ignored_so_patterns_array.get() != nullptr) {
+ jint pattern_count = env->GetArrayLength(j_ignored_so_patterns_array.get());
+ for (int i = 0; i < pattern_count; ++i) {
+ auto j_pattern = MakeScopedLocalRef((jstring) env->GetObjectArrayElement(
+ j_ignored_so_patterns_array.get(), i));
+ RETURN_ON_EXCEPTION(env, false);
+ if (j_pattern.get() == nullptr) {
+ continue;
+ }
+ jsize mutf_len = env->GetStringUTFLength(j_pattern.get());
+ const char* mutf_pattern = env->GetStringUTFChars(j_pattern.get(), nullptr);
+ opts->ignoredSOPatterns.emplace_back(std::string(mutf_pattern, mutf_len));
+ env->ReleaseStringUTFChars(j_pattern.get(), mutf_pattern);
+ }
+ }
+
+ return true;
+}
\ No newline at end of file
diff --git a/matrix/matrix-android/matrix-hooks/src/main/cpp/memguard/jni/com_tencent_mm_tools_memguard_MemGuard_00024Options.h b/matrix/matrix-android/matrix-hooks/src/main/cpp/memguard/jni/com_tencent_mm_tools_memguard_MemGuard_00024Options.h
new file mode 100644
index 000000000..9a11fc159
--- /dev/null
+++ b/matrix/matrix-android/matrix-hooks/src/main/cpp/memguard/jni/com_tencent_mm_tools_memguard_MemGuard_00024Options.h
@@ -0,0 +1,18 @@
+//
+// Created by tomystang on 2020/11/19.
+//
+
+#ifndef __MEMGUARD_COM_TENCENT_MM_TOOLS_MEMGUARD_MEMGUARD_00024OPTIONS_H__
+#define __MEMGUARD_COM_TENCENT_MM_TOOLS_MEMGUARD_MEMGUARD_00024OPTIONS_H__
+
+
+#include
+
+namespace memguard {
+ namespace jni {
+ extern bool FillOptWithJavaOptions(JNIEnv *env, jobject jopts, Options* opts);
+ }
+}
+
+
+#endif //__MEMGUARD_COM_TENCENT_MM_TOOLS_MEMGUARD_MEMGUARD_00024OPTIONS_H__
diff --git a/matrix/matrix-android/matrix-hooks/src/main/cpp/memguard/memguard.map b/matrix/matrix-android/matrix-hooks/src/main/cpp/memguard/memguard.map
new file mode 100644
index 000000000..ddea0d383
--- /dev/null
+++ b/matrix/matrix-android/matrix-hooks/src/main/cpp/memguard/memguard.map
@@ -0,0 +1,7 @@
+memguard {
+ global:
+ JNI_OnLoad;
+ Java_*;
+ local:
+ *;
+};
\ No newline at end of file
diff --git a/matrix/matrix-android/matrix-hooks/src/main/cpp/memguard/port/FdSanWrapper.cpp b/matrix/matrix-android/matrix-hooks/src/main/cpp/memguard/port/FdSanWrapper.cpp
new file mode 100644
index 000000000..62d3317f4
--- /dev/null
+++ b/matrix/matrix-android/matrix-hooks/src/main/cpp/memguard/port/FdSanWrapper.cpp
@@ -0,0 +1,23 @@
+//
+// Created by YinSheng Tang on 2021/9/22.
+//
+
+#include "util/FdSanWrapper.h"
+
+extern "C" enum android_fdsan_error_level android_fdsan_get_error_level() __attribute__((__weak__));
+
+extern "C" enum android_fdsan_error_level android_fdsan_set_error_level(enum android_fdsan_error_level new_level) __attribute__((__weak__));
+
+android_fdsan_error_level AndroidFdSanGetErrorLevel() {
+ if (!android_fdsan_get_error_level) {
+ return android_fdsan_error_level::ANDROID_FDSAN_ERROR_LEVEL_DISABLED;
+ }
+ return android_fdsan_get_error_level();
+}
+
+android_fdsan_error_level AndroidFdSanSetErrorLevel(enum android_fdsan_error_level new_level) {
+ if (!android_fdsan_set_error_level) {
+ return android_fdsan_error_level::ANDROID_FDSAN_ERROR_LEVEL_DISABLED;
+ }
+ return android_fdsan_set_error_level(new_level);
+}
\ No newline at end of file
diff --git a/matrix/matrix-android/matrix-hooks/src/main/cpp/memguard/port/Hook.cpp b/matrix/matrix-android/matrix-hooks/src/main/cpp/memguard/port/Hook.cpp
new file mode 100644
index 000000000..cf5deac03
--- /dev/null
+++ b/matrix/matrix-android/matrix-hooks/src/main/cpp/memguard/port/Hook.cpp
@@ -0,0 +1,75 @@
+//
+// Created by tomystang on 2020/10/15.
+//
+
+#include
+#include