Skip to content

Commit

Permalink
[vm/ffi] Add script to extract existing positive ffi tests into bundl…
Browse files Browse the repository at this point in the history
…e to be used for flutter/flutter integration test

The `tests/ffi/prepare_flutter_bundle.dart` script will try to discover
all synchronous, positive test from the "ffi" test suite, rewrite them
slightly and output a directory which can be used in a flutter/flutter
FFI integration test.

We split the ffi test functions into two parts, because a subset of the
C functions will issue calls to the VM via `dart_api.h`, which is not
available for the flutter/flutter integration test.

=> We make `runtime/bin/ffi_test/ffi_test_functions.cc` a pure C
   library, usable without `dart_api.h` and move the remaining VM
   specific code to .../ffi_test_functions_special.cc contains.

All tests from `tests/ffi/*_test.dart` will be included in the generated
bundle, which

   * don't use async/isolates
   * don't rely on VM api
   * don't rely on DynamicLibrary.{process,executable}
   * don't take too long to execute

The script can be used as follows:

   sdk % dart tests/ffi/prepare_flutter_bundle.dart foo
   Using SDK root: .../sdk
   The following tests will be included:
      aliasing_test.dart
      data_not_asan_test.dart
      data_test.dart
      extension_methods_test.dart
      external_typed_data_test.dart
      function_structs_test.dart
      negative_function_test.dart
      regress_37254_test.dart
      regress_39044_test.dart
      regress_39063_test.dart
      regress_39068_test.dart
      stacktrace_regress_37910_test.dart
      structs_test.dart
      variance_function_test.dart
   The following tests were filtered due to using dart_api.h/async/DynamicLibrary.{process,executable}/...
      function_callbacks_test.dart
      function_gc_test.dart
      function_test.dart
      object_gc_test.dart
      regress_37100_test.dart
      regress_37511_callbacks_test.dart
      regress_37511_test.dart
      regress_37780_test.dart

   Please copy generated files into FFI flutter test application
      * foo/lib/src/generated
      * foo/ios/Classes

Change-Id: Ia13f97df3bbc90829bb8fde8265a7e1d2c0f8260
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/127006
Reviewed-by: Daco Harkes <dacoharkes@google.com>
Commit-Queue: Martin Kustermann <kustermann@google.com>
  • Loading branch information
mkustermann authored and commit-bot@chromium.org committed Dec 4, 2019
1 parent f9327d3 commit b0155a7
Show file tree
Hide file tree
Showing 18 changed files with 507 additions and 269 deletions.
1 change: 1 addition & 0 deletions runtime/bin/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -1122,6 +1122,7 @@ shared_library("ffi_test_functions") {
]
sources = [
"ffi_test/ffi_test_functions.cc",
"ffi_test/ffi_test_functions_special.cc",
]
if (is_win && current_cpu == "x64") {
sources += [ "ffi_test/clobber_x64_win.S" ]
Expand Down
7 changes: 6 additions & 1 deletion runtime/bin/ffi_test/ffi_test_dynamic_library.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

#include "include/dart_api.h"
#if defined(_WIN32)
#define DART_EXPORT extern "C" __declspec(dllexport)
#else
#define DART_EXPORT \
extern "C" __attribute__((visibility("default"))) __attribute((used))
#endif

DART_EXPORT int return42() {
return 42;
Expand Down
253 changes: 9 additions & 244 deletions runtime/bin/ffi_test/ffi_test_functions.cc
Original file line number Diff line number Diff line change
Expand Up @@ -7,28 +7,17 @@
#include <stddef.h>
#include <stdlib.h>
#include <sys/types.h>
#include <csignal>

#include "platform/globals.h"
#if defined(HOST_OS_WINDOWS)
#include <psapi.h>
#else
#include <unistd.h>

// Only OK to use here because this is test code.
#include <condition_variable> // NOLINT(build/c++11)
#include <functional> // NOLINT(build/c++11)
#include <mutex> // NOLINT(build/c++11)
#include <thread> // NOLINT(build/c++11)
#endif

#include <setjmp.h>
#include <signal.h>
#include <cmath>
#include <iostream>
#include <limits>

#include "include/dart_api.h"
#include "include/dart_native_api.h"
#if defined(_WIN32)
#define DART_EXPORT extern "C" __declspec(dllexport)
#else
#define DART_EXPORT \
extern "C" __attribute__((visibility("default"))) __attribute((used))
#endif

namespace dart {

Expand Down Expand Up @@ -108,7 +97,7 @@ DART_EXPORT intptr_t TakeMinInt16(int16_t x) {
}

DART_EXPORT intptr_t TakeMinInt32(int32_t x) {
const int64_t expected = kMinInt32;
const int64_t expected = INT32_MIN;
const int64_t received = x;
return expected == received ? 1 : 0;
}
Expand Down Expand Up @@ -509,125 +498,6 @@ DART_EXPORT float InventFloatValue() {
return retval;
}

////////////////////////////////////////////////////////////////////////////////
// Functions for stress-testing.

DART_EXPORT int64_t MinInt64() {
Dart_ExecuteInternalCommand("gc-on-nth-allocation",
reinterpret_cast<void*>(1));
return 0x8000000000000000;
}

DART_EXPORT int64_t MinInt32() {
Dart_ExecuteInternalCommand("gc-on-nth-allocation",
reinterpret_cast<void*>(1));
return 0x80000000;
}

DART_EXPORT double SmallDouble() {
Dart_ExecuteInternalCommand("gc-on-nth-allocation",
reinterpret_cast<void*>(1));
return 0x80000000 * -1.0;
}

// Requires boxing on 32-bit and 64-bit systems, even if the top 32-bits are
// truncated.
DART_EXPORT void* LargePointer() {
Dart_ExecuteInternalCommand("gc-on-nth-allocation",
reinterpret_cast<void*>(1));
uint64_t origin = 0x8100000082000000;
return reinterpret_cast<void*>(origin);
}

DART_EXPORT void TriggerGC(uint64_t count) {
Dart_ExecuteInternalCommand("gc-now", nullptr);
}

DART_EXPORT void CollectOnNthAllocation(intptr_t num_allocations) {
Dart_ExecuteInternalCommand("gc-on-nth-allocation",
reinterpret_cast<void*>(num_allocations));
}

// Triggers GC. Has 11 dummy arguments as unboxed odd integers which should be
// ignored by GC.
DART_EXPORT void Regress37069(uint64_t a,
uint64_t b,
uint64_t c,
uint64_t d,
uint64_t e,
uint64_t f,
uint64_t g,
uint64_t h,
uint64_t i,
uint64_t j,
uint64_t k) {
Dart_ExecuteInternalCommand("gc-now", nullptr);
}

#if !defined(HOST_OS_WINDOWS)
DART_EXPORT void* UnprotectCodeOtherThread(void* isolate,
std::condition_variable* var,
std::mutex* mut) {
std::function<void()> callback = [&]() {
mut->lock();
var->notify_all();
mut->unlock();

// Wait for mutator thread to continue (and block) before leaving the
// safepoint.
while (Dart_ExecuteInternalCommand("is-mutator-in-native", isolate) !=
nullptr) {
usleep(10 * 1000 /*10 ms*/);
}
};

struct {
void* isolate;
std::function<void()>* callback;
} args = {.isolate = isolate, .callback = &callback};

Dart_ExecuteInternalCommand("run-in-safepoint-and-rw-code", &args);
return nullptr;
}

struct HelperThreadState {
std::mutex mutex;
std::condition_variable cvar;
std::unique_ptr<std::thread> helper;
};

DART_EXPORT void* TestUnprotectCode(void (*fn)(void*)) {
HelperThreadState* state = new HelperThreadState;

{
std::unique_lock<std::mutex> lock(state->mutex); // locks the mutex
state->helper.reset(new std::thread(UnprotectCodeOtherThread,
Dart_CurrentIsolate(), &state->cvar,
&state->mutex));

state->cvar.wait(lock);
}

if (fn != nullptr) {
fn(state);
return nullptr;
} else {
return state;
}
}

DART_EXPORT void WaitForHelper(HelperThreadState* helper) {
helper->helper->join();
delete helper;
}
#else
// Our version of VSC++ doesn't support std::thread yet.
DART_EXPORT void WaitForHelper(void* helper) {}
DART_EXPORT void* TestUnprotectCode(void (*fn)(void)) {
return nullptr;
}
#endif

////////////////////////////////////////////////////////////////////////////////
// Tests for callbacks.

Expand Down Expand Up @@ -663,7 +533,7 @@ DART_EXPORT int TestSimpleMultiply(double (*fn)(double)) {
}

DART_EXPORT int TestSimpleMultiplyFloat(float (*fn)(float)) {
CHECK(std::abs(fn(2.0) - 2.0 * 1.337) < 0.001);
CHECK(::std::abs(fn(2.0) - 2.0 * 1.337) < 0.001);
return 0;
}

Expand Down Expand Up @@ -741,24 +611,6 @@ DART_EXPORT int TestNullPointers(int64_t* (*fn)(int64_t* ptr)) {
return 0;
}

// Defined in ffi_test_functions.S.
//
// Clobbers some registers with special meaning in Dart before re-entry, for
// stress-testing. Not used on 32-bit Windows due to complications with Windows
// "safeseh".
#if defined(TARGET_OS_WINDOWS) && defined(HOST_ARCH_IA32)
void ClobberAndCall(void (*fn)()) {
fn();
}
#else
extern "C" void ClobberAndCall(void (*fn)());
#endif

DART_EXPORT int TestGC(void (*do_gc)()) {
ClobberAndCall(do_gc);
return 0;
}

DART_EXPORT int TestReturnVoid(int (*return_void)()) {
CHECK_EQ(return_void(), 0);
return 0;
Expand All @@ -779,93 +631,6 @@ DART_EXPORT int TestThrowException(int (*fn)()) {
return 0;
}

struct CallbackTestData {
int success;
void (*callback)();
};

#if defined(TARGET_OS_LINUX)

thread_local sigjmp_buf buf;
void CallbackTestSignalHandler(int) {
siglongjmp(buf, 1);
}

int ExpectAbort(void (*fn)()) {
fprintf(stderr, "**** EXPECT STACKTRACE TO FOLLOW. THIS IS OK. ****\n");

struct sigaction old_action = {};
int result = __sigsetjmp(buf, /*savesigs=*/1);
if (result == 0) {
// Install signal handler.
struct sigaction handler = {};
handler.sa_handler = CallbackTestSignalHandler;
sigemptyset(&handler.sa_mask);
handler.sa_flags = 0;

sigaction(SIGABRT, &handler, &old_action);

fn();
} else {
// Caught the setjmp.
sigaction(SIGABRT, &old_action, NULL);
exit(0);
}
fprintf(stderr, "Expected abort!!!\n");
exit(1);
}

void* TestCallbackOnThreadOutsideIsolate(void* parameter) {
CallbackTestData* data = reinterpret_cast<CallbackTestData*>(parameter);
data->success = ExpectAbort(data->callback);
return NULL;
}

int TestCallbackOtherThreadHelper(void* (*tester)(void*), void (*fn)()) {
CallbackTestData data = {1, fn};
pthread_attr_t attr;
int result = pthread_attr_init(&attr);
CHECK_EQ(result, 0);

pthread_t tid;
result = pthread_create(&tid, &attr, tester, &data);
CHECK_EQ(result, 0);

result = pthread_attr_destroy(&attr);
CHECK_EQ(result, 0);

void* retval;
result = pthread_join(tid, &retval);

// Doesn't actually return because the other thread will exit when the test is
// finished.
return 1;
}

// Run a callback on another thread and verify that it triggers SIGABRT.
DART_EXPORT int TestCallbackWrongThread(void (*fn)()) {
return TestCallbackOtherThreadHelper(&TestCallbackOnThreadOutsideIsolate, fn);
}

// Verify that we get SIGABRT when invoking a native callback outside an
// isolate.
DART_EXPORT int TestCallbackOutsideIsolate(void (*fn)()) {
Dart_Isolate current = Dart_CurrentIsolate();

Dart_ExitIsolate();
CallbackTestData data = {1, fn};
TestCallbackOnThreadOutsideIsolate(&data);
Dart_EnterIsolate(current);

return data.success;
}

DART_EXPORT int TestCallbackWrongIsolate(void (*fn)()) {
return ExpectAbort(fn);
}

#endif // defined(TARGET_OS_LINUX)

// Receives some pointer (Pointer<NativeType> in Dart) and writes some bits.
DART_EXPORT void NativeTypePointerParam(void* p) {
uint8_t* p2 = reinterpret_cast<uint8_t*>(p);
Expand Down
Loading

0 comments on commit b0155a7

Please sign in to comment.