[lldb] Add a testcase for MainThreadCheckerRuntime plugin

This adds a simple testcase for MainThreadCheckerRuntime. The tool (Main Thread Checker) is only available on Darwin, so the test also detects the presence of libMainThreadChecker.dylib and is skipped if the tool is not available.

llvm-svn: 307170
This commit is contained in:
Kuba Mracek 2017-07-05 16:29:36 +00:00
parent 9a91a18110
commit c40ca13519
4 changed files with 92 additions and 0 deletions

View File

@ -0,0 +1,6 @@
LEVEL = ../../../make
OBJC_SOURCES := main.m
LDFLAGS = $(CFLAGS) -lobjc -framework Foundation -framework AppKit
include $(LEVEL)/Makefile.rules

View File

@ -0,0 +1,57 @@
"""
Tests basic Main Thread Checker support (detecting a main-thread-only violation).
"""
import os
import time
import lldb
from lldbsuite.test.lldbtest import *
from lldbsuite.test.decorators import *
import lldbsuite.test.lldbutil as lldbutil
from lldbsuite.test.lldbplatformutil import *
import json
class MTCSimpleTestCase(TestBase):
mydir = TestBase.compute_mydir(__file__)
@skipUnlessDarwin
def test(self):
self.mtc_dylib_path = findMainThreadCheckerDylib()
if self.mtc_dylib_path == "":
self.skipTest("This test requires libMainThreadChecker.dylib.")
self.build()
self.mtc_tests()
def setUp(self):
# Call super's setUp().
TestBase.setUp(self)
def mtc_tests(self):
# Load the test
exe = os.path.join(os.getcwd(), "a.out")
self.expect("file " + exe, patterns=["Current executable set to .*a.out"])
self.runCmd("env DYLD_INSERT_LIBRARIES=%s" % self.mtc_dylib_path)
self.runCmd("run")
process = self.dbg.GetSelectedTarget().process
thread = process.GetSelectedThread()
frame = thread.GetSelectedFrame()
self.expect("thread info", substrs=['stop reason = -[NSView superview] must be called from main thread only'])
self.expect(
"thread info -s",
substrs=["instrumentation_class", "api_name", "class_name", "selector", "description"])
self.assertEqual(thread.GetStopReason(), lldb.eStopReasonInstrumentation)
output_lines = self.res.GetOutput().split('\n')
json_line = '\n'.join(output_lines[2:])
data = json.loads(json_line)
self.assertEqual(data["instrumentation_class"], "MainThreadChecker")
self.assertEqual(data["api_name"], "-[NSView superview]")
self.assertEqual(data["class_name"], "NSView")
self.assertEqual(data["selector"], "superview")
self.assertEqual(data["description"], "-[NSView superview] must be called from main thread only")

View File

@ -0,0 +1,15 @@
#import <Foundation/Foundation.h>
#import <AppKit/AppKit.h>
int main() {
NSView *view = [[NSView alloc] init];
dispatch_group_t g = dispatch_group_create();
dispatch_group_enter(g);
[NSThread detachNewThreadWithBlock:^{
@autoreleasepool {
[view superview];
}
dispatch_group_leave(g);
}];
dispatch_group_wait(g, DISPATCH_TIME_FOREVER);
}

View File

@ -8,6 +8,7 @@ import itertools
import re
import subprocess
import sys
import os
# Third-party modules
import six
@ -140,6 +141,19 @@ def platformIsDarwin():
return getPlatform() in getDarwinOSTriples()
def findMainThreadCheckerDylib():
if not platformIsDarwin():
return ""
with os.popen('xcode-select -p') as output:
xcode_developer_path = output.read().strip()
mtc_dylib_path = '%s/usr/lib/libMainThreadChecker.dylib' % xcode_developer_path
if os.path.isfile(mtc_dylib_path):
return mtc_dylib_path
return ""
class _PlatformContext(object):
"""Value object class which contains platform-specific options."""