Merge pull request #2546 from schellap/breadcrumb

Breadcrumbs for servicing
This commit is contained in:
Senthil 2016-04-20 15:10:01 -07:00
commit d02e09a5fb
12 changed files with 298 additions and 37 deletions

View file

@ -13,7 +13,7 @@ arguments_t::arguments_t() :
app_argc(0),
app_argv(nullptr),
dotnet_packages_cache(_X("")),
dotnet_extensions(_X("")),
core_servicing(_X("")),
deps_path(_X(""))
{
}
@ -94,7 +94,7 @@ bool parse_arguments(
}
pal::getenv(_X("DOTNET_HOSTING_OPTIMIZATION_CACHE"), &args.dotnet_packages_cache);
pal::get_default_extensions_directory(&args.dotnet_extensions);
pal::get_default_servicing_directory(&args.core_servicing);
return true;
}

View file

@ -91,7 +91,7 @@ struct arguments_t
pal::string_t own_path;
pal::string_t app_dir;
pal::string_t deps_path;
pal::string_t dotnet_extensions;
pal::string_t core_servicing;
std::vector<pal::string_t> probe_paths;
pal::string_t dotnet_packages_cache;
pal::string_t managed_application;
@ -105,8 +105,8 @@ struct arguments_t
{
if (trace::is_enabled())
{
trace::verbose(_X("-- arguments_t: own_path=%s app_dir=%s deps=%s extensions=%s packages_cache=%s mgd_app=%s"),
own_path.c_str(), app_dir.c_str(), deps_path.c_str(), dotnet_extensions.c_str(), dotnet_packages_cache.c_str(), managed_application.c_str());
trace::verbose(_X("-- arguments_t: own_path=%s app_dir=%s deps=%s core_svc=%s packages_cache=%s mgd_app=%s"),
own_path.c_str(), app_dir.c_str(), deps_path.c_str(), core_servicing.c_str(), dotnet_packages_cache.c_str(), managed_application.c_str());
for (const auto& probe : probe_paths)
{
trace::verbose(_X("-- arguments_t: probe dir: [%s]"), probe.c_str());

View file

@ -0,0 +1,91 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#include <thread>
#include "pal.h"
#include "utils.h"
#include "trace.h"
#include "breadcrumbs.h"
breadcrumb_writer_t::breadcrumb_writer_t(const std::unordered_set<pal::string_t>* files)
: m_status(false)
, m_files(files)
{
if (!pal::get_default_breadcrumb_store(&m_breadcrumb_store))
{
m_breadcrumb_store.clear();
}
}
// Begin breadcrumb writing: write synchronously or launch a
// thread to write breadcrumbs.
void breadcrumb_writer_t::begin_write()
{
trace::verbose(_X("--- Begin breadcrumb write"));
if (m_breadcrumb_store.empty())
{
trace::verbose(_X("Breadcrumb store was not obtained... skipping write."));
m_status = false;
return;
}
trace::verbose(_X("Number of breadcrumb files to write is %d"), m_files->size());
if (m_files->empty())
{
m_status = true;
return;
}
m_thread = std::thread(write_worker_callback, this);
trace::verbose(_X("Breadcrumbs will be written using a background thread"));
}
// Write the breadcrumbs. This method should be called
// only from the background thread.
void breadcrumb_writer_t::write_callback()
{
bool successful = true;
for (const auto& file : *m_files)
{
pal::string_t file_path = m_breadcrumb_store;
pal::string_t file_name = _X("netcore,") + file;
append_path(&file_path, file_name.c_str());
if (!pal::file_exists(file_path))
{
if (!pal::touch_file(file_path))
{
successful = false;
}
}
}
// m_status should not be modified by anyone else.
m_status = successful;
}
// ThreadProc for the background writer.
void breadcrumb_writer_t::write_worker_callback(breadcrumb_writer_t* p_this)
{
try
{
trace::verbose(_X("Breadcrumb thread write callback..."));
p_this->write_callback();
}
catch (...)
{
trace::warning(_X("An unexpected exception was thrown while leaving breadcrumbs"));
}
}
// Wait for completion of the background tasks, if any.
bool breadcrumb_writer_t::end_write()
{
if (m_thread.joinable())
{
trace::verbose(_X("Waiting for breadcrumb thread to exit..."));
// Block on the thread to exit.
m_thread.join();
}
trace::verbose(_X("--- End breadcrumb write %d"), m_status);
return m_status;
}

View file

@ -0,0 +1,27 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#ifndef __BREADCRUMBS_H__
#define __BREADCRUMBS_H__
#include <thread>
class breadcrumb_writer_t
{
public:
breadcrumb_writer_t(const std::unordered_set<pal::string_t>* files);
void begin_write();
bool end_write();
private:
void write_callback();
static void write_worker_callback(breadcrumb_writer_t* p_this);
pal::string_t m_breadcrumb_store;
std::thread m_thread;
const std::unordered_set<pal::string_t>* m_files;
volatile bool m_status;
};
#endif // __BREADCRUMBS_H__

View file

@ -187,9 +187,9 @@ void deps_resolver_t::setup_probe_config(
const hostpolicy_init_t& init,
const arguments_t& args)
{
if (pal::directory_exists(args.dotnet_extensions))
if (pal::directory_exists(args.core_servicing))
{
pal::string_t ext_ni = args.dotnet_extensions;
pal::string_t ext_ni = args.core_servicing;
append_path(&ext_ni, get_arch());
if (pal::directory_exists(ext_ni))
{
@ -198,7 +198,7 @@ void deps_resolver_t::setup_probe_config(
}
// Servicing normal probe.
pal::string_t ext_pkgs = args.dotnet_extensions;
pal::string_t ext_pkgs = args.core_servicing;
append_path(&ext_pkgs, _X("pkgs"));
m_probes.push_back(probe_config_t::svc(ext_pkgs, false, false));
}
@ -384,7 +384,8 @@ pal::string_t deps_resolver_t::resolve_coreclr_dir()
void deps_resolver_t::resolve_tpa_list(
const pal::string_t& clr_dir,
pal::string_t* output)
pal::string_t* output,
std::unordered_set<pal::string_t>* breadcrumb)
{
const std::vector<deps_entry_t> empty(0);
@ -400,11 +401,15 @@ void deps_resolver_t::resolve_tpa_list(
auto process_entry = [&](const pal::string_t& deps_dir, deps_json_t* deps, const dir_assemblies_t& dir_assemblies, const deps_entry_t& entry)
{
if (entry.is_serviceable)
{
breadcrumb->insert(entry.library_name + _X(",") + entry.library_version);
breadcrumb->insert(entry.library_name);
}
if (items.count(entry.asset_name))
{
return;
}
pal::string_t candidate;
trace::info(_X("Processing TPA for deps entry [%s, %s, %s]"), entry.library_name.c_str(), entry.library_version.c_str(), entry.relative_path.c_str());
@ -478,7 +483,8 @@ void deps_resolver_t::resolve_tpa_list(
void deps_resolver_t::resolve_probe_dirs(
deps_entry_t::asset_types asset_type,
const pal::string_t& clr_dir,
pal::string_t* output)
pal::string_t* output,
std::unordered_set<pal::string_t>* breadcrumb)
{
bool is_resources = asset_type == deps_entry_t::asset_types::resources;
assert(is_resources || asset_type == deps_entry_t::asset_types::native);
@ -504,6 +510,12 @@ void deps_resolver_t::resolve_probe_dirs(
bool track_api_sets = true;
auto add_package_cache_entry = [&](const deps_entry_t& entry)
{
if (entry.is_serviceable)
{
breadcrumb->insert(entry.library_name + _X(",") + entry.library_version);
breadcrumb->insert(entry.library_name);
}
if (probe_entry_in_configs(entry, &candidate))
{
// For standalone apps, on win7, coreclr needs ApiSets which has to be in the DLL search path.
@ -587,10 +599,10 @@ void deps_resolver_t::resolve_probe_dirs(
// resolved path ordering.
//
//
bool deps_resolver_t::resolve_probe_paths(const pal::string_t& clr_dir, probe_paths_t* probe_paths)
bool deps_resolver_t::resolve_probe_paths(const pal::string_t& clr_dir, probe_paths_t* probe_paths, std::unordered_set<pal::string_t>* breadcrumb)
{
resolve_tpa_list(clr_dir, &probe_paths->tpa);
resolve_probe_dirs(deps_entry_t::asset_types::native, clr_dir, &probe_paths->native);
resolve_probe_dirs(deps_entry_t::asset_types::resources, clr_dir, &probe_paths->resources);
resolve_tpa_list(clr_dir, &probe_paths->tpa, breadcrumb);
resolve_probe_dirs(deps_entry_t::asset_types::native, clr_dir, &probe_paths->native, breadcrumb);
resolve_probe_dirs(deps_entry_t::asset_types::resources, clr_dir, &probe_paths->resources, breadcrumb);
return true;
}

View file

@ -25,8 +25,6 @@ class deps_resolver_t
{
public:
deps_resolver_t(const hostpolicy_init_t& init, const arguments_t& args)
// Important: FX dir should come from "init" than "config",
// since the host could be launching from FX dir.
: m_fx_dir(init.fx_dir)
, m_app_dir(args.app_dir)
, m_coreclr_index(-1)
@ -62,7 +60,8 @@ public:
bool resolve_probe_paths(
const pal::string_t& clr_dir,
probe_paths_t* probe_paths);
probe_paths_t* probe_paths,
std::unordered_set<pal::string_t>* breadcrumb);
pal::string_t resolve_coreclr_dir();
@ -93,13 +92,15 @@ private:
// Resolve order for TPA lookup.
void resolve_tpa_list(
const pal::string_t& clr_dir,
pal::string_t* output);
pal::string_t* output,
std::unordered_set<pal::string_t>* breadcrumb);
// Resolve order for culture and native DLL lookup.
void resolve_probe_dirs(
deps_entry_t::asset_types asset_type,
const pal::string_t& clr_dir,
pal::string_t* output);
pal::string_t* output,
std::unordered_set<pal::string_t>* breadcrumb);
// Populate assemblies from the directory.
void get_dir_assemblies(

View file

@ -26,6 +26,7 @@ set(SOURCES
../json/casablanca/src/json/json_serialization.cpp
../json/casablanca/src/utilities/asyncrt_utils.cpp
../fxr/fx_ver.cpp
../breadcrumbs.cpp
../args.cpp
../hostpolicy.cpp
../coreclr.cpp

View file

@ -60,7 +60,7 @@ int execute_app(
if (code != StatusCode::Success)
{
trace::error(_X("Could not load host policy library from [%s]"), impl_dll_dir.c_str());
trace::error(_X("Expected to load %s from [%s]"), LIBHOSTPOLICY_NAME, impl_dll_dir.c_str());
if (init->fx_dir() == impl_dll_dir)
{
pal::string_t name = init->fx_name();
@ -68,6 +68,10 @@ int execute_app(
trace::error(_X("This may be because the targeted framework [\"%s\": \"%s\"] was not found."),
name.c_str(), version.c_str());
}
else
{
trace::error(_X("This may be because of an invalid .NET Core FX configuration in the folder."));
}
return code;
}
@ -86,7 +90,7 @@ int execute_app(
bool hostpolicy_exists_in_svc(pal::string_t* resolved_dir)
{
pal::string_t svc_dir;
pal::get_default_extensions_directory(&svc_dir);
pal::get_default_servicing_directory(&svc_dir);
append_path(&svc_dir, _X("pkgs"));
pal::string_t version = _STRINGIFY(HOST_POLICY_PKG_VER);

View file

@ -11,6 +11,7 @@
#include "cpprest/json.h"
#include "libhost.h"
#include "error_codes.h"
#include "breadcrumbs.h"
hostpolicy_init_t g_init;
@ -36,8 +37,18 @@ int run(const arguments_t& args)
trace::info(_X("CoreCLR directory: %s"), clr_path.c_str());
}
// Setup breadcrumbs
pal::string_t policy_name = _STRINGIFY(HOST_POLICY_PKG_NAME);
pal::string_t policy_version = _STRINGIFY(HOST_POLICY_PKG_VER);
// Always insert the hostpolicy that the code is running on.
std::unordered_set<pal::string_t> breadcrumbs;
breadcrumbs.insert(policy_name);
breadcrumbs.insert(policy_name + _X(",") + policy_version);
probe_paths_t probe_paths;
if (!resolver.resolve_probe_paths(clr_path, &probe_paths))
if (!resolver.resolve_probe_paths(clr_path, &probe_paths, &breadcrumbs))
{
return StatusCode::ResolverResolveFailure;
}
@ -52,7 +63,8 @@ int run(const arguments_t& args)
"AppDomainCompatSwitch",
// Workaround: mscorlib does not resolve symlinks for AppContext.BaseDirectory dotnet/coreclr/issues/2128
"APP_CONTEXT_BASE_DIRECTORY",
"APP_CONTEXT_DEPS_FILES"
"APP_CONTEXT_DEPS_FILES",
"FX_DEPS_FILE"
};
auto tpa_paths_cstr = pal::to_stdstring(probe_paths.tpa);
@ -60,7 +72,8 @@ int run(const arguments_t& args)
auto native_dirs_cstr = pal::to_stdstring(probe_paths.native);
auto resources_dirs_cstr = pal::to_stdstring(probe_paths.resources);
std::string deps = pal::to_stdstring(resolver.get_deps_file() + _X(";") + resolver.get_fx_deps_file());
std::string fx_deps = pal::to_stdstring(resolver.get_fx_deps_file());
std::string deps = pal::to_stdstring(resolver.get_deps_file() + _X(";")) + fx_deps;
std::vector<const char*> property_values = {
// TRUSTED_PLATFORM_ASSEMBLIES
@ -79,6 +92,8 @@ int run(const arguments_t& args)
app_base_cstr.c_str(),
// APP_CONTEXT_DEPS_FILES,
deps.c_str(),
// FX_DEPS_FILE
fx_deps.c_str()
};
for (int i = 0; i < g_init.cfg_keys.size(); ++i)
@ -155,6 +170,10 @@ int run(const arguments_t& args)
std::string managed_app = pal::to_stdstring(args.managed_application);
// Leave breadcrumbs for servicing.
breadcrumb_writer_t writer(&breadcrumbs);
writer.begin_write();
// Execute the application
unsigned int exit_code = 1;
hr = coreclr::execute_assembly(
@ -164,6 +183,7 @@ int run(const arguments_t& args)
argv.data(),
managed_app.c_str(),
&exit_code);
if (!SUCCEEDED(hr))
{
trace::error(_X("Failed to execute managed app, HRESULT: 0x%X"), hr);
@ -179,6 +199,9 @@ int run(const arguments_t& args)
coreclr::unload();
// Finish breadcrumb writing
writer.end_write();
return exit_code;
}

View file

@ -165,6 +165,7 @@ namespace pal
inline void to_stdstring(const char_t* str, std::string* out) { out->assign(str); }
#endif
bool touch_file(const pal::string_t& path);
bool realpath(string_t* path);
bool file_exists(const string_t& path);
inline bool directory_exists(const string_t& path) { return file_exists(path); }
@ -173,7 +174,8 @@ namespace pal
bool get_own_executable_path(string_t* recv);
bool getenv(const char_t* name, string_t* recv);
bool get_default_extensions_directory(string_t* recv);
bool get_default_servicing_directory(string_t* recv);
bool get_default_breadcrumb_store(string_t* recv);
bool is_path_rooted(const string_t& path);
int xtoi(const char_t* input);

View file

@ -12,7 +12,7 @@
#include <sys/types.h>
#include <pwd.h>
#include <unistd.h>
#include <fcntl.h>
#include <fnmatch.h>
#if defined(__APPLE__)
@ -34,6 +34,18 @@ pal::string_t pal::to_lower(const pal::string_t& in)
return ret;
}
bool pal::touch_file(const pal::string_t& path)
{
int fd = open(path.c_str(), (O_CREAT | O_EXCL), (S_IRUSR | S_IRGRP | S_IROTH));
if (fd == -1)
{
trace::warning(_X("Failed to open() file descriptor in %s(%s)"), __FUNCTION__, path.c_str());
return false;
}
(void) close(fd);
return true;
}
bool pal::getcwd(pal::string_t* recv)
{
recv->clear();
@ -112,11 +124,68 @@ bool pal::is_path_rooted(const pal::string_t& path)
return path.front() == '/';
}
bool pal::get_default_extensions_directory(string_t* recv)
bool pal::get_default_breadcrumb_store(string_t* recv)
{
recv->clear();
append_path(recv, _X("opt"));
append_path(recv, _X("dotnetextensions"));
pal::string_t ext;
if (pal::getenv(_X("CORE_BREADCRUMBS"), &ext) && pal::realpath(&ext))
{
// We should have the path in ext.
trace::info(_X("Realpath CORE_BREADCRUMBS [%s]"), ext.c_str());
}
if (!pal::directory_exists(ext))
{
trace::info(_X("Directory core breadcrumbs [%s] was not specified or found"), ext.c_str());
ext.clear();
append_path(&ext, _X("opt"));
append_path(&ext, _X("corebreadcrumbs"));
if (!pal::directory_exists(ext))
{
trace::info(_X("Fallback directory core breadcrumbs at [%s] was not found"), ext.c_str());
return false;
}
}
if (access(ext.c_str(), (R_OK | W_OK)) != 0)
{
trace::info(_X("Breadcrumb store [%s] is not ACL-ed with rw-"), ext.c_str());
}
recv->assign(ext);
return true;
}
bool pal::get_default_servicing_directory(string_t* recv)
{
recv->clear();
pal::string_t ext;
if (pal::getenv(_X("CORE_SERVICING"), &ext) && pal::realpath(&ext))
{
// We should have the path in ext.
trace::info(_X("Realpath CORE_SERVICING [%s]"), ext.c_str());
}
if (!pal::directory_exists(ext))
{
trace::info(_X("Directory core servicing at [%s] was not specified or found"), ext.c_str());
ext.clear();
append_path(&ext, _X("opt"));
append_path(&ext, _X("coreservicing"));
if (!pal::directory_exists(ext))
{
trace::info(_X("Fallback directory core servicing at [%s] was not found"), ext.c_str());
return false;
}
}
if (access(ext.c_str(), R_OK) != 0)
{
trace::info(_X("Directory core servicing at [%s] was not ACL-ed properly"), ext.c_str());
}
recv->assign(ext);
trace::info(_X("Using core servicing at [%s]"), ext.c_str());
return true;
}

View file

@ -8,8 +8,9 @@
#include <cassert>
#include <locale>
#include <codecvt>
#include <ShlObj.h>
static std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> g_converter;
static thread_local std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> t_converter;
pal::string_t pal::to_lower(const pal::string_t& in)
{
@ -54,6 +55,18 @@ bool pal::find_coreclr(pal::string_t* recv)
return false;
}
bool pal::touch_file(const pal::string_t& path)
{
HANDLE hnd = ::CreateFileW(path.c_str(), 0, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
if (hnd == INVALID_HANDLE_VALUE)
{
trace::verbose(_X("Failed to leave breadcrumb"), HRESULT_FROM_WIN32(GetLastError()));
return false;
}
::CloseHandle(hnd);
return true;
}
void pal::setup_api_sets(const std::unordered_set<pal::string_t>& api_sets)
{
if (api_sets.empty())
@ -154,7 +167,25 @@ void pal::unload_library(dll_t library)
// No-op. On windows, we pin the library, so it can't be unloaded.
}
bool pal::get_default_extensions_directory(string_t* recv)
bool pal::get_default_breadcrumb_store(string_t* recv)
{
recv->clear();
pal::char_t* prog_dat;
HRESULT hr = ::SHGetKnownFolderPath(FOLDERID_ProgramData, 0, NULL, &prog_dat);
if (hr != S_OK)
{
trace::verbose(_X("Failed to read default breadcrumb store 0x%X"), hr);
return false;
}
recv->assign(prog_dat);
append_path(recv, _X("Microsoft"));
append_path(recv, _X("NetFramework"));
append_path(recv, _X("BreadcrumbStore"));
return true;
}
bool pal::get_default_servicing_directory(string_t* recv)
{
recv->clear();
@ -169,7 +200,7 @@ bool pal::get_default_extensions_directory(string_t* recv)
return false;
}
append_path(recv, _X("dotnetextensions"));
append_path(recv, _X("coreservicing"));
return true;
}
@ -224,22 +255,22 @@ bool pal::get_own_executable_path(string_t* recv)
std::string pal::to_stdstring(const string_t& str)
{
return g_converter.to_bytes(str);
return t_converter.to_bytes(str);
}
pal::string_t pal::to_palstring(const std::string& str)
{
return g_converter.from_bytes(str);
return t_converter.from_bytes(str);
}
void pal::to_palstring(const char* str, pal::string_t* out)
{
out->assign(g_converter.from_bytes(str));
out->assign(t_converter.from_bytes(str));
}
void pal::to_stdstring(const pal::char_t* str, std::string* out)
{
out->assign(g_converter.to_bytes(str));
out->assign(t_converter.to_bytes(str));
}
bool pal::realpath(string_t* path)