Revert PR #509, #510 (#513)
[platform/core/dotnet/launcher.git] / NativeLauncher / launcher / lib / core_runtime.cc
index 3fd90bb..ba874aa 100644 (file)
@@ -17,6 +17,7 @@
 
 #include <dlfcn.h>
 #include <signal.h>
+#include <dirent.h>
 
 #include <string>
 #include <fstream>
 #include <sys/wait.h>
 #include <unistd.h>
 #include <linux/limits.h>
+#include <pthread.h>
 
-#include <storage.h>
 #include <vconf.h>
 #include <app_common.h>
+#include <tzplatform_config.h>
 
 #include <Ecore.h>
 
@@ -45,7 +47,6 @@
 #include "core_runtime.h"
 #include "plugin_manager.h"
 #include "path_manager.h"
-#include "log_manager.h"
 
 namespace tizen {
 namespace runtime {
@@ -56,88 +57,16 @@ static coreclr_execute_assembly_ptr executeAssembly = nullptr;
 static coreclr_shutdown_ptr shutdown = nullptr;
 static coreclr_create_delegate_ptr createDelegate = nullptr;
 static set_environment_variable_ptr setEnvironmentVariable = nullptr;
+static stop_profile_after_delay_ptr stopProfileAfterDelay = nullptr;
+static set_switch_ptr setSwitch = nullptr;
 static void* __coreclrLib = nullptr;
 static void* __hostHandle = nullptr;
 static unsigned int __domainId = -1;
-static int __fd = -1;
 static bool __initialized = false;
 static bool __isProfileMode = false;
+PathManager* CoreRuntime::__pm = nullptr;
 
-#define __XSTR(x) #x
-#define __STR(x) __XSTR(x)
-
-#ifdef NATIVE_LIB_DIR
-static std::string __nativeLibDirectory = __STR(NATIVE_LIB_DIR);
-#endif
-
-#undef __STR
-#undef __XSTR
-
-#if defined (__aarch64__)
-#define ARCHITECTURE_IDENTIFIER "arm64"
-
-#elif defined (__arm__)
-#define ARCHITECTURE_IDENTIFIER "armel"
-
-#elif defined (__x86_64__)
-#define ARCHITECTURE_IDENTIFIER "x64"
-
-#elif defined (__i386__)
-#define ARCHITECTURE_IDENTIFIER "x86"
-
-#else
-#error "Unknown target"
-#endif
-
-static const char* __TIZEN_RID_VERSION_KEY = "db/dotnet/tizen_rid_version";
-
-// The sequence of RID_FALLBACK graphs must be:
-// 1. Tizen + Version + Architecture
-// 2. Tizen + Version
-// 3. OS(tizen, linux, unix) + Architecture
-// 4. OS(tizen, linux, unix)
-// 5. any, base
-static std::string getExtraNativeLibDirs(const std::string& appRoot)
-{
-       std::vector<std::string> RID_FALLBACK_GRAPH;
-       char* tizen_rid = vconf_get_str(__TIZEN_RID_VERSION_KEY);
-       if (tizen_rid) {
-               std::vector<std::string> version;
-               splitPath(tizen_rid, version);
-               std::reverse(std::begin(version), std::end(version));
-               for (unsigned int i = 0; i < version.size(); i++) {
-                       RID_FALLBACK_GRAPH.push_back(std::string("tizen." + version[i] + "-" + ARCHITECTURE_IDENTIFIER));
-                       RID_FALLBACK_GRAPH.push_back(std::string("tizen." + version[i]));
-               }
-               free(tizen_rid);
-       }
-
-       std::vector<std::string> RID_FALLBACK_OS = {"tizen", "linux", "unix"};
-       for (unsigned int i = 0; i < RID_FALLBACK_OS.size(); i++) {
-               RID_FALLBACK_GRAPH.push_back(std::string(RID_FALLBACK_OS[i] + "-" +  ARCHITECTURE_IDENTIFIER));
-               RID_FALLBACK_GRAPH.push_back(std::string(RID_FALLBACK_OS[i]));
-       }
-       RID_FALLBACK_GRAPH.push_back("any");
-       RID_FALLBACK_GRAPH.push_back("base");
-
-       std::string candidate;
-       for (unsigned int i = 0; i < RID_FALLBACK_GRAPH.size(); i++) {
-               if (!candidate.empty()) {
-                       candidate += ":";
-               }
-               candidate += concatPath(appRoot, "bin/runtimes/" + RID_FALLBACK_GRAPH[i] + "/native");
-       }
-
-       candidate = candidate + ":" + concatPath(appRoot, "lib/" ARCHITECTURE_IDENTIFIER);
-       if (!strncmp(ARCHITECTURE_IDENTIFIER, "arm64", 5)) {
-               candidate = candidate + ":" + concatPath(appRoot, "lib/aarch64");
-       } else if (!strncmp(ARCHITECTURE_IDENTIFIER, "armel", 5)) {
-               candidate = candidate + ":" + concatPath(appRoot, "lib/arm");
-       }
-
-       return candidate;
-}
-
+#define MAX_DELAY_SEC 100
 
 static std::vector<std::string> __envList;
 
@@ -214,61 +143,36 @@ static void registerSigHandler()
        }
 }
 
-static bool storage_cb(int id, storage_type_e type, storage_state_e state, const char *path, void *user_data)
-{
-       int* tmp = (int*)user_data;
-       if (type == STORAGE_TYPE_INTERNAL)
-       {
-               *tmp = id;
-               return false;
-       }
-
-       return true;
-}
-
 static void initEnvForSpecialFolder()
 {
-       int storageId;
-       int error;
-       char *path = NULL;
-
-       error = storage_foreach_device_supported(storage_cb, &storageId);
-       if (error != STORAGE_ERROR_NONE) {
-               return;
-       }
-
-       error = storage_get_directory(storageId, STORAGE_DIRECTORY_IMAGES, &path);
-       if (error == STORAGE_ERROR_NONE && path != NULL) {
-               setenv("XDG_PICTURES_DIR", const_cast<char *>(path), 1);
-               free(path);
-               path = NULL;
+       const char* path = NULL;
+       if (getenv("XDG_PICTURES_DIR") == NULL) {
+               path = tzplatform_getenv(TZ_USER_IMAGES);
+               if (path) {
+                       setenv("XDG_PICTURES_DIR", path, 1);
+               }
        }
 
-       error = storage_get_directory(storageId, STORAGE_DIRECTORY_MUSIC, &path);
-       if (error == STORAGE_ERROR_NONE && path != NULL) {
-               setenv("XDG_MUSIC_DIR", const_cast<char *>(path), 1);
-               free(path);
-               path = NULL;
+       if (getenv("XDG_MUSIC_DIR") == NULL) {
+               path = tzplatform_getenv(TZ_USER_MUSIC);
+               if (path) {
+                       setenv("XDG_MUSIC_DIR", path, 1);
+               }
        }
 
-       error = storage_get_directory(storageId, STORAGE_DIRECTORY_VIDEOS, &path);
-       if (error == STORAGE_ERROR_NONE && path != NULL) {
-               setenv("XDG_VIDEOS_DIR", const_cast<char *>(path), 1);
-               free(path);
-               path = NULL;
+       if (getenv("XDG_VIDEOS_DIR") == NULL) {
+               path = tzplatform_getenv(TZ_USER_VIDEOS);
+               if (path) {
+                       setenv("XDG_VIDEOS_DIR", path, 1);
+               }
        }
 }
 
-// terminate candidate process when language changed
-// icu related data (CultureInfo, etc) should be recreated.
-static void langChangedCB(keynode_t *key, void* data)
-{
-       _INFO("terminiate candidate process to update language.");
-       exit(0);
-}
-
 static void setLang()
 {
+       //To reduce search overhead of libicuuc.so.xx
+       setenv("CLR_ICU_VERSION_OVERRIDE", "build", 1);
+
        char* lang = vconf_get_str(VCONFKEY_LANGSET);
        if (!lang) {
                _ERR("Fail to get language from vconf");
@@ -299,6 +203,31 @@ static std::string readSelfPath()
        return "";
 }
 
+static void removeDebugPipe()
+{
+       DIR *dir;
+       struct dirent* entry;
+       char debugPipeFiles[PATH_MAX];;
+       sprintf(debugPipeFiles, "/tmp/clr-debug-pipe-%d-", getpid());
+
+       dir = opendir("/tmp");
+       if (dir == nullptr) {
+               _ERR("Fail to open /tmp directory");
+               return;
+       }
+
+       while ((entry = readdir(dir)) != nullptr) {
+               std::string path = concatPath("/tmp", entry->d_name);
+               if (path.find(debugPipeFiles) != std::string::npos) {
+                       if (!removeFile(path)) {
+                               _ERR("Fail to remove file (%s)", path.c_str());
+                       }
+               }
+       }
+
+       closedir(dir);
+}
+
 void preload()
 {
        typedef void (*PreloadDelegate)();
@@ -316,35 +245,37 @@ void preload()
        } else {
                preloadDelegate();
        }
+
+       pluginPreload();
 }
 
-bool initializeCoreClr(const char* appId,
-                                                                        const char* assemblyProbePaths,
-                                                                        const char* NIProbePaths,
-                                                                        const char* pinvokeProbePaths,
-                                                                        const char* tpaList)
+bool initializeCoreClr(PathManager* pm, const std::string& tpa)
 {
+       bool ncdbStartupHook = isNCDBStartupHookProvided();
+
        const char *propertyKeys[] = {
                "TRUSTED_PLATFORM_ASSEMBLIES",
                "APP_PATHS",
                "APP_NI_PATHS",
                "NATIVE_DLL_SEARCH_DIRECTORIES",
-               "AppDomainCompatSwitch"
+               "AppDomainCompatSwitch",
+               ncdbStartupHook ? "STARTUP_HOOKS" : "" // must be the last one
        };
 
        const char *propertyValues[] = {
-               tpaList,
-               assemblyProbePaths,
-               NIProbePaths,
-               pinvokeProbePaths,
-               "UseLatestBehaviorWhenTFMNotSpecified"
+               tpa.c_str(),
+               pm->getAppPaths().c_str(),
+               pm->getAppNIPaths().c_str(),
+               pm->getNativeDllSearchingPaths().c_str(),
+               "UseLatestBehaviorWhenTFMNotSpecified",
+               ncdbStartupHook ? getNCDBStartupHook() : "" // must be the last one
        };
 
        std::string selfPath = readSelfPath();
 
        int st = initializeClr(selfPath.c_str(),
-                                                       appId,
-                                                       sizeof(propertyKeys) / sizeof(propertyKeys[0]),
+                                                       "TizenDotnetApp",
+                                                       sizeof(propertyKeys) / sizeof(propertyKeys[0]) - (ncdbStartupHook ? 0 : 1),
                                                        propertyKeys,
                                                        propertyValues,
                                                        &__hostHandle,
@@ -368,9 +299,6 @@ int CoreRuntime::initialize(const char* appType, LaunchMode launchMode)
                return -1;
        }
 
-       // Intiailize ecore first (signal handlers, etc.) before runtime init.
-       ecore_init();
-
        // set language environment to support ICU
        setLang();
 
@@ -387,6 +315,7 @@ int CoreRuntime::initialize(const char* appType, LaunchMode launchMode)
        // So, plugin initialize should be called before creating threads.
        if (initializePluginManager(appType) < 0) {
                _ERR("Failed to initialize PluginManager");
+               return -1;
        }
 
        // checkInjection checks dotnet-launcher run mode
@@ -404,10 +333,10 @@ int CoreRuntime::initialize(const char* appType, LaunchMode launchMode)
        // See https://github.com/dotnet/coreclr/issues/6698
        //
        // libunwind use following methods to unwind stack frame.
-       // UNW_ARM_METHOD_ALL          0xFF
-       // UNW_ARM_METHOD_DWARF        0x01
-       // UNW_ARM_METHOD_FRAME        0x02
-       // UNW_ARM_METHOD_EXIDX        0x04
+       // UNW_ARM_METHOD_ALL           0xFF
+       // UNW_ARM_METHOD_DWARF         0x01
+       // UNW_ARM_METHOD_FRAME         0x02
+       // UNW_ARM_METHOD_EXIDX         0x04
        putenv(const_cast<char *>("UNW_ARM_UNWIND_METHOD=6"));
 #endif // __arm__
 
@@ -422,29 +351,43 @@ int CoreRuntime::initialize(const char* appType, LaunchMode launchMode)
        putenv(const_cast<char *>("COMPlus_UseDefaultBaseAddr=1"));
 #endif // USE_DEFAULT_BASE_ADDR
 
+       // Disable config cache to set environment after coreclr_initialize()
+       putenv(const_cast<char *>("COMPlus_DisableConfigCache=1"));
+
+       // Set environment variable for System.Environment.SpecialFolder
+       initEnvForSpecialFolder();
+
        // read string from external file and set them to environment value.
        setEnvFromFile();
 
-       if (initializePathManager(std::string(), std::string(), std::string()) < 0) {
-               _ERR("Failed to initialize PathManager");
+       try {
+               __pm = new PathManager();
+       } catch (const std::exception& e) {
+               _ERR("Failed to create PathManager");
                return -1;
        }
 
-       if (!pluginHasLogControl()) {
-               if (initializeLogManager() < 0) {
-                       _ERR("Failed to initnialize LogManager");
-                       return -1;
-               }
+       char* pluginDllPaths = pluginGetDllPath();
+       if (pluginDllPaths && pluginDllPaths[0] != '\0') {
+               __pm->addPlatformAssembliesPaths(pluginDllPaths, true);
+       }
+
+       char* pluginNativePaths = pluginGetNativeDllSearchingPath();
+       if (pluginNativePaths && pluginNativePaths[0] != '\0') {
+               __pm->addNativeDllSearchingPaths(pluginNativePaths, true);
        }
 
-       std::string libCoreclr(concatPath(getRuntimeDir(), "libcoreclr.so"));
+       char* pluginExtraDllPaths = pluginGetExtraDllPath();
+       if (pluginExtraDllPaths && pluginExtraDllPaths[0] != '\0') {
+               __pm->setExtraDllPaths(pluginExtraDllPaths);
+       }
+
+       std::string libCoreclr(concatPath(__pm->getRuntimePath(), "libcoreclr.so"));
 
        __coreclrLib = dlopen(libCoreclr.c_str(), RTLD_NOW | RTLD_LOCAL);
        if (__coreclrLib == nullptr) {
                char *err = dlerror();
                _ERR("dlopen failed to open libcoreclr.so with error %s", err);
-               if (access(libCoreclr.c_str(), R_OK) == -1)
-                       _ERR("access '%s': %s\n", libCoreclr.c_str(), strerror(errno));
                return -1;
        }
 
@@ -466,51 +409,56 @@ int CoreRuntime::initialize(const char* appType, LaunchMode launchMode)
 
        _INFO("libcoreclr dlopen and dlsym success");
 
-       // Set environment for System.Environment.SpecialFolder
-       // Below function creates dbus connection by callging storage API.
-       // If dbus connection is created bofere fork(), forked process cannot use dbus.
-       // To avoid gdbus blocking issue, below function should be called after fork()
-       initEnvForSpecialFolder();
-
-       __fd = open("/proc/self", O_DIRECTORY);
-       if (__fd < 0) {
-               _ERR("Failed to open /proc/self");
-               return -1;
+       std::string tpa;
+       char* pluginTPA = pluginGetTPA();
+       if (pluginTPA && pluginTPA[0] != '\0') {
+               tpa = std::string(pluginTPA);
+       } else {
+               addAssembliesFromDirectories(__pm->getPlatformAssembliesPaths(), tpa);
        }
 
-       std::string tpa = getTPA();
-       std::string runtimeDir = getRuntimeDir();
-       std::string appName = std::string("dotnet-launcher-") + std::to_string(getpid());
-       std::string appRoot = std::string("/proc/self/fd/") + std::to_string(__fd);
-       std::string appBin = concatPath(appRoot, "bin");
-       std::string appLib = concatPath(appRoot, "lib");
-       std::string appTac = concatPath(appBin, TAC_SYMLINK_SUB_DIR);
-       std::string probePath = appRoot + ":" + appBin + ":" + appLib + ":" + appTac;
-       std::string NIprobePath = concatPath(appBin, APP_NI_SUB_DIR) + ":" + concatPath(appLib, APP_NI_SUB_DIR) + ":" + appTac;
-       std::string nativeLibPath = runtimeDir + ":" + __nativeLibDirectory + ":" + getExtraNativeLibDirs(appRoot) + ":" + appBin + ":" + appLib;
-
-       if (!initializeCoreClr(appName.c_str(), probePath.c_str(), NIprobePath.c_str(), nativeLibPath.c_str(), tpa.c_str())) {
+       if (!initializeCoreClr(__pm, tpa)) {
                _ERR("Failed to initialize coreclr");
                return -1;
        }
 
+       // VD has their own signal handler.
+       if (!pluginHasLogControl()) {
+               registerSigHandler();
+       }
+
        int st = createDelegate(__hostHandle, __domainId, "Tizen.Runtime", "Tizen.Runtime.Environment", "SetEnvironmentVariable", (void**)&setEnvironmentVariable);
        if (st < 0 || setEnvironmentVariable == nullptr) {
                _ERR("Create delegate for Tizen.Runtime.dll -> Tizen.Runtime.Environment -> SetEnvironmentVariable failed (0x%08x)", st);
                return -1;
        }
 
-       __initialized = true;
+       st = createDelegate(__hostHandle, __domainId, "Tizen.Runtime", "Tizen.Runtime.Profiler", "StopProfileAfterDelay", (void**)&stopProfileAfterDelay);
+       if (st < 0 || stopProfileAfterDelay == nullptr) {
+               _ERR("Create delegate for Tizen.Runtime.dll -> Tizen.Runtime.Profiler -> StopProfileAfterDelay failed (0x%08x)", st);
+               return -1;
+       }
+
+       st = createDelegate(__hostHandle, __domainId, "Tizen.Runtime", "Tizen.Runtime.AppSetting", "SetSwitch", (void**)&setSwitch);
+       if (st < 0 || setSwitch == nullptr) {
+               _ERR("Create delegate for Tizen.Runtime.dll -> Tizen.Runtime.AppSetting -> SetSwitch failed (0x%08x)", st);
+               return -1;
+       }
 
        if (launchMode == LaunchMode::loader) {
-               // terminate candidate process if language is changed.
-               // CurrentCulture created for preloaded dlls should be updated.
-               vconf_notify_key_changed(VCONFKEY_LANGSET, langChangedCB, NULL);
+               // preload libraries and manage dlls for optimizing startup time
+               preload();
 
-               pluginPreload();
-               preload();              // Preload common managed code
+               // The debug pipe created in the candidate process has a "User" label.
+               // As a result, smack deny occurs when app process try to access the debug pipe.
+               // Also, since debugging is performed only in standalone mode,
+               // the debug pipe doesnot be used in the candidate process.
+               // So, to avoid smack deny error, delete unused debug pipe files.
+               removeDebugPipe();
        }
 
+       __initialized = true;
+
        _INFO("CoreRuntime initialize success");
 
        return 0;
@@ -518,11 +466,6 @@ int CoreRuntime::initialize(const char* appType, LaunchMode launchMode)
 
 void CoreRuntime::finalize()
 {
-       if (!__initialized) {
-               _ERR("Runtime is not initialized");
-               return;
-       }
-
        // call plugin finalize function to notify finalize to plugin
        // dlclose shoud be done after coreclr shutdown to avoid breaking signal chain
        pluginFinalize();
@@ -531,7 +474,7 @@ void CoreRuntime::finalize()
        checkOnTerminate = true;
 
        // workaround : to prevent crash while process terminate on profiling mode,
-       //              kill process immediately.
+       //                              kill process immediately.
        // see https://github.com/dotnet/coreclr/issues/26687
        if (__isProfileMode) {
                _INFO("shutdown process immediately.");
@@ -554,14 +497,16 @@ void CoreRuntime::finalize()
        }
 
        finalizePluginManager();
-       finalizePathManager();
+
+       delete __pm;
+       __pm = NULL;
 
        __envList.clear();
 
        _INFO("CoreRuntime finalized");
 }
 
-int CoreRuntime::launch(const char* appId, const char* root, const char* path, int argc, char* argv[])
+int CoreRuntime::launch(const char* appId, const char* root, const char* path, int argc, char* argv[], bool profile)
 {
        if (!__initialized) {
                _ERR("Runtime is not initialized");
@@ -578,38 +523,52 @@ int CoreRuntime::launch(const char* appId, const char* root, const char* path, i
                return -1;
        }
 
-       // launchpad override stdout and stderr to journalctl before launch application.
-       // we have to re-override that to input pipe for logging thread.
-       // if LogManager is not initialized, below redirectFD will return 0;
-       if (redirectFD() < 0) {
-               _ERR("Failed to redirect FD");
-               return -1;
-       }
-
-       // VD has their own signal handler.
-       if (!pluginHasLogControl()) {
-               registerSigHandler();
-       }
-
        pluginSetAppInfo(appId, path);
 
-       // override root path for application launch mode (candidate / standalone mode)
-       if (__fd >= 0) {
-               int fd2 = open(root, O_DIRECTORY);
-               dup3(fd2, __fd, O_CLOEXEC);
-               if (fd2 >= 0)
-                       close(fd2);
-       }
+       // temporal root path is overrided to real application root path
+       __pm->setAppRootPath(root);
 
        // set application data path to coreclr environment.
        // application data path can be changed by owner. So, we have to set data path just before launching.
        char* localDataPath = app_get_data_path();
        if (localDataPath != nullptr) {
                setEnvironmentVariable("XDG_DATA_HOME", localDataPath);
+
+               // set profile.data path and collect/use it if it non-exists/exists.
+               if (profile) {
+                       char multiCoreJitProfile[strlen(localDataPath) + strlen(PROFILE_BASENAME) + 1];
+                       memcpy(multiCoreJitProfile, localDataPath, strlen(localDataPath) + 1);
+                       strncat(multiCoreJitProfile, PROFILE_BASENAME, strlen(PROFILE_BASENAME));
+
+                       setEnvironmentVariable("COMPlus_MultiCoreJitProfile", multiCoreJitProfile);
+                       setEnvironmentVariable("COMPlus_MultiCoreJitMinNumCpus", "1");
+
+                       if (exist(multiCoreJitProfile)) {
+                               setEnvironmentVariable("COMPlus_MultiCoreJitNoProfileGather", "1");
+                               _INFO("MCJ playing start for %s", appId);
+                       } else {
+                               setEnvironmentVariable("COMPlus_MultiCoreJitNoProfileGather", "0");
+                               // stop profiling and write collected data after delay if env value is set.
+                               char *env = getenv("CLR_MCJ_PROFILE_WRITE_DELAY");
+                               if (env != nullptr) {
+                                       int delay = std::atoi(env);
+                                       // To avoid undefined behavior by out-of-range input(atoi), set max delay value to 100.
+                                       if (delay > 0) {
+                                               if (delay > MAX_DELAY_SEC) delay = MAX_DELAY_SEC;
+                                               stopProfileAfterDelay(delay);
+                                       }
+                               }
+                               _INFO("MCJ recording start for %s", appId);
+                       }
+               }
                free(localDataPath);
        }
 
-       vconf_ignore_key_changed(VCONFKEY_LANGSET, langChangedCB);
+       if (exist(__pm->getAppRootPath() + "/bin/" + DISABLE_IPV6_FILE)) {
+               setSwitch("System.Net.DisableIPv6", true);
+       }
+
+       setSwitch("Switch.System.Diagnostics.StackTrace.ShowILOffsets", true);
 
        pluginBeforeExecute();