diff --git a/src/coreclr/gc/CMakeLists.txt b/src/coreclr/gc/CMakeLists.txt index 12426bd27dc337..0fe0b8cc340900 100644 --- a/src/coreclr/gc/CMakeLists.txt +++ b/src/coreclr/gc/CMakeLists.txt @@ -133,3 +133,6 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/env) install_clr(TARGETS clrgc DESTINATIONS . sharedFramework COMPONENT runtime) +if (CLR_CMAKE_TARGET_ARCH_ARM64 OR CLR_CMAKE_TARGET_ARCH_AMD64) + install_clr(TARGETS clrgcexp DESTINATIONS . sharedFramework COMPONENT runtime) +endif (CLR_CMAKE_TARGET_ARCH_ARM64 OR CLR_CMAKE_TARGET_ARCH_AMD64) \ No newline at end of file diff --git a/src/coreclr/gc/gc.cpp b/src/coreclr/gc/gc.cpp index 1cf604da8a87bc..ed80110e24870f 100644 --- a/src/coreclr/gc/gc.cpp +++ b/src/coreclr/gc/gc.cpp @@ -11856,7 +11856,13 @@ void gc_heap::return_free_region (heap_segment* region) } clear_region_info (region); +#ifndef MULTIPLE_HEAPS + free_list_snapshot::record(return_free_region_start, free_regions_basic, &free_regions[(int)basic_free_region]); +#endif region_free_list::add_region_descending (region, free_regions); +#ifndef MULTIPLE_HEAPS + free_list_snapshot::record(return_free_region_end, free_regions_basic, &free_regions[(int)basic_free_region]); +#endif uint8_t* region_start = get_region_start (region); uint8_t* region_end = heap_segment_reserved (region); @@ -11887,8 +11893,14 @@ heap_segment* gc_heap::get_free_region (int gen_number, size_t size) if (gen_number <= max_generation) { +#ifndef MULTIPLE_HEAPS + free_list_snapshot::record(get_free_region_start, free_regions_basic, &free_regions[(int)basic_free_region]); +#endif assert (size == 0); region = free_regions[basic_free_region].unlink_region_front(); +#ifndef MULTIPLE_HEAPS + free_list_snapshot::record(get_free_region_end, free_regions_basic, &free_regions[(int)basic_free_region]); +#endif } else { @@ -12747,6 +12759,11 @@ static int64_t add_regions (region_free_list* free_list, region_free_list* surpl return added_count; } +void region_assert(bool value) +{ + if (!value) free_list_snapshot::fail(); +} + region_free_list::region_free_list() : num_free_regions (0), size_free_regions (0), size_committed_in_free_regions (0), @@ -12759,38 +12776,46 @@ region_free_list::region_free_list() : num_free_regions (0), void region_free_list::verify (bool empty_p) { -#ifdef _DEBUG - assert ((num_free_regions == 0) == empty_p); - assert ((size_free_regions == 0) == empty_p); - assert ((size_committed_in_free_regions == 0) == empty_p); - assert ((head_free_region == nullptr) == empty_p); - assert ((tail_free_region == nullptr) == empty_p); - assert (num_free_regions == (num_free_regions_added - num_free_regions_removed)); + region_assert ((num_free_regions == 0) == empty_p); + region_assert ((size_free_regions == 0) == empty_p); + region_assert ((size_committed_in_free_regions == 0) == empty_p); + region_assert ((head_free_region == nullptr) == empty_p); + region_assert ((tail_free_region == nullptr) == empty_p); + region_assert (num_free_regions == (num_free_regions_added - num_free_regions_removed)); if (!empty_p) { - assert (heap_segment_next (tail_free_region) == nullptr); - assert (heap_segment_prev_free_region (head_free_region) == nullptr); + region_assert (heap_segment_next (tail_free_region) == nullptr); + region_assert (heap_segment_prev_free_region (head_free_region) == nullptr); size_t actual_count = 0; + size_t actual_size = 0; + size_t actual_committed_size = 0; heap_segment* last_region = nullptr; for (heap_segment* region = head_free_region; region != nullptr; region = heap_segment_next(region)) { last_region = region; actual_count++; - } - assert (num_free_regions == actual_count); - assert (last_region == tail_free_region); + size_t region_size = get_region_size (region); + actual_size += region_size; + size_t region_committed_size = get_region_committed_size (region); + actual_committed_size += region_committed_size; + region_assert (region_size >= region_committed_size); + } + region_assert (num_free_regions == actual_count); + region_assert (size_free_regions == actual_size); + region_assert (size_committed_in_free_regions == actual_committed_size); + region_assert (actual_size >= actual_committed_size); + region_assert (last_region == tail_free_region); heap_segment* first_region = nullptr; for (heap_segment* region = tail_free_region; region != nullptr; region = heap_segment_prev_free_region(region)) { first_region = region; actual_count--; } - assert (actual_count == 0); - assert (head_free_region == first_region); + region_assert (actual_count == 0); + region_assert (head_free_region == first_region); } -#endif } void region_free_list::reset() @@ -12820,12 +12845,13 @@ void region_free_list::update_added_region_info (heap_segment* region) void region_free_list::add_region_front (heap_segment* region) { - assert (heap_segment_containing_free_list (region) == nullptr); + verify (num_free_regions == 0); + region_assert (heap_segment_containing_free_list (region) == nullptr); heap_segment_containing_free_list(region) = this; if (head_free_region != nullptr) { heap_segment_prev_free_region(head_free_region) = region; - assert (tail_free_region != nullptr); + region_assert (tail_free_region != nullptr); } else { @@ -12842,7 +12868,8 @@ void region_free_list::add_region_front (heap_segment* region) // we find a region whose committed size is >= this region's committed or we reach the head. void region_free_list::add_region_in_descending_order (heap_segment* region_to_add) { - assert (heap_segment_containing_free_list (region_to_add) == nullptr); + verify (num_free_regions == 0); + region_assert (heap_segment_containing_free_list (region_to_add) == nullptr); heap_segment_containing_free_list (region_to_add) = this; heap_segment_age_in_free (region_to_add) = 0; heap_segment* prev_region = nullptr; @@ -12878,7 +12905,7 @@ void region_free_list::add_region_in_descending_order (heap_segment* region_to_a } else { - assert (region == head_free_region); + region_assert (region == head_free_region); head_free_region = region_to_add; } @@ -12891,7 +12918,7 @@ void region_free_list::add_region_in_descending_order (heap_segment* region_to_a } else { - assert (prev_region == tail_free_region); + region_assert (prev_region == tail_free_region); tail_free_region = region_to_add; } @@ -12903,7 +12930,7 @@ heap_segment* region_free_list::unlink_region_front() heap_segment* region = head_free_region; if (region != nullptr) { - assert (heap_segment_containing_free_list (region) == this); + region_assert (heap_segment_containing_free_list (region) == this); unlink_region (region); } return region; @@ -12919,25 +12946,25 @@ void region_free_list::unlink_region (heap_segment* region) if (prev != nullptr) { - assert (region != rfl->head_free_region); - assert (heap_segment_next (prev) == region); + region_assert (region != rfl->head_free_region); + region_assert (heap_segment_next (prev) == region); heap_segment_next (prev) = next; } else { - assert (region == rfl->head_free_region); + region_assert (region == rfl->head_free_region); rfl->head_free_region = next; } if (next != nullptr) { - assert (region != rfl->tail_free_region); - assert (heap_segment_prev_free_region (next) == region); + region_assert (region != rfl->tail_free_region); + region_assert (heap_segment_prev_free_region (next) == region); heap_segment_prev_free_region (next) = prev; } else { - assert (region == rfl->tail_free_region); + region_assert (region == rfl->tail_free_region); rfl->tail_free_region = prev; } heap_segment_containing_free_list (region) = nullptr; @@ -12946,12 +12973,14 @@ void region_free_list::unlink_region (heap_segment* region) rfl->num_free_regions_removed++; size_t region_size = get_region_size (region); - assert (rfl->size_free_regions >= region_size); + region_assert (rfl->size_free_regions >= region_size); rfl->size_free_regions -= region_size; size_t region_committed_size = get_region_committed_size (region); - assert (rfl->size_committed_in_free_regions >= region_committed_size); + region_assert (rfl->size_committed_in_free_regions >= region_committed_size); rfl->size_committed_in_free_regions -= region_committed_size; + + rfl->verify (rfl->num_free_regions == 0); } free_region_kind region_free_list::get_region_kind (heap_segment* region) @@ -12966,7 +12995,7 @@ free_region_kind region_free_list::get_region_kind (heap_segment* region) return large_free_region; else { - assert(region_size > LARGE_REGION_SIZE); + region_assert(region_size > LARGE_REGION_SIZE); return huge_free_region; } } @@ -12985,7 +13014,7 @@ heap_segment* region_free_list::unlink_smallest_region (size_t minimum_size) size_t region_size = get_region_size (region); const size_t LARGE_REGION_SIZE = global_region_allocator.get_large_region_alignment(); - assert (region_size >= LARGE_REGION_SIZE * 2); + region_assert (region_size >= LARGE_REGION_SIZE * 2); if (region_size >= minimum_size) { // found a region that is large enough - see if it's smaller than the smallest so far @@ -12998,7 +13027,7 @@ heap_segment* region_free_list::unlink_smallest_region (size_t minimum_size) if (region_size == LARGE_REGION_SIZE * 2) { // we won't find a smaller one on this list - assert (region == smallest_region); + region_assert (region == smallest_region); break; } } @@ -13197,7 +13226,7 @@ static heap_segment* merge_sort_by_committed_and_age (heap_segment *head, size_t if (head != nullptr) { - assert (mid == nullptr); + region_assert (mid == nullptr); heap_segment_next (new_tail) = head; } else @@ -13219,16 +13248,128 @@ void region_free_list::sort_by_committed_and_age() for (heap_segment* region = new_head; region != nullptr; region = heap_segment_next (region)) { heap_segment_prev_free_region (region) = prev; - assert ((prev == nullptr) || (compare_by_committed_and_age (prev, region) <= 0)); + region_assert ((prev == nullptr) || (compare_by_committed_and_age (prev, region) <= 0)); prev = region; } tail_free_region = prev; } + +int* free_list_snapshot::s_nullptr; +int free_list_snapshot::s_dummy; + +VOLATILE(int32_t) free_list_snapshot::s_lock; + +free_list_snapshot* free_list_snapshot::s_buffer; + +int free_list_snapshot::s_counter_full; +int free_list_snapshot::s_counter; + +void free_list_snapshot::init() +{ + s_nullptr = nullptr; + s_dummy = 0; + s_lock = 0; + s_counter = 0; + s_buffer = new (nothrow) free_list_snapshot[NUM_SNAPSHOTS]; +} + +void free_list_snapshot::fail() +{ + s_dummy = *s_nullptr; +} + +void free_list_snapshot::record(heap_segment_snapshot* dst, heap_segment* src) +{ + dst->next = src->next; + dst->prev_free_region = src->prev_free_region; + + dst->heap_segment = src; + + dst->allocated = src->allocated; + dst->committed = src->committed; + dst->reserved = src->reserved; + dst->used = src->used; + dst->mem = src->mem; + + dst->gen_num = src->gen_num; + // We need a read of s_dummy somewhere in order to avoid fail() being completely + // optimized away. + dst->valid = 1 + static_cast(free_list_snapshot::s_dummy); + dst->age_in_free = src->age_in_free; +} + +void free_list_snapshot::record(snapshot_stage stage, freelist_type type, region_free_list* free_list) +{ + // Enter, should be no contention + if (Interlocked::CompareExchange(&s_lock, 1, 0) != 0) fail(); + +#ifndef MULTIPLE_HEAPS + s_buffer[s_counter].gc_index = VolatileLoadWithoutBarrier (&gc_heap::settings.gc_index); +#endif + s_buffer[s_counter].snapshot_index = s_counter_full; + s_buffer[s_counter].stage = stage; + s_buffer[s_counter].type = type; + + s_buffer[s_counter].free_list = free_list; + s_buffer[s_counter].num_free_regions = free_list->num_free_regions; + s_buffer[s_counter].size_free_regions = free_list->size_free_regions; + s_buffer[s_counter].size_committed_in_free_regions = free_list->size_committed_in_free_regions; + s_buffer[s_counter].num_free_regions_added = free_list->num_free_regions_added; + s_buffer[s_counter].num_free_regions_removed = free_list->num_free_regions_removed; + + // Zero these because the writes below are conditional + memset(s_buffer[s_counter].start, 0, SNAPSHOT_SIZE * sizeof(heap_segment_snapshot)); + memset(s_buffer[s_counter].end, 0, SNAPSHOT_SIZE * sizeof(heap_segment_snapshot)); + + int i; + heap_segment* current; + for (i = 0, current = free_list->head_free_region; + (i < SNAPSHOT_SIZE) && (current != nullptr); + ++i, current = current->next) + { + record(&s_buffer[s_counter].start[i], current); + } + + for (i = 0, current = free_list->tail_free_region; + (i < SNAPSHOT_SIZE) && (current != nullptr); + ++i, current = current->prev_free_region) + { + record(&s_buffer[s_counter].end[i], current); + } + + // Increment counters. Keep both for easy debugger inspection. + s_counter_full++; + s_counter = (s_counter + 1) % NUM_SNAPSHOTS; + +#if 0 +#ifndef MULTIPLE_HEAPS + static bool sabotaged = false; + if (!sabotaged + && (s_counter_full >= 2700) + && (gc_heap::free_regions[basic_free_region].get_num_free_regions() > 3)) + { + heap_segment_prev_free_region + (heap_segment_next + (heap_segment_next + (gc_heap::free_regions[(int)basic_free_region].head_free_region))) = nullptr; + sabotaged = true; + } +#endif +#endif + + // Exit, should have no corruption + if (Interlocked::CompareExchange(&s_lock, 0, 1) != 1) fail(); +} + #endif //USE_REGIONS void gc_heap::distribute_free_regions() { #ifdef USE_REGIONS +#ifndef MULTIPLE_HEAPS + free_list_snapshot::record(distribute_free_regions_start, free_regions_basic, &free_regions[(int)basic_free_region]); + free_list_snapshot::record(distribute_free_regions_start, global_decommit_basic, &global_regions_to_decommit[(int)basic_free_region]); +#endif const int kind_count = large_free_region + 1; #ifdef MULTIPLE_HEAPS @@ -13292,6 +13433,11 @@ void gc_heap::distribute_free_regions() } } +#ifndef MULTIPLE_HEAPS + free_list_snapshot::record(aggressive_end, free_regions_basic, &free_regions[(int)basic_free_region]); + free_list_snapshot::record(aggressive_end, global_decommit_basic, &global_regions_to_decommit[(int)basic_free_region]); +#endif + return; } @@ -13312,6 +13458,11 @@ void gc_heap::distribute_free_regions() // use these to fill the budget as well surplus_regions[kind].transfer_regions (&global_regions_to_decommit[kind]); } +#ifndef MULTIPLE_HEAPS + free_list_snapshot::record(added_surplus, free_regions_basic, &free_regions[(int)basic_free_region]); + free_list_snapshot::record(added_surplus, global_decommit_basic, &global_regions_to_decommit[(int)basic_free_region]); + free_list_snapshot::record(added_surplus, surplus_basic, &surplus_regions[(int)basic_free_region]); +#endif #ifdef MULTIPLE_HEAPS for (int i = 0; i < n_heaps; i++) { @@ -13356,6 +13507,11 @@ void gc_heap::distribute_free_regions() heap_budget_in_region_units[i][large_free_region] = 0; } +#ifndef MULTIPLE_HEAPS + free_list_snapshot::record(moved_old_oom, free_regions_basic, &free_regions[(int)basic_free_region]); + free_list_snapshot::record(moved_old_oom, global_decommit_basic, &global_regions_to_decommit[(int)basic_free_region]); +#endif + for (int gen = soh_gen0; gen < total_generation_count; gen++) { if ((gen <= soh_gen2) && @@ -13527,6 +13683,12 @@ void gc_heap::distribute_free_regions() } } +#ifndef MULTIPLE_HEAPS + free_list_snapshot::record(processed_balance, free_regions_basic, &free_regions[(int)basic_free_region]); + free_list_snapshot::record(processed_balance, global_decommit_basic, &global_regions_to_decommit[(int)basic_free_region]); + free_list_snapshot::record(processed_balance, surplus_basic, &surplus_regions[(int)basic_free_region]); +#endif + for (int kind = basic_free_region; kind < kind_count; kind++) { #ifdef MULTIPLE_HEAPS @@ -13547,6 +13709,7 @@ void gc_heap::distribute_free_regions() remove_surplus_regions (&hp->free_regions[kind], &surplus_regions[kind], heap_budget_in_region_units[i][kind]); } } + // finally go through all the heaps and distribute any surplus regions to heaps having too few free regions for (int i = 0; i < n_heaps; i++) { @@ -13578,6 +13741,12 @@ void gc_heap::distribute_free_regions() } } +#ifndef MULTIPLE_HEAPS + free_list_snapshot::record(sorted, free_regions_basic, &free_regions[(int)basic_free_region]); + free_list_snapshot::record(sorted, global_decommit_basic, &global_regions_to_decommit[(int)basic_free_region]); + free_list_snapshot::record(sorted, surplus_basic, &surplus_regions[(int)basic_free_region]); +#endif + #ifdef MULTIPLE_HEAPS for (int kind = basic_free_region; kind < count_free_region_kinds; kind++) { @@ -13612,6 +13781,11 @@ void gc_heap::distribute_free_regions() free_regions[kind].transfer_regions (&global_regions_to_decommit[kind]); } } + + free_list_snapshot::record(distribute_free_regions_end, free_regions_basic, &free_regions[(int)basic_free_region]); + free_list_snapshot::record(distribute_free_regions_end, global_decommit_basic, &global_regions_to_decommit[(int)basic_free_region]); + free_list_snapshot::record(distribute_free_regions_end, surplus_basic, &surplus_regions[(int)basic_free_region]); + #endif //MULTIPLE_HEAPS #endif //USE_REGIONS } @@ -22249,6 +22423,20 @@ void gc_heap::gc1() assert (settings.concurrent == (uint32_t)(bgc_thread_id.IsCurrentThread())); #endif //BACKGROUND_GC +#ifdef USE_REGIONS +#ifndef MULTIPLE_HEAPS + { + region_free_list* basic_free_regions = &free_regions[(int)basic_free_region]; + free_list_snapshot::record(gc_start, free_regions_basic, basic_free_regions); + basic_free_regions->verify (basic_free_regions->get_num_free_regions() == 0); + + region_free_list* global_decommit_basic_regions = &global_regions_to_decommit[(int)basic_free_region]; + free_list_snapshot::record(gc_start, global_decommit_basic, global_decommit_basic_regions); + global_decommit_basic_regions->verify (global_decommit_basic_regions->get_num_free_regions() == 0); + } +#endif +#endif + verify_soh_segment_list(); int n = settings.condemned_generation; @@ -22859,6 +23047,21 @@ void gc_heap::gc1() update_end_ngc_time(); update_end_gc_time_per_heap(); add_to_history_per_heap(); + +#ifdef USE_REGIONS +#ifndef MULTIPLE_HEAPS + { + region_free_list* basic_free_regions = &free_regions[(int)basic_free_region]; + free_list_snapshot::record(gc_end, free_regions_basic, basic_free_regions); + basic_free_regions->verify (basic_free_regions->get_num_free_regions() == 0); + + region_free_list* global_decommit_basic_regions = &global_regions_to_decommit[(int)basic_free_region]; + free_list_snapshot::record(gc_end, global_decommit_basic, global_decommit_basic_regions); + global_decommit_basic_regions->verify (global_decommit_basic_regions->get_num_free_regions() == 0); + } +#endif +#endif + do_post_gc(); } @@ -32392,6 +32595,9 @@ void gc_heap::plan_phase (int condemned_gen_number) dprintf(3,( " From %zx to %zx", (size_t)x, (size_t)end)); #ifdef USE_REGIONS +#ifndef MULTIPLE_HEAPS + free_list_snapshot::record(plan_should_sweep_start, free_regions_basic, &free_regions[(int)basic_free_region]); +#endif if (should_sweep_in_plan (seg1)) { sweep_region_in_plan (seg1, use_mark_list, mark_list_next, mark_list_index); @@ -32965,6 +33171,12 @@ void gc_heap::plan_phase (int condemned_gen_number) x = find_next_marked (x, end, use_mark_list, mark_list_next, mark_list_index); } +#ifdef USE_REGIONS +#ifndef MULTIPLE_HEAPS + free_list_snapshot::record(plan_should_sweep_end, free_regions_basic, &free_regions[(int)basic_free_region]); +#endif +#endif + #ifndef USE_REGIONS while (!pinned_plug_que_empty_p()) { @@ -48280,6 +48492,9 @@ HRESULT GCHeap::Initialize() uint32_t max_nhp_from_config = (uint32_t)GCConfig::GetMaxHeapCount(); #ifndef MULTIPLE_HEAPS +#ifdef USE_REGIONS + free_list_snapshot::init(); +#endif GCConfig::SetServerGC(false); #else //!MULTIPLE_HEAPS GCConfig::SetServerGC(true); diff --git a/src/coreclr/gc/gcpriv.h b/src/coreclr/gc/gcpriv.h index cce6c5ee28adf0..5f647cc3fa2a77 100644 --- a/src/coreclr/gc/gcpriv.h +++ b/src/coreclr/gc/gcpriv.h @@ -1413,6 +1413,8 @@ static_assert(count_free_region_kinds == FREE_REGION_KINDS, "Keep count_free_reg class region_free_list { + friend class free_list_snapshot; + size_t num_free_regions; size_t size_free_regions; size_t size_committed_in_free_regions; @@ -1449,6 +1451,107 @@ class region_free_list }; static_assert(sizeof(region_free_list) == sizeof(dac_region_free_list), "The DAC relies on the size of these two types matching for pointer arithmetic."); + +enum snapshot_stage +{ + distribute_free_regions_start = 1, + aggressive_end = 2, + added_surplus = 3, + moved_old_oom = 4, + processed_balance = 5, + sorted = 6, + distribute_free_regions_end = 7, + plan_should_sweep_start = 8, + plan_should_sweep_end = 9, + get_free_region_start = 10, + get_free_region_end = 11, + return_free_region_start = 12, + return_free_region_end = 13, + gc_start = 14, + gc_end = 15, +}; + +enum freelist_type +{ + free_regions_basic = 0x101, // "1" 257 + global_decommit_basic = 0x102, // "2" 258 + surplus_basic = 0x103, // "3" 259 +}; + +class heap_segment_snapshot +{ +public: + PTR_heap_segment next; + PTR_heap_segment prev_free_region; + + heap_segment* heap_segment; + + uint8_t* allocated; + uint8_t* committed; + uint8_t* reserved; + uint8_t* used; + uint8_t* mem; + + uint8_t gen_num; + uint8_t valid; + int age_in_free; +}; + +template struct TAssertEquality { + static_assert(A==B, "Not equal"); + static constexpr bool _cResult = (A==B); +}; + +#ifdef HOST_64BIT +static constexpr bool _sizeof_heapsegment_snapshot_equal = + TAssertEquality::_cResult; +#endif + +const int NUM_SNAPSHOTS = 1000; +const int SNAPSHOT_SIZE = 50; + +class free_list_snapshot +{ +private: + static int* s_nullptr; // always null + static int s_dummy; // always zero + + static VOLATILE(int32_t) s_lock; + + static free_list_snapshot* s_buffer; + + static int s_counter_full; + static int s_counter; + + size_t gc_index; + int snapshot_index; + snapshot_stage stage; + freelist_type type; + + region_free_list* free_list; + size_t num_free_regions; + size_t size_free_regions; + size_t size_committed_in_free_regions; + size_t num_free_regions_added; + size_t num_free_regions_removed; + heap_segment_snapshot start[SNAPSHOT_SIZE]; + heap_segment_snapshot end[SNAPSHOT_SIZE]; + +public: + static void init(); + static void fail(); + static void record(heap_segment_snapshot* dst, heap_segment* src); + static void record(snapshot_stage stage, freelist_type type, region_free_list* free_list); +}; + +#ifdef HOST_64BIT +static constexpr bool _sizeof_freelist_snapshot_symbolic_equal = + TAssertEquality::_cResult; +static constexpr bool _sizeof_freelist_snapshot_value_equal = + TAssertEquality::_cResult; +static constexpr bool _sizeof_freelist_snapshot_buffer_equal = + TAssertEquality::_cResult; +#endif #endif enum bookkeeping_element @@ -1495,6 +1598,7 @@ float median_of_3 (float a, float b, float c); //class definition of the internal class class gc_heap { + friend class free_list_snapshot; friend class GCHeap; #ifdef FEATURE_PREMORTEM_FINALIZATION friend class CFinalize; diff --git a/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props b/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props index c44ffae5a7caff..9426c718b8beb7 100644 --- a/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props +++ b/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props @@ -107,6 +107,9 @@ + + +