diff options
author | Oliver Nguyen <olivernguyen@google.com> | 2020-07-10 10:46:29 -0700 |
---|---|---|
committer | Oliver Nguyen <olivernguyen@google.com> | 2020-09-15 15:58:51 -0700 |
commit | cb523bf186baa02b7478c81a7a64d2a2ee3d6b5f (patch) | |
tree | 63b392b15575674d7208746579d8d12e9dff0a13 /org.jacoco.agent.rt/src/org/jacoco/agent/rt/internal/Offline.java | |
parent | 774bc62453f5790fc072f2856c019b6571117c56 (diff) | |
download | jacoco-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.java | 76 |
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 /** |