////////////////////////////////////////////////////////////////////////////////
//
//  Copyright (c) Microsoft Corporation.  All rights reserved.
//
//  File:   MemoryDiagnosis.cs
//
//

using System;
using System.Threading;
using System.Runtime.CompilerServices;

using Microsoft.Singularity;
using Microsoft.Singularity.V1.Services;    
using Microsoft.Singularity.Eventing;
using System.Collections;

namespace Microsoft.Singularity.Eventing
{

    [CLSCompliant(false)]
    public class SystemAllocationLogger : EventSource {

        private const uint DefaultLogSize = 0x10000;

        public static SystemAllocationLogger Create(string sourceName) {

            EventingStorage storage = EventingStorage.CreateLocalStorage(QualityOfService.RecyclableEvents,
                                                                         DefaultLogSize);

            if (storage == null) {

                return null;                                                  
            }
            
            SystemAllocationLogger Logger  = new SystemAllocationLogger(sourceName, 
                                                                        storage,
                                                                        ENABLE_ALL_MASK);

            if (Logger != null) {

                Logger.Register();
            }

            return Logger;
        }


        //[EventType=Interrupt]
        [NoHeapAllocation]
        public bool Log(ushort processTag, ushort memoryType, UIntPtr address, UIntPtr size) {  // !!!!! this line must be entered to define a schema

    
            if ((ControlFlags & MemoryAllocation_Control_flag) != 0) {

                unsafe {

                    MemoryAllocationEvent memEvent; 

                    memEvent.MemoryType = memoryType;
                    memEvent.ProcessTag = processTag;
                    memEvent.Address = address;
                    memEvent.Size = size;

                    return (LogEntry(ControlFlags, 
                                      eventTypeMemoryAllocation, 
                                      (byte *)&memEvent, 
                                      sizeof(MemoryAllocationEvent)) != 0);
                }
            }
            
            return false;
        }

        public unsafe struct MemoryAllocationEvent {

            public UIntPtr Address;
            public UIntPtr Size;
            public ushort ProcessTag;
            public ushort MemoryType;
        };

        UIntPtr eventTypeMemoryAllocation;
        const uint MemoryAllocation_Control_flag = 0x10000;

        public override bool Register() {

            if (!base.Register()) {

                return false;
            }

            if (HostController.RegisterEvent("SystemMemoryAllocation", 
                                             "Memory allocation event: Address={0}, Memory type = {3}",
                                             ref eventTypeMemoryAllocation)) {

                HostController.RegisterEventField(eventTypeMemoryAllocation, 
                                                  "Address", 
                                                  0, 
                                                  DataType.__UIntPtr);

                HostController.RegisterEventField(eventTypeMemoryAllocation, 
                                                  "Size", 
                                                  0, 
                                                  DataType.__UIntPtr);

                HostController.RegisterEventField(eventTypeMemoryAllocation, 
                                                  "ProcessTag", 
                                                  0, 
                                                  DataType.__uint16);

                HostController.RegisterEventField(eventTypeMemoryAllocation, 
                                                  "MemoryType", 
                                                  0, 
                                                  DataType.__uint16);
                
            } else {

                // The event might have been registered already
                // Check whether we foundit already in the table or not

                if (eventTypeMemoryAllocation == 0) {
                    return false;
                }
            }

            return true;
        }

        SystemAllocationLogger (string sourceName, EventingStorage storage, uint controlFlags) 
            :base(sourceName, storage, controlFlags) {}
      
    }

    [CLSCompliant(false)]
    [NoCCtor]
    public class FreeMemoryDistribution : ActiveSource{

        //  Empty function declaration. Active counters are never logged through functions
        //  Present here for further usage with Phoenix codegen
        // [ActiveCounter]
        void TraceFreeBlock(UIntPtr BlockCount, UIntPtr TotalBlocksSize){}

        //  Here should start the codegen's work. Do it by hand for now

        public struct tmpFreeBlockEntry{

            public UIntPtr BlockCount;
            public UIntPtr TotalBucketSize;
        }

        internal tmpFreeBlockEntry[] Buffer;

        public static FreeMemoryDistribution Create(string sourceName, int size) {

            FreeMemoryDistribution Logger = new FreeMemoryDistribution(sourceName, size, ENABLE_ALL_MASK);

            if (Logger != null) {

                Logger.Register();
            }

            return Logger;
        }

        public override bool Register() {

            Buffer = new tmpFreeBlockEntry[Count];

            if (Buffer == null) {
                return false;
            }

            if (HostController.RegisterEvent("MemoryFreeBucket",
                                             "Memory free buckets: Count = {0}",
                                              ref EventTypeHandle)) {

                HostController.RegisterEventField(EventTypeHandle, 
                                                  "BlockCount", 
                                                  0, 
                                                  DataType.__UIntPtr);
                
                HostController.RegisterEventField(EventTypeHandle, 
                                                  "TotalBucketSize", 
                                                  0, 
                                                  DataType.__UIntPtr);
            } else {

                // The event might have been registered already
                // Check whether we foundit already in the table or not

                if (EventTypeHandle == 0) {
                    return false;
                }
            }

            unsafe {

                fixed (void * ptr = &Buffer[0]) {

                    DebugBufferAddress = (UIntPtr)ptr;
                }
            }

            // After all internal fields are setup, we can go ahead and register with the controller
            
            if (!base.Register()) {

                return false;
            }
                
            return true;
        }

        public tmpFreeBlockEntry this[int index]
        {
            get {
                return Buffer[index];
            }
        }

        public override unsafe bool GetActiveEntry(int index,
                                                       UIntPtr * type,
                                                       byte * buffer,
                                                       UInt16 bufferSize )
        {
            if (!base.GetActiveEntry(index, type, buffer, bufferSize)) {
                return false;
            }

            *(tmpFreeBlockEntry *)buffer = Buffer[index];
            return true;
        }

        FreeMemoryDistribution(string sourceName, int size, uint controlFlags) 
            :base(sourceName, size, controlFlags) 
        {
            unsafe {
                EntrySize = (ushort)sizeof(tmpFreeBlockEntry);
            }
        }
    }
}


