Index: snapshot/mac/process_reader_test.cc |
diff --git a/snapshot/mac/process_reader_test.cc b/snapshot/mac/process_reader_test.cc |
index d46bfd7259199d4c611bbbf580b7ca34dbb61c53..2c0520be6c1750e5946c3640f1d491b69f2ebbaa 100644 |
--- a/snapshot/mac/process_reader_test.cc |
+++ b/snapshot/mac/process_reader_test.cc |
@@ -14,9 +14,11 @@ |
#include "snapshot/mac/process_reader.h" |
+#include <AvailabilityMacros.h> |
#include <mach-o/dyld.h> |
#include <mach-o/dyld_images.h> |
#include <mach/mach.h> |
+#include <OpenCL/opencl.h> |
#include <string.h> |
#include <sys/stat.h> |
@@ -32,6 +34,7 @@ |
#include "gtest/gtest.h" |
#include "snapshot/mac/mach_o_image_reader.h" |
#include "util/file/file_io.h" |
+#include "util/mac/mac_util.h" |
#include "util/mach/mach_extensions.h" |
#include "util/stdlib/pointer_container.h" |
#include "util/synchronization/semaphore.h" |
@@ -519,7 +522,116 @@ TEST(ProcessReader, ChildSeveralThreads) { |
process_reader_threaded_child.Run(); |
} |
+// cl_kernels images (OpenCL kernels) are weird. They’re not ld output and don’t |
+// exist as files on disk. On Mac OS X 10.10, their Mach-O structure isn’t |
+// perfect. They show up loaded into many executables, so these quirks should be |
+// tolerated. |
+// |
+// Create an object of this class to ensure that at least one cl_kernels image |
+// is present in a process, to be able to test that all of the process-reading |
+// machinery tolerates them. On systems where cl_kernels modules have known |
+// quirks, the image that an object of this class produces will also have those |
+// quirks. |
+// |
+// https://openradar.appspot.com/20239912 |
+class ScopedOpenCLNoOpKernel { |
+ public: |
+ ScopedOpenCLNoOpKernel() |
+ : context_(nullptr), |
+ program_(nullptr), |
+ kernel_(nullptr) { |
+ } |
+ |
+ ~ScopedOpenCLNoOpKernel() { |
+ if (kernel_) { |
+ cl_int rv = clReleaseKernel(kernel_); |
+ EXPECT_EQ(CL_SUCCESS, rv) << "clReleaseKernel"; |
+ } |
+ |
+ if (program_) { |
+ cl_int rv = clReleaseProgram(program_); |
+ EXPECT_EQ(CL_SUCCESS, rv) << "clReleaseProgram"; |
+ } |
+ |
+ if (context_) { |
+ cl_int rv = clReleaseContext(context_); |
+ EXPECT_EQ(CL_SUCCESS, rv) << "clReleaseContext"; |
+ } |
+ } |
+ |
+ void SetUp() { |
+ cl_platform_id platform_id; |
+ cl_int rv = clGetPlatformIDs(1, &platform_id, nullptr); |
+ ASSERT_EQ(CL_SUCCESS, rv) << "clGetPlatformIDs"; |
+ |
+ // Use CL_DEVICE_TYPE_CPU to ensure that the kernel would execute on the |
+ // CPU. This is the only device type that a cl_kernels image will be created |
+ // for. |
+ cl_device_id device_id; |
+ rv = |
+ clGetDeviceIDs(platform_id, CL_DEVICE_TYPE_CPU, 1, &device_id, nullptr); |
+ ASSERT_EQ(CL_SUCCESS, rv) << "clGetDeviceIDs"; |
+ |
+ context_ = clCreateContext(nullptr, 1, &device_id, nullptr, nullptr, &rv); |
+ ASSERT_EQ(CL_SUCCESS, rv) << "clCreateContext"; |
+ |
+ // The goal of the program in |sources| is to produce a cl_kernels image |
+ // that doesn’t strictly conform to Mach-O expectations. On Mac OS X 10.10, |
+ // cl_kernels modules show up with an __LD,__compact_unwind section, showing |
+ // up in the __TEXT segment. MachOImageSegmentReader would normally reject |
+ // modules for this problem, but a special exception is made when this |
+ // occurs in cl_kernels images. This portion of the test is aimed at making |
+ // sure that this exception works correctly. |
+ // |
+ // A true no-op program doesn’t actually produce unwind data, so there would |
+ // be no errant __LD,__compact_unwind section on 10.10, and the test |
+ // wouldn’t be complete. This simple no-op, which calls a built-in function, |
+ // does produce unwind data provided optimization is disabled. |
+ // "-cl-opt-disable" is given to clBuildProgram() below. |
+ const char* sources[] = { |
+ "__kernel void NoOp(void) {barrier(CLK_LOCAL_MEM_FENCE);}", |
+ }; |
+ const size_t source_lengths[] = { |
+ strlen(sources[0]), |
+ }; |
+ static_assert(arraysize(sources) == arraysize(source_lengths), |
+ "arrays must be parallel"); |
+ |
+ program_ = clCreateProgramWithSource( |
+ context_, arraysize(sources), sources, source_lengths, &rv); |
+ ASSERT_EQ(CL_SUCCESS, rv) << "clCreateProgramWithSource"; |
+ |
+ rv = clBuildProgram( |
+ program_, 1, &device_id, "-cl-opt-disable", nullptr, nullptr); |
+ ASSERT_EQ(CL_SUCCESS, rv) << "clBuildProgram"; |
+ |
+ kernel_ = clCreateKernel(program_, "NoOp", &rv); |
+ ASSERT_EQ(CL_SUCCESS, rv) << "clCreateKernel"; |
+ } |
+ |
+ private: |
+ cl_context context_; |
+ cl_program program_; |
+ cl_kernel kernel_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(ScopedOpenCLNoOpKernel); |
+}; |
+ |
+// Although Mac OS X 10.6 has OpenCL and can compile and execute OpenCL code, |
+// OpenCL kernels that run on the CPU do not result in cl_kernels images |
+// appearing on that OS version. |
+bool ExpectCLKernels() { |
+#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_7 |
+ return true; |
+#else |
+ return MacOSXMinorVersion() >= 7; |
+#endif |
+} |
+ |
TEST(ProcessReader, SelfModules) { |
+ ScopedOpenCLNoOpKernel ensure_cl_kernels; |
+ ASSERT_NO_FATAL_FAILURE(ensure_cl_kernels.SetUp()); |
+ |
ProcessReader process_reader; |
ASSERT_TRUE(process_reader.Initialize(mach_task_self())); |
@@ -534,6 +646,7 @@ TEST(ProcessReader, SelfModules) { |
// does. |
ASSERT_EQ(dyld_image_count + 1, modules.size()); |
+ bool found_cl_kernels = false; |
for (uint32_t index = 0; index < dyld_image_count; ++index) { |
SCOPED_TRACE(base::StringPrintf( |
"index %u, name %s", index, modules[index].name.c_str())); |
@@ -549,6 +662,11 @@ TEST(ProcessReader, SelfModules) { |
// dyld didn’t load the main executable, so it couldn’t record its |
// timestamp, and it is reported as 0. |
EXPECT_EQ(0, modules[index].timestamp); |
+ } else if (modules[index].reader->FileType() == MH_BUNDLE && |
+ modules[index].name == "cl_kernels") { |
+ // cl_kernels doesn’t exist as a file. |
+ EXPECT_EQ(0, modules[index].timestamp); |
+ found_cl_kernels = true; |
} else { |
// Hope that the module didn’t change on disk. |
struct stat stat_buf; |
@@ -560,6 +678,8 @@ TEST(ProcessReader, SelfModules) { |
} |
} |
+ EXPECT_EQ(ExpectCLKernels(), found_cl_kernels); |
+ |
size_t index = modules.size() - 1; |
EXPECT_EQ("/usr/lib/dyld", modules[index].name); |
@@ -603,6 +723,7 @@ class ProcessReaderModulesChild final : public MachMultiprocess { |
ASSERT_EQ(expect_modules, modules.size()); |
+ bool found_cl_kernels = false; |
for (size_t index = 0; index < modules.size(); ++index) { |
SCOPED_TRACE(base::StringPrintf( |
"index %zu, name %s", index, modules[index].name.c_str())); |
@@ -625,6 +746,11 @@ class ProcessReaderModulesChild final : public MachMultiprocess { |
// dyld didn’t load the main executable or itself, so it couldn’t record |
// these timestamps, and they are reported as 0. |
EXPECT_EQ(0, modules[index].timestamp); |
+ } else if (modules[index].reader->FileType() == MH_BUNDLE && |
+ modules[index].name == "cl_kernels") { |
+ // cl_kernels doesn’t exist as a file. |
+ EXPECT_EQ(0, modules[index].timestamp); |
+ found_cl_kernels = true; |
} else { |
// Hope that the module didn’t change on disk. |
struct stat stat_buf; |
@@ -635,6 +761,8 @@ class ProcessReaderModulesChild final : public MachMultiprocess { |
} |
} |
} |
+ |
+ EXPECT_EQ(ExpectCLKernels(), found_cl_kernels); |
} |
void MachMultiprocessChild() override { |
@@ -689,6 +817,9 @@ class ProcessReaderModulesChild final : public MachMultiprocess { |
}; |
TEST(ProcessReader, ChildModules) { |
+ ScopedOpenCLNoOpKernel ensure_cl_kernels; |
+ ASSERT_NO_FATAL_FAILURE(ensure_cl_kernels.SetUp()); |
+ |
ProcessReaderModulesChild process_reader_modules_child; |
process_reader_modules_child.Run(); |
} |