/*++

Copyright (c) 1992  Microsoft Corporation

Module Name:

    Debug.c

Abstract:

    This module declares the Debug only code used by the NetWare redirector
    file system.

Author:

    Colin Watson    [ColinW]    05-Jan-1993

Revision History:

--*/
#include "procs.h"
#include <stdio.h>
#include <stdarg.h>

#define LINE_SIZE 511
#define BUFFER_LINES 50


#ifdef NWDBG

#include <stdlib.h>    // rand()
int FailAllocateMdl = 0;

ULONG MaxDump = 256;
CHAR DBuffer[BUFFER_LINES*LINE_SIZE+1];
PCHAR DBufferPtr = DBuffer;

//
// The reference count debug buffer.
//

CHAR RBuffer[BUFFER_LINES*LINE_SIZE+1];
PCHAR RBufferPtr = RBuffer;

LIST_ENTRY MdlList;

VOID
HexDumpLine (
    PCHAR       pch,
    ULONG       len,
    PCHAR       s,
    PCHAR       t,
    USHORT      flag
    );

ULONG
NwMemDbg (
    IN PCH Format,
    ...
    )

//++
//
//  Routine Description:
//
//      Effectively DbgPrint to the debugging console.
//
//  Arguments:
//
//      Same as for DbgPrint
//
//--

{
    va_list arglist;
    int Length;

    //
    // Format the output into a buffer and then print it.
    //

    va_start(arglist, Format);

    Length = _vsnprintf(DBufferPtr, LINE_SIZE, Format, arglist);

    if (Length < 0) {
        DbgPrint( "NwRdr: Message is too long for NwMemDbg\n");
        return 0;
    }

    va_end(arglist);

    ASSERT( Length <= LINE_SIZE );
    ASSERT( Length != 0 );
    ASSERT( DBufferPtr < &DBuffer[BUFFER_LINES*LINE_SIZE+1]);
    ASSERT( DBufferPtr >= DBuffer);

    DBufferPtr += Length;
    DBufferPtr[0] = '\0';

    // Avoid running off the end of the buffer and exit

    if (DBufferPtr >= (DBuffer+((BUFFER_LINES-1) * LINE_SIZE))) {
        DBufferPtr = DBuffer;

    }

    return 0;
}

VOID
RefDbgTrace (
    PVOID Resource,
    DWORD Count,
    BOOLEAN Reference,
    PBYTE FileName,
    UINT Line
)
/**

  Routine Description:

      NwRefDebug logs reference count operations to expose
      reference count errors or leaks in the redirector.

  Arguments:

    Resource  - The object we're adjusting the reference count on.
    Count     - The current count on the object.
    Reference - If TRUE we are doing a REFERENCE.
                Otherwise, we are doing a DEREFERENCE.
    FileName  - The callers file name.
    Line      - The callers line number.

**/
{
    int Length;
    int NextCount;

    //
    // Format the output into a buffer and then print it.
    //

    if ( Reference )
        NextCount = Count + 1;
    else
       NextCount = Count - 1;

    Length = sprintf( RBufferPtr,
                      "%p: R=%p, %lu -> %lu (%s, line %d)\n",
                      (PVOID)PsGetCurrentThread(),
                      Resource,
                      Count,
                      NextCount,
                      FileName,
                      Line );

    if (Length < 0) {
        DbgPrint( "NwRdr: Message is too long for NwRefDbg\n");
        return;
    }

    ASSERT( Length <= LINE_SIZE );
    ASSERT( Length != 0 );
    ASSERT( RBufferPtr < &RBuffer[BUFFER_LINES*LINE_SIZE+1]);
    ASSERT( RBufferPtr >= RBuffer);

    RBufferPtr += Length;
    RBufferPtr[0] = '\0';

    // Avoid running off the end of the buffer and exit

    if (RBufferPtr >= (RBuffer+((BUFFER_LINES-1) * LINE_SIZE))) {
        RBufferPtr = RBuffer;
    }

    return;
}

VOID
RealDebugTrace(
    LONG Indent,
    ULONG Level,
    PCH Message,
    PVOID Parameter
    )
/*++

Routine Description:


Arguments:


Return Value:

    None.

--*/

{
    if ( (Level == 0) || (NwMemDebug & Level )) {
        NwMemDbg( Message, PsGetCurrentThread(), 1, "", Parameter );
    }

    if ( (Level == 0) || (NwDebug & Level )) {

        if ( Indent < 0) {
            NwDebugTraceIndent += Indent;
        }

        DbgPrint( Message, PsGetCurrentThread(), NwDebugTraceIndent, "", Parameter );

        if ( Indent > 0) {
            NwDebugTraceIndent += Indent;
        }

        if (NwDebugTraceIndent < 0) {
            NwDebugTraceIndent = 0;
        }
    }
}

VOID
dump(
    IN ULONG Level,
    IN PVOID far_p,
    IN ULONG  len
    )
/*++

Routine Description:
    Dump Min(len, MaxDump) bytes in classic hex dump style if debug
    output is turned on for this level.

Arguments:

    IN Level - 0 if always display. Otherwise only display if a
    corresponding bit is set in NwDebug.

    IN far_p - address of buffer to start dumping from.

    IN len - length in bytes of buffer.

Return Value:

    None.

--*/
{
    ULONG     l;
    char    s[80], t[80];
    PCHAR far_pchar = (PCHAR)far_p;

    if ( (Level == 0) || (NwDebug & Level )) {
        if (len > MaxDump)
            len = MaxDump;

        while (len) {
            l = len < 16 ? len : 16;

            DbgPrint("\n%lx ", far_pchar);
            HexDumpLine (far_pchar, l, s, t, 0);
            DbgPrint("%s%.*s%s", s, 1 + ((16 - l) * 3), "", t);
            NwMemDbg ( "%lx: %s%.*s%s\n",
                        far_pchar, s, 1 + ((16 - l) * 3), "", t);

            len    -= l;
            far_pchar  += l;
        }
        DbgPrint("\n");

    }
}

VOID
dumpMdl(
    IN ULONG Level,
    IN PMDL Mdl
    )
/*++

Routine Description:
    Dump the memory described by each part of a chained Mdl.

Arguments:

    IN Level - 0 if always display. Otherwise only display if a
    corresponding bit is set in NwDebug.

    Mdl - Supplies the addresses of the memory to be dumped.

Return Value:

    None.

--*/
{
    PMDL Next;
    ULONG len;


    if ( (Level == 0) || (NwDebug & Level )) {
        Next = Mdl; len = 0;
        do {

            dump(Level, MmGetSystemAddressForMdlSafe(Next, LowPagePriority), MIN(MmGetMdlByteCount(Next), MaxDump-len));

            len += MmGetMdlByteCount(Next);
        } while ( (Next = Next->Next) != NULL &&
                    len <= MaxDump);
    }
}

VOID
HexDumpLine (
    PCHAR       pch,
    ULONG       len,
    PCHAR       s,
    PCHAR       t,
    USHORT      flag
    )
{
    static UCHAR rghex[] = "0123456789ABCDEF";

    UCHAR    c;
    UCHAR    *hex, *asc;


    hex = s;
    asc = t;

    *(asc++) = '*';
    while (len--) {
        c = *(pch++);
        *(hex++) = rghex [c >> 4] ;
        *(hex++) = rghex [c & 0x0F];
        *(hex++) = ' ';
        *(asc++) = (c < ' '  ||  c > '~') ? (CHAR )'.' : c;
    }
    *(asc++) = '*';
    *asc = 0;
    *hex = 0;

    flag;
}

typedef struct _NW_POOL_HEADER {
    ULONG Signature;
    ULONG BufferSize;
    ULONG BufferType;
    LIST_ENTRY ListEntry;
    ULONG Pad;  // Pad to Q-word align
} NW_POOL_HEADER, *PNW_POOL_HEADER;

typedef struct _NW_POOL_TRAILER {
    ULONG Signature;
} NW_POOL_TRAILER;

typedef NW_POOL_TRAILER UNALIGNED *PNW_POOL_TRAILER;

PVOID
NwAllocatePool(
    ULONG Type,
    ULONG Size,
    BOOLEAN RaiseStatus
    )
{
    PCHAR Buffer;
    PNW_POOL_HEADER PoolHeader;
    PNW_POOL_TRAILER PoolTrailer;

    if ( RaiseStatus ) {
        Buffer = FsRtlAllocatePoolWithTag(
                     Type,
                     sizeof( NW_POOL_HEADER ) + sizeof( NW_POOL_TRAILER ) + Size,
                     'scwn' );
    } else {
#ifndef QFE_BUILD
        Buffer = ExAllocatePoolWithTag(
                     Type,
                     sizeof( NW_POOL_HEADER )+sizeof( NW_POOL_TRAILER )+Size,
                     'scwn' );
#else
        Buffer = ExAllocatePool(
                     Type,
                     sizeof( NW_POOL_HEADER )+sizeof( NW_POOL_TRAILER )+Size );
#endif

        if ( Buffer == NULL ) {
            return( NULL );
        }
    }

    PoolHeader = (PNW_POOL_HEADER)Buffer;
    PoolTrailer = (PNW_POOL_TRAILER)(Buffer + sizeof( NW_POOL_HEADER ) + Size);

    PoolHeader->Signature = 0x11111111;
    PoolHeader->BufferSize = Size;
    PoolHeader->BufferType = Type;

    PoolTrailer->Signature = 0x99999999;

    if ( Type == PagedPool ) {
        ExAcquireResourceExclusive( &NwDebugResource, TRUE );
        InsertTailList( &NwPagedPoolList, &PoolHeader->ListEntry );
        ExReleaseResource( &NwDebugResource );
    } else if ( Type == NonPagedPool ) {
        ExInterlockedInsertTailList( &NwNonpagedPoolList, &PoolHeader->ListEntry, &NwDebugInterlock );
    } else {
        KeBugCheck( RDR_FILE_SYSTEM );
    }

    return( Buffer + sizeof( NW_POOL_HEADER ) );
}

VOID
NwFreePool(
    PVOID Buffer
    )
{
    PNW_POOL_HEADER PoolHeader;
    PNW_POOL_TRAILER PoolTrailer;
    KIRQL OldIrql;

    PoolHeader = (PNW_POOL_HEADER)((PCHAR)Buffer - sizeof( NW_POOL_HEADER ));
    ASSERT( PoolHeader->Signature == 0x11111111 );
    ASSERT( PoolHeader->BufferType == PagedPool ||
            PoolHeader->BufferType == NonPagedPool );

    PoolTrailer = (PNW_POOL_TRAILER)((PCHAR)Buffer + PoolHeader->BufferSize );
    ASSERT( PoolTrailer->Signature == 0x99999999 );

    if ( PoolHeader->BufferType == PagedPool ) {
        ExAcquireResourceExclusive( &NwDebugResource, TRUE );
        RemoveEntryList( &PoolHeader->ListEntry );
        ExReleaseResource( &NwDebugResource );
    } else {
        KeAcquireSpinLock( &NwDebugInterlock, &OldIrql );
        RemoveEntryList( &PoolHeader->ListEntry );
        KeReleaseSpinLock( &NwDebugInterlock, OldIrql );
    }

    ExFreePool( PoolHeader );
}

//
//  Debug functions for allocating and deallocating IRPs and MDLs
//

PIRP
NwAllocateIrp(
    CCHAR Size,
    BOOLEAN ChargeQuota
    )
{
    ExInterlockedIncrementLong( &IrpCount, &NwDebugInterlock );
    return IoAllocateIrp( Size, ChargeQuota );
}

VOID
NwFreeIrp(
    PIRP Irp
    )
{
    ExInterlockedDecrementLong( &IrpCount, &NwDebugInterlock );
    IoFreeIrp( Irp );
}

typedef struct _NW_MDL {
    LIST_ENTRY  Next;
    PUCHAR      File;
    int         Line;
    PMDL        pMdl;
} NW_MDL, *PNW_MDL;

//int DebugLine = 2461;

PMDL
NwAllocateMdl(
    PVOID Va,
    ULONG Length,
    BOOLEAN Secondary,
    BOOLEAN ChargeQuota,
    PIRP Irp,
    PUCHAR FileName,
    int Line
    )
{
    PNW_MDL Buffer;

    static BOOLEAN MdlSetup = FALSE;

    if (MdlSetup == FALSE) {

        InitializeListHead( &MdlList );

        MdlSetup = TRUE;
    }

    if ( FailAllocateMdl != 0 ) {
        if ( ( rand() % FailAllocateMdl ) == 0 ) {
            return(NULL);
        }
    }

#ifndef QFE_BUILD
    Buffer = ExAllocatePoolWithTag(
                 NonPagedPool,
                 sizeof( NW_MDL),
                 'scwn' );
#else
    Buffer = ExAllocatePool(
                 NonPagedPool,
                 sizeof( NW_MDL));
#endif

    if ( Buffer == NULL ) {
        return( NULL );
    }

    ExInterlockedIncrementLong( &MdlCount, &NwDebugInterlock );

    Buffer->File = FileName;
    Buffer->Line = Line;
    Buffer->pMdl = IoAllocateMdl( Va, Length, Secondary, ChargeQuota, Irp );

    ExInterlockedInsertTailList( &MdlList, &Buffer->Next, &NwDebugInterlock );

/*
    if (DebugLine == Line) {
        DebugTrace( 0, DEBUG_TRACE_MDL, "AllocateMdl -> %08lx\n", Buffer->pMdl );
        DebugTrace( 0, DEBUG_TRACE_MDL, "AllocateMdl -> %08lx\n", Line );
    }
*/
    return(Buffer->pMdl);
}

VOID
NwFreeMdl(
    PMDL Mdl
    )
{
    PLIST_ENTRY MdlEntry;
    PNW_MDL Buffer;
    KIRQL OldIrql;

    ExInterlockedDecrementLong( &MdlCount, &NwDebugInterlock );

    KeAcquireSpinLock( &NwDebugInterlock, &OldIrql );
    //  Find the Mdl in the list and remove it.

    for (MdlEntry = MdlList.Flink ;
         MdlEntry != &MdlList ;
         MdlEntry =  MdlEntry->Flink ) {

        Buffer = CONTAINING_RECORD( MdlEntry, NW_MDL, Next );

        if (Buffer->pMdl == Mdl) {

            RemoveEntryList( &Buffer->Next );

            KeReleaseSpinLock( &NwDebugInterlock, OldIrql );

            IoFreeMdl( Mdl );
            DebugTrace( 0, DEBUG_TRACE_MDL, "FreeMDL - %08lx\n", Mdl );
/*
            if (DebugLine == Buffer->Line) {
                DebugTrace( 0, DEBUG_TRACE_MDL, "FreeMdl -> %08lx\n", Mdl );
                DebugTrace( 0, DEBUG_TRACE_MDL, "FreeMdl -> %08lx\n", Buffer->Line );
            }
*/
            ExFreePool(Buffer);

            return;
        }
    }
    ASSERT( FALSE );

    KeReleaseSpinLock( &NwDebugInterlock, OldIrql );
}

/*
VOID
NwLookForMdl(
    )
{
    PLIST_ENTRY MdlEntry;
    PNW_MDL Buffer;
    KIRQL OldIrql;

    KeAcquireSpinLock( &NwDebugInterlock, &OldIrql );
    //  Find the Mdl in the list and remove it.

    for (MdlEntry = MdlList.Flink ;
         MdlEntry != &MdlList ;
         MdlEntry =  MdlEntry->Flink ) {

        Buffer = CONTAINING_RECORD( MdlEntry, NW_MDL, Next );

        if (Buffer->Line == DebugLine) {

            DebugTrace( 0, DEBUG_TRACE_MDL, "LookForMdl -> %08lx\n", Buffer );
            DbgBreakPoint();

        }
    }

    KeReleaseSpinLock( &NwDebugInterlock, OldIrql );
}
*/

//
//  Function version of resource macro, to make debugging easier.
//

VOID
NwAcquireExclusiveRcb(
    PRCB Rcb,
    BOOLEAN Wait )
{
    ExAcquireResourceExclusive( &((Rcb)->Resource), Wait );
}

VOID
NwAcquireSharedRcb(
    PRCB Rcb,
    BOOLEAN Wait )
{
    ExAcquireResourceShared( &((Rcb)->Resource), Wait );
}

VOID
NwReleaseRcb(
    PRCB Rcb )
{
    ExReleaseResource( &((Rcb)->Resource) );
}

VOID
NwAcquireExclusiveFcb(
    PNONPAGED_FCB pFcb,
    BOOLEAN Wait )
{
    ExAcquireResourceExclusive( &((pFcb)->Resource), Wait );
}

VOID
NwAcquireSharedFcb(
    PNONPAGED_FCB pFcb,
    BOOLEAN Wait )
{
    ExAcquireResourceShared( &((pFcb)->Resource), Wait );
}

VOID
NwReleaseFcb(
    PNONPAGED_FCB pFcb )
{
    ExReleaseResource( &((pFcb)->Resource) );
}

VOID
NwAcquireOpenLock(
    VOID
    )
{
    ExAcquireResourceExclusive( &NwOpenResource, TRUE );
}

VOID
NwReleaseOpenLock(
    VOID
    )
{
    ExReleaseResource( &NwOpenResource );
}


//
// code to dump ICBs
//

VOID DumpIcbs(VOID)
{
    PVCB Vcb;
    PFCB Fcb;
    PICB Icb;
    PLIST_ENTRY VcbListEntry;
    PLIST_ENTRY FcbListEntry;
    PLIST_ENTRY IcbListEntry;
    KIRQL OldIrql;

    NwAcquireExclusiveRcb( &NwRcb, TRUE );
    KeAcquireSpinLock( &ScbSpinLock, &OldIrql );

    DbgPrint("\nICB      Pid      State    Scb/Fcb  Name\n", 0);
    for ( VcbListEntry = GlobalVcbList.Flink;
          VcbListEntry != &GlobalVcbList ;
          VcbListEntry = VcbListEntry->Flink ) {

        Vcb = CONTAINING_RECORD( VcbListEntry, VCB, GlobalVcbListEntry );

        for ( FcbListEntry = Vcb->FcbList.Flink;
              FcbListEntry != &(Vcb->FcbList) ;
              FcbListEntry = FcbListEntry->Flink ) {

            Fcb = CONTAINING_RECORD( FcbListEntry, FCB, FcbListEntry );

            for ( IcbListEntry = Fcb->IcbList.Flink;
                  IcbListEntry != &(Fcb->IcbList) ;
                  IcbListEntry = IcbListEntry->Flink ) {

                Icb = CONTAINING_RECORD( IcbListEntry, ICB, ListEntry );

                DbgPrint("%08lx", Icb);
                DbgPrint(" %08lx",(DWORD)Icb->Pid);
                DbgPrint(" %08lx",Icb->State);
                DbgPrint(" %08lx",Icb->SuperType.Scb);
                DbgPrint(" %wZ\n",
                           &(Icb->FileObject->FileName) );
            }
        }
    }

    KeReleaseSpinLock( &ScbSpinLock, OldIrql );
    NwReleaseRcb( &NwRcb );
}

#endif // ifdef NWDBG

//
// Ref counting debug routines.
//

#ifdef NWDBG

VOID
ChkNwReferenceScb(
    PNONPAGED_SCB pNpScb,
    PBYTE FileName,
    UINT Line,
    BOOLEAN Silent
) {

    if ( (pNpScb)->NodeTypeCode != NW_NTC_SCBNP ) {
        DbgBreakPoint();
    }

    if ( !Silent) {
        RefDbgTrace( pNpScb, pNpScb->Reference, TRUE, FileName, Line );
    }

    ExInterlockedIncrementLong( &(pNpScb)->Reference, &(pNpScb)->NpScbInterLock );
}

VOID
ChkNwDereferenceScb(
    PNONPAGED_SCB pNpScb,
    PBYTE FileName,
    UINT Line,
    BOOLEAN Silent
) {

    if ( (pNpScb)->Reference == 0 ) {
        DbgBreakPoint();
    }

    if ( (pNpScb)->NodeTypeCode != NW_NTC_SCBNP ) {
        DbgBreakPoint();
    }

    if ( !Silent ) {
        RefDbgTrace( pNpScb, pNpScb->Reference, FALSE, FileName, Line );
    }

    ExInterlockedDecrementLong( &(pNpScb)->Reference, &(pNpScb)->NpScbInterLock );
}

#endif