Skip to content
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
323 changes: 264 additions & 59 deletions docs/design/datacontracts/GC.md

Large diffs are not rendered by default.

19 changes: 17 additions & 2 deletions src/coreclr/gc/datadescriptor/datadescriptor.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,16 @@ struct cdac_data<GC_NAMESPACE::gc_heap>
#ifdef BACKGROUND_GC
static constexpr c_gc_state* CurrentGCState = const_cast<c_gc_state*>(&GC_NAMESPACE::gc_heap::current_c_gc_state);
#endif // BACKGROUND_GC
#ifdef DYNAMIC_HEAP_COUNT
static constexpr int* DynamicAdaptationMode = &GC_NAMESPACE::gc_heap::dynamic_adaptation_mode;
#endif // DYNAMIC_HEAP_COUNT
#ifdef SERVER_GC
static constexpr GC_NAMESPACE::gc_heap*** Heaps = &GC_NAMESPACE::gc_heap::g_heaps;
#endif // SERVER_GC

GC_HEAP_FIELD(OomData, oom_info)

/* For use in GCHeapDetails APIs */
GC_HEAP_FIELD(MarkArray, mark_array)
GC_HEAP_FIELD(NextSweepObj, next_sweep_obj)
GC_HEAP_FIELD(BackgroundMinSavedAddr, background_saved_lowest_address)
Expand All @@ -53,13 +59,22 @@ struct cdac_data<GC_NAMESPACE::gc_heap>
GC_HEAP_FIELD(EphemeralHeapSegment, ephemeral_heap_segment)
GC_HEAP_FIELD(CardTable, card_table)
GC_HEAP_FIELD(FinalizeQueue, finalize_queue)

GC_HEAP_FIELD(GenerationTable, generation_table)

#ifndef USE_REGIONS
GC_HEAP_FIELD(SavedSweepEphemeralSeg, saved_sweep_ephemeral_seg)
GC_HEAP_FIELD(SavedSweepEphemeralStart, saved_sweep_ephemeral_start)
#endif // !USE_REGIONS

/* For use in GCHeapAnalyzeData APIs */
GC_HEAP_FIELD(InternalRootArray, internal_root_array)
GC_HEAP_FIELD(InternalRootArrayIndex, internal_root_array_index)
GC_HEAP_FIELD(HeapAnalyzeSuccess, heap_analyze_success)

/* For use in GCInterestingInfo APIs */
GC_HEAP_FIELD(InterestingData, interesting_data_per_heap)
GC_HEAP_FIELD(CompactReasons, compact_reasons_per_heap)
GC_HEAP_FIELD(ExpandMechanisms, expand_mechanisms_per_heap)
GC_HEAP_FIELD(InterestingMechanismBits, interesting_mechanism_bits_per_heap)
};

template<>
Expand Down
77 changes: 71 additions & 6 deletions src/coreclr/gc/datadescriptor/datadescriptor.inc
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ CDAC_TYPE_FIELD(GCHeap, /*pointer*/, GenerationTable, cdac_data<GC_NAMESPACE::gc
CDAC_TYPE_FIELD(GCHeap, /*pointer*/, SavedSweepEphemeralSeg, cdac_data<GC_NAMESPACE::gc_heap>::SavedSweepEphemeralSeg)
CDAC_TYPE_FIELD(GCHeap, /*pointer*/, SavedSweepEphemeralStart, cdac_data<GC_NAMESPACE::gc_heap>::SavedSweepEphemeralStart)
#endif // !USE_REGIONS
CDAC_TYPE_FIELD(GCHeap, /*oom_history*/, OomData, cdac_data<GC_NAMESPACE::gc_heap>::OomData)
CDAC_TYPE_FIELD(GCHeap, /*pointer*/, InternalRootArray, cdac_data<GC_NAMESPACE::gc_heap>::InternalRootArray)
CDAC_TYPE_FIELD(GCHeap, /*nuint*/, InternalRootArrayIndex, cdac_data<GC_NAMESPACE::gc_heap>::InternalRootArrayIndex)
CDAC_TYPE_FIELD(GCHeap, /*int*/, HeapAnalyzeSuccess, cdac_data<GC_NAMESPACE::gc_heap>::HeapAnalyzeSuccess)
CDAC_TYPE_FIELD(GCHeap, /*pointer*/, InterestingData, cdac_data<GC_NAMESPACE::gc_heap>::InterestingData)
CDAC_TYPE_FIELD(GCHeap, /*pointer*/, CompactReasons, cdac_data<GC_NAMESPACE::gc_heap>::CompactReasons)
CDAC_TYPE_FIELD(GCHeap, /*pointer*/, ExpandMechanisms, cdac_data<GC_NAMESPACE::gc_heap>::ExpandMechanisms)
CDAC_TYPE_FIELD(GCHeap, /*pointer*/, InterestingMechanismBits, cdac_data<GC_NAMESPACE::gc_heap>::InterestingMechanismBits)
CDAC_TYPE_END(GCHeap)
#endif // SERVER_GC

Expand All @@ -43,12 +51,46 @@ CDAC_TYPE_INDETERMINATE(CFinalize)
CDAC_TYPE_FIELD(CFinalize, /*pointer*/, FillPointers, cdac_data<GC_NAMESPACE::CFinalize>::FillPointers)
CDAC_TYPE_END(CFinalize)

CDAC_TYPE_BEGIN(HeapSegment)
CDAC_TYPE_INDETERMINATE(HeapSegment)
CDAC_TYPE_FIELD(HeapSegment, /*pointer*/, Allocated, offsetof(GC_NAMESPACE::heap_segment, allocated))
CDAC_TYPE_FIELD(HeapSegment, /*pointer*/, Committed, offsetof(GC_NAMESPACE::heap_segment, committed))
CDAC_TYPE_FIELD(HeapSegment, /*pointer*/, Reserved, offsetof(GC_NAMESPACE::heap_segment, reserved))
CDAC_TYPE_FIELD(HeapSegment, /*pointer*/, Used, offsetof(GC_NAMESPACE::heap_segment, used))
CDAC_TYPE_FIELD(HeapSegment, /*pointer*/, Mem, offsetof(GC_NAMESPACE::heap_segment, mem))
CDAC_TYPE_FIELD(HeapSegment, /*nuint*/, Flags, offsetof(GC_NAMESPACE::heap_segment, flags))
CDAC_TYPE_FIELD(HeapSegment, /*pointer*/, Next, offsetof(GC_NAMESPACE::heap_segment, next))
CDAC_TYPE_FIELD(HeapSegment, /*pointer*/, BackgroundAllocated, offsetof(GC_NAMESPACE::heap_segment, background_allocated))
#ifdef MULTIPLE_HEAPS
CDAC_TYPE_FIELD(HeapSegment, /*pointer*/, Heap, offsetof(GC_NAMESPACE::heap_segment, heap))
#endif // MULTIPLE_HEAPS
CDAC_TYPE_END(HeapSegment)

CDAC_TYPE_BEGIN(OomHistory)
CDAC_TYPE_INDETERMINATE(OomHistory)
CDAC_TYPE_FIELD(OomHistory, /*int32*/, Reason, offsetof(oom_history, reason))
CDAC_TYPE_FIELD(OomHistory, /*nuint*/, AllocSize, offsetof(oom_history, alloc_size))
CDAC_TYPE_FIELD(OomHistory, /*pointer*/, Reserved, offsetof(oom_history, reserved))
CDAC_TYPE_FIELD(OomHistory, /*pointer*/, Allocated, offsetof(oom_history, allocated))
CDAC_TYPE_FIELD(OomHistory, /*nuint*/, GcIndex, offsetof(oom_history, gc_index))
CDAC_TYPE_FIELD(OomHistory, /*int32*/, Fgm, offsetof(oom_history, fgm))
CDAC_TYPE_FIELD(OomHistory, /*nuint*/, Size, offsetof(oom_history, size))
CDAC_TYPE_FIELD(OomHistory, /*nuint*/, AvailablePagefileMb, offsetof(oom_history, available_pagefile_mb))
CDAC_TYPE_FIELD(OomHistory, /*uint32*/, LohP, offsetof(oom_history, loh_p))
CDAC_TYPE_END(OomHistory)

CDAC_TYPES_END()

CDAC_GLOBALS_BEGIN()

CDAC_GLOBAL(TotalGenerationCount, /*uint32*/, (uint32_t)total_generation_count)
CDAC_GLOBAL(CFinalizeFillPointersLength, /*uint32*/, (uint32_t)cdac_data<GC_NAMESPACE::CFinalize>::FillPointersLength)
CDAC_GLOBAL(InterestingDataLength, /*uint32*/, NUM_GC_DATA_POINTS)
CDAC_GLOBAL(CompactReasonsLength, /*uint32*/, MAX_COMPACT_REASONS_COUNT)
CDAC_GLOBAL(ExpandMechanismsLength, /*uint32*/, MAX_EXPAND_MECHANISMS_COUNT)
CDAC_GLOBAL(InterestingMechanismBitsLength, /*uint32*/, MAX_GC_MECHANISM_BITS_COUNT)
CDAC_GLOBAL(GlobalMechanismsLength, /*uint32*/, MAX_GLOBAL_GC_MECHANISMS_COUNT)


#ifndef SERVER_GC
CDAC_GLOBAL_POINTER(GCHeapMarkArray, cdac_data<GC_NAMESPACE::gc_heap>::MarkArray)
Expand All @@ -64,6 +106,14 @@ CDAC_GLOBAL_POINTER(GCHeapGenerationTable, cdac_data<GC_NAMESPACE::gc_heap>::Gen
CDAC_GLOBAL_POINTER(GCHeapSavedSweepEphemeralSeg, cdac_data<GC_NAMESPACE::gc_heap>::SavedSweepEphemeralSeg)
CDAC_GLOBAL_POINTER(GCHeapSavedSweepEphemeralStart, cdac_data<GC_NAMESPACE::gc_heap>::SavedSweepEphemeralStart)
#endif // !USE_REGIONS
CDAC_GLOBAL_POINTER(GCHeapOomData, cdac_data<GC_NAMESPACE::gc_heap>::OomData)
CDAC_GLOBAL_POINTER(GCHeapInternalRootArray, cdac_data<GC_NAMESPACE::gc_heap>::InternalRootArray)
CDAC_GLOBAL_POINTER(GCHeapInternalRootArrayIndex, cdac_data<GC_NAMESPACE::gc_heap>::InternalRootArrayIndex)
CDAC_GLOBAL_POINTER(GCHeapHeapAnalyzeSuccess, cdac_data<GC_NAMESPACE::gc_heap>::HeapAnalyzeSuccess)
CDAC_GLOBAL_POINTER(GCHeapInterestingData, cdac_data<GC_NAMESPACE::gc_heap>::InterestingData)
CDAC_GLOBAL_POINTER(GCHeapCompactReasons, cdac_data<GC_NAMESPACE::gc_heap>::CompactReasons)
CDAC_GLOBAL_POINTER(GCHeapExpandMechanisms, cdac_data<GC_NAMESPACE::gc_heap>::ExpandMechanisms)
CDAC_GLOBAL_POINTER(GCHeapInterestingMechanismBits, cdac_data<GC_NAMESPACE::gc_heap>::InterestingMechanismBits)
#endif // !SERVER_GC

#ifdef SERVER_GC
Expand All @@ -78,15 +128,22 @@ CDAC_GLOBAL_POINTER(GCHeapSavedSweepEphemeralStart, cdac_data<GC_NAMESPACE::gc_h
#define HEAP_TYPE segments
#endif // USE_REGIONS

#ifdef BACKGROUND_GC
#define BACKGROUND_TYPE background
#else // BACKGROUND_GC
#define BACKGROUND_TYPE
#endif // BACKGROUND_GC

#ifdef DYNAMIC_HEAP_COUNT
#define DYNAMIC_HEAP_TYPE dynamic_heap
#else // DYNAMIC_HEAP_COUNT
#define DYNAMIC_HEAP_TYPE
#endif // DYNAMIC_HEAP_COUNT

// CDAC_GLOBAL_STRING takes a single value argument.
// To avoid issues with commas in the string we wrap the input string in a macro.
#define GC_IDENTIFIER(...) __VA_ARGS__ // GC_IDENTIFIER(gc, heap) expands to: gc, heap

#ifdef BACKGROUND_GC
CDAC_GLOBAL_STRING(GCIdentifiers, GC_IDENTIFIER(GC_TYPE, HEAP_TYPE, background))
#else
CDAC_GLOBAL_STRING(GCIdentifiers, GC_IDENTIFIER(GC_TYPE, HEAP_TYPE))
#endif // BACKGROUND_GC
CDAC_GLOBAL_STRING(GCIdentifiers, GC_IDENTIFIER(GC_TYPE, HEAP_TYPE, BACKGROUND_TYPE, DYNAMIC_HEAP_TYPE))

CDAC_GLOBAL_POINTER(MaxGeneration, &::g_max_generation)
CDAC_GLOBAL_POINTER(StructureInvalidCount, &GCScan::m_GcStructuresInvalidCnt)
Expand All @@ -100,4 +157,12 @@ CDAC_GLOBAL_POINTER(Heaps, cdac_data<GC_NAMESPACE::gc_heap>::Heaps)
CDAC_GLOBAL_POINTER(CurrentGCState, cdac_data<GC_NAMESPACE::gc_heap>::CurrentGCState)
#endif // BACKGROUND_GC

#ifdef DYNAMIC_HEAP_COUNT
CDAC_GLOBAL_POINTER(DynamicAdaptationMode, cdac_data<GC_NAMESPACE::gc_heap>::DynamicAdaptationMode)
#endif // DYNAMIC_HEAP_COUNT

#ifdef GC_CONFIG_DRIVEN
CDAC_GLOBAL_POINTER(GCGlobalMechanisms, &gc_global_mechanisms)
#endif // GC_CONFIG_DRIVEN

CDAC_GLOBALS_END()
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ public static class GCIdentifiers
public const string Segments = "segments";

public const string Background = "background";

public const string DynamicHeapCount = "dynamic_heap";
}

public readonly struct GCHeapData
Expand All @@ -23,17 +25,22 @@ public readonly struct GCHeapData
public TargetPointer NextSweepObject { get; init; }
public TargetPointer BackGroundSavedMinAddress { get; init; }
public TargetPointer BackGroundSavedMaxAddress { get; init; }

public TargetPointer AllocAllocated { get; init; }
public TargetPointer EphemeralHeapSegment { get; init; }
public TargetPointer CardTable { get; init; }
public IReadOnlyList<GCGenerationData> GenerationTable { get; init; }

public IReadOnlyList<TargetPointer> FillPointers { get; init; }
public TargetPointer SavedSweepEphemeralSegment { get; init; } /* Only valid in segment GC builds */
public TargetPointer SavedSweepEphemeralStart { get; init; } /* Only valid in segment GC builds */

// Fields only valid in segment GC builds
public TargetPointer SavedSweepEphemeralSegment { get; init; }
public TargetPointer SavedSweepEphemeralStart { get; init; }
public TargetPointer InternalRootArray { get; init; }
public TargetNUInt InternalRootArrayIndex { get; init; }
public bool HeapAnalyzeSuccess { get; init; }

public IReadOnlyList<TargetNUInt> InterestingData { get; init; }
public IReadOnlyList<TargetNUInt> CompactReasons { get; init; }
public IReadOnlyList<TargetNUInt> ExpandMechanisms { get; init; }
public IReadOnlyList<TargetNUInt> InterestingMechanismBits { get; init; }
}

public readonly struct GCGenerationData
Expand All @@ -44,6 +51,32 @@ public readonly struct GCGenerationData
public TargetPointer AllocationContextLimit { get; init; }
}

public readonly struct GCHeapSegmentData
{
public TargetPointer Allocated { get; init; }
public TargetPointer Committed { get; init; }
public TargetPointer Reserved { get; init; }
public TargetPointer Used { get; init; }
public TargetPointer Mem { get; init; }
public TargetNUInt Flags { get; init; }
public TargetPointer Next { get; init; }
public TargetPointer BackgroundAllocated { get; init; }
public TargetPointer Heap { get; init; }
}

public readonly struct GCOomData
{
public int Reason { get; init; }
public TargetNUInt AllocSize { get; init; }
public TargetPointer Reserved { get; init; }
public TargetPointer Allocated { get; init; }
public TargetNUInt GCIndex { get; init; }
public int Fgm { get; init; }
public TargetNUInt Size { get; init; }
public TargetNUInt AvailablePagefileMB { get; init; }
public bool LohP { get; init; }
}

public interface IGC : IContract
{
static string IContract.Name { get; } = nameof(GC);
Expand All @@ -55,13 +88,20 @@ public interface IGC : IContract
uint GetMaxGeneration() => throw new NotImplementedException();
void GetGCBounds(out TargetPointer minAddr, out TargetPointer maxAddr) => throw new NotImplementedException();
uint GetCurrentGCState() => throw new NotImplementedException();
bool TryGetGCDynamicAdaptationMode(out int mode) => throw new NotImplementedException();
GCHeapSegmentData GetHeapSegmentData(TargetPointer segmentAddress) => throw new NotImplementedException();
IReadOnlyList<TargetNUInt> GetGlobalMechanisms() => throw new NotImplementedException();
IEnumerable<TargetPointer> GetGCHeaps() => throw new NotImplementedException();

/* WKS only APIs */
GCHeapData WKSGetHeapData() => throw new NotImplementedException();
// workstation variant
GCHeapData GetHeapData() => throw new NotImplementedException();
// server variant
GCHeapData GetHeapData(TargetPointer heapAddress) => throw new NotImplementedException();

/* SVR only APIs */
GCHeapData SVRGetHeapData(TargetPointer heapAddress) => throw new NotImplementedException();
// workstation variant
GCOomData GetOomData() => throw new NotImplementedException();
// server variant
GCOomData GetOomData(TargetPointer heapAddress) => throw new NotImplementedException();
}

public readonly struct GC : IGC
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,4 +138,6 @@ public enum DataType
GCHeap,
Generation,
CFinalize,
HeapSegment,
OomHistory,
}
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,16 @@ public static class Globals
public const string NumHeaps = nameof(NumHeaps);
public const string Heaps = nameof(Heaps);
public const string CurrentGCState = nameof(CurrentGCState);
public const string CFinalizeFillPointersLength = nameof(CFinalizeFillPointersLength);
public const string DynamicAdaptationMode = nameof(DynamicAdaptationMode);
public const string GCGlobalMechanisms = nameof(GCGlobalMechanisms);

public const string TotalGenerationCount = nameof(TotalGenerationCount);
public const string CFinalizeFillPointersLength = nameof(CFinalizeFillPointersLength);
public const string InterestingDataLength = nameof(InterestingDataLength);
public const string CompactReasonsLength = nameof(CompactReasonsLength);
public const string ExpandMechanismsLength = nameof(ExpandMechanismsLength);
public const string InterestingMechanismBitsLength = nameof(InterestingMechanismBitsLength);
public const string GlobalMechanismsLength = nameof(GlobalMechanismsLength);

public const string GCHeapMarkArray = nameof(GCHeapMarkArray);
public const string GCHeapNextSweepObj = nameof(GCHeapNextSweepObj);
Expand All @@ -99,6 +107,14 @@ public static class Globals
public const string GCHeapGenerationTable = nameof(GCHeapGenerationTable);
public const string GCHeapSavedSweepEphemeralSeg = nameof(GCHeapSavedSweepEphemeralSeg);
public const string GCHeapSavedSweepEphemeralStart = nameof(GCHeapSavedSweepEphemeralStart);
public const string GCHeapOomData = nameof(GCHeapOomData);
public const string GCHeapInternalRootArray = nameof(GCHeapInternalRootArray);
public const string GCHeapInternalRootArrayIndex = nameof(GCHeapInternalRootArrayIndex);
public const string GCHeapHeapAnalyzeSuccess = nameof(GCHeapHeapAnalyzeSuccess);
public const string GCHeapInterestingData = nameof(GCHeapInterestingData);
public const string GCHeapCompactReasons = nameof(GCHeapCompactReasons);
public const string GCHeapExpandMechanisms = nameof(GCHeapExpandMechanisms);
public const string GCHeapInterestingMechanismBits = nameof(GCHeapInterestingMechanismBits);
}
public static class FieldNames
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace Microsoft.Diagnostics.DataContractReader.Contracts.GCHelpers;

internal sealed class GCHeapWKS : IGCHeap
{
public GCHeapWKS(Target target)
{
MarkArray = target.ReadPointer(target.ReadGlobalPointer(Constants.Globals.GCHeapMarkArray));
NextSweepObj = target.ReadPointer(target.ReadGlobalPointer(Constants.Globals.GCHeapNextSweepObj));
BackgroundMinSavedAddr = target.ReadPointer(target.ReadGlobalPointer(Constants.Globals.GCHeapBackgroundMinSavedAddr));
BackgroundMaxSavedAddr = target.ReadPointer(target.ReadGlobalPointer(Constants.Globals.GCHeapBackgroundMaxSavedAddr));
AllocAllocated = target.ReadPointer(target.ReadGlobalPointer(Constants.Globals.GCHeapAllocAllocated));
EphemeralHeapSegment = target.ReadPointer(target.ReadGlobalPointer(Constants.Globals.GCHeapEphemeralHeapSegment));
CardTable = target.ReadPointer(target.ReadGlobalPointer(Constants.Globals.GCHeapCardTable));
FinalizeQueue = target.ReadPointer(target.ReadGlobalPointer(Constants.Globals.GCHeapFinalizeQueue));
GenerationTable = target.ReadGlobalPointer(Constants.Globals.GCHeapGenerationTable);

if (target.TryReadGlobalPointer(Constants.Globals.GCHeapSavedSweepEphemeralSeg, out TargetPointer? savedSweepEphemeralSegPtr))
SavedSweepEphemeralSeg = target.ReadPointer(savedSweepEphemeralSegPtr.Value);
if (target.TryReadGlobalPointer(Constants.Globals.GCHeapSavedSweepEphemeralStart, out TargetPointer? savedSweepEphemeralStartPtr))
SavedSweepEphemeralStart = target.ReadPointer(savedSweepEphemeralStartPtr.Value);

OomData = target.ProcessedData.GetOrAdd<Data.OomHistory>(target.ReadGlobalPointer(Constants.Globals.GCHeapOomData));

InternalRootArray = target.ReadPointer(target.ReadGlobalPointer(Constants.Globals.GCHeapInternalRootArray));
InternalRootArrayIndex = target.ReadNUInt(target.ReadGlobalPointer(Constants.Globals.GCHeapInternalRootArrayIndex));
HeapAnalyzeSuccess = target.Read<int>(target.ReadGlobalPointer(Constants.Globals.GCHeapHeapAnalyzeSuccess)) != 0;

InterestingData = target.ReadGlobalPointer(Constants.Globals.GCHeapInterestingData);
CompactReasons = target.ReadGlobalPointer(Constants.Globals.GCHeapCompactReasons);
ExpandMechanisms = target.ReadGlobalPointer(Constants.Globals.GCHeapExpandMechanisms);
InterestingMechanismBits = target.ReadGlobalPointer(Constants.Globals.GCHeapInterestingMechanismBits);
}

public TargetPointer MarkArray { get; }
public TargetPointer NextSweepObj { get; }
public TargetPointer BackgroundMinSavedAddr { get; }
public TargetPointer BackgroundMaxSavedAddr { get; }
public TargetPointer AllocAllocated { get; }
public TargetPointer EphemeralHeapSegment { get; }
public TargetPointer CardTable { get; }
public TargetPointer FinalizeQueue { get; }
public TargetPointer GenerationTable { get; }

public TargetPointer? SavedSweepEphemeralSeg { get; }
public TargetPointer? SavedSweepEphemeralStart { get; }

public Data.OomHistory OomData { get; }

public TargetPointer InternalRootArray { get; }
public TargetNUInt InternalRootArrayIndex { get; }
public bool HeapAnalyzeSuccess { get; }

public TargetPointer InterestingData { get; }
public TargetPointer CompactReasons { get; }
public TargetPointer ExpandMechanisms { get; }
public TargetPointer InterestingMechanismBits { get; }
}
Loading