Add API to selectively redirect VM logs to apps
Apps now should call setVmOutputsCaptured() to receive console and log streams. Bug: 238593451 Test: atest MicrodroidTestApp MicrodroidHostTestCases Test: build, install, and run MicrodroidDemoApp Change-Id: Ieb0719b2c000a661edf7ad7663d030104a153e29
This commit is contained in:
parent
40e2b1d894
commit
8961f10818
|
@ -246,6 +246,7 @@ public class MainActivity extends AppCompatActivity {
|
|||
builder.setProtectedVm(true);
|
||||
if (debug) {
|
||||
builder.setDebugLevel(VirtualMachineConfig.DEBUG_LEVEL_FULL);
|
||||
builder.setVmOutputCaptured(true);
|
||||
}
|
||||
VirtualMachineConfig config = builder.build();
|
||||
VirtualMachineManager vmm =
|
||||
|
@ -261,10 +262,12 @@ public class MainActivity extends AppCompatActivity {
|
|||
mVirtualMachine.setCallback(Executors.newSingleThreadExecutor(), callback);
|
||||
mStatus.postValue(mVirtualMachine.getStatus());
|
||||
|
||||
InputStream console = mVirtualMachine.getConsoleOutput();
|
||||
InputStream log = mVirtualMachine.getLogOutput();
|
||||
mExecutorService.execute(new Reader("console", mConsoleOutput, console));
|
||||
mExecutorService.execute(new Reader("log", mLogOutput, log));
|
||||
if (debug) {
|
||||
InputStream console = mVirtualMachine.getConsoleOutput();
|
||||
InputStream log = mVirtualMachine.getLogOutput();
|
||||
mExecutorService.execute(new Reader("console", mConsoleOutput, console));
|
||||
mExecutorService.execute(new Reader("log", mLogOutput, log));
|
||||
}
|
||||
} catch (VirtualMachineException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
|
|
@ -65,6 +65,7 @@ package android.system.virtualmachine {
|
|||
method public boolean isCompatibleWith(@NonNull android.system.virtualmachine.VirtualMachineConfig);
|
||||
method public boolean isEncryptedStorageEnabled();
|
||||
method public boolean isProtectedVm();
|
||||
method public boolean isVmOutputCaptured();
|
||||
field public static final int DEBUG_LEVEL_FULL = 1; // 0x1
|
||||
field public static final int DEBUG_LEVEL_NONE = 0; // 0x0
|
||||
}
|
||||
|
@ -79,6 +80,7 @@ package android.system.virtualmachine {
|
|||
method @NonNull public android.system.virtualmachine.VirtualMachineConfig.Builder setNumCpus(@IntRange(from=1) int);
|
||||
method @NonNull public android.system.virtualmachine.VirtualMachineConfig.Builder setPayloadBinaryName(@NonNull String);
|
||||
method @NonNull public android.system.virtualmachine.VirtualMachineConfig.Builder setProtectedVm(boolean);
|
||||
method @NonNull public android.system.virtualmachine.VirtualMachineConfig.Builder setVmOutputCaptured(boolean);
|
||||
}
|
||||
|
||||
public final class VirtualMachineDescriptor implements android.os.Parcelable {
|
||||
|
|
|
@ -293,6 +293,7 @@ public class VirtualMachine implements AutoCloseable {
|
|||
/** Lock protecting callbacks. */
|
||||
private final Object mCallbackLock = new Object();
|
||||
|
||||
private final boolean mVmOutputCaptured;
|
||||
|
||||
/** The configuration that is currently associated with this VM. */
|
||||
@GuardedBy("mLock")
|
||||
|
@ -371,6 +372,7 @@ public class VirtualMachine implements AutoCloseable {
|
|||
? new File(thisVmDir, ENCRYPTED_STORE_FILE)
|
||||
: null;
|
||||
|
||||
mVmOutputCaptured = config.isVmOutputCaptured();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -772,7 +774,9 @@ public class VirtualMachine implements AutoCloseable {
|
|||
IVirtualizationService service = mVirtualizationService.connect();
|
||||
|
||||
try {
|
||||
createVmPipes();
|
||||
if (mVmOutputCaptured) {
|
||||
createVmPipes();
|
||||
}
|
||||
|
||||
VirtualMachineAppConfig appConfig = getConfig().toVsConfig();
|
||||
appConfig.name = mName;
|
||||
|
@ -890,17 +894,26 @@ public class VirtualMachine implements AutoCloseable {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the stream object representing the console output from the virtual machine.
|
||||
* Returns the stream object representing the console output from the virtual machine. The
|
||||
* console output is only available if the {@link VirtualMachineConfig} specifies that it should
|
||||
* be {@linkplain VirtualMachineConfig#isVmOutputCaptured captured}.
|
||||
*
|
||||
* <p>If you turn on output capture, you must consume data from {@code getConsoleOutput} -
|
||||
* because otherwise the code in the VM may get blocked when the pipe buffer fills up.
|
||||
*
|
||||
* <p>NOTE: This method may block and should not be called on the main thread.
|
||||
*
|
||||
* @throws VirtualMachineException if the stream could not be created.
|
||||
* @throws VirtualMachineException if the stream could not be created, or capturing is turned
|
||||
* off.
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@WorkerThread
|
||||
@NonNull
|
||||
public InputStream getConsoleOutput() throws VirtualMachineException {
|
||||
if (!mVmOutputCaptured) {
|
||||
throw new VirtualMachineException("Capturing vm outputs is turned off");
|
||||
}
|
||||
synchronized (mLock) {
|
||||
createVmPipes();
|
||||
return new FileInputStream(mConsoleReader.getFileDescriptor());
|
||||
|
@ -908,17 +921,26 @@ public class VirtualMachine implements AutoCloseable {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the stream object representing the log output from the virtual machine.
|
||||
* Returns the stream object representing the log output from the virtual machine. The log
|
||||
* output is only available if the VirtualMachineConfig specifies that it should be {@linkplain
|
||||
* VirtualMachineConfig#isVmOutputCaptured captured}.
|
||||
*
|
||||
* <p>If you turn on output capture, you must consume data from {@code getLogOutput} - because
|
||||
* otherwise the code in the VM may get blocked when the pipe buffer fills up.
|
||||
*
|
||||
* <p>NOTE: This method may block and should not be called on the main thread.
|
||||
*
|
||||
* @throws VirtualMachineException if the stream could not be created.
|
||||
* @throws VirtualMachineException if the stream could not be created, or capturing is turned
|
||||
* off.
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@WorkerThread
|
||||
@NonNull
|
||||
public InputStream getLogOutput() throws VirtualMachineException {
|
||||
if (!mVmOutputCaptured) {
|
||||
throw new VirtualMachineException("Capturing vm outputs is turned off");
|
||||
}
|
||||
synchronized (mLock) {
|
||||
createVmPipes();
|
||||
return new FileInputStream(mLogReader.getFileDescriptor());
|
||||
|
|
|
@ -58,7 +58,7 @@ public final class VirtualMachineConfig {
|
|||
private static final String[] EMPTY_STRING_ARRAY = {};
|
||||
|
||||
// These define the schema of the config file persisted on disk.
|
||||
private static final int VERSION = 2;
|
||||
private static final int VERSION = 3;
|
||||
private static final String KEY_VERSION = "version";
|
||||
private static final String KEY_APKPATH = "apkPath";
|
||||
private static final String KEY_PAYLOADCONFIGPATH = "payloadConfigPath";
|
||||
|
@ -68,6 +68,7 @@ public final class VirtualMachineConfig {
|
|||
private static final String KEY_MEMORY_MIB = "memoryMib";
|
||||
private static final String KEY_NUM_CPUS = "numCpus";
|
||||
private static final String KEY_ENCRYPTED_STORAGE_KIB = "encryptedStorageKib";
|
||||
private static final String KEY_VM_OUTPUT_CAPTURED = "vmOutputCaptured";
|
||||
|
||||
/** @hide */
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
|
@ -124,6 +125,9 @@ public final class VirtualMachineConfig {
|
|||
/** The size of storage in KiB. 0 indicates that encryptedStorage is not required */
|
||||
private final long mEncryptedStorageKib;
|
||||
|
||||
/** Whether the app can read console and log output. */
|
||||
private final boolean mVmOutputCaptured;
|
||||
|
||||
private VirtualMachineConfig(
|
||||
@NonNull String apkPath,
|
||||
@Nullable String payloadConfigPath,
|
||||
|
@ -132,7 +136,8 @@ public final class VirtualMachineConfig {
|
|||
boolean protectedVm,
|
||||
int memoryMib,
|
||||
int numCpus,
|
||||
long encryptedStorageKib) {
|
||||
long encryptedStorageKib,
|
||||
boolean vmOutputCaptured) {
|
||||
// This is only called from Builder.build(); the builder handles parameter validation.
|
||||
mApkPath = apkPath;
|
||||
mPayloadConfigPath = payloadConfigPath;
|
||||
|
@ -142,6 +147,7 @@ public final class VirtualMachineConfig {
|
|||
mMemoryMib = memoryMib;
|
||||
mNumCpus = numCpus;
|
||||
mEncryptedStorageKib = encryptedStorageKib;
|
||||
mVmOutputCaptured = vmOutputCaptured;
|
||||
}
|
||||
|
||||
/** Loads a config from a file. */
|
||||
|
@ -210,6 +216,7 @@ public final class VirtualMachineConfig {
|
|||
if (encryptedStorageKib != 0) {
|
||||
builder.setEncryptedStorageKib(encryptedStorageKib);
|
||||
}
|
||||
builder.setVmOutputCaptured(b.getBoolean(KEY_VM_OUTPUT_CAPTURED));
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
@ -239,6 +246,7 @@ public final class VirtualMachineConfig {
|
|||
if (mEncryptedStorageKib > 0) {
|
||||
b.putLong(KEY_ENCRYPTED_STORAGE_KIB, mEncryptedStorageKib);
|
||||
}
|
||||
b.putBoolean(KEY_VM_OUTPUT_CAPTURED, mVmOutputCaptured);
|
||||
b.writeToStream(output);
|
||||
}
|
||||
|
||||
|
@ -345,6 +353,18 @@ public final class VirtualMachineConfig {
|
|||
return mEncryptedStorageKib;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the app can read the VM console or log output. If not, the VM output is
|
||||
* automatically forwarded to the host logcat.
|
||||
*
|
||||
* @see #setVmOutputCaptured
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public boolean isVmOutputCaptured() {
|
||||
return mVmOutputCaptured;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if this config is compatible with other config. Being compatible means that the configs
|
||||
* can be interchangeably used for the same virtual machine; they do not change the VM identity
|
||||
|
@ -360,6 +380,7 @@ public final class VirtualMachineConfig {
|
|||
return this.mDebugLevel == other.mDebugLevel
|
||||
&& this.mProtectedVm == other.mProtectedVm
|
||||
&& this.mEncryptedStorageKib == other.mEncryptedStorageKib
|
||||
&& this.mVmOutputCaptured == other.mVmOutputCaptured
|
||||
&& Objects.equals(this.mPayloadConfigPath, other.mPayloadConfigPath)
|
||||
&& Objects.equals(this.mPayloadBinaryName, other.mPayloadBinaryName)
|
||||
&& this.mApkPath.equals(other.mApkPath);
|
||||
|
@ -417,6 +438,7 @@ public final class VirtualMachineConfig {
|
|||
private int mMemoryMib;
|
||||
private int mNumCpus = 1;
|
||||
private long mEncryptedStorageKib;
|
||||
private boolean mVmOutputCaptured = false;
|
||||
|
||||
/**
|
||||
* Creates a builder for the given context.
|
||||
|
@ -469,6 +491,10 @@ public final class VirtualMachineConfig {
|
|||
throw new IllegalStateException("setProtectedVm must be called explicitly");
|
||||
}
|
||||
|
||||
if (mVmOutputCaptured && mDebugLevel != DEBUG_LEVEL_FULL) {
|
||||
throw new IllegalStateException("debug level must be FULL to capture output");
|
||||
}
|
||||
|
||||
return new VirtualMachineConfig(
|
||||
apkPath,
|
||||
mPayloadConfigPath,
|
||||
|
@ -477,7 +503,8 @@ public final class VirtualMachineConfig {
|
|||
mProtectedVm,
|
||||
mMemoryMib,
|
||||
mNumCpus,
|
||||
mEncryptedStorageKib);
|
||||
mEncryptedStorageKib,
|
||||
mVmOutputCaptured);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -654,5 +681,31 @@ public final class VirtualMachineConfig {
|
|||
mEncryptedStorageKib = encryptedStorageKib;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether to allow the app to read the VM outputs (console / log). Default is {@code
|
||||
* false}.
|
||||
*
|
||||
* <p>By default, console and log outputs of a {@linkplain #setDebugLevel debuggable} VM are
|
||||
* automatically forwarded to the host logcat. Setting this as {@code true} will allow the
|
||||
* app to directly read {@linkplain VirtualMachine#getConsoleOutput console output} and
|
||||
* {@linkplain VirtualMachine#getLogOutput log output}, instead of forwarding them to the
|
||||
* host logcat.
|
||||
*
|
||||
* <p>If you turn on output capture, you must consume data from {@link
|
||||
* VirtualMachine#getConsoleOutput} and {@link VirtualMachine#getLogOutput} - because
|
||||
* otherwise the code in the VM may get blocked when the pipe buffer fills up.
|
||||
*
|
||||
* <p>The {@linkplain #setDebugLevel debug level} must be {@link #DEBUG_LEVEL_FULL} to be
|
||||
* set as true.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@NonNull
|
||||
public Builder setVmOutputCaptured(boolean captured) {
|
||||
mVmOutputCaptured = captured;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -211,6 +211,7 @@ public class MicrodroidBenchmarks extends MicrodroidDeviceTestBase {
|
|||
newVmConfigBuilder()
|
||||
.setPayloadBinaryName("MicrodroidIdleNativeLib.so")
|
||||
.setDebugLevel(DEBUG_LEVEL_FULL)
|
||||
.setVmOutputCaptured(true)
|
||||
.setMemoryMib(256)
|
||||
.build();
|
||||
forceCreateNewVirtualMachine("test_vm_boot_time_debug", normalConfig);
|
||||
|
|
|
@ -212,9 +212,11 @@ public abstract class MicrodroidDeviceTestBase {
|
|||
throws VirtualMachineException, InterruptedException {
|
||||
vm.setCallback(mExecutorService, this);
|
||||
vm.run();
|
||||
logVmOutputAndMonitorBootEvents(
|
||||
logTag, vm.getConsoleOutput(), "Console", mConsoleOutput);
|
||||
logVmOutput(logTag, vm.getLogOutput(), "Log", mLogOutput);
|
||||
if (vm.getConfig().isVmOutputCaptured()) {
|
||||
logVmOutputAndMonitorBootEvents(
|
||||
logTag, vm.getConsoleOutput(), "Console", mConsoleOutput);
|
||||
logVmOutput(logTag, vm.getLogOutput(), "Log", mLogOutput);
|
||||
}
|
||||
mExecutorService.awaitTermination(300, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
|
|
|
@ -153,6 +153,7 @@ public class MicrodroidTests extends MicrodroidDeviceTestBase {
|
|||
.setPayloadBinaryName("MicrodroidTestNativeLib.so")
|
||||
.setMemoryMib(minMemoryRequired())
|
||||
.setDebugLevel(DEBUG_LEVEL_NONE)
|
||||
.setVmOutputCaptured(false)
|
||||
.build();
|
||||
VirtualMachine vm = forceCreateNewVirtualMachine("test_vm", config);
|
||||
|
||||
|
@ -332,18 +333,21 @@ public class MicrodroidTests extends MicrodroidDeviceTestBase {
|
|||
assertThat(minimal.isProtectedVm()).isEqualTo(isProtectedVm());
|
||||
assertThat(minimal.isEncryptedStorageEnabled()).isFalse();
|
||||
assertThat(minimal.getEncryptedStorageKib()).isEqualTo(0);
|
||||
assertThat(minimal.isVmOutputCaptured()).isEqualTo(false);
|
||||
|
||||
// Maximal has everything that can be set to some non-default value. (And has different
|
||||
// values than minimal for the required fields.)
|
||||
int maxCpus = Runtime.getRuntime().availableProcessors();
|
||||
VirtualMachineConfig.Builder maximalBuilder =
|
||||
newVmConfigBuilder()
|
||||
new VirtualMachineConfig.Builder(getContext())
|
||||
.setProtectedVm(mProtectedVm)
|
||||
.setPayloadConfigPath("config/path")
|
||||
.setApkPath("/apk/path")
|
||||
.setNumCpus(maxCpus)
|
||||
.setDebugLevel(DEBUG_LEVEL_FULL)
|
||||
.setMemoryMib(42)
|
||||
.setEncryptedStorageKib(1024);
|
||||
.setEncryptedStorageKib(1024)
|
||||
.setVmOutputCaptured(true);
|
||||
VirtualMachineConfig maximal = maximalBuilder.build();
|
||||
|
||||
assertThat(maximal.getApkPath()).isEqualTo("/apk/path");
|
||||
|
@ -355,6 +359,7 @@ public class MicrodroidTests extends MicrodroidDeviceTestBase {
|
|||
assertThat(maximal.isProtectedVm()).isEqualTo(isProtectedVm());
|
||||
assertThat(maximal.isEncryptedStorageEnabled()).isTrue();
|
||||
assertThat(maximal.getEncryptedStorageKib()).isEqualTo(1024);
|
||||
assertThat(maximal.isVmOutputCaptured()).isEqualTo(true);
|
||||
|
||||
assertThat(minimal.isCompatibleWith(maximal)).isFalse();
|
||||
assertThat(minimal.isCompatibleWith(minimal)).isTrue();
|
||||
|
@ -392,6 +397,14 @@ public class MicrodroidTests extends MicrodroidDeviceTestBase {
|
|||
new VirtualMachineConfig.Builder(getContext()).setPayloadBinaryName("binary.so");
|
||||
e = assertThrows(IllegalStateException.class, () -> protectedNotSet.build());
|
||||
assertThat(e).hasMessageThat().contains("setProtectedVm must be called");
|
||||
|
||||
VirtualMachineConfig.Builder captureOutputOnNonDebuggable =
|
||||
newVmConfigBuilder()
|
||||
.setPayloadBinaryName("binary.so")
|
||||
.setDebugLevel(VirtualMachineConfig.DEBUG_LEVEL_NONE)
|
||||
.setVmOutputCaptured(true);
|
||||
e = assertThrows(IllegalStateException.class, () -> captureOutputOnNonDebuggable.build());
|
||||
assertThat(e).hasMessageThat().contains("debug level must be FULL to capture output");
|
||||
}
|
||||
|
||||
private VirtualMachineConfig.Builder newBaselineBuilder() {
|
||||
|
@ -431,6 +444,11 @@ public class MicrodroidTests extends MicrodroidDeviceTestBase {
|
|||
assertConfigCompatible(baseline, newBaselineBuilder().setApkPath("/different")).isFalse();
|
||||
assertConfigCompatible(baseline, newBaselineBuilder().setEncryptedStorageKib(100))
|
||||
.isFalse();
|
||||
|
||||
VirtualMachineConfig.Builder debuggableBuilder =
|
||||
newBaselineBuilder().setDebugLevel(DEBUG_LEVEL_FULL);
|
||||
VirtualMachineConfig debuggable = debuggableBuilder.build();
|
||||
assertConfigCompatible(debuggable, debuggableBuilder.setVmOutputCaptured(true)).isFalse();
|
||||
}
|
||||
|
||||
private BooleanSubject assertConfigCompatible(
|
||||
|
@ -662,6 +680,7 @@ public class MicrodroidTests extends MicrodroidDeviceTestBase {
|
|||
.setPayloadBinaryName("MicrodroidTestNativeLib.so")
|
||||
.setMemoryMib(memMib)
|
||||
.setDebugLevel(DEBUG_LEVEL_NONE)
|
||||
.setVmOutputCaptured(false)
|
||||
.build();
|
||||
VirtualMachine vm = forceCreateNewVirtualMachine("low_mem", lowMemConfig);
|
||||
final CompletableFuture<Boolean> onPayloadReadyExecuted = new CompletableFuture<>();
|
||||
|
@ -704,7 +723,8 @@ public class MicrodroidTests extends MicrodroidDeviceTestBase {
|
|||
VirtualMachineConfig.Builder builder =
|
||||
newVmConfigBuilder()
|
||||
.setPayloadBinaryName("MicrodroidTestNativeLib.so")
|
||||
.setDebugLevel(fromLevel);
|
||||
.setDebugLevel(fromLevel)
|
||||
.setVmOutputCaptured(false);
|
||||
VirtualMachineConfig normalConfig = builder.build();
|
||||
forceCreateNewVirtualMachine("test_vm", normalConfig);
|
||||
assertThat(tryBootVm(TAG, "test_vm").payloadStarted).isTrue();
|
||||
|
@ -1031,9 +1051,12 @@ public class MicrodroidTests extends MicrodroidDeviceTestBase {
|
|||
|
||||
@Test
|
||||
public void bootFailsWhenBinaryIsMissingEntryFunction() throws Exception {
|
||||
VirtualMachineConfig.Builder builder =
|
||||
newVmConfigBuilder().setPayloadBinaryName("MicrodroidEmptyNativeLib.so");
|
||||
VirtualMachineConfig normalConfig = builder.setDebugLevel(DEBUG_LEVEL_FULL).build();
|
||||
VirtualMachineConfig normalConfig =
|
||||
newVmConfigBuilder()
|
||||
.setPayloadBinaryName("MicrodroidEmptyNativeLib.so")
|
||||
.setDebugLevel(DEBUG_LEVEL_FULL)
|
||||
.setVmOutputCaptured(true)
|
||||
.build();
|
||||
VirtualMachine vm = forceCreateNewVirtualMachine("test_vm_missing_entry", normalConfig);
|
||||
|
||||
assertThatPayloadFailsDueTo(vm, "Failed to find entrypoint");
|
||||
|
@ -1041,9 +1064,12 @@ public class MicrodroidTests extends MicrodroidDeviceTestBase {
|
|||
|
||||
@Test
|
||||
public void bootFailsWhenBinaryTriesToLinkAgainstPrivateLibs() throws Exception {
|
||||
VirtualMachineConfig.Builder builder =
|
||||
newVmConfigBuilder().setPayloadBinaryName("MicrodroidPrivateLinkingNativeLib.so");
|
||||
VirtualMachineConfig normalConfig = builder.setDebugLevel(DEBUG_LEVEL_FULL).build();
|
||||
VirtualMachineConfig normalConfig =
|
||||
newVmConfigBuilder()
|
||||
.setPayloadBinaryName("MicrodroidPrivateLinkingNativeLib.so")
|
||||
.setDebugLevel(DEBUG_LEVEL_FULL)
|
||||
.setVmOutputCaptured(true)
|
||||
.build();
|
||||
VirtualMachine vm = forceCreateNewVirtualMachine("test_vm_private_linking", normalConfig);
|
||||
|
||||
assertThatPayloadFailsDueTo(vm, "Failed to dlopen");
|
||||
|
@ -1233,6 +1259,29 @@ public class MicrodroidTests extends MicrodroidDeviceTestBase {
|
|||
assertThat(testResults.mFileContent).isEqualTo("Hello, I am a file!");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void outputsShouldBeExplicitlyForwarded() throws Exception {
|
||||
assumeSupportedKernel();
|
||||
|
||||
final VirtualMachineConfig vmConfig =
|
||||
new VirtualMachineConfig.Builder(ApplicationProvider.getApplicationContext())
|
||||
.setProtectedVm(mProtectedVm)
|
||||
.setPayloadBinaryName("MicrodroidTestNativeLib.so")
|
||||
.setDebugLevel(DEBUG_LEVEL_FULL)
|
||||
.build();
|
||||
final VirtualMachine vm = forceCreateNewVirtualMachine("test_vm_forward_log", vmConfig);
|
||||
vm.run();
|
||||
|
||||
try {
|
||||
assertThrowsVmExceptionContaining(
|
||||
() -> vm.getConsoleOutput(), "Capturing vm outputs is turned off");
|
||||
assertThrowsVmExceptionContaining(
|
||||
() -> vm.getLogOutput(), "Capturing vm outputs is turned off");
|
||||
} finally {
|
||||
vm.stop();
|
||||
}
|
||||
}
|
||||
|
||||
private void assertFileContentsAreEqualInTwoVms(String fileName, String vmName1, String vmName2)
|
||||
throws IOException {
|
||||
File file1 = getVmFile(vmName1, fileName);
|
||||
|
|
Loading…
Reference in New Issue