@@ -138,6 +138,12 @@ libpthread-routines = nptl-init vars events version pt-interp \
# pthread_setgid pthread_setegid pthread_setregid \
# pthread_setresgid
+ifeq ($(build-infinity),yes)
+infinity-routines = infinity-map_lwp2thr
+
+libpthread-routines += $(infinity-routines)
+endif
+
libpthread-shared-only-routines = version pt-interp pt-allocrtsig \
unwind-forcedunwind
libpthread-static-only-routines = pthread_atfork
@@ -413,6 +419,20 @@ ifneq ($(have-cxx-thread_local),yes)
tests-unsupported += tst-thread_local1
endif
+tests += tst-infinity
+ifeq ($(build-infinity),yes)
+infinity-tests = $(addprefix tst-,$(addsuffix .py,$(infinity-routines)))
+
+$(objpfx)tst-infinity.out: $(objpfx)libpthread.so$(libpthread.so-version) \
+ $(infinity-tests)
+ $(I8X) -i $< -I$(common-objpfx) $(infinity-tests) > $@; \
+ $(evaluate-test)
+
+tests-special += $(objpfx)tst-infinity.out
+else
+tests-unsupported += tst-infinity
+endif
+
include ../Rules
ifeq (yes,$(build-shared))
new file mode 100644
@@ -0,0 +1,39 @@
+/* Which thread is running on an LWP?
+ Copyright (C) 2003-2016 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>. */
+
+
+/* Given an lwpid_t identifying an LWP, return TD_OK and the
+ descriptor of the thread running on it, or a non-TD_OK
+ td_err_e code indicating the reason for failure and an
+ undefined value that must be ignored. The caller must
+ ensure that __pthread_initialize_minimal has gotten far
+ enough; see the comments in infinity_map_lwp2thr.i8 for
+ specifics. */
+
+define MODULE_NAME::__lookup_th_unique returns td_err_e, pthread_t
+ argument lwpid_t lwpid
+
+ load I8_TS_CTA_VALUE
+ call procservice::get_thread_area
+ bne PS_OK, get_thread_area_failed
+ load TD_OK
+ return
+
+get_thread_area_failed:
+ load TD_ERR
+ return
new file mode 100644
@@ -0,0 +1,41 @@
+/* Which thread is running on an LWP?
+ Copyright (C) 2003-2016 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>. */
+
+
+/* Given an lwpid_t identifying an LWP, return TD_OK and the
+ descriptor of the thread running on it, or a non-TD_OK
+ td_err_e code indicating the reason for failure and an
+ undefined value that must be ignored. The caller must
+ ensure that __pthread_initialize_minimal has gotten far
+ enough; see the comments in infinity_map_lwp2thr.i8 for
+ specifics. */
+
+define MODULE_NAME::__lookup_th_unique returns td_err_e, pthread_t
+ argument lwpid_t lwpid
+
+ load I8_TS_REG_OFFSET
+ call procservice::get_register
+ cast 1, ptr
+ bne PS_OK, get_register_failed
+ add I8_TS_REG_BIAS
+ load TD_OK
+ return
+
+get_register_failed:
+ load TD_ERR
+ return
new file mode 100644
@@ -0,0 +1,50 @@
+/* Which thread is running on an LWP?
+ Copyright (C) 2003-2016 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>. */
+
+
+/* Given an lwpid_t identifying an LWP, return TD_OK and the
+ descriptor of the thread running on it, or a non-TD_OK
+ td_err_e code indicating the reason for failure and an
+ undefined value that must be ignored. The caller must
+ ensure that __pthread_initialize_minimal has gotten far
+ enough; see the comments in infinity_map_lwp2thr.i8 for
+ specifics. */
+
+define MODULE_NAME::__lookup_th_unique returns td_err_e, pthread_t
+ argument lwpid_t lwpid
+
+ load lwpid
+ load I8_TS_RTA_OFFSET
+ call procservice::get_register
+ bne PS_OK, get_register_failed
+
+ shr I8_TS_RTA_SCALE
+ call procservice::get_thread_area
+ bne PS_OK, get_thread_area_failed
+
+ load TD_OK
+ return
+
+get_register_failed:
+ cast 0, ptr
+ load TD_ERR
+ return
+
+get_thread_area_failed:
+ load TD_ERR
+ return
new file mode 100644
@@ -0,0 +1,71 @@
+/* Which thread is running on an LWP?
+ Copyright (C) 2003-2016 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>. */
+
+#include "infinity-nptl.i8"
+#include "infinity-thread_self.h"
+
+#if I8_THREAD_SELF == I8_TS_CONST_THREAD_AREA
+# include "infinity-lookup_th_unique_cta.i8"
+#endif
+#if I8_THREAD_SELF == I8_TS_REGISTER
+# include "infinity-lookup_th_unique_reg.i8"
+#endif
+#if I8_THREAD_SELF == I8_TS_REGISTER_THREAD_AREA
+# include "infinity-lookup_th_unique_rta.i8"
+#endif
+
+/* Given an lwpid_t identifying an LWP, return TD_OK and the
+ descriptor of the thread running on it, or a non-TD_OK
+ td_err_e code indicating the reason for failure and an
+ undefined value that must be ignored. Thread descriptors
+ are opaque pointers and should not be dereferenced outside
+ of this library. */
+
+define MODULE_NAME::map_lwp2thr returns td_err_e, pthread_t
+ argument lwpid_t lwpid
+
+ /* We cannot rely on thread registers and such information at all
+ before __pthread_initialize_minimal has gotten far enough: they
+ sometimes contain garbage left by the kernel at exec that would
+ confuse us. If it looks like initialization is incomplete we
+ fake a special descriptor of NULL to indicate the initial thread.
+ Other routines in this library recognise this special descriptor
+ and act accordingly. */
+ deref LIST_T_NEXT_OFFSET(__stack_user), ptr
+ beq NULL, libpthread_uninitialized
+ call __lookup_th_unique
+ return
+
+libpthread_uninitialized:
+
+ /* Load the pointer we'll return beneath lwpid. I8C can't yet
+ hoist, but it'll optimize the branch away if we do the hoist
+ ourselves. */
+ load NULL
+ swap
+
+ call procservice::getpid
+ beq is_main_thread
+
+not_main_thread:
+ load TD_ERR
+ return
+
+is_main_thread:
+ load TD_OK
+ return
new file mode 100644
@@ -0,0 +1,152 @@
+from i8c.runtime import TestCase
+import struct
+
+TestCase.import_builtin_constants()
+TestCase.import_constants_from("infinity-nptl-constants.h")
+TestCase.import_constants_from("infinity-nptl_db-constants.h")
+
+class TestMapLwp2Thr(TestCase):
+ TESTFUNC = "libpthread::map_lwp2thr(i)ip"
+ MAIN_PID = 30000
+
+ def setUp(self):
+ # Set up the test address space for the __stack_user.next
+ # dereference.
+ with self.memory.builder() as mem:
+ stack_user = mem.alloc("__stack_user")
+ if self.STACK_USER_SETUP:
+ stack_user_next = mem.alloc()
+ else:
+ stack_user_next = NULL
+ stack_user.store_ptr(LIST_T_NEXT_OFFSET, stack_user_next)
+ # Initialize flags so we can see what was called.
+ self.ps_get_register_called = False
+ self.ps_get_thread_area_called = False
+
+ def call_procservice_getpid(self):
+ """Implementation of procservice::getpid."""
+ return self.MAIN_PID
+
+ def call_procservice_get_register(self, lwpid, offset):
+ """Implementation of procservice::get_register."""
+ self.assertFalse(self.ps_get_register_called)
+ result = getattr(self, "PS_GETREG_RESULT", None)
+ if result is None:
+ self.fail("unexpected ps_get_register")
+ self.assertEqual(lwpid, self.lwpid)
+ self.assertNotEqual(offset, self.lwpid)
+ self.assertGreaterEqual(offset, 0)
+ # We can't really say much about offset. It's an offset into
+ # a prgregset_t structure, so it's probably not huge and it's
+ # probably aligned to the machine's wordsize.
+ self.assertLess(offset, 128 * 8) # =128 64-bit registers (IA-64)
+ bytes_per_word, check = divmod(self.i8ctx.wordsize, 8)
+ self.assertNotEqual(bytes_per_word, 0)
+ self.assertEqual(check, 0)
+ self.assertEqual(offset % bytes_per_word, 0)
+ self.ps_get_register_called = True
+ return result
+
+ def call_procservice_get_thread_area(self, lwpid, idx):
+ """Implementation of procservice::get_thread_area."""
+ self.assertFalse(self.ps_get_thread_area_called)
+ result = getattr(self, "PS_GET_TA_RESULT", None)
+ if result is None:
+ self.fail("unexpected ps_get_thread_area")
+ self.assertEqual(lwpid, self.lwpid)
+ self.assertNotEqual(idx, self.lwpid)
+ self.ps_get_thread_area_called = True
+ return result
+
+ def check_I8_TS_CONST_THREAD_AREA_result(self, result):
+ # The result is whatever ps_get_thread_area returned.
+ self.assertTrue(self.ps_get_thread_area_called)
+ self.assertEqual(result[0], TD_OK)
+ self.assertNotEqual(result[1], 0)
+ self.assertEqual(result[1], self.PS_GET_TA_RESULT[1])
+
+ def check_I8_TS_REGISTER_result(self, result):
+ # The result is what ps_get_register returned with some
+ # bias added. We'll assume the bias is fairly small.
+ self.assertTrue(self.ps_get_register_called)
+ self.assertEqual(result[0], TD_OK)
+ self.assertNotEqual(result[1], 0)
+ bias = result[1] - self.PS_GETREG_RESULT[1]
+ self.assertLess(abs(bias), 0x10000)
+
+ def check_I8_TS_REGISTER_THREAD_AREA_result(self, result):
+ # The result is whatever ps_get_thread_area returned.
+ self.assertTrue(self.ps_get_register_called)
+ self.assertTrue(self.ps_get_thread_area_called)
+ self.assertEqual(result[0], TD_OK)
+ self.assertNotEqual(result[1], 0)
+ self.assertEqual(result[1], self.PS_GET_TA_RESULT[1])
+
+class TestMapLwp2Thr_uninit(TestMapLwp2Thr):
+ STACK_USER_SETUP = False
+
+ def test_uninit(self):
+ """Test map_lwp2thr with NPTL uninitialized"""
+ result = self.i8ctx.call(self.TESTFUNC, self.MAIN_PID)
+ self.assertEqual(len(result), 2)
+ self.assertEqual(result[0], TD_OK)
+ self.assertEqual(result[1], NULL)
+
+class TestMapLwp2Thr_uninit_wrongpid(TestMapLwp2Thr):
+ STACK_USER_SETUP = False
+
+ def test_uninit_wrongpid(self):
+ """Test map_lwp2thr with NPTL uninitialized and lwpid != main pid"""
+ result = self.i8ctx.call(self.TESTFUNC, self.MAIN_PID + 1)
+ self.assertEqual(len(result), 2)
+ self.assertEqual(result[0], TD_ERR)
+
+class TestMapLwp2Thr_getreg_fail(TestMapLwp2Thr):
+ STACK_USER_SETUP = True
+ PS_GETREG_RESULT = PS_ERR, 0x23ff00fa
+ PS_GET_TA_RESULT = PS_OK, 0x89ab1234
+
+ def test_ps_getreg_fail(self):
+ """Check map_lwp2thr handles ps_get_register failures"""
+ self.lwpid = self.MAIN_PID + 1
+ result = self.i8ctx.call(self.TESTFUNC, self.lwpid)
+ self.assertEqual(len(result), 2)
+ if self.ps_get_register_called:
+ self.assertEqual(result[0], TD_ERR)
+ else:
+ # This failure isn't a problem for this platform.
+ self.check_I8_TS_CONST_THREAD_AREA_result(result)
+
+class TestMapLwp2Thr_gta_fail(TestMapLwp2Thr):
+ STACK_USER_SETUP = True
+ PS_GETREG_RESULT = PS_OK, 0x23ff00fa
+ PS_GET_TA_RESULT = PS_ERR, 0x89ab1234
+
+ def test_ps_gta_fail(self):
+ """Check map_lwp2thr handles ps_get_thread_area failures"""
+ self.lwpid = self.MAIN_PID + 1
+ result = self.i8ctx.call(self.TESTFUNC, self.lwpid)
+ self.assertEqual(len(result), 2)
+ if self.ps_get_thread_area_called:
+ self.assertEqual(result[0], TD_ERR)
+ else:
+ # This failure isn't a problem for this platform.
+ self.check_I8_TS_REGISTER_result(result)
+
+class TestMapLwp2Thr_mainpath(TestMapLwp2Thr):
+ STACK_USER_SETUP = True
+ PS_GETREG_RESULT = PS_OK, 0x23ff00fa
+ PS_GET_TA_RESULT = PS_OK, 0x89ab1234
+
+ def test_mainpath(self):
+ """Test the main path through map_lwp2thr"""
+ self.lwpid = self.MAIN_PID + 1
+ result = self.i8ctx.call(self.TESTFUNC, self.lwpid)
+ self.assertEqual(len(result), 2)
+ if self.ps_get_thread_area_called:
+ if self.ps_get_register_called:
+ self.check_I8_TS_REGISTER_THREAD_AREA_result(result)
+ else:
+ self.check_I8_TS_CONST_THREAD_AREA_result(result)
+ else:
+ self.check_I8_TS_REGISTER_result(result)