diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2021-06-23 02:30:49 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2021-06-23 02:30:49 +0000 |
commit | f5d4437fd0945e9601628077cd6f5c3cf510d4f8 (patch) | |
tree | 362a5a2b6e57fc8955d3af74ce2cafbbca7f65b3 | |
parent | 24e6c281dfeedde6fdb8a1cdd0f9f3b4b30f3697 (diff) | |
parent | e806b919fa39dea80bb0aa330d568c5464407e9e (diff) | |
download | jacoco-android12-mainline-neuralnetworks-release.tar.gz |
Snap for 7483611 from e806b919fa39dea80bb0aa330d568c5464407e9e to mainline-neuralnetworks-releaseandroid-mainline-12.0.0_r92android-mainline-12.0.0_r78android-mainline-12.0.0_r50android-mainline-12.0.0_r33android-mainline-12.0.0_r3android12-mainline-neuralnetworks-release
Change-Id: I9df3c2333ac69099837fe9d6ed412b2480b5a237
24 files changed, 907 insertions, 87 deletions
@@ -18,6 +18,44 @@ // // Note: this is only intended to be used for the platform development. This is *not* intended // to be used in the SDK where apps can use the official jacoco release. +package { + default_applicable_licenses: ["external_jacoco_license"], +} + +// Added automatically by a large-scale-change that took the approach of +// 'apply every license found to every target'. While this makes sure we respect +// every license restriction, it may not be entirely correct. +// +// e.g. GPL in an MIT project might only apply to the contrib/ directory. +// +// Please consider splitting the single license below into multiple licenses, +// taking care not to lose any license_kind information, and overriding the +// default license using the 'licenses: [...]' property on targets as needed. +// +// For unused files, consider creating a 'fileGroup' with "//visibility:private" +// to attach the license to, and including a comment whether the files may be +// used in the current project. +// +// large-scale-change included anything that looked like it might be a license +// text as a license_text. e.g. LICENSE, NOTICE, COPYING etc. +// +// Please consider removing redundant or irrelevant files from 'license_text:'. +// See: http://go/android-license-faq +license { + name: "external_jacoco_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + "SPDX-license-identifier-BSD", + "SPDX-license-identifier-EPL", + "SPDX-license-identifier-MIT", + ], + license_text: [ + "LICENSE.md", + "NOTICE", + ], +} + java_library { name: "jacocoagent", installable: true, @@ -66,7 +104,10 @@ java_library { // Generates stubs containing the classes that will be referenced by instrumented bytecode. droidstubs { name: "jacoco-stubs-gen", - srcs: ["org.jacoco.agent.rt/src/org/jacoco/agent/rt/internal/Offline.java"], + srcs: [ + "org.jacoco.agent.rt/src/org/jacoco/agent/rt/internal/Offline.java", + "org.jacoco.core/src/org/jacoco/core/data/IExecutionData.java", + ], } // A stubs target containing the parts of JaCoCo that we need to add to the hidden API whitelist. @@ -10,6 +10,7 @@ third_party { value: "https://github.com/jacoco/jacoco.git" } version: "v0.8.4" + license_type: RECIPROCAL last_upgrade_date { year: 2019 month: 5 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 69a9909e..99a054e7 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,12 +11,21 @@ *******************************************************************************/ package org.jacoco.agent.rt.internal; +import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.channels.FileChannel; +import java.nio.ByteBuffer; import java.util.HashMap; import java.util.Map; import java.util.Properties; import org.jacoco.core.data.ExecutionData; +import org.jacoco.core.data.ExecutionDataWriter; +import org.jacoco.core.data.ExecutionDataDelegate; +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; @@ -28,7 +37,8 @@ public final class Offline { // BEGIN android-change // private static final RuntimeData DATA; - private static final Map<Long, ExecutionData> DATA = new HashMap<Long, ExecutionData>(); + private static final Map<Long, ExecutionDataDelegate> DATA = new HashMap<Long, ExecutionDataDelegate>(); + private static FileChannel CHANNEL; // END android-change private static final String CONFIG_RESOURCE = "/jacoco-agent.properties"; @@ -44,6 +54,7 @@ public final class Offline { // no instances } + // BEGIN android-change /** * API for offline instrumented classes. * @@ -53,27 +64,69 @@ public final class Offline { * VM class name * @param probecount * probe count for this class - * @return probe array instance for this class + * @return IExecutionData instance for this class */ - public static boolean[] getProbes(final long classid, + public static IExecutionData getExecutionData(final long classid, final String classname, final int probecount) { - // BEGIN android-change // return DATA.getExecutionData(Long.valueOf(classid), classname, // probecount).getProbes(); synchronized (DATA) { - ExecutionData entry = DATA.get(classid); + ExecutionDataDelegate entry = DATA.get(classid); if (entry == null) { - entry = new ExecutionData(classid, classname, probecount); + entry = new ExecutionDataDelegate( + classid, classname, probecount, CHANNEL); DATA.put(classid, entry); } else { entry.assertCompatibility(classid, classname, probecount); } - return entry.getProbes(); + return entry; } - // END android-change } - // BEGIN android-change + /** + * Enables memory-mapped execution data and converts existing + * {@link ExecutionDataDelegate}s. + */ + public static void enableMemoryMappedData() { + try { + prepareFile(getPid()); + for (ExecutionDataDelegate data : DATA.values()) { + data.convert(CHANNEL); + } + } catch (IOException e) { + // TODO(olivernguyen): Add logging to debug issues more easily. + } + } + + /** + * Creates the output file that will be mapped for probe data. + */ + private static void prepareFile(int pid) throws IOException { + // Write header information to the file. + ByteBuffer headerBuffer = ByteBuffer.allocate(5); + headerBuffer.put(ExecutionDataWriter.BLOCK_HEADER); + headerBuffer.putChar(ExecutionDataWriter.MAGIC_NUMBER); + headerBuffer.putChar(ExecutionDataWriter.FORMAT_VERSION); + headerBuffer.flip(); + + // If this file already exists (due to pid re-usage), the previous coverage data + // will be lost when the file is overwritten. + File outputFile = new File("/data/misc/trace/jacoco-" + pid + ".mm.ec"); + CHANNEL = new RandomAccessFile(outputFile, "rw").getChannel(); + synchronized (CHANNEL) { + CHANNEL.write(headerBuffer); + } + } + + /** + * 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 + /** * Creates a default agent, using config loaded from the classpath resource and the system * properties, and a runtime data instance populated with the execution data accumulated by @@ -87,7 +140,7 @@ public final class Offline { System.getProperties()); synchronized (DATA) { ExecutionDataStore store = new ExecutionDataStore(); - for (ExecutionData data : DATA.values()) { + for (IExecutionData data : DATA.values()) { store.put(data); } return Agent.getInstance(new AgentOptions(config), new RuntimeData(store)); diff --git a/org.jacoco.cli/src/org/jacoco/cli/internal/commands/ExecInfo.java b/org.jacoco.cli/src/org/jacoco/cli/internal/commands/ExecInfo.java index cacf4cbe..4519d28f 100644 --- a/org.jacoco.cli/src/org/jacoco/cli/internal/commands/ExecInfo.java +++ b/org.jacoco.cli/src/org/jacoco/cli/internal/commands/ExecInfo.java @@ -20,7 +20,7 @@ import java.util.Date; import java.util.List; import org.jacoco.cli.internal.Command; -import org.jacoco.core.data.ExecutionData; +import org.jacoco.core.data.IExecutionData; import org.jacoco.core.data.ExecutionDataReader; import org.jacoco.core.data.IExecutionDataVisitor; import org.jacoco.core.data.ISessionInfoVisitor; @@ -68,13 +68,15 @@ public class ExecInfo extends Command { } }); reader.setExecutionDataVisitor(new IExecutionDataVisitor() { - public void visitClassExecution(final ExecutionData data) { + // BEGIN android-change + public void visitClassExecution(final IExecutionData data) { out.printf("%016x %3d of %3d %s%n", Long.valueOf(data.getId()), - Integer.valueOf(getHitCount(data.getProbes())), - Integer.valueOf(data.getProbes().length), + Integer.valueOf(getHitCount(data.getProbesCopy())), + Integer.valueOf(data.getProbeCount()), data.getName()); } + // END android-change }); reader.read(); in.close(); diff --git a/org.jacoco.core/src/org/jacoco/core/analysis/Analyzer.java b/org.jacoco.core/src/org/jacoco/core/analysis/Analyzer.java index 76b7be3c..7759b89d 100644 --- a/org.jacoco.core/src/org/jacoco/core/analysis/Analyzer.java +++ b/org.jacoco.core/src/org/jacoco/core/analysis/Analyzer.java @@ -20,7 +20,7 @@ import java.util.zip.GZIPInputStream; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; -import org.jacoco.core.data.ExecutionData; +import org.jacoco.core.data.IExecutionData; import org.jacoco.core.data.ExecutionDataStore; import org.jacoco.core.internal.ContentTypeDetector; import org.jacoco.core.internal.InputStreams; @@ -78,16 +78,18 @@ public class Analyzer { */ private ClassVisitor createAnalyzingVisitor(final long classid, final String className) { - final ExecutionData data = executionData.get(classid); + // BEGIN android-change + final IExecutionData data = executionData.get(classid); final boolean[] probes; final boolean noMatch; if (data == null) { probes = null; noMatch = executionData.contains(className); } else { - probes = data.getProbes(); + probes = data.getProbesCopy(); noMatch = false; } + // END android-change final ClassCoverageImpl coverage = new ClassCoverageImpl(className, classid, noMatch); final ClassAnalyzer analyzer = new ClassAnalyzer(coverage, probes, diff --git a/org.jacoco.core/src/org/jacoco/core/data/ExecutionData.java b/org.jacoco.core/src/org/jacoco/core/data/ExecutionData.java index d98775eb..a728e033 100644 --- a/org.jacoco.core/src/org/jacoco/core/data/ExecutionData.java +++ b/org.jacoco.core/src/org/jacoco/core/data/ExecutionData.java @@ -20,7 +20,9 @@ import java.util.Arrays; * has to be taken about the probe data array of type <code>boolean[]</code> * which can be modified. */ -public final class ExecutionData { +// BEGIN android-change +public final class ExecutionData implements IExecutionData { +// END android-change private final long id; @@ -81,15 +83,43 @@ public final class ExecutionData { return name; } + // BEGIN android-change /** - * Returns the execution data probes. A value of <code>true</code> indicates - * that the corresponding probe was executed. + * Returns a copy of the current probe values. + * + * @return copy of the probe array + */ + public boolean[] getProbesCopy() { + return Arrays.copyOf(probes, probes.length); + } + + /** + * The number of probes in this ExecutionData. + * + * @return the number of probes + */ + public int getProbeCount() { + return probes.length; + } + + /** + * Returns the execution data probe for a given index. A value of + * <code>true</code> indicates that the corresponding probe was + * executed. * * @return probe data */ - public boolean[] getProbes() { - return probes; + public boolean getProbe(final int index) { + return probes[index]; + } + + /** + * Sets the execution data probe for a given index to <code>true</code>. + */ + public void setProbe(final int index) { + probes[index] = true; } + // END android-change /** * Sets all probes to <code>false</code>. @@ -127,7 +157,9 @@ public final class ExecutionData { * @param other * execution data to merge */ - public void merge(final ExecutionData other) { + // BEGIN android-change + public void merge(final IExecutionData other) { + // END android-change merge(other, true); } @@ -154,10 +186,12 @@ public final class ExecutionData { * @param flag * merge mode */ - public void merge(final ExecutionData other, final boolean flag) { + public void merge(final IExecutionData other, final boolean flag) { + // BEGIN android-change assertCompatibility(other.getId(), other.getName(), - other.getProbes().length); - final boolean[] otherData = other.getProbes(); + other.getProbeCount()); + final boolean[] otherData = other.getProbesCopy(); + // END android-change for (int i = 0; i < probes.length; i++) { if (otherData[i]) { probes[i] = flag; diff --git a/org.jacoco.core/src/org/jacoco/core/data/ExecutionDataDelegate.java b/org.jacoco.core/src/org/jacoco/core/data/ExecutionDataDelegate.java new file mode 100644 index 00000000..7cbe989f --- /dev/null +++ b/org.jacoco.core/src/org/jacoco/core/data/ExecutionDataDelegate.java @@ -0,0 +1,197 @@ +// BEGIN android-change +package org.jacoco.core.data; + +import java.io.IOException; +import java.nio.channels.FileChannel; + +/** + * Class that delegates calls to {@link IExecutionData} interface methods to another instance. + * Also has the ability to convert the underlying implementation to a {@link MappedExecutionData} + * instance. + */ +public class ExecutionDataDelegate implements IExecutionData { + private IExecutionData delegate; + + public ExecutionDataDelegate( + final long id, final String name, final int probeCount, final FileChannel channel) { + if ((channel != null) && canMapData(name)) { + try { + delegate = new MappedExecutionData(id, name, probeCount, channel); + } catch (IOException e) { + delegate = new ExecutionData(id, name, probeCount); + } + } else { + delegate = new ExecutionData(id, name, probeCount); + } + } + + /** + * Converts the existing delegate into a {@link MappedExecutionData} instance if it is not + * already one, and copies any probe data into the new instance. + */ + public synchronized void convert(final FileChannel channel) throws IOException { + if (!(delegate instanceof MappedExecutionData) && canMapData(getName())) { + IExecutionData newDelegate = new MappedExecutionData( + getId(), getName(), getProbeCount(), channel); + newDelegate.merge(delegate); + delegate = newDelegate; + } + } + + /** + * Determines if a class can be converted to memory-mapped. + */ + private static boolean canMapData(final String name) { + if (name.startsWith("android/app/") + || name.startsWith("android/os/") + || name.startsWith("com/android/internal/util/") + || name.startsWith("java/")) { + return false; + } + return true; + } + + /** + * Return the unique identifier for this class. The identifier is the CRC64 + * checksum of the raw class file definition. + * + * @return class identifier + */ + public long getId() { + return delegate.getId(); + } + + /** + * The VM name of the class. + * + * @return VM name + */ + public String getName() { + return delegate.getName(); + } + + /** + * The number of instrumentation probes for this class. + * + * @return number of probes + */ + public int getProbeCount() { + return delegate.getProbeCount(); + } + + /** + * Returns a copy of the probe data as a boolean array. + * + * Changes to the returned array will not be reflected in the execution data. + * + * @return copy of the probe data + */ + public boolean[] getProbesCopy() { + return delegate.getProbesCopy(); + } + + /** + * Sets all probes to <code>false</code>. + */ + public void reset() { + delegate.reset(); + } + + /** + * Checks whether any probe has been hit. + * + * @return <code>true</code>, if at least one probe has been hit + */ + public boolean hasHits() { + return delegate.hasHits(); + } + + /** + * Merges the given execution data into the probe data of this object. I.e. + * a probe entry in this object is marked as executed (<code>true</code>) if + * this probe or the corresponding other probe was executed. So the result + * is + * + * <pre> + * A or B + * </pre> + * + * The probe array of the other object is not modified. + * + * @param other + * execution data to merge + */ + public void merge(final IExecutionData other) { + delegate.merge(other); + } + + /** + * Merges the given execution data into the probe data of this object. A + * probe in this object is set to the value of <code>flag</code> if the + * corresponding other probe was executed. For <code>flag==true</code> this + * corresponds to + * + * <pre> + * A or B + * </pre> + * + * For <code>flag==false</code> this can be considered as a subtraction + * + * <pre> + * A and not B + * </pre> + * + * The probe array of the other object is not modified. + * + * @param other + * execution data to merge + * @param flag + * merge mode + */ + public void merge(final IExecutionData other, boolean flag) { + delegate.merge(other, flag); + } + + /** + * Asserts that this execution data object is compatible with the given + * parameters. The purpose of this check is to detect a very unlikely class + * id collision. + * + * @param id + * other class id, must be the same + * @param name + * other name, must be equal to this name + * @param probecount + * probe data length, must be the same as for this data + * @throws IllegalStateException + * if the given parameters do not match this instance + */ + public void assertCompatibility(final long id, final String name, final int probeCount) + throws IllegalStateException { + delegate.assertCompatibility(id, name, probeCount); + } + + /** + * Returns the execution data probe for a given index. A value of + * <code>true</code> indicates that the corresponding probe was + * executed. + * + * @param index the probe's index to look up + * + * @return probe data + */ + public boolean getProbe(final int index) { + return delegate.getProbe(index); + } + + /** + * Sets the execution data probe at the given index to <code>true</code>. + * + * @param index the probe's index to set + * @param value the value to set the probe to + */ + public void setProbe(final int index) { + delegate.setProbe(index); + } + } + // END android-change diff --git a/org.jacoco.core/src/org/jacoco/core/data/ExecutionDataStore.java b/org.jacoco.core/src/org/jacoco/core/data/ExecutionDataStore.java index 3e567a3b..ce0bec47 100644 --- a/org.jacoco.core/src/org/jacoco/core/data/ExecutionDataStore.java +++ b/org.jacoco.core/src/org/jacoco/core/data/ExecutionDataStore.java @@ -28,7 +28,9 @@ import java.util.Set; */ public final class ExecutionDataStore implements IExecutionDataVisitor { - private final Map<Long, ExecutionData> entries = new HashMap<Long, ExecutionData>(); + // BEGIN android-change + private final Map<Long, IExecutionData> entries = new HashMap<Long, IExecutionData>(); + // END android-change private final Set<String> names = new HashSet<String>(); @@ -44,9 +46,11 @@ public final class ExecutionDataStore implements IExecutionDataVisitor { * to a corresponding one, that is already contained * @see ExecutionData#assertCompatibility(long, String, int) */ - public void put(final ExecutionData data) throws IllegalStateException { + // BEGIN android-change + public void put(final IExecutionData data) throws IllegalStateException { final Long id = Long.valueOf(data.getId()); - final ExecutionData entry = entries.get(id); + final IExecutionData entry = entries.get(id); + // END android-change if (entry == null) { entries.put(id, data); names.add(data.getName()); @@ -68,9 +72,11 @@ public final class ExecutionDataStore implements IExecutionDataVisitor { * to a corresponding one, that is already contained * @see ExecutionData#assertCompatibility(long, String, int) */ - public void subtract(final ExecutionData data) throws IllegalStateException { + // BEGIN android-change + public void subtract(final IExecutionData data) throws IllegalStateException { final Long id = Long.valueOf(data.getId()); - final ExecutionData entry = entries.get(id); + final IExecutionData entry = entries.get(id); + // END android-change if (entry != null) { entry.merge(data, false); } @@ -84,7 +90,9 @@ public final class ExecutionDataStore implements IExecutionDataVisitor { * @see #subtract(ExecutionData) */ public void subtract(final ExecutionDataStore store) { - for (final ExecutionData data : store.getContents()) { + // BEGIN android-change + for (final IExecutionData data : store.getContents()) { + // END android-change subtract(data); } } @@ -97,7 +105,9 @@ public final class ExecutionDataStore implements IExecutionDataVisitor { * class id * @return execution data or <code>null</code> */ - public ExecutionData get(final long id) { + // BEGIN android-change + public IExecutionData get(final long id) { + // END android-change return entries.get(Long.valueOf(id)); } @@ -126,9 +136,11 @@ public final class ExecutionDataStore implements IExecutionDataVisitor { * probe data length * @return execution data */ - public ExecutionData get(final Long id, final String name, + // BEGIN android-change + public IExecutionData get(final Long id, final String name, final int probecount) { - ExecutionData entry = entries.get(id); + IExecutionData entry = entries.get(id); + // END android-change if (entry == null) { entry = new ExecutionData(id.longValue(), name, probecount); entries.put(id, entry); @@ -144,7 +156,9 @@ public final class ExecutionDataStore implements IExecutionDataVisitor { * execution data objects itself are not removed. */ public void reset() { - for (final ExecutionData executionData : this.entries.values()) { + // BEGIN android-change + for (final IExecutionData executionData : this.entries.values()) { + // END android-change executionData.reset(); } } @@ -154,9 +168,11 @@ public final class ExecutionDataStore implements IExecutionDataVisitor { * * @return current contents */ - public Collection<ExecutionData> getContents() { - return new ArrayList<ExecutionData>(entries.values()); + // BEGIN android-change + public Collection<IExecutionData> getContents() { + return new ArrayList<IExecutionData>(entries.values()); } + // END android-change /** * Writes the content of the store to the given visitor interface. @@ -165,14 +181,18 @@ public final class ExecutionDataStore implements IExecutionDataVisitor { * interface to write content to */ public void accept(final IExecutionDataVisitor visitor) { - for (final ExecutionData data : getContents()) { + // BEGIN android-change + for (final IExecutionData data : getContents()) { + // END android-change visitor.visitClassExecution(data); } } // === IExecutionDataVisitor === - public void visitClassExecution(final ExecutionData data) { + // BEGIN android-change + public void visitClassExecution(final IExecutionData data) { + // END android-change put(data); } } diff --git a/org.jacoco.core/src/org/jacoco/core/data/ExecutionDataWriter.java b/org.jacoco.core/src/org/jacoco/core/data/ExecutionDataWriter.java index e697dda3..bdf3445d 100644 --- a/org.jacoco.core/src/org/jacoco/core/data/ExecutionDataWriter.java +++ b/org.jacoco.core/src/org/jacoco/core/data/ExecutionDataWriter.java @@ -94,13 +94,17 @@ public class ExecutionDataWriter implements ISessionInfoVisitor, } } - public void visitClassExecution(final ExecutionData data) { + // BEGIN android-change + public void visitClassExecution(final IExecutionData data) { + // END android-change if (data.hasHits()) { try { out.writeByte(BLOCK_EXECUTIONDATA); out.writeLong(data.getId()); out.writeUTF(data.getName()); - out.writeBooleanArray(data.getProbes()); + // BEGIN android-change + out.writeBooleanArray(data.getProbesCopy()); + // END android-change } catch (final IOException e) { throw new RuntimeException(e); } diff --git a/org.jacoco.core/src/org/jacoco/core/data/IExecutionData.java b/org.jacoco.core/src/org/jacoco/core/data/IExecutionData.java new file mode 100644 index 00000000..289c3726 --- /dev/null +++ b/org.jacoco.core/src/org/jacoco/core/data/IExecutionData.java @@ -0,0 +1,129 @@ +// BEGIN android-change +package org.jacoco.core.data; + +/** + * Interface for interacting with execution data for a single Java class. + */ +public interface IExecutionData { + + /** + * Return the unique identifier for this class. The identifier is the CRC64 + * checksum of the raw class file definition. + * + * @return class identifier + */ + public abstract long getId(); + + /** + * The VM name of the class. + * + * @return VM name + */ + public abstract String getName(); + + /** + * The number of instrumentation probes for this class. + * + * @return number of probes + */ + public abstract int getProbeCount(); + + /** + * Returns a copy of the probe data as a boolean array. + * + * Changes to the returned array will not be reflected in the execution data. + * + * @return copy of the probe data + */ + public abstract boolean[] getProbesCopy(); + + /** + * Sets all probes to <code>false</code>. + */ + public abstract void reset(); + + /** + * Checks whether any probe has been hit. + * + * @return <code>true</code>, if at least one probe has been hit + */ + public abstract boolean hasHits(); + + /** + * Merges the given execution data into the probe data of this object. I.e. + * a probe entry in this object is marked as executed (<code>true</code>) if + * this probe or the corresponding other probe was executed. So the result + * is + * + * <pre> + * A or B + * </pre> + * + * The probe array of the other object is not modified. + * + * @param other + * execution data to merge + */ + public abstract void merge(final IExecutionData other); + + /** + * Merges the given execution data into the probe data of this object. A + * probe in this object is set to the value of <code>flag</code> if the + * corresponding other probe was executed. For <code>flag==true</code> this + * corresponds to + * + * <pre> + * A or B + * </pre> + * + * For <code>flag==false</code> this can be considered as a subtraction + * + * <pre> + * A and not B + * </pre> + * + * The probe array of the other object is not modified. + * + * @param other + * execution data to merge + * @param flag + * merge mode + */ + public abstract void merge(final IExecutionData other, boolean flag); + + /** + * Asserts that this execution data object is compatible with the given + * parameters. The purpose of this check is to detect a very unlikely class + * id collision. + * + * @param id + * other class id, must be the same + * @param name + * other name, must be equal to this name + * @param probecount + * probe data length, must be the same as for this data + * @throws IllegalStateException + * if the given parameters do not match this instance + */ + public abstract void assertCompatibility(final long id, final String name, final int probeCount) throws IllegalStateException; + + /** + * Returns the execution data probe for a given index. A value of + * <code>true</code> indicates that the corresponding probe was + * executed. + * + * @param index the probe's index to look up + * + * @return probe data + */ + public abstract boolean getProbe(final int index); + + /** + * Sets the execution data probe at the given index to <code>true</code>. + * + * @param index the probe's index to set + * @param value the value to set the probe to + */ + public abstract void setProbe(final int index); +} +// END android-change diff --git a/org.jacoco.core/src/org/jacoco/core/data/IExecutionDataVisitor.java b/org.jacoco.core/src/org/jacoco/core/data/IExecutionDataVisitor.java index 6cea7c57..14eabbe7 100644 --- a/org.jacoco.core/src/org/jacoco/core/data/IExecutionDataVisitor.java +++ b/org.jacoco.core/src/org/jacoco/core/data/IExecutionDataVisitor.java @@ -24,6 +24,8 @@ public interface IExecutionDataVisitor { * @param data * execution data for a class */ - void visitClassExecution(ExecutionData data); + // BEGIN android-change + void visitClassExecution(IExecutionData data); + // END android-change } diff --git a/org.jacoco.core/src/org/jacoco/core/data/MappedExecutionData.java b/org.jacoco.core/src/org/jacoco/core/data/MappedExecutionData.java new file mode 100644 index 00000000..7e74d17e --- /dev/null +++ b/org.jacoco.core/src/org/jacoco/core/data/MappedExecutionData.java @@ -0,0 +1,304 @@ +// BEGIN android-change +package org.jacoco.core.data; + +import static java.lang.String.format; + +import java.io.IOException; +import java.nio.channels.FileChannel; +import java.nio.charset.Charset; +import java.nio.ByteBuffer; +import java.nio.MappedByteBuffer; + +import org.jacoco.core.data.ExecutionDataWriter; + +/** + * Memory-mapped execution data implementation. + * + * Stores the probe data in a section of a memory-mapped file, so that it will be available without + * requiring a flush of the coverage data. + */ +public final class MappedExecutionData implements IExecutionData { + private final long id; + private final String name; + private final int probeCount; + + // Stores the memory-mapped byte buffer containing the probe data, packed as bits. + private MappedByteBuffer probeBuffer; + + /** + * Creates the mapped execution data. + */ + public MappedExecutionData( + final long id, final String name, final int probeCount, final FileChannel channel) + throws IOException { + this.id = id; + this.name = name; + this.probeCount = probeCount; + + createMemoryMappedProbeArray(id, name, probeCount, channel); + } + + public MappedExecutionData( + final long id, + final String name, + final boolean[] probes, + final FileChannel channel) throws IOException { + this.id = id; + this.name = name; + this.probeCount = probes.length; + + createMemoryMappedProbeArray(id, name, probes.length, channel); + for (int i = 0; i < probes.length; i++) { + if (probes[i]) { + setProbe(i); + } + } + } + + /** + * Maps a section of the already-opened file for the probe data. + * + * @param id the class id + * @param name the VM class name + * @param probeCount the number of probes for this class + */ + private void createMemoryMappedProbeArray( + final long id, final String name, final int probeCount, final FileChannel channel) + throws IOException { + synchronized (channel) { + int byteCount = (probeCount + 7) / 8; + + // Write the ExecutionData block info. + ByteBuffer execDataBuffer = ByteBuffer.allocate(11); + execDataBuffer.put(ExecutionDataWriter.BLOCK_EXECUTIONDATA); + execDataBuffer.putLong(id); + execDataBuffer.putShort((short) name.length()); + execDataBuffer.flip(); + channel.write(execDataBuffer); + channel.write(ByteBuffer.wrap(name.getBytes(Charset.forName("UTF-8")))); + + // Write the probe info and map part of this file for the probe data. + channel.write(toVarIntByteBuffer(probeCount)); + probeBuffer = channel.map(FileChannel.MapMode.READ_WRITE, channel.position(), byteCount); + channel.position(channel.position() + byteCount); + } + } + + /** + * Writes a variable-length int to a {@link ByteBuffer}. + * + * @param value the value to write + * @return a ByteBuffer that can be used to write to a FileChannel containing the value + */ + private ByteBuffer toVarIntByteBuffer(int value) { + ByteBuffer buffer = ByteBuffer.allocate(5); + if (value == 0) { + buffer.put((byte) 0); + } else { + while (value > 0) { + if ((value & 0xFFFFFF80) == 0) { + buffer.put((byte) value); + } else { + buffer.put((byte) (0x80 | (value & 0x7F))); + } + value >>>= 7; + } + } + buffer.flip(); + return buffer; + } + + /** + * Return the unique identifier for this class. The identifier is the CRC64 + * checksum of the raw class file definition. + * + * @return class identifier + */ + public long getId() { + return id; + } + + /** + * The VM name of the class. + * + * @return VM name + */ + public String getName() { + return name; + } + + /** + * The number of instrumentation probes for this class. + * + * @return number of probes + */ + public int getProbeCount() { + return probeCount; + } + + /** + * Returns a copy of the probe data as a boolean array. + * + * Changes to the returned array will not be reflected in the execution data. + * + * @return copy of the probe data + */ + public boolean[] getProbesCopy() { + final int bytes = (probeCount + 7) / 8; + boolean[] probes = new boolean[probeCount]; + for (int index = 0; index < probeCount; index += 8) { + byte byteProbe = probeBuffer.get(index / 8); + for (int bit = 0; (bit < 8) && ((index + bit) < probeCount); bit++) { + probes[index + bit] = ((byteProbe & 0x1) > 0); + byteProbe >>>= 1; + } + } + return probes; + } + + /** + * Sets all probes to <code>false</code>. + */ + public void reset() { + final int bytes = (probeCount + 7) / 8; + synchronized (probeBuffer) { + for (int i = 0; i < bytes; i++) { + probeBuffer.put(i, (byte) 0); + } + } + } + + /** + * Checks whether any probe has been hit. + * + * @return <code>true</code>, if at least one probe has been hit + */ + public boolean hasHits() { + final int bytes = (probeCount + 7) / 8; + synchronized (probeBuffer) { + for (int i = 0; i < bytes; i++) { + if (probeBuffer.get(i) > 0) { + return true; + } + } + } + return false; + } + + /** + * Merges the given execution data into the probe data of this object. I.e. + * a probe entry in this object is marked as executed (<code>true</code>) if + * this probe or the corresponding other probe was executed. So the result + * is + * + * <pre> + * A or B + * </pre> + * + * The probe array of the other object is not modified. + * + * @param other + * execution data to merge + */ + public void merge(final IExecutionData other) { + merge(other, true); + } + + /** + * Merges the given execution data into the probe data of this object. A + * probe in this object is set to the value of <code>flag</code> if the + * corresponding other probe was executed. For <code>flag==true</code> this + * corresponds to + * + * <pre> + * A or B + * </pre> + * + * For <code>flag==false</code> this can be considered as a subtraction + * + * <pre> + * A and not B + * </pre> + * + * The probe array of the other object is not modified. + * + * @param other + * execution data to merge + * @param flag + * merge mode + */ + public void merge(final IExecutionData other, final boolean flag) { + synchronized (probeBuffer) { + for (int i = 0; i < probeCount; i++) { + if (other.getProbe(i)) { + setProbe(i); + } + } + } + } + + /** + * Asserts that this execution data object is compatible with the given + * parameters. The purpose of this check is to detect a very unlikely class + * id collision. + * + * @param id + * other class id, must be the same + * @param name + * other name, must be equal to this name + * @param probecount + * probe data length, must be the same as for this data + * @throws IllegalStateException + * if the given parameters do not match this instance + */ + public void assertCompatibility(final long id, final String name, final int probeCount) + throws IllegalStateException { + if (this.id != id) { + throw new IllegalStateException(format( + "Different ids (%016x and %016x).", Long.valueOf(this.id), + Long.valueOf(id))); + } + if (!this.name.equals(name)) { + throw new IllegalStateException(format( + "Different class names %s and %s for id %016x.", this.name, + name, Long.valueOf(id))); + } + if (this.probeCount != probeCount) { + throw new IllegalStateException(format( + "Incompatible execution data for class %s with id %016x.", + name, Long.valueOf(id))); + } + } + + /** + * Returns the execution data probe for a given index. A value of + * <code>true</code> indicates that the corresponding probe was + * executed. + * + * @param index the probe's index to look up + * + * @return probe data + */ + public boolean getProbe(final int index) { + int offset = index / 8; + int bit = 1 << (index % 8); + return (probeBuffer.get(offset) & bit) != 0; + } + + /** + * Sets the execution data probe at the given index to <code>true</code>. + * + * @param index the probe's index to set + */ + public void setProbe(final int index) { + int offset = index / 8; + int bit = 1 << (index % 8); + byte currentValue = probeBuffer.get(offset); + if ((currentValue & bit) == 0) { + synchronized (probeBuffer) { + probeBuffer.put(offset, (byte) (probeBuffer.get(offset) | bit)); + } + } + } +} +// END android-change diff --git a/org.jacoco.core/src/org/jacoco/core/internal/instr/InstrSupport.java b/org.jacoco.core/src/org/jacoco/core/internal/instr/InstrSupport.java index 85e83a3a..4d4e1ba1 100644 --- a/org.jacoco.core/src/org/jacoco/core/internal/instr/InstrSupport.java +++ b/org.jacoco.core/src/org/jacoco/core/internal/instr/InstrSupport.java @@ -77,7 +77,9 @@ public final class InstrSupport { * Data type of the field that stores coverage information for a class ( * <code>boolean[]</code>). */ - public static final String DATAFIELD_DESC = "[Z"; + // BEGIN android-change + public static final String DATAFIELD_DESC = "Lorg/jacoco/core/data/IExecutionData;"; + // END android-change // === Init Method === @@ -89,7 +91,9 @@ public final class InstrSupport { /** * Descriptor of the initialization method. */ - public static final String INITMETHOD_DESC = "()[Z"; + // BEGIN android-change + public static final String INITMETHOD_DESC = "()Lorg/jacoco/core/data/IExecutionData;"; + // END android-change /** * Access modifiers of the initialization method. diff --git a/org.jacoco.core/src/org/jacoco/core/internal/instr/ProbeInserter.java b/org.jacoco.core/src/org/jacoco/core/internal/instr/ProbeInserter.java index 63fbf765..0cac8f8f 100644 --- a/org.jacoco.core/src/org/jacoco/core/internal/instr/ProbeInserter.java +++ b/org.jacoco.core/src/org/jacoco/core/internal/instr/ProbeInserter.java @@ -67,25 +67,20 @@ class ProbeInserter extends MethodVisitor implements IProbeInserter { public void insertProbe(final int id) { - // For a probe we set the corresponding position in the boolean[] array - // to true. + // BEGIN android-change + // For a probe we call setProbe on the IExecutionData object. mv.visitVarInsn(Opcodes.ALOAD, variable); - // Stack[0]: [Z + // Stack[0]: Lorg/jacoco/core/data/IExecutionData; InstrSupport.push(mv, id); // Stack[1]: I - // Stack[0]: [Z + // Stack[0]: Lorg/jacoco/core/data/IExecutionData; - mv.visitInsn(Opcodes.ICONST_1); - - // Stack[2]: I - // Stack[1]: I - // Stack[0]: [Z - - mv.visitInsn(Opcodes.BASTORE); + mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, "org/jacoco/core/data/IExecutionData", "setProbe", "(I)V", true); + // END android-change } @Override diff --git a/org.jacoco.core/src/org/jacoco/core/runtime/LoggerRuntime.java b/org.jacoco.core/src/org/jacoco/core/runtime/LoggerRuntime.java index 72553956..bef42030 100644 --- a/org.jacoco.core/src/org/jacoco/core/runtime/LoggerRuntime.java +++ b/org.jacoco.core/src/org/jacoco/core/runtime/LoggerRuntime.java @@ -168,7 +168,9 @@ public class LoggerRuntime extends AbstractRuntime { @Override public void publish(final LogRecord record) { if (key.equals(record.getMessage())) { - data.getProbes(record.getParameters()); + // BEGIN android-change + data.getExecutionData(record.getParameters()); + // END android-change } } diff --git a/org.jacoco.core/src/org/jacoco/core/runtime/OfflineInstrumentationAccessGenerator.java b/org.jacoco.core/src/org/jacoco/core/runtime/OfflineInstrumentationAccessGenerator.java index af6671ee..a5e88b60 100644 --- a/org.jacoco.core/src/org/jacoco/core/runtime/OfflineInstrumentationAccessGenerator.java +++ b/org.jacoco.core/src/org/jacoco/core/runtime/OfflineInstrumentationAccessGenerator.java @@ -50,8 +50,10 @@ public class OfflineInstrumentationAccessGenerator implements mv.visitLdcInsn(Long.valueOf(classid)); mv.visitLdcInsn(classname); InstrSupport.push(mv, probecount); - mv.visitMethodInsn(Opcodes.INVOKESTATIC, runtimeClassName, "getProbes", - "(JLjava/lang/String;I)[Z", false); + // BEGIN android-change + mv.visitMethodInsn(Opcodes.INVOKESTATIC, runtimeClassName, "getExecutionData", + "(JLjava/lang/String;I)Lorg/jacoco/core/data/IExecutionData;", false); + // END android-change return 4; } diff --git a/org.jacoco.core/src/org/jacoco/core/runtime/RuntimeData.java b/org.jacoco.core/src/org/jacoco/core/runtime/RuntimeData.java index afb5b7f3..c0fbb654 100644 --- a/org.jacoco.core/src/org/jacoco/core/runtime/RuntimeData.java +++ b/org.jacoco.core/src/org/jacoco/core/runtime/RuntimeData.java @@ -11,7 +11,7 @@ *******************************************************************************/ package org.jacoco.core.runtime; -import org.jacoco.core.data.ExecutionData; +import org.jacoco.core.data.IExecutionData; import org.jacoco.core.data.ExecutionDataStore; import org.jacoco.core.data.IExecutionDataVisitor; import org.jacoco.core.data.ISessionInfoVisitor; @@ -128,15 +128,18 @@ public class RuntimeData { * probe data length * @return execution data */ - public ExecutionData getExecutionData(final Long id, final String name, + // BEGIN android-change + public IExecutionData getExecutionData(final Long id, final String name, final int probecount) { + // END android-change synchronized (store) { return store.get(id, name, probecount); } } + // BEGIN android-change /** - * Retrieves the execution probe array for a given class. The passed + * Retrieves the execution data for a given class. The passed * {@link Object} array instance is used for parameters and the return value * as follows. Call parameters: * @@ -149,18 +152,19 @@ public class RuntimeData { * Return value: * * <ul> - * <li>args[0]: probe array (<code>boolean[]</code>) + * <li>args[0]: execution data ({@link IExecutionData}) * </ul> * * @param args * parameter array of length 3 */ - public void getProbes(final Object[] args) { + public void getExecutionData(final Object[] args) { final Long classid = (Long) args[0]; final String name = (String) args[1]; final int probecount = ((Integer) args[2]).intValue(); - args[0] = getExecutionData(classid, name, probecount).getProbes(); + args[0] = getExecutionData(classid, name, probecount); } + // END android-change /** * In violation of the regular semantic of {@link Object#equals(Object)} @@ -173,7 +177,9 @@ public class RuntimeData { @Override public boolean equals(final Object args) { if (args instanceof Object[]) { - getProbes((Object[]) args); + // BEGIN android-change + getExecutionData((Object[]) args); + // END android-change } return super.equals(args); } diff --git a/org.jacoco.report/src/org/jacoco/report/IReportVisitor.java b/org.jacoco.report/src/org/jacoco/report/IReportVisitor.java index db36796d..1cf4aed4 100644 --- a/org.jacoco.report/src/org/jacoco/report/IReportVisitor.java +++ b/org.jacoco.report/src/org/jacoco/report/IReportVisitor.java @@ -15,7 +15,7 @@ import java.io.IOException; import java.util.Collection; import java.util.List; -import org.jacoco.core.data.ExecutionData; +import org.jacoco.core.data.IExecutionData; import org.jacoco.core.data.SessionInfo; /** @@ -39,7 +39,9 @@ public interface IReportVisitor extends IReportGroupVisitor { * in case of IO problems with the report writer */ void visitInfo(List<SessionInfo> sessionInfos, - Collection<ExecutionData> executionData) throws IOException; + // BEGIN android-change + Collection<IExecutionData> executionData) throws IOException; + // END android-change /** * Has to be called after all report data has been emitted. diff --git a/org.jacoco.report/src/org/jacoco/report/MultiReportVisitor.java b/org.jacoco.report/src/org/jacoco/report/MultiReportVisitor.java index 3cd5e558..f4fefa85 100644 --- a/org.jacoco.report/src/org/jacoco/report/MultiReportVisitor.java +++ b/org.jacoco.report/src/org/jacoco/report/MultiReportVisitor.java @@ -17,7 +17,7 @@ import java.util.Collection; import java.util.List; import org.jacoco.core.analysis.IBundleCoverage; -import org.jacoco.core.data.ExecutionData; +import org.jacoco.core.data.IExecutionData; import org.jacoco.core.data.SessionInfo; /** @@ -41,7 +41,9 @@ public class MultiReportVisitor extends MultiGroupVisitor implements } public void visitInfo(final List<SessionInfo> sessionInfos, - final Collection<ExecutionData> executionData) throws IOException { + // BEGIN android-change + final Collection<IExecutionData> executionData) throws IOException { + // END android-chnage for (final IReportVisitor v : visitors) { v.visitInfo(sessionInfos, executionData); } diff --git a/org.jacoco.report/src/org/jacoco/report/check/RulesChecker.java b/org.jacoco.report/src/org/jacoco/report/check/RulesChecker.java index cc6acf04..f7435600 100644 --- a/org.jacoco.report/src/org/jacoco/report/check/RulesChecker.java +++ b/org.jacoco.report/src/org/jacoco/report/check/RulesChecker.java @@ -17,7 +17,7 @@ import java.util.Collection; import java.util.List; import org.jacoco.core.analysis.IBundleCoverage; -import org.jacoco.core.data.ExecutionData; +import org.jacoco.core.data.IExecutionData; import org.jacoco.core.data.SessionInfo; import org.jacoco.report.ILanguageNames; import org.jacoco.report.IReportGroupVisitor; @@ -86,7 +86,9 @@ public class RulesChecker { } public void visitInfo(final List<SessionInfo> sessionInfos, - final Collection<ExecutionData> executionData) + // BEGIN android-change + final Collection<IExecutionData> executionData) + // END android-change throws IOException { } diff --git a/org.jacoco.report/src/org/jacoco/report/csv/CSVFormatter.java b/org.jacoco.report/src/org/jacoco/report/csv/CSVFormatter.java index 793e2153..42939daa 100644 --- a/org.jacoco.report/src/org/jacoco/report/csv/CSVFormatter.java +++ b/org.jacoco.report/src/org/jacoco/report/csv/CSVFormatter.java @@ -17,7 +17,7 @@ import java.io.OutputStreamWriter; import java.util.Collection; import java.util.List; -import org.jacoco.core.data.ExecutionData; +import org.jacoco.core.data.IExecutionData; import org.jacoco.core.data.SessionInfo; import org.jacoco.report.ILanguageNames; import org.jacoco.report.IReportVisitor; @@ -84,7 +84,9 @@ public class CSVFormatter { } public void visitInfo(final List<SessionInfo> sessionInfos, - final Collection<ExecutionData> executionData) + // BEGIN android-change + final Collection<IExecutionData> executionData) + // END android-change throws IOException { // Info not used for CSV report } diff --git a/org.jacoco.report/src/org/jacoco/report/html/HTMLFormatter.java b/org.jacoco.report/src/org/jacoco/report/html/HTMLFormatter.java index 9994ced0..ce8cd2d9 100644 --- a/org.jacoco.report/src/org/jacoco/report/html/HTMLFormatter.java +++ b/org.jacoco.report/src/org/jacoco/report/html/HTMLFormatter.java @@ -18,7 +18,7 @@ import java.util.Locale; import org.jacoco.core.analysis.IBundleCoverage; import org.jacoco.core.analysis.ICoverageNode.CounterEntity; -import org.jacoco.core.data.ExecutionData; +import org.jacoco.core.data.IExecutionData; import org.jacoco.core.data.SessionInfo; import org.jacoco.report.ILanguageNames; import org.jacoco.report.IMultiReportOutput; @@ -193,12 +193,16 @@ public class HTMLFormatter implements IHTMLReportContext { return new IReportVisitor() { private List<SessionInfo> sessionInfos; - private Collection<ExecutionData> executionData; + // BEGIN android-change + private Collection<IExecutionData> executionData; + // END android-change private HTMLGroupVisitor groupHandler; public void visitInfo(final List<SessionInfo> sessionInfos, - final Collection<ExecutionData> executionData) + // BEGIN android-change + final Collection<IExecutionData> executionData) + // END android-change throws IOException { this.sessionInfos = sessionInfos; this.executionData = executionData; diff --git a/org.jacoco.report/src/org/jacoco/report/internal/html/page/SessionsPage.java b/org.jacoco.report/src/org/jacoco/report/internal/html/page/SessionsPage.java index 67de4941..3a489025 100644 --- a/org.jacoco.report/src/org/jacoco/report/internal/html/page/SessionsPage.java +++ b/org.jacoco.report/src/org/jacoco/report/internal/html/page/SessionsPage.java @@ -20,7 +20,7 @@ import java.util.Comparator; import java.util.Date; import java.util.List; -import org.jacoco.core.data.ExecutionData; +import org.jacoco.core.data.IExecutionData; import org.jacoco.core.data.SessionInfo; import org.jacoco.report.ILanguageNames; import org.jacoco.report.internal.ReportOutputFolder; @@ -48,7 +48,7 @@ public class SessionsPage extends ReportPage { private final DateFormat dateFormat; - private final List<ExecutionData> executionData; + private final List<IExecutionData> executionData; private final ElementIndex index; @@ -69,18 +69,24 @@ public class SessionsPage extends ReportPage { * settings context */ public SessionsPage(final List<SessionInfo> sessionInfos, - final Collection<ExecutionData> executionData, + // BEGIN android-change + final Collection<IExecutionData> executionData, + // END android-change final ElementIndex index, final ReportPage parent, final ReportOutputFolder folder, final IHTMLReportContext context) { super(parent, folder, context); this.sessionInfos = sessionInfos; - this.executionData = new ArrayList<ExecutionData>(executionData); + // BEGIN android-change + this.executionData = new ArrayList<IExecutionData>(executionData); + // END android-change this.index = index; this.dateFormat = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, context.getLocale()); final ILanguageNames names = context.getLanguageNames(); - Collections.sort(this.executionData, new Comparator<ExecutionData>() { - public int compare(final ExecutionData e1, final ExecutionData e2) { + // BEGIN android-change + Collections.sort(this.executionData, new Comparator<IExecutionData>() { + public int compare(final IExecutionData e1, final IExecutionData e2) { + // END android-change return names.getQualifiedClassName(e1.getName()).compareTo( names.getQualifiedClassName(e2.getName())); } @@ -129,7 +135,9 @@ public class SessionsPage extends ReportPage { } final HTMLElement tbody = table.tbody(); final ILanguageNames names = context.getLanguageNames(); - for (final ExecutionData e : executionData) { + // BEGIN android-change + for (final IExecutionData e : executionData) { + // END android-change final HTMLElement tr = tbody.tr(); final String link = index.getLinkToClass(e.getId()); final String qualifiedName = names.getQualifiedClassName(e diff --git a/org.jacoco.report/src/org/jacoco/report/xml/XMLFormatter.java b/org.jacoco.report/src/org/jacoco/report/xml/XMLFormatter.java index a1fee86e..3bda5ed4 100644 --- a/org.jacoco.report/src/org/jacoco/report/xml/XMLFormatter.java +++ b/org.jacoco.report/src/org/jacoco/report/xml/XMLFormatter.java @@ -17,7 +17,7 @@ import java.util.Collection; import java.util.List; import org.jacoco.core.analysis.IBundleCoverage; -import org.jacoco.core.data.ExecutionData; +import org.jacoco.core.data.IExecutionData; import org.jacoco.core.data.SessionInfo; import org.jacoco.report.IReportGroupVisitor; import org.jacoco.report.IReportVisitor; @@ -61,7 +61,9 @@ public class XMLFormatter { private XMLGroupVisitor groupVisitor; public void visitInfo(final List<SessionInfo> sessionInfos, - final Collection<ExecutionData> executionData) + // BEGIN android-change + final Collection<IExecutionData> executionData) + // END android-change throws IOException { this.sessionInfos = sessionInfos; } |