Commit cb8903b1 by Le Hoang Quyen Committed by Commit Bot

Metal: Ignore OS's internal shader cache when testing.

Internal shader cache caused timeout in some dEQP tests. Work-around: ignore the cache by hooking fopen function and return null when the cache related files are accessed. Bug: angleproject:5354 Change-Id: I12ca228540925e67454bf24ce1ba83d703882c87 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2580918 Commit-Queue: Le Hoang Quyen <le.hoang.q@gmail.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarKenneth Russell <kbr@chromium.org>
parent fd7733e9
...@@ -346,6 +346,12 @@ template("angle_test") { ...@@ -346,6 +346,12 @@ template("angle_test") {
] ]
} }
if (is_mac && angle_enable_metal) {
# On macOS, compiling Metal shader sometimes takes very long time due to internal caching
# mechanism. This hooking library is a way to bypass this caching mechanism.
deps += [ "$angle_root/src/libANGLE/renderer/metal/file_hooking:metal_shader_cache_file_hooking" ]
}
if ((is_linux && !is_chromeos) || if ((is_linux && !is_chromeos) ||
(build_with_chromium && chromeos_is_browser_only)) { (build_with_chromium && chromeos_is_browser_only)) {
use_xvfb = true use_xvfb = true
......
# Copyright 2020 The ANGLE Project Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
#
# This file houses the build configuration for the ANGLE Metal back-end's file hooking library.
import("../../../../../gni/angle.gni")
angle_shared_library("metal_shader_cache_file_hooking") {
sources = [ "shader_cache_file_hooking.cpp" ]
}
//
// Copyright 2020 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// shader_cache_file_hooking:
// Hooks file API functions to intercept Metal shader cache access and return as if the file
// doesn't exist. This is to avoid slow cache access happened after the cache becomes huge over
// time.
//
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <iostream>
#include <regex>
namespace
{
constexpr char kMetalCacheExpr[] = R"(.*com\.apple\.metal.*(libraries|functions).*\.(maps|data))";
}
int HookedOpen(const char *path, int flags, mode_t mode)
{
std::regex expr(kMetalCacheExpr);
if (!std::regex_match(path, expr))
{
return open(path, flags, mode);
}
#if !defined(NDEBUG)
std::cerr << "open(\"" << path << "\", \"" << flags << "\") is skipped" << std::endl;
#endif
if (flags & O_CREAT)
{
errno = EACCES;
return -1;
}
// Treat as if the cache doesn't exist
errno = ENOENT;
return -1;
}
FILE *HookedFopen(const char *filename, const char *mode)
{
std::regex expr(kMetalCacheExpr);
if (!std::regex_match(filename, expr))
{
return fopen(filename, mode);
}
#if !defined(NDEBUG)
std::cerr << "fopen(\"" << filename << "\", \"" << mode << "\") is skipped" << std::endl;
#endif
if (strstr(mode, "r"))
{
// Treat as if the cache doesn't exist
errno = ENOENT;
return nullptr;
}
errno = EACCES;
return nullptr;
}
// See https://opensource.apple.com/source/dyld/dyld-210.2.3/include/mach-o/dyld-interposing.h
#define DYLD_INTERPOSE(_replacment, _replacee) \
__attribute__((used)) static struct \
{ \
const void *replacment; \
const void *replacee; \
} _interpose_##_replacee __attribute__((section("__DATA,__interpose"))) = { \
(const void *)(unsigned long)&_replacment, (const void *)(unsigned long)&_replacee};
DYLD_INTERPOSE(HookedOpen, open)
DYLD_INTERPOSE(HookedFopen, fopen)
...@@ -19,6 +19,12 @@ void InitTestHarness(int *argc, char **argv); ...@@ -19,6 +19,12 @@ void InitTestHarness(int *argc, char **argv);
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
#if defined(ANGLE_PLATFORM_MACOS)
// By default, we should hook file API functions on macOS to avoid slow Metal shader caching
// file access.
angle::InitMetalFileAPIHooking(argc, argv);
#endif
angle::InitTestHarness(&argc, argv); angle::InitTestHarness(&argc, argv);
angle::TestSuite testSuite(&argc, argv); angle::TestSuite testSuite(&argc, argv);
return testSuite.run(); return testSuite.run();
......
...@@ -903,6 +903,12 @@ TestSuite::TestSuite(int *argc, char **argv) ...@@ -903,6 +903,12 @@ TestSuite::TestSuite(int *argc, char **argv)
Optional<int> filterArgIndex; Optional<int> filterArgIndex;
bool alsoRunDisabledTests = false; bool alsoRunDisabledTests = false;
#if defined(ANGLE_PLATFORM_MACOS)
// By default, we should hook file API functions on macOS to avoid slow Metal shader caching
// file access.
angle::InitMetalFileAPIHooking(*argc, argv);
#endif
#if defined(ANGLE_PLATFORM_WINDOWS) #if defined(ANGLE_PLATFORM_WINDOWS)
testing::GTEST_FLAG(catch_exceptions) = false; testing::GTEST_FLAG(catch_exceptions) = false;
#endif #endif
......
...@@ -24,15 +24,26 @@ ...@@ -24,15 +24,26 @@
#include "common/debug.h" #include "common/debug.h"
#include "common/platform.h" #include "common/platform.h"
#include "common/system_utils.h"
#if !defined(ANGLE_PLATFORM_FUCHSIA) #if !defined(ANGLE_PLATFORM_FUCHSIA)
# include <sys/resource.h> # include <sys/resource.h>
#endif #endif
#if defined(ANGLE_PLATFORM_MACOS)
# include <crt_externs.h>
#endif
namespace angle namespace angle
{ {
namespace namespace
{ {
#if defined(ANGLE_PLATFORM_MACOS)
// Argument to skip the file hooking step. Might be automatically added by InitMetalFileAPIHooking()
constexpr char kSkipFileHookingArg[] = "--skip-file-hooking";
#endif
struct ScopedPipe struct ScopedPipe
{ {
~ScopedPipe() ~ScopedPipe()
...@@ -447,4 +458,77 @@ const char *GetNativeEGLLibraryNameWithExtension() ...@@ -447,4 +458,77 @@ const char *GetNativeEGLLibraryNameWithExtension()
return "unknown_libegl"; return "unknown_libegl";
#endif #endif
} }
#if defined(ANGLE_PLATFORM_MACOS)
void InitMetalFileAPIHooking(int argc, char **argv)
{
if (argc < 1)
{
return;
}
for (int i = 0; i < argc; ++i)
{
if (strncmp(argv[i], kSkipFileHookingArg, strlen(kSkipFileHookingArg)) == 0)
{
return;
}
}
constexpr char kInjectLibVarName[] = "DYLD_INSERT_LIBRARIES";
constexpr size_t kInjectLibVarNameLen = sizeof(kInjectLibVarName) - 1;
std::string exeDir = GetExecutableDirectory();
if (!exeDir.empty() && exeDir.back() != '/')
{
exeDir += "/";
}
# if !defined(NDEBUG)
std::cerr << "Preloading " << exeDir << "libmetal_shader_cache_file_hooking.dylib" << std::endl;
# endif
// Intercept Metal shader cache access and return as if the cache doesn't exist.
// This is to avoid slow shader cache mechanism that caused the test timeout in the past.
// In order to do that, we need to hook the file API functions by making sure
// libmetal_shader_cache_file_hooking.dylib library is loaded first before any other libraries.
std::string injectLibsVar =
std::string(kInjectLibVarName) + "=" + exeDir + "libmetal_shader_cache_file_hooking.dylib";
char skipHookOption[sizeof(kSkipFileHookingArg)];
memcpy(skipHookOption, kSkipFileHookingArg, sizeof(kSkipFileHookingArg));
// Construct environment variables
std::vector<char *> newEnv;
char **environ = *_NSGetEnviron();
for (int i = 0; environ[i]; ++i)
{
if (strncmp(environ[i], kInjectLibVarName, kInjectLibVarNameLen) == 0)
{
injectLibsVar += ':';
injectLibsVar += environ[i] + kInjectLibVarNameLen + 1;
}
else
{
newEnv.push_back(environ[i]);
}
}
newEnv.push_back(strdup(injectLibsVar.data()));
newEnv.push_back(nullptr);
// Construct arguments with kSkipFileHookingArg flag to skip the hooking after re-launching.
std::vector<char *> newArgs;
newArgs.push_back(argv[0]);
newArgs.push_back(skipHookOption);
for (int i = 1; i < argc; ++i)
{
newArgs.push_back(argv[i]);
}
newArgs.push_back(nullptr);
// Re-launch the app with file API hooked.
ASSERT(-1 != execve(argv[0], newArgs.data(), newEnv.data()));
}
#endif
} // namespace angle } // namespace angle
...@@ -121,6 +121,16 @@ Process *LaunchProcess(const std::vector<const char *> &args, ...@@ -121,6 +121,16 @@ Process *LaunchProcess(const std::vector<const char *> &args,
int NumberOfProcessors(); int NumberOfProcessors();
const char *GetNativeEGLLibraryNameWithExtension(); const char *GetNativeEGLLibraryNameWithExtension();
// Intercept Metal shader cache access to avoid slow caching mechanism that caused the test timeout
// in the past. Note:
// - If there is NO "--skip-file-hooking" switch in the argument list:
// - This function will re-launch the app with additional argument "--skip-file-hooking".
// - The running process's image & memory will be re-created.
// - If there is "--skip-file-hooking" switch in the argument list, this function will do nothing.
#if defined(ANGLE_PLATFORM_APPLE)
void InitMetalFileAPIHooking(int argc, char **argv);
#endif
} // namespace angle } // namespace angle
#endif // UTIL_TEST_UTILS_H_ #endif // UTIL_TEST_UTILS_H_
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment