Remove some unnecessary string copies in hosting layer (#53631)
authorElinor Fung <elfung@microsoft.com>
Wed, 2 Jun 2021 22:40:38 +0000 (15:40 -0700)
committerGitHub <noreply@github.com>
Wed, 2 Jun 2021 22:40:38 +0000 (15:40 -0700)
src/native/corehost/deps_format.cpp
src/native/corehost/fxr/command_line.cpp
src/native/corehost/fxr/command_line.h
src/native/corehost/fxr/fx_muxer.cpp
src/native/corehost/fxr/fx_resolver.cpp
src/native/corehost/hostmisc/pal.h
src/native/corehost/hostmisc/pal.unix.cpp
src/native/corehost/hostmisc/pal.windows.cpp
src/native/corehost/hostpolicy/deps_resolver.cpp
src/native/corehost/runtime_config.cpp
src/native/corehost/test/nativehost/nativehost.cpp

index e60ccd5..f0beb6e 100644 (file)
@@ -70,7 +70,9 @@ void deps_json_t::reconcile_libraries_with_targets(
         for (size_t i = 0; i < deps_entry_t::s_known_asset_types.size(); ++i)
         {
             bool rid_specific = false;
-            for (const auto& asset : get_assets_fn(library.name.GetString(), i, &rid_specific))
+            const vec_asset_t& assets = get_assets_fn(lib_name, i, &rid_specific);
+            m_deps_entries[i].reserve(assets.size());
+            for (const auto& asset : assets)
             {
                 auto asset_name = asset.name;
                 if (ends_with(asset_name, _X(".ni"), false))
@@ -139,7 +141,7 @@ pal::string_t deps_json_t::get_current_rid(const rid_fallback_graph_t& rid_fallb
 bool deps_json_t::perform_rid_fallback(rid_specific_assets_t* portable_assets, const rid_fallback_graph_t& rid_fallback_graph)
 {
     pal::string_t host_rid = get_current_rid(rid_fallback_graph);
-    
+
     for (auto& package : portable_assets->libs)
     {
         for (size_t asset_type_index = 0; asset_type_index < deps_entry_t::asset_types::count; asset_type_index++)
@@ -235,13 +237,16 @@ bool deps_json_t::process_runtime_targets(const json_parser_t::value_t& json, co
 
                 const auto& rid = file.value[_X("rid")].GetString();
 
-                trace::info(_X("Adding runtimeTargets %s asset %s rid=%s assemblyVersion=%s fileVersion=%s from %s"),
-                    deps_entry_t::s_known_asset_types[asset_type_index],
-                    asset.relative_path.c_str(),
-                    rid,
-                    asset.assembly_version.as_str().c_str(),
-                    asset.file_version.as_str().c_str(),
-                    package.name.GetString());
+                if (trace::is_enabled())
+                {
+                    trace::info(_X("Adding runtimeTargets %s asset %s rid=%s assemblyVersion=%s fileVersion=%s from %s"),
+                        deps_entry_t::s_known_asset_types[asset_type_index],
+                        asset.relative_path.c_str(),
+                        rid,
+                        asset.assembly_version.as_str().c_str(),
+                        asset.file_version.as_str().c_str(),
+                        package.name.GetString());
+                }
 
                 assets.libs[package.name.GetString()][asset_type_index].rid_assets[rid].push_back(asset);
             }
@@ -270,7 +275,10 @@ bool deps_json_t::process_targets(const json_parser_t::value_t& json, const pal:
                 continue;
             }
 
-            for (const auto& file : iter->value.GetObject())
+            const auto& files = iter->value.GetObject();
+            vec_asset_t& asset_files = assets.libs[package.name.GetString()][i];
+            asset_files.reserve(files.MemberCount());
+            for (const auto& file : files)
             {
                 version_t assembly_version, file_version;
 
@@ -289,14 +297,17 @@ bool deps_json_t::process_targets(const json_parser_t::value_t& json, const pal:
                 pal::string_t file_name{file.name.GetString()};
                 deps_asset_t asset(get_filename_without_ext(file_name), file_name, assembly_version, file_version);
 
-                trace::info(_X("Adding %s asset %s assemblyVersion=%s fileVersion=%s from %s"),
-                    deps_entry_t::s_known_asset_types[i],
-                    asset.relative_path.c_str(),
-                    asset.assembly_version.as_str().c_str(),
-                    asset.file_version.as_str().c_str(),
-                    package.name.GetString());
+                if (trace::is_enabled())
+                {
+                    trace::info(_X("Adding %s asset %s assemblyVersion=%s fileVersion=%s from %s"),
+                        deps_entry_t::s_known_asset_types[i],
+                        asset.relative_path.c_str(),
+                        asset.assembly_version.as_str().c_str(),
+                        asset.file_version.as_str().c_str(),
+                        package.name.GetString());
+                }
 
-                assets.libs[package.name.GetString()][i].push_back(asset);
+                asset_files.push_back(asset);
             }
         }
     }
@@ -374,7 +385,9 @@ bool deps_json_t::load_self_contained(const pal::string_t& deps_path, const json
         for (const auto& rid : json[_X("runtimes")].GetObject())
         {
             auto& vec = m_rid_fallback_graph[rid.name.GetString()];
-            for (const auto& fallback : rid.value.GetArray())
+            const auto& fallback_array = rid.value.GetArray();
+            vec.reserve(fallback_array.Size());
+            for (const auto& fallback : fallback_array)
             {
                 vec.push_back(fallback.GetString());
             }
@@ -403,7 +416,7 @@ bool deps_json_t::has_package(const pal::string_t& name, const pal::string_t& ve
     pal::string_t pv = name;
     pv.push_back(_X('/'));
     pv.append(ver);
-    
+
     auto iter = m_rid_assets.libs.find(pv);
     if (iter != m_rid_assets.libs.end())
     {
@@ -415,7 +428,7 @@ bool deps_json_t::has_package(const pal::string_t& name, const pal::string_t& ve
             }
         }
     }
-    
+
     return m_assets.libs.count(pv);
 }
 
index 1146d40..082f81d 100644 (file)
@@ -14,9 +14,9 @@ namespace
 {
     struct host_option
     {
-        const pal::string_t option;
-        const pal::string_t argument;
-        const pal::string_t description;
+        const pal::char_t* option;
+        const pal::char_t* argument;
+        const pal::char_t* description;
     };
 
     const host_option KnownHostOptions[] =
@@ -85,7 +85,7 @@ namespace
         int arg_i = *num_args;
         while (arg_i < argc)
         {
-            pal::string_t arg = argv[arg_i];
+            const pal::char_t* arg = argv[arg_i];
             pal::string_t arg_lower = pal::to_lower(arg);
             const auto &iter = std::find_if(known_opts.cbegin(), known_opts.cend(),
                 [&](const known_options &opt) { return arg_lower == get_host_option(opt).option; });
@@ -101,7 +101,7 @@ namespace
                 return false;
             }
 
-            trace::verbose(_X("Parsed known arg %s = %s"), arg.c_str(), argv[arg_i + 1]);
+            trace::verbose(_X("Parsed known arg %s = %s"), arg, argv[arg_i + 1]);
             (*opts)[*iter].push_back(argv[arg_i + 1]);
 
             // Increment for both the option and its value.
@@ -134,7 +134,7 @@ namespace
             for (const auto& opt : known_opts)
             {
                 const host_option &arg = get_host_option(opt);
-                trace::error(_X("  %-37s  %s"), (arg.option + _X(" ") + arg.argument).c_str(), arg.description.c_str());
+                trace::error(_X("  %s %-*s  %s"), arg.option, 36 - pal::strlen(arg.option), arg.argument, arg.description);
             }
             return StatusCode::InvalidArgFailure;
         }
@@ -211,7 +211,7 @@ pal::string_t command_line::get_option_value(
     return default_value;
 }
 
-const pal::string_t& command_line::get_option_name(known_options opt)
+const pal::char_t* command_line::get_option_name(known_options opt)
 {
     return get_host_option(opt).option;
 }
@@ -325,7 +325,7 @@ void command_line::print_muxer_usage(bool is_sdk_present)
     for (const auto& opt : known_opts)
     {
         const host_option &arg = get_host_option(opt);
-        trace::println(_X("  %-30s  %s"), (arg.option + _X(" ") + arg.argument).c_str(), arg.description.c_str());
+        trace::println(_X("  %s %-*s  %s"), arg.option, 29 - pal::strlen(arg.option), arg.argument, arg.description);
     }
     trace::println(_X("  --list-runtimes                 Display the installed runtimes"));
     trace::println(_X("  --list-sdks                     Display the installed SDKs"));
@@ -334,7 +334,7 @@ void command_line::print_muxer_usage(bool is_sdk_present)
     {
         trace::println();
         trace::println(_X("Common Options:"));
-        trace::println(_X("  -h|--help                           Displays this help."));
-        trace::println(_X("  --info                              Display .NET information."));
+        trace::println(_X("  -h|--help                       Displays this help."));
+        trace::println(_X("  --info                          Display .NET information."));
     }
 }
index 7dc96a0..118206b 100644 (file)
@@ -33,7 +33,7 @@ typedef std::unordered_map<known_options, std::vector<pal::string_t>, known_opti
 
 namespace command_line
 {
-    const pal::string_t& get_option_name(known_options opt);
+    const pal::char_t* get_option_name(known_options opt);
     pal::string_t get_option_value(
         const opt_map_t& opts,
         known_options opt,
index 35673fd..6a20206 100644 (file)
@@ -404,7 +404,7 @@ namespace
             auto val = roll_forward_option_from_string(roll_forward);
             if (val == roll_forward_option::__Last)
             {
-                trace::error(_X("Invalid value for command line argument '%s'"), command_line::get_option_name(known_options::roll_forward).c_str());
+                trace::error(_X("Invalid value for command line argument '%s'"), command_line::get_option_name(known_options::roll_forward));
                 return StatusCode::InvalidArgFailure;
             }
 
@@ -417,8 +417,8 @@ namespace
             if (override_settings.has_roll_forward)
             {
                 trace::error(_X("It's invalid to use both '%s' and '%s' command line options."),
-                    command_line::get_option_name(known_options::roll_forward).c_str(),
-                    command_line::get_option_name(known_options::roll_forward_on_no_candidate_fx).c_str());
+                    command_line::get_option_name(known_options::roll_forward),
+                    command_line::get_option_name(known_options::roll_forward_on_no_candidate_fx));
                 return StatusCode::InvalidArgFailure;
             }
 
index 2da9af4..5a5fef7 100644 (file)
@@ -54,13 +54,16 @@ namespace
                 }
             }
 
-            if (best_match_version == fx_ver_t())
+            if (trace::is_enabled())
             {
-                trace::verbose(_X("No match greater than or equal to [%s] found."), fx_ref.get_fx_version().c_str());
-            }
-            else
-            {
-                trace::verbose(_X("Found version [%s]"), best_match_version.as_str().c_str());
+                if (best_match_version == fx_ver_t())
+                {
+                    trace::verbose(_X("No match greater than or equal to [%s] found."), fx_ref.get_fx_version().c_str());
+                }
+                else
+                {
+                    trace::verbose(_X("Found version [%s]"), best_match_version.as_str().c_str());
+                }
             }
         }
 
@@ -89,14 +92,20 @@ namespace
                 apply_patch_from_version = fx_ref.get_fx_version_number();
             }
 
-            trace::verbose(
-                _X("Applying patch roll forward from [%s] on %s"),
-                apply_patch_from_version.as_str().c_str(),
-                release_only ? _X("release only") : _X("release/pre-release"));
+            if (trace::is_enabled())
+            {
+                trace::verbose(
+                    _X("Applying patch roll forward from [%s] on %s"),
+                    apply_patch_from_version.as_str().c_str(),
+                    release_only ? _X("release only") : _X("release/pre-release"));
+            }
 
             for (const auto& ver : version_list)
             {
-                trace::verbose(_X("Inspecting version... [%s]"), ver.as_str().c_str());
+                if (trace::is_enabled())
+                {
+                    trace::verbose(_X("Inspecting version... [%s]"), ver.as_str().c_str());
+                }
 
                 if ((!release_only || !ver.is_prerelease()) &&
                     (fx_ref.get_apply_patches() || ver.get_patch() == apply_patch_from_version.get_patch()) &&
@@ -170,7 +179,7 @@ namespace
             best_match = fx_ref.get_fx_version_number();
             trace::verbose(_X("Framework reference didn't resolve to any available version."));
         }
-        else
+        else if (trace::is_enabled())
         {
             trace::verbose(_X("Framework reference resolved to version '%s'."), best_match.as_str().c_str());
         }
index 21bb1cf..27daf76 100644 (file)
@@ -252,7 +252,7 @@ namespace pal
     string_t get_timestamp();
 
     bool getcwd(string_t* recv);
-    string_t to_lower(const string_t& in);
+    string_t to_lower(const char_t* in);
 
 
     inline void file_flush(FILE *f) { std::fflush(f); }
index 3aa4d5d..2db2dd8 100644 (file)
@@ -50,7 +50,7 @@
 #error "Don't know how to obtain max path on this platform"
 #endif
 
-pal::string_t pal::to_lower(const pal::string_t& in)
+pal::string_t pal::to_lower(const pal::char_t* in)
 {
     pal::string_t ret = in;
     std::transform(ret.begin(), ret.end(), ret.begin(), ::tolower);
index 7b53fec..8a9e35a 100644 (file)
@@ -40,7 +40,7 @@ bool GetModuleHandleFromAddress(void *addr, HMODULE *hModule)
     return (res != FALSE);
 }
 
-pal::string_t pal::to_lower(const pal::string_t& in)
+pal::string_t pal::to_lower(const pal::char_t* in)
 {
     pal::string_t ret = in;
     std::transform(ret.begin(), ret.end(), ret.begin(), ::towlower);
index 5af59fc..aae9e87 100644 (file)
 #include <utils.h>
 #include <fx_ver.h>
 
-const pal::string_t MissingAssemblyMessage = _X(
-    "%s:\n"
-    "  An assembly specified in the application dependencies manifest (%s) was not found:\n"
-    "    package: '%s', version: '%s'\n"
-    "    path: '%s'");
-
-const pal::string_t ManifestListMessage = _X(
-    "  This assembly was expected to be in the local runtime store as the application was published using the following target manifest files:\n"
-    "    %s");
-
-const pal::string_t DuplicateAssemblyWithDifferentExtensionMessage = _X(
-    "Error:\n"
-    "  An assembly specified in the application dependencies manifest (%s) has already been found but with a different file extension:\n"
-    "    package: '%s', version: '%s'\n"
-    "    path: '%s'\n"
-    "    previously found assembly: '%s'");
-
 namespace
 {
-// -----------------------------------------------------------------------------
-// A uniqifying append helper that doesn't let two "paths" to be identical in
-// the "output" string.
-//
-void add_unique_path(
-    deps_entry_t::asset_types asset_type,
-    const pal::string_t& path,
-    std::unordered_set<pal::string_t>* existing,
-    pal::string_t* serviced,
-    pal::string_t* non_serviced,
-    const pal::string_t& svc_dir)
-{
-    // To optimize startup time, we avoid calling realpath here.
-    // Because of this, there might be duplicates in the output
-    // whenever path is eiter non-normalized or a symbolic link.
-    if (existing->count(path))
+    const pal::char_t* MissingAssemblyMessage = _X(
+        "%s:\n"
+        "  An assembly specified in the application dependencies manifest (%s) was not found:\n"
+        "    package: '%s', version: '%s'\n"
+        "    path: '%s'");
+
+    const pal::char_t* ManifestListMessage = _X(
+        "  This assembly was expected to be in the local runtime store as the application was published using the following target manifest files:\n"
+        "    %s");
+
+    const pal::char_t* DuplicateAssemblyWithDifferentExtensionMessage = _X(
+        "Error:\n"
+        "  An assembly specified in the application dependencies manifest (%s) has already been found but with a different file extension:\n"
+        "    package: '%s', version: '%s'\n"
+        "    path: '%s'\n"
+        "    previously found assembly: '%s'");
+
+    // -----------------------------------------------------------------------------
+    // A uniqifying append helper that doesn't let two "paths" to be identical in
+    // the "output" string.
+    //
+    void add_unique_path(
+        deps_entry_t::asset_types asset_type,
+        const pal::string_t& path,
+        std::unordered_set<pal::string_t>* existing,
+        pal::string_t* serviced,
+        pal::string_t* non_serviced,
+        const pal::string_t& svc_dir)
     {
-        return;
-    }
-
-    trace::verbose(_X("Adding to %s path: %s"), deps_entry_t::s_known_asset_types[asset_type], path.c_str());
+        // To optimize startup time, we avoid calling realpath here.
+        // Because of this, there might be duplicates in the output
+        // whenever path is eiter non-normalized or a symbolic link.
+        if (existing->count(path))
+        {
+            return;
+        }
 
-    if (starts_with(path, svc_dir, false))
-    {
-        serviced->append(path);
-        serviced->push_back(PATH_SEPARATOR);
-    }
-    else
-    {
-        non_serviced->append(path);
-        non_serviced->push_back(PATH_SEPARATOR);
-    }
+        trace::verbose(_X("Adding to %s path: %s"), deps_entry_t::s_known_asset_types[asset_type], path.c_str());
 
-    existing->insert(path);
-}
+        if (starts_with(path, svc_dir, false))
+        {
+            serviced->append(path);
+            serviced->push_back(PATH_SEPARATOR);
+        }
+        else
+        {
+            non_serviced->append(path);
+            non_serviced->push_back(PATH_SEPARATOR);
+        }
 
-// Return the filename from deps path; a deps path always uses a '/' for the separator.
-pal::string_t get_deps_filename(const pal::string_t& path)
-{
-    if (path.empty())
-    {
-        return path;
+        existing->insert(path);
     }
 
-    auto name_pos = path.find_last_of('/');
-    if (name_pos == pal::string_t::npos)
+    // Return the filename from deps path; a deps path always uses a '/' for the separator.
+    pal::string_t get_deps_filename(const pal::string_t& path)
     {
-        return path;
-    }
+        if (path.empty())
+        {
+            return path;
+        }
 
-    return path.substr(name_pos + 1);
-}
+        auto name_pos = path.find_last_of('/');
+        if (name_pos == pal::string_t::npos)
+        {
+            return path;
+        }
 
+        return path.substr(name_pos + 1);
+    }
 } // end of anonymous namespace
 
   // -----------------------------------------------------------------------------
@@ -97,10 +96,13 @@ void deps_resolver_t::add_tpa_asset(
     name_to_resolved_asset_map_t::iterator existing = items->find(resolved_asset.asset.name);
     if (existing == items->end())
     {
-        trace::verbose(_X("Adding tpa entry: %s, AssemblyVersion: %s, FileVersion: %s"),
-            resolved_asset.resolved_path.c_str(),
-            resolved_asset.asset.assembly_version.as_str().c_str(),
-            resolved_asset.asset.file_version.as_str().c_str());
+        if (trace::is_enabled())
+        {
+            trace::verbose(_X("Adding tpa entry: %s, AssemblyVersion: %s, FileVersion: %s"),
+                resolved_asset.resolved_path.c_str(),
+                resolved_asset.asset.assembly_version.as_str().c_str(),
+                resolved_asset.asset.file_version.as_str().c_str());
+        }
 
         items->emplace(resolved_asset.asset.name, resolved_asset);
     }
@@ -312,7 +314,8 @@ bool deps_resolver_t::probe_deps_entry(const deps_entry_t& entry, const pal::str
             trace::verbose(_X("    Skipping... not runtime asset"));
             continue;
         }
-        pal::string_t probe_dir = config.probe_dir;
+
+        const pal::string_t& probe_dir = config.probe_dir;
         uint32_t search_options = deps_entry_t::search_options::none;
         if (needs_file_existence_checks())
         {
@@ -391,32 +394,32 @@ bool report_missing_assembly_in_manifest(const deps_entry_t& entry, bool continu
         // Treat missing resource assemblies as informational.
         continueResolving = true;
 
-        trace::info(MissingAssemblyMessage.c_str(), _X("Info"),
+        trace::info(MissingAssemblyMessage, _X("Info"),
             entry.deps_file.c_str(), entry.library_name.c_str(), entry.library_version.c_str(), entry.asset.relative_path.c_str());
 
         if (showManifestListMessage)
         {
-            trace::info(ManifestListMessage.c_str(), entry.runtime_store_manifest_list.c_str());
+            trace::info(ManifestListMessage, entry.runtime_store_manifest_list.c_str());
         }
     }
     else if (continueResolving)
     {
-        trace::warning(MissingAssemblyMessage.c_str(), _X("Warning"),
+        trace::warning(MissingAssemblyMessage, _X("Warning"),
             entry.deps_file.c_str(), entry.library_name.c_str(), entry.library_version.c_str(), entry.asset.relative_path.c_str());
 
         if (showManifestListMessage)
         {
-            trace::warning(ManifestListMessage.c_str(), entry.runtime_store_manifest_list.c_str());
+            trace::warning(ManifestListMessage, entry.runtime_store_manifest_list.c_str());
         }
     }
     else
     {
-        trace::error(MissingAssemblyMessage.c_str(), _X("Error"),
+        trace::error(MissingAssemblyMessage, _X("Error"),
             entry.deps_file.c_str(), entry.library_name.c_str(), entry.library_version.c_str(), entry.asset.relative_path.c_str());
 
         if (showManifestListMessage)
         {
-            trace::error(ManifestListMessage.c_str(), entry.runtime_store_manifest_list.c_str());
+            trace::error(ManifestListMessage, entry.runtime_store_manifest_list.c_str());
         }
     }
 
@@ -476,7 +479,7 @@ bool deps_resolver_t::resolve_tpa_list(
             if (get_deps_filename(entry.asset.relative_path) != get_filename(existing->second.resolved_path))
             {
                 trace::error(
-                    DuplicateAssemblyWithDifferentExtensionMessage.c_str(),
+                    DuplicateAssemblyWithDifferentExtensionMessage,
                     entry.deps_file.c_str(),
                     entry.library_name.c_str(),
                     entry.library_version.c_str(),
@@ -599,7 +602,7 @@ bool deps_resolver_t::resolve_tpa_list(
         }
     }
 
-    // Convert the paths into a string and return it 
+    // Convert the paths into a string and return it
     for (const auto& item : items)
     {
         output->append(item.second.resolved_path);
@@ -664,14 +667,14 @@ void deps_resolver_t::resolve_additional_deps(const arguments_t& args, const dep
         {
             if (pal::file_exists(additional_deps_path))
             {
-                trace::verbose(_X("Using specified additional deps.json: '%s'"), 
+                trace::verbose(_X("Using specified additional deps.json: '%s'"),
                     additional_deps_path.c_str());
 
                 m_additional_deps_files.push_back(additional_deps_path);
             }
             else
             {
-                trace::warning(_X("Warning: Specified additional deps.json does not exist: '%s'"), 
+                trace::warning(_X("Warning: Specified additional deps.json does not exist: '%s'"),
                     additional_deps_path.c_str());
             }
         }
@@ -815,7 +818,7 @@ bool deps_resolver_t::resolve_probe_dirs(
             return true;
         }
 
-        trace::verbose(_X("Processing native/culture for deps entry [%s, %s, %s]"), 
+        trace::verbose(_X("Processing native/culture for deps entry [%s, %s, %s]"),
             entry.library_name.c_str(), entry.library_version.c_str(), entry.asset.relative_path.c_str());
 
         bool found_in_bundle = false;
index 218394e..986c44c 100644 (file)
@@ -89,7 +89,9 @@ bool runtime_config_t::parse_opts(const json_parser_t::value_t& opts)
     const auto& properties = opts_obj.FindMember(_X("configProperties"));
     if (properties != opts_obj.MemberEnd())
     {
-        for (const auto& property : properties->value.GetObject())
+        const auto& properties_obj = properties->value.GetObject();
+        m_properties.reserve(properties_obj.MemberCount());
+        for (const auto& property : properties_obj)
         {
             if (property.value.IsString())
             {
index 8987cb8..b323f95 100644 (file)
@@ -40,7 +40,7 @@ int main(const int argc, const pal::char_t *argv[])
         // args: ... [<explicit_load>] [<assembly_path>] [<dotnet_root>] [<hostfxr_to_load>]
         bool explicit_load = false;
         if (argc >= 3)
-            explicit_load = pal::strcmp(pal::to_lower(pal::string_t{argv[2]}).c_str(), _X("true")) == 0;
+            explicit_load = pal::strcmp(pal::to_lower(argv[2]).c_str(), _X("true")) == 0;
 
         const pal::char_t *assembly_path = nullptr;
         if (argc >= 4 && pal::strcmp(argv[3], _X("nullptr")) != 0)
@@ -117,7 +117,7 @@ int main(const int argc, const pal::char_t *argv[])
         if (static_cast<StatusCode>(res) == StatusCode::Success)
         {
             std::cout << "get_hostfxr_path succeeded" << std::endl;
-            std::cout << "hostfxr_path: " << tostr(pal::to_lower(fxr_path)).data() << std::endl;
+            std::cout << "hostfxr_path: " << tostr(pal::to_lower(fxr_path.c_str())).data() << std::endl;
             return EXIT_SUCCESS;
         }
         else