Centralize libc++ test skipping logic

Summary:
This aims to replace the different decorators we've had on each libc++
test with a single solution. Each libc++ will be assigned to the
"libc++" category and a single central piece of code will decide whether
we are actually able to run libc++ test in the given configuration by
enabling or disabling the category (while giving the user the
opportunity to override this).

I started this effort because I wanted to get libc++ tests running on
android, and none of the existing decorators worked for this use case:
 - skipIfGcc - incorrect, we can build libc++ executables on android
 with gcc (in fact, after this, we can now do it on linux as well)
 - lldbutil.skip_if_library_missing - this checks whether libc++.so is
 loaded in the proces, which fails in case of a statically linked
 libc++ (this makes copying executables to the remote target easier to
 manage).

To make this work I needed to split out the pseudo_barrier code from the
force-included file, as libc++'s atomic does not play well with gcc on
linux, and this made every test fail, even though we need the code only
in the threading tests.

So far, I am only annotating one of the tests with this category. If
this does not break anything, I'll proceed to update the rest.

Reviewers: jingham, zturner, EricWF

Subscribers: srhines, lldb-commits

Differential Revision: https://reviews.llvm.org/D30984

llvm-svn: 299028
This commit is contained in:
Pavel Labath 2017-03-29 21:01:14 +00:00
parent 32093a1c28
commit 01a28ca7f8
18 changed files with 92 additions and 79 deletions

View File

@ -1061,6 +1061,29 @@ def checkCompiler():
configuration.compiler = cmd_output.split('\n')[0]
print("'xcrun -find %s' returning %s" % (c, configuration.compiler))
def canRunLibcxxTests():
from lldbsuite.test import lldbplatformutil
platform = lldbplatformutil.getPlatform()
if lldbplatformutil.target_is_android() or lldbplatformutil.platformIsDarwin():
return True, "libc++ always present"
if platform == "linux":
if not os.path.isdir("/usr/include/c++/v1"):
return False, "Unable to find libc++ installation"
return True, "Headers found, let's hope they work"
return False, "Don't know how to build with libc++ on %s" % platform
def checkLibcxxSupport():
result, reason = canRunLibcxxTests()
if result:
return # libc++ supported
if "libc++" in configuration.categoriesList:
return # libc++ category explicitly requested, let it run.
print("Libc++ tests will not be run because: " + reason)
configuration.skipCategories.append("libc++")
def run_suite():
# On MacOS X, check to make sure that domain for com.apple.DebugSymbols defaults
@ -1164,6 +1187,8 @@ def run_suite():
target_platform = lldb.DBG.GetSelectedPlatform().GetTriple().split('-')[2]
checkLibcxxSupport()
# Don't do debugserver tests on everything except OS X.
configuration.dont_do_debugserver_test = "linux" in target_platform or "freebsd" in target_platform or "windows" in target_platform

View File

@ -23,9 +23,10 @@ class LibcxxStringDataFormatterTestCase(TestBase):
TestBase.setUp(self)
# Find the line number to break at.
self.line = line_number('main.cpp', '// Set break point at this line.')
ns = 'ndk' if lldbplatformutil.target_is_android() else ''
self.namespace = 'std::__' + ns + '1'
@skipIf(compiler="gcc")
@skipIfWindows # libc++ not ported to Windows yet
@add_test_categories(["libc++"])
def test_with_run_command(self):
"""Test that that file and class static variables display correctly."""
self.build()
@ -36,9 +37,6 @@ class LibcxxStringDataFormatterTestCase(TestBase):
self.runCmd("run", RUN_SUCCEEDED)
lldbutil.skip_if_library_missing(
self, self.target(), lldbutil.PrintableRegex("libc\+\+"))
# The stop reason of the thread should be breakpoint.
self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,
substrs=['stopped',
@ -58,17 +56,18 @@ class LibcxxStringDataFormatterTestCase(TestBase):
# Execute the cleanup function during test case tear down.
self.addTearDownHook(cleanup)
ns = self.namespace
self.expect(
"frame variable",
substrs=[
'(std::__1::wstring) s = L"hello world! מזל טוב!"',
'(std::__1::wstring) S = L"!!!!"',
'(%s::wstring) s = L"hello world! מזל טוב!"'%ns,
'(%s::wstring) S = L"!!!!"'%ns,
'(const wchar_t *) mazeltov = 0x',
'L"מזל טוב"',
'(std::__1::string) q = "hello world"',
'(std::__1::string) Q = "quite a long std::strin with lots of info inside it"',
'(std::__1::string) IHaveEmbeddedZeros = "a\\0b\\0c\\0d"',
'(std::__1::wstring) IHaveEmbeddedZerosToo = L"hello world!\\0てざ ル゜䋨ミ㠧槊 きゅへ狦穤襩 じゃ馩リョ 䤦監"'])
'(%s::string) q = "hello world"'%ns,
'(%s::string) Q = "quite a long std::strin with lots of info inside it"'%ns,
'(%s::string) IHaveEmbeddedZeros = "a\\0b\\0c\\0d"'%ns,
'(%s::wstring) IHaveEmbeddedZerosToo = L"hello world!\\0てざ ル゜䋨ミ㠧槊 きゅへ狦穤襩 じゃ馩リョ 䤦監"'%ns])
self.runCmd("n")
@ -91,11 +90,11 @@ class LibcxxStringDataFormatterTestCase(TestBase):
self.expect(
"frame variable",
substrs=[
'(std::__1::wstring) s = L"hello world! מזל טוב!"',
'(std::__1::wstring) S = L"!!!!!"',
'(%s::wstring) s = L"hello world! מזל טוב!"'%ns,
'(%s::wstring) S = L"!!!!!"'%ns,
'(const wchar_t *) mazeltov = 0x',
'L"מזל טוב"',
'(std::__1::string) q = "hello world"',
'(std::__1::string) Q = "quite a long std::strin with lots of info inside it"',
'(std::__1::string) IHaveEmbeddedZeros = "a\\0b\\0c\\0d"',
'(std::__1::wstring) IHaveEmbeddedZerosToo = L"hello world!\\0てざ ル゜䋨ミ㠧槊 きゅへ狦穤襩 じゃ馩リョ 䤦監"'])
'(%s::string) q = "hello world"'%ns,
'(%s::string) Q = "quite a long std::strin with lots of info inside it"'%ns,
'(%s::string) IHaveEmbeddedZeros = "a\\0b\\0c\\0d"'%ns,
'(%s::wstring) IHaveEmbeddedZerosToo = L"hello world!\\0てざ ル゜䋨ミ㠧槊 きゅへ狦穤襩 じゃ馩リョ 䤦監"'%ns])

View File

@ -9,7 +9,7 @@
// This test verifies the correct handling of child thread exits.
#include <atomic>
#include "pseudo_barrier.h"
#include <thread>
#include <csignal>

View File

@ -9,7 +9,7 @@
// This test verifies the correct handling of child thread exits.
#include <atomic>
#include "pseudo_barrier.h"
#include <thread>
#include <csignal>

View File

@ -13,7 +13,7 @@
// breakpoint is hit. The test case should be flexible enough to treat that
// as success.
#include <atomic>
#include "pseudo_barrier.h"
#include <chrono>
#include <thread>

View File

@ -13,7 +13,7 @@
// the main thread (before any worker threads are spawned) and modify variables
// which control the number of threads that are spawned for each action.
#include <atomic>
#include "pseudo_barrier.h"
#include <vector>
using namespace std;

View File

@ -10,7 +10,7 @@
// This test is intended to create a situation in which one thread will be
// created while the debugger is stepping in another thread.
#include <atomic>
#include "pseudo_barrier.h"
#include <thread>
#define do_nothing()

View File

@ -13,7 +13,7 @@
// breakpoint is hit. The test case should be flexible enough to treat that
// as success.
#include <atomic>
#include "pseudo_barrier.h"
#include <chrono>
#include <thread>

View File

@ -10,6 +10,7 @@
// This test is intended to create a situation in which one thread will exit
// while the debugger is stepping in another thread.
#include "pseudo_barrier.h"
#include <thread>
#define do_nothing()

View File

@ -12,7 +12,7 @@
// the breakpoint in the second thread will be hit while the breakpoint handler
// in the first thread is trying to stop all threads.
#include <atomic>
#include "pseudo_barrier.h"
#include <thread>
pseudo_barrier_t g_barrier;

View File

@ -10,7 +10,7 @@
// This test is intended to create a situation in which two threads are stopped
// at a breakpoint and the debugger issues a step-out command.
#include <atomic>
#include "pseudo_barrier.h"
#include <thread>
pseudo_barrier_t g_barrier;

View File

@ -9,7 +9,7 @@
// This test verifies the correct handling of child thread exits.
#include <atomic>
#include "pseudo_barrier.h"
#include <thread>
pseudo_barrier_t g_barrier1;

View File

@ -52,12 +52,12 @@ def _run_adb_command(cmd, device_id):
return p.returncode, stdout, stderr
def _target_is_android():
if not hasattr(_target_is_android, 'result'):
def target_is_android():
if not hasattr(target_is_android, 'result'):
triple = lldb.DBG.GetSelectedPlatform().GetTriple()
match = re.match(".*-.*-.*-android", triple)
_target_is_android.result = match is not None
return _target_is_android.result
target_is_android.result = match is not None
return target_is_android.result
def android_device_api():
@ -84,7 +84,7 @@ def android_device_api():
def match_android_device(device_arch, valid_archs=None, valid_api_levels=None):
if not _target_is_android():
if not target_is_android():
return False
if valid_archs is not None and device_arch not in valid_archs:
return False
@ -95,7 +95,7 @@ def match_android_device(device_arch, valid_archs=None, valid_api_levels=None):
def finalize_build_dictionary(dictionary):
if _target_is_android():
if target_is_android():
if dictionary is None:
dictionary = {}
dictionary["OS"] = "Android"

View File

@ -72,7 +72,7 @@ ifeq (1,$(USE_LIBCPP))
ARCH_LDFLAGS += \
-L$(NDK_ROOT)/sources/cxx-stl/llvm-libc++/libs/$(STL_ARCH) \
-l$(NDK_ROOT)/sources/cxx-stl/llvm-libc++/libs/$(STL_ARCH)/libc++.a
$(NDK_ROOT)/sources/cxx-stl/llvm-libc++/libs/$(STL_ARCH)/libc++.a
else
ARCH_CFLAGS += \
-isystem $(NDK_ROOT)/sources/cxx-stl/gnu-libstdc++/4.9/include \

View File

@ -202,7 +202,7 @@ else
CFLAGS += $(ARCHFLAG)$(ARCH) $(FRAMEWORK_INCLUDES) $(CFLAGS_EXTRAS) -I$(LLDB_BASE_DIR)include
endif
CFLAGS += -include $(THIS_FILE_DIR)test_common.h $(ARCH_CFLAGS)
CFLAGS += -include $(THIS_FILE_DIR)test_common.h -I$(THIS_FILE_DIR) $(ARCH_CFLAGS)
# Use this one if you want to build one part of the result without debug information:
ifeq "$(OS)" "Darwin"
@ -324,23 +324,21 @@ ifeq (1,$(USE_LIBSTDCPP))
endif
ifeq (1,$(USE_LIBCPP))
# Clang requires an extra flag: -stdlib=libstdc++
ifneq (,$(findstring clang,$(CC)))
CXXFLAGS += -DLLDB_USING_LIBCPP
ifeq "$(OS)" "Linux"
# This is the default install location on Ubuntu 14.04
ifneq ($(wildcard /usr/include/c++/v1/.),)
CXXFLAGS += -stdlib=libc++
LDFLAGS += -stdlib=libc++
CXXFLAGS += -I/usr/include/c++/v1
endif
else ifeq "$(OS)" "Android"
# Nothing to do, this is already handled in
# Android.rules.
else
CXXFLAGS += -DLLDB_USING_LIBCPP
ifeq "$(OS)" "Linux"
ifneq (,$(findstring clang,$(CC)))
CXXFLAGS += -stdlib=libc++
LDFLAGS += -stdlib=libc++
else
CXXFLAGS += -isystem /usr/include/c++/v1
LDFLAGS += -lc++
endif
else ifeq "$(OS)" "Android"
# Nothing to do, this is already handled in
# Android.rules.
else
CXXFLAGS += -stdlib=libc++
LDFLAGS += -stdlib=libc++
endif
endif

View File

@ -0,0 +1,20 @@
#include <atomic>
// Note that although hogging the CPU while waiting for a variable to change
// would be terrible in production code, it's great for testing since it
// avoids a lot of messy context switching to get multiple threads synchronized.
typedef std::atomic<int> pseudo_barrier_t;
#define pseudo_barrier_wait(barrier) \
do \
{ \
--(barrier); \
while ((barrier).load() > 0) \
; \
} while (0)
#define pseudo_barrier_init(barrier, count) \
do \
{ \
(barrier) = (count); \
} while (0)

View File

@ -45,34 +45,3 @@
#define lldb_enable_attach()
#endif
#if defined(__APPLE__) && defined(LLDB_USING_LIBSTDCPP)
// on Darwin, libstdc++ is missing <atomic>, so this would cause any test to fail building
// since this header file is being included in every C-family test case, we need to not include it
// on Darwin, most tests use libc++ by default, so this will only affect tests that explicitly require libstdc++
#else
#ifdef __cplusplus
#include <atomic>
// Note that although hogging the CPU while waiting for a variable to change
// would be terrible in production code, it's great for testing since it
// avoids a lot of messy context switching to get multiple threads synchronized.
typedef std::atomic<int> pseudo_barrier_t;
#define pseudo_barrier_wait(barrier) \
do \
{ \
--(barrier); \
while ((barrier).load() > 0) \
; \
} while (0)
#define pseudo_barrier_init(barrier, count) \
do \
{ \
(barrier) = (count); \
} while (0)
#endif // __cplusplus
#endif // defined(__APPLE__) && defined(LLDB_USING_LIBSTDCPP)

View File

@ -25,6 +25,7 @@ all_categories = {
'dsym': 'Tests that can be run with DSYM debug information',
'gmodules': 'Tests that can be run with -gmodules debug information',
'expression': 'Tests related to the expression parser',
'libc++': 'Test for libc++ data formatters',
'objc': 'Tests related to the Objective-C programming language support',
'pyapi': 'Tests related to the Python API',
'basic_process': 'Basic process execution sniff tests.',