aboutsummaryrefslogtreecommitdiff
path: root/org.jacoco.agent.rt/src/org/jacoco/agent/rt/internal/Offline.java
diff options
context:
space:
mode:
authorOliver Nguyen <olivernguyen@google.com>2020-07-10 10:46:29 -0700
committerOliver Nguyen <olivernguyen@google.com>2020-09-15 15:58:51 -0700
commitcb523bf186baa02b7478c81a7a64d2a2ee3d6b5f (patch)
tree63b392b15575674d7208746579d8d12e9dff0a13 /org.jacoco.agent.rt/src/org/jacoco/agent/rt/internal/Offline.java
parent774bc62453f5790fc072f2856c019b6571117c56 (diff)
downloadjacoco-cb523bf186baa02b7478c81a7a64d2a2ee3d6b5f.tar.gz
Implement memory-mapped ExecutionData class.
This allows apps to write coverage to disk during execution and not require any flushing mechanism in order to collect coverage. The results will be left in /data/misc/trace/jacoco-*.mm.ec and can be collected later for coverage analysis. These .mm.ec files conform to the current exec file format and can be loaded with the command-line tool. Test: m -j EMMA_INSTRUMENT=true EMMA_INSTRUMENT_FRAMEWORK=true and generate coverage report from /data/misc/trace Bug: 147904124 Change-Id: If08492405b86a063ae714c259a2fb954bc5aa946
Diffstat (limited to 'org.jacoco.agent.rt/src/org/jacoco/agent/rt/internal/Offline.java')
-rw-r--r--org.jacoco.agent.rt/src/org/jacoco/agent/rt/internal/Offline.java76
1 files changed, 75 insertions, 1 deletions
diff --git a/org.jacoco.agent.rt/src/org/jacoco/agent/rt/internal/Offline.java b/org.jacoco.agent.rt/src/org/jacoco/agent/rt/internal/Offline.java
index ee9d40b1..445722af 100644
--- a/org.jacoco.agent.rt/src/org/jacoco/agent/rt/internal/Offline.java
+++ b/org.jacoco.agent.rt/src/org/jacoco/agent/rt/internal/Offline.java
@@ -11,6 +11,8 @@
*******************************************************************************/
package org.jacoco.agent.rt.internal;
+import java.io.File;
+import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
@@ -18,6 +20,7 @@ import java.util.Properties;
import org.jacoco.core.data.ExecutionData;
import org.jacoco.core.data.IExecutionData;
import org.jacoco.core.data.ExecutionDataStore;
+import org.jacoco.core.data.MappedExecutionData;
import org.jacoco.core.runtime.AgentOptions;
import org.jacoco.core.runtime.RuntimeData;
@@ -30,6 +33,7 @@ public final class Offline {
// BEGIN android-change
// private static final RuntimeData DATA;
private static final Map<Long, IExecutionData> DATA = new HashMap<Long, IExecutionData>();
+ private static int PID = -1;
// END android-change
private static final String CONFIG_RESOURCE = "/jacoco-agent.properties";
@@ -64,7 +68,49 @@ public final class Offline {
synchronized (DATA) {
IExecutionData entry = DATA.get(classid);
if (entry == null) {
- entry = new ExecutionData(classid, classname, probecount);
+ // BEGIN android-change
+ // The list below are not allowed to use the memory-mapped
+ // implementation due either:
+ // 1) They are loaded at VM initialization time.
+ // a) android.*
+ // b) dalvik.*
+ // c) libcore.*
+ // 2) They are used by the memory-mapped execution data
+ // implementation, which would cause a stack overflow due
+ // to the circular dependency.
+ // a) com.android.i18n.*
+ // b) com.android.icu.*
+ // c) java.*
+ // d) org.apache.*
+ // e) sun.*
+ // These classes can still have coverage collected through the
+ // normal execution data process, but requires a flush to be done
+ // instead of simply being available on disk at all times.
+ if (classname.startsWith("android/")
+ || classname.startsWith("com/android/i18n/")
+ || classname.startsWith("com/android/icu/")
+ || classname.startsWith("dalvik/")
+ || classname.startsWith("java/")
+ || classname.startsWith("libcore/")
+ || classname.startsWith("org/apache/")
+ || classname.startsWith("sun/")) {
+ entry = new ExecutionData(classid, classname, probecount);
+ } else {
+ try {
+ int pid = getPid();
+ if (PID != pid) {
+ PID = pid;
+ rebuildExecutionData(pid);
+ }
+ entry = new MappedExecutionData(
+ classid, classname, probecount);
+ } catch (IOException e) {
+ // Fall back to non-memory-mapped execution data.
+ entry = new ExecutionData(
+ classid, classname, probecount);
+ }
+ }
+ // END android-change
DATA.put(classid, entry);
} else {
entry.assertCompatibility(classid, classname, probecount);
@@ -72,6 +118,34 @@ public final class Offline {
return entry;
}
}
+
+ private static void rebuildExecutionData(int pid) throws IOException {
+ MappedExecutionData.prepareFile(pid);
+ synchronized (DATA) {
+ for (IExecutionData execData : DATA.values()) {
+ if (execData instanceof MappedExecutionData) {
+ // Create new instances of MappedExecutionData using the
+ // new file, but don't copy the old data. Old data will
+ // remain in its existing file and can be merged during
+ // post-processing.
+ DATA.put(
+ execData.getId(),
+ new MappedExecutionData(
+ execData.getId(),
+ execData.getName(),
+ execData.getProbeCount()));
+ }
+ }
+ }
+ }
+
+ /**
+ * Helper function to determine the pid of this process.
+ */
+ private static int getPid() throws IOException {
+ // Read /proc/self and resolve it to obtain its pid.
+ return Integer.parseInt(new File("/proc/self").getCanonicalFile().getName());
+ }
// END android-change
/**