/*++

Copyright (c) 1992  Microsoft Corporation

Module Name:

    lpc.c

Abstract:

    WinDbg Extension Api

Author:

    Ramon J San Andres (ramonsa) 8-Nov-1993

Environment:

    User Mode.

Revision History:

--*/


#include "precomp.h"
#pragma hdrstop

//
// Nuke these definitions from kxmips.h as they conflict with
// LPC_MESSAGE structure in ntlpcapi.h
//

#undef s1
#undef s2

typedef struct _LPCP_DUMP_PORT_INFO {
    struct _LPCP_DUMP_PORT_INFO *Next;
    PLPCP_PORT_OBJECT           pPortObject;
    LPCP_PORT_OBJECT            PortObject;
    HANDLE                      Creator;
    ULONG                       AssociatedPortIndex;
    WCHAR                       Name[ MAX_PATH ];
    union {
        PLPCP_PORT_OBJECT       ConnectedPort;
        PLPCP_PORT_OBJECT       CommunicationPort;
    };
} LPCP_DUMP_PORT_INFO, *PLPCP_DUMP_PORT_INFO;

typedef struct _LPCP_DUMP_INFO {
    PLPCP_DUMP_PORT_INFO PortInfo;
} LPCP_DUMP_INFO, *PLPCP_DUMP_INFO;


char *LpcpMessageTypeName[] = {
    "UNUSED_MSG_TYPE",
    "LPC_REQUEST",
    "LPC_REPLY",
    "LPC_DATAGRAM",
    "LPC_LOST_REPLY",
    "LPC_PORT_CLOSED",
    "LPC_CLIENT_DIED",
    "LPC_EXCEPTION",
    "LPC_DEBUG_EVENT",
    "LPC_ERROR_EVENT",
    "LPC_CONNECTION_REQUEST"
};





VOID
LpcpDumpClientPort(
    ULONG                   dwProcessor,
    PLPCP_DUMP_INFO         PortsInfo,
    PLPCP_DUMP_PORT_INFO    PortInfo
    );

VOID
LpcpDumpConnectionPort(
    ULONG                   dwProcessor,
    PLPCP_DUMP_INFO         PortsInfo,
    PLPCP_DUMP_PORT_INFO    PortInfo
    );

BOOLEAN
CapturePorts(
    IN PVOID            pObjectHeader,
    IN POBJECT_HEADER   ObjectHeader,
    IN PVOID            Parameter
    );

VOID
LpcpDumpMessage(
    IN char         *Indent,
    IN PLPCP_MESSAGE pMsg
    );

VOID
LpcpGetProcessImageName(
    IN HANDLE UniqueProcess,
    IN OUT PUCHAR ImageFileName
    );

DECLARE_API( lpc )

/*++

Routine Description:

    Dump lpc ports and messages

Arguments:

    args -

Return Value:

    None

--*/

{
    ULONG                   Result;
    PLPCP_PORT_ZONE         pLpcpZone;
    LPCP_PORT_ZONE          LpcpZone;
    LPCP_DUMP_INFO          PortsInfo;
    PLPCP_DUMP_PORT_INFO    PortInfo, p;
    PZONE_SEGMENT_HEADER    pZoneSegment;
    ZONE_SEGMENT_HEADER     ZoneSegment;
    LONG                    SegmentSize;
    PLPCP_MESSAGE           pMsg;

    if (!FetchObjectManagerVariables()) {
        return;
    }

    if (!FetchProcessStructureVariables()) {
        return;
    }

    memset( &PortsInfo, 0, sizeof( PortsInfo ) );
    if (!WalkObjectsByType( "Port", CapturePorts, &PortsInfo )) {
        goto dumpMessages;
    }

    PortInfo = PortsInfo.PortInfo;
    while (PortInfo) {
        if ((PortInfo->PortObject.Flags & PORT_TYPE) == SERVER_CONNECTION_PORT) {
            LpcpDumpConnectionPort( dwProcessor, &PortsInfo, PortInfo );
        }

        PortInfo = PortInfo->Next;

        if ( CheckControlC() ) {
            return;
        }
    }

dumpMessages:
    pLpcpZone = (PLPCP_PORT_ZONE)GetExpression( "LpcpZone" );

    if ( !pLpcpZone ||
         !ReadMemory( (DWORD)pLpcpZone,
                      &LpcpZone,
                      sizeof(LpcpZone),
                      &Result) ) {
        dprintf( "%08lx: Unable to access LpcpZone\n", pLpcpZone );
    } else {
        dprintf( "Status of allocated messages in LpcpZone\n" );
        pZoneSegment = (PZONE_SEGMENT_HEADER)LpcpZone.Zone.SegmentList.Next;
        while (pZoneSegment != NULL) {
            SegmentSize = PAGE_SIZE;
            pMsg = (PLPCP_MESSAGE)(pZoneSegment+1);
            while (SegmentSize >= (LONG)LpcpZone.Zone.BlockSize) {
                LpcpDumpMessage( "    ", pMsg );
                pMsg = (PLPCP_MESSAGE)((PCHAR)pMsg + LpcpZone.Zone.BlockSize);
                SegmentSize -= LpcpZone.Zone.BlockSize;

                if ( CheckControlC() ) {
                    return;
                }
            }

            if ( !ReadMemory( (DWORD)pZoneSegment,
                              &ZoneSegment,
                              sizeof(ZoneSegment),
                              &Result) ) {
                dprintf( "%08lx: Unable to access zone segment\n", pZoneSegment );
                break;
            } else {
                pZoneSegment = (PZONE_SEGMENT_HEADER)ZoneSegment.SegmentList.Next;
            }

            if ( CheckControlC() ) {
                return;
            }
        }
    }

    PortInfo = PortsInfo.PortInfo;
    while (PortInfo) {
        p = PortInfo->Next;
        LocalFree( PortInfo );
        PortInfo = p;
    }

    return;
}


VOID
LpcpGetProcessImageName(
    IN HANDLE UniqueProcess,
    IN OUT PUCHAR ImageFileName
    )
{
    PEPROCESS       pProcess;
    EPROCESS        Process;
    ULONG           Result;
    PUCHAR          s;
    int             i;

    pProcess = LookupUniqueId( UniqueProcess );
    if (pProcess != NULL) {
        if (ReadMemory( (DWORD)pProcess,
                         &Process,
                         sizeof(EPROCESS),
                         &Result
                      )
           ) {
            i = 16;
            s = Process.ImageFileName;
            while (i--) {
                if (*s == '\0') {
                    if (i == 15) {
                        i = 0;
                        }
                    break;
                    }

                if (*s < ' ' || *s >= '|') {
                    i = 0;
                    break;
                    }

                s += 1;
                }

            if (i != 0) {
                strcpy( ImageFileName, Process.ImageFileName );
                return;
                }
            }
        }

    sprintf( ImageFileName, "Invalid Process %04x", UniqueProcess );
    return;
}

VOID
LpcpDumpClientPort(
    ULONG                   dwProcessor,
    PLPCP_DUMP_INFO         PortsInfo,
    PLPCP_DUMP_PORT_INFO    PortInfo
    )
{
    PLPCP_DUMP_PORT_INFO    PortInfo1;
    LIST_ENTRY      ListEntry;
    PLIST_ENTRY     Next;
    PLIST_ENTRY     Head;
    PLPCP_MESSAGE   Msg;
    ULONG           Result;
    PETHREAD        pThread;
    ETHREAD         Thread;
    UCHAR           ImageFileName[ 32 ];
    KSEMAPHORE              Semaphore;
    ULONG                   SignalState;

    LpcpGetProcessImageName( PortInfo->PortObject.Creator.UniqueProcess, ImageFileName );
    dprintf( "    Client Port Object at %08x (connected to %08x) - created by %s\n",
             PortInfo->pPortObject,
             PortInfo->PortObject.ConnectedPort,
             ImageFileName
           );

    Head        = &(PortInfo->pPortObject->MsgQueue.ReceiveHead);
    ListEntry   = PortInfo->PortObject.MsgQueue.ReceiveHead;
    Next        = ListEntry.Flink;

    if (Next != NULL && Next != Head) {
        if (!ReadMemory( (DWORD) PortInfo->PortObject.MsgQueue.Semaphore,
                         &Semaphore,
                         sizeof( KSEMAPHORE ),
                         &Result)) {
            dprintf("Unable to read KSEMAPHORE at %08lx\n",PortInfo->PortObject.MsgQueue.Semaphore);
            SignalState = 0;
        } else {
            SignalState = Semaphore.Header.SignalState;
        }
        dprintf( "    Receive queue head: %08x . %08x  Semaphore Count: %x\n",
                 ListEntry.Flink, ListEntry.Blink,
                 SignalState
               );
        while (Next != Head) {
            if ( !ReadMemory( (DWORD)Next,
                              &ListEntry,
                              sizeof( ListEntry ),
                              &Result) ) {
                dprintf( "%08lx: Unable to read list\n", Next );
                break;
            }

            Msg  = CONTAINING_RECORD( Next, LPCP_MESSAGE, Entry );
            LpcpDumpMessage( "        ", Msg );
            Next = ListEntry.Flink;

            if ( CheckControlC() ) {
                return;
            }
        }
        dprintf( "\n" );
    }

    PortInfo1 = PortsInfo->PortInfo;
    while (PortInfo1->pPortObject != PortInfo->PortObject.ConnectedPort) {
        PortInfo1 = PortInfo1->Next;
    }

    if (PortInfo1 != NULL) {
        Head = &(PortInfo1->pPortObject->LpcReplyChainHead);
        ListEntry = PortInfo1->PortObject.LpcReplyChainHead;
        Next = ListEntry.Flink;
        if (Next != NULL && Next != Head) {
            dprintf( "    Reply Chain head: %08x . %08x\n", ListEntry.Flink, ListEntry.Blink );
            while (Next != Head) {
                if ( !ReadMemory( (DWORD)Next,
                                  &ListEntry,
                                  sizeof( ListEntry ),
                                  &Result) ) {
                    dprintf( "%08lx: Unable to read list\n", Next );
                    break;
                }

                pThread = CONTAINING_RECORD( Next, ETHREAD, LpcReplyChain );
                if ( !ReadMemory( (DWORD)pThread,
                                  &Thread,
                                  sizeof(ETHREAD),
                                  &Result) ) {
                    dprintf("%08lx: Unable to get thread contents\n", pThread );
                } else {
                    DumpThread (dwProcessor,"        ", &Thread, pThread, 0x0f);
                }

                Next = ListEntry.Flink;
                if ( CheckControlC() ) {
                    return;
                }
            }
            dprintf( "\n" );
        }
    }

    return;
}


VOID
LpcpDumpConnectionPort(
    ULONG                   dwProcessor,
    PLPCP_DUMP_INFO         PortsInfo,
    PLPCP_DUMP_PORT_INFO    PortInfo
    )
{
    LIST_ENTRY              ListEntry;
    PLIST_ENTRY             Next;
    PLIST_ENTRY             Head;
    PLPCP_MESSAGE           Msg;
    ULONG                   Result;
    PLPCP_DUMP_PORT_INFO    p;
    UCHAR                   ImageFileName[ 32 ];
    KSEMAPHORE              Semaphore;
    ULONG                   SignalState;

    LpcpGetProcessImageName( PortInfo->PortObject.Creator.UniqueProcess, ImageFileName );
    dprintf( "Connection Port Object at %08x - Name='%ws' created by %s\n",
             PortInfo->pPortObject,
             PortInfo->Name,
             ImageFileName
           );

    Head = &(PortInfo->pPortObject->MsgQueue.ReceiveHead);
    ListEntry = PortInfo->PortObject.MsgQueue.ReceiveHead;
    Next = ListEntry.Flink;
    if (Next != NULL && Next != Head) {
        if (!ReadMemory( (DWORD) PortInfo->PortObject.MsgQueue.Semaphore,
                         &Semaphore,
                         sizeof( KSEMAPHORE ),
                         &Result)) {
            dprintf("Unable to read KSEMAPHORE at %08lx\n",PortInfo->PortObject.MsgQueue.Semaphore);
            SignalState = 0;
        } else {
            SignalState = Semaphore.Header.SignalState;
        }
        dprintf( "    Receive queue head: %08x . %08x  Semaphore Count: %x\n",
                 ListEntry.Flink, ListEntry.Blink,
                 SignalState
               );
        while (Next != Head) {
            if ( !ReadMemory( (DWORD)Next,
                              &ListEntry,
                              sizeof( ListEntry ),
                              &Result) ) {
                dprintf( "%08lx: Unable to read list\n", Next );
                break;
            }

            Msg  = CONTAINING_RECORD( Next, LPCP_MESSAGE, Entry );
            LpcpDumpMessage( "        ", Msg );
            Next = ListEntry.Flink;

            if ( CheckControlC() ) {
                return;
            }
        }
        dprintf( "\n" );
    }

    Head = &(PortInfo->pPortObject->LpcDataInfoChainHead);
    ListEntry = PortInfo->PortObject.LpcDataInfoChainHead;
    Next = ListEntry.Flink;
    if (Next != NULL && Next != Head) {
        dprintf( "    DataInfo chain head: %08x . %08x\n",
                 ListEntry.Flink, ListEntry.Blink
               );
        while (Next != Head) {
            if ( !ReadMemory( (DWORD)Next,
                              &ListEntry,
                              sizeof( ListEntry ),
                              &Result) ) {
                dprintf( "%08lx: Unable to read list\n", Next );
                break;
            }

            Msg  = CONTAINING_RECORD( Next, LPCP_MESSAGE, Entry );
            LpcpDumpMessage( "        ", Msg );
            Next = ListEntry.Flink;

            if ( CheckControlC() ) {
                return;
            }
        }
        dprintf( "\n" );
    }

    p = PortsInfo->PortInfo;
    while (p != NULL) {
        if (p != PortInfo &&
            p->PortObject.ConnectionPort == PortInfo->pPortObject &&
            (p->PortObject.Flags & PORT_TYPE) == CLIENT_COMMUNICATION_PORT
           ) {
            LpcpDumpClientPort( dwProcessor, PortsInfo, p );
        }

        p = p->Next;

        if ( CheckControlC() ) {
            return;
        }
    }

    dprintf( "\n" );
    return;
}



BOOLEAN
CapturePorts(
    IN PVOID            pObjectHeader,
    IN POBJECT_HEADER   ObjectHeader,
    IN PVOID            Parameter
    )
{
    ULONG           Result;
    PLPCP_DUMP_INFO PortsInfo = (PLPCP_DUMP_INFO)Parameter;
    PLPCP_DUMP_PORT_INFO PortInfo;

    PortInfo = LocalAlloc(LPTR, sizeof( *PortInfo ) );
    if (PortInfo == NULL) {
        dprintf( "*** out of memory.\n" );
        return FALSE;
    }

    PortInfo->pPortObject = (PLPCP_PORT_OBJECT)&(((POBJECT_HEADER)pObjectHeader)->Body);
    if ( !ReadMemory( (DWORD)PortInfo->pPortObject,
                      &PortInfo->PortObject,
                      sizeof( PortInfo->PortObject ),
                      &Result) ) {
        dprintf( "%08lx: Unable to read port object\n", PortInfo->pPortObject );
        return FALSE;
    }

    CaptureObjectName( pObjectHeader, ObjectHeader, PortInfo->Name, MAX_PATH );

    // dprintf( "Port %08x - %ws\n", PortInfo->pPortObject, PortInfo->Name );
    PortInfo->Next = PortsInfo->PortInfo;
    PortsInfo->PortInfo = PortInfo;
    return TRUE;
}


VOID
LpcpDumpMessage(
    IN char         *Indent,
    IN PLPCP_MESSAGE pMsg
    )
{
    ULONG           Result;
    LPCP_MESSAGE    Msg;
    ULONG           i;
    ULONG           cb;
    ULONG           MsgData[ 8 ];
    UCHAR           ImageFileName[ 32 ];

    if ( !ReadMemory( (DWORD)pMsg,
                      &Msg,
                      sizeof(Msg),
                      &Result) ) {
        dprintf( "%s*** unable to read LPC message at %08x\n", Indent, pMsg );
        return;
    }

    if (Msg.Request.MessageId == 0) {
        return;
    }


    LpcpGetProcessImageName( Msg.Request.ClientId.UniqueProcess, ImageFileName );
    dprintf( "%s%04x %08x - %s  Id=%04x  From: %04x.%04x (%s)",
             Indent,

             Msg.ZoneIndex & ~LPCP_ZONE_MESSAGE_ALLOCATED,
             pMsg,
             Msg.Reserved0 != 0 ? "Busy" : "Free",
             Msg.Request.MessageId,
             Msg.Request.ClientId.UniqueProcess,
             Msg.Request.ClientId.UniqueThread,
             ImageFileName
           );

    if (Msg.Entry.Flink != &pMsg->Entry) {
        dprintf( "  [%x . %x]", Msg.Entry.Blink, Msg.Entry.Flink );
        }

    dprintf( "\n%s           Length=%08x  Type=%08x (%s)\n",
             Indent,
             Msg.Request.u1.Length,
             Msg.Request.u2.ZeroInit,
             Msg.Request.u2.s2.Type > LPC_CONNECTION_REQUEST ? LpcpMessageTypeName[ 0 ]
                                                              : LpcpMessageTypeName[ Msg.Request.u2.s2.Type ]
           );

    cb = Msg.Request.u1.s1.DataLength > sizeof( MsgData ) ? sizeof( MsgData )
                                                          : (ULONG)Msg.Request.u1.s1.DataLength;

    if ( !ReadMemory( (DWORD)(pMsg + 1),
                      MsgData,
                      cb,
                      &Result) ) {
        dprintf( "%s*** unable to read LPC message data at %08x\n", Indent, pMsg + 1 );
        return;
    }

    dprintf( "%s           Data:", Indent );
    for (i=0; i<(Msg.Request.u1.s1.DataLength / sizeof( ULONG )); i++) {
        if (i > 5) {
            break;
        }

        dprintf( " %08x", MsgData[ i ] );
    }
    dprintf( "\n" );
    return;
}