/*++

Copyright (c) 2001 Microsoft Corporation
All rights reserved.

Module Name:

    dbgutil.hxx

Abstract:

    LSA Utility functions header

Author:

    Larry Zhu (Lzhu)  May 1, 2001

Revision History:

--*/
#ifndef _LSAUTIL_HXX_
#define _LSAUTIL_HXX_

#include <string.h>
#include <stdio.h>
#include <align.h>

namespace LSA_NS {

enum ELsaExceptionCode {
    kInvalid,
    kIncorrectSymbols,
    kExitOnControlC,
};

LPCTSTR
StripPathFromFileName(
    IN LPCTSTR pszFile
    );

ULONG
GetStructFieldVerbose(
    IN ULONG64 addrStructBase,
    IN PCSTR pszStructTypeName,
    IN PCSTR pszStructFieldName,
    IN ULONG BufferSize,
    OUT PVOID Buffer);

ULONG
GetStructPtrFieldVerbose(
    IN ULONG64 addrStructBase,
    IN PCSTR pszStructTypeName,
    IN PCSTR pszStructFieldName,
    OUT PULONG64 Buffer);

HRESULT
GetCurrentProcessor(
    IN PDEBUG_CLIENT Client,
    OPTIONAL OUT PULONG pProcessor,
    OPTIONAL OUT PHANDLE phCurrentThread);

PCSTR
ReadStructStrField(
    IN ULONG64 addrStructBase,
    IN PCSTR pszStructTypeName,
    IN PCSTR pszStructStringFieldName);

PCWSTR
ReadStructWStrField(
    IN ULONG64 addrStructBase,
    IN PCSTR pszStructTypeName,
    IN PCSTR pszStructWStringFieldName);
BOOL
WINAPI
LsaLookupAccountNameA(
    IN LPCSTR lpSystemName,
    IN LPCSTR lpAccountName,
    OUT PSID Sid,
    IN OUT LPDWORD cbSid,
    OUT LPSTR ReferencedDomainName,
    IN OUT LPDWORD cbReferencedDomainName,
    OUT PSID_NAME_USE peUse
    );

BOOL
WINAPI
LsaLookupAccountSidA(
    IN LPCSTR lpSystemName,
    IN PSID Sid,
    OUT LPSTR Name,
    IN OUT LPDWORD cbName,
    OUT LPSTR ReferencedDomainName,
    IN OUT LPDWORD cbReferencedDomainName,
    OUT PSID_NAME_USE peUse
    );

void LocalPrintGuid(IN const GUID *pGuid);

BOOL IsAddressInNonePAEKernelAddressSpace(IN ULONG64 addr);

HRESULT HResultFromWin32(IN DWORD dwError);

HRESULT GetLastErrorAsHResult(void);

BOOLEAN IsEmpty(IN PCSTR pszArgs);

void debugPrintHandle(IN HANDLE handle);

NTSTATUS NtStatusFromWin32(IN DWORD dwError);

NTSTATUS GetLastErrorAsNtStatus(void);

HRESULT ProcessKnownOptions(IN PCSTR pszArgs OPTIONAL);

HRESULT ProcessHelpRequest(IN PCSTR pszArgs OPTIONAL);

LARGE_INTEGER
ULONG642LargeInteger(
    IN ULONG64 value
    );

VOID
ShowSystemTimeAsLocalTime(
    IN PCSTR pszBanner,
    IN ULONG64 ul64Time
    );

void handleLsaException(
    IN ELsaExceptionCode eExceptionCode,
    IN PCSTR pszMsg,    OPTIONAL
    IN PCSTR pszSymHint OPTIONAL
    );

HRESULT GetFileNamePart(IN PCSTR pszFullPath, OUT PCSTR *ppszFileName);

void debugPrintHex(IN const void* buffer, IN ULONG cbBuffer);

#define LSA_NONE                           0x00
#define LSA_WARN                           0x01
#define LSA_ERROR                          0x02
#define LSA_LOG                            0x04
#define LSA_LOG_MORE                       0x08
#define LSA_LOG_ALL                        0x10

inline void debugPrintLevel(IN ULONG ulLevel)
{
    PCSTR pszText = NULL;

    switch (ulLevel) {
    case LSA_WARN:
        pszText = "[warn] ";
        break;

    case LSA_ERROR:
        pszText = "[error] ";
        break;

    case LSA_LOG:
        pszText = "[log] ";
        break;

    case LSA_LOG_MORE:
        pszText = "[more] ";
        break;

    case LSA_LOG_ALL:
        pszText = "[all] ";
        break;

    default:
        pszText = kstrInvalid;
        break;
    }

    OutputDebugStringA(pszText);
}

inline void debugPrintf(IN PCSTR pszFmt, ...)
{
    CHAR szBuffer[4096] = {0};

    OutputDebugStringA(g_Globals.pszDbgPrompt ? g_Globals.pszDbgPrompt : kstrEmptyA);

    va_list pArgs;

    va_start(pArgs, pszFmt);

    _vsnprintf(szBuffer, sizeof(szBuffer) - 1, pszFmt, pArgs);

    OutputDebugStringA(szBuffer);

    va_end(pArgs);
}

inline PCSTR IoctlErrorStatusToStr(IN PCSTR pszPad, IN ULONG err)
{
   static CHAR szBuf[1024] = {0};

#define BRANCH_AND_PRINT(x)                                              \
                                                                         \
    do {                                                                 \
        if (x == err) {                                                  \
            _snprintf(szBuf, sizeof(szBuf) - 1, "%s%s", pszPad, #x);     \
            return szBuf;                                                \
        }                                                                \
    } while (0)

    BRANCH_AND_PRINT(MEMORY_READ_ERROR);
    BRANCH_AND_PRINT(SYMBOL_TYPE_INDEX_NOT_FOUND);
    BRANCH_AND_PRINT(SYMBOL_TYPE_INFO_NOT_FOUND);
    BRANCH_AND_PRINT(FIELDS_DID_NOT_MATCH);
    BRANCH_AND_PRINT(NULL_SYM_DUMP_PARAM);
    BRANCH_AND_PRINT(NULL_FIELD_NAME);
    BRANCH_AND_PRINT(INCORRECT_VERSION_INFO);
    BRANCH_AND_PRINT(EXIT_ON_CONTROLC);
    BRANCH_AND_PRINT(CANNOT_ALLOCATE_MEMORY);
    BRANCH_AND_PRINT(INSUFFICIENT_SPACE_TO_COPY);

#undef BRANCH_AND_PRINT

    _snprintf(szBuf, sizeof(szBuf) - 1, "%s%#x", pszPad, err);

    return szBuf;
}

BOOL IsPtr64WithVoidStar(void);

inline ULONG64 toPtr(IN ULONG64 addr)
{
   static BOOL bIsPtr64 = IsPtr64WithVoidStar();

   if (!bIsPtr64) {

       addr = static_cast<ULONG>(addr);
   }

   return addr;
}

inline ULONG GetPtrSize(void)
{
    static ULONG cbPtrSize = 0;

    if (!cbPtrSize) {

        cbPtrSize = IsPtr64WithVoidStar() ? sizeof(ULONG64) : sizeof(ULONG);
    }

    return cbPtrSize;
}

inline ULONG ReadPtrSize(void)
{
    static ULONG cbPtrSize = GetPtrSize();

    if (!cbPtrSize) {

        throw "ReadPtrSize failed";
    }

    return cbPtrSize;
}

inline ULONG GetPtrWithVoidStar(IN ULONG64 Addr, IN ULONG64* pPointer)
{
    return GetFieldData(Addr, kstrVoidStar, NULL, sizeof(ULONG64), (PVOID) pPointer);
}

inline PCSTR EasyStr(IN PCSTR pszName)
{
    return pszName ? pszName : kstrNullPtrA;
}

inline void HandleIoctlErrors(IN PCSTR pszBanner, IN ULONG ErrorCode)
{
    if (SYMBOL_TYPE_INFO_NOT_FOUND == ErrorCode) {

        throw kIncorrectSymbols;

    } else if (ErrorCode) {

        DBG_LOG(LSA_ERROR, ("%s: Ioctl failed with error code %s\n", EasyStr(pszBanner), EasyStr(IoctlErrorStatusToStr(kstrEmptyA, ErrorCode))));

        throw "Ioctl failed";
    }
}

inline ULONG64 ReadPtrWithVoidStar(IN ULONG64 Addr)
{
    ULONG64 Pointer = 0;

    HandleIoctlErrors("ReadPtrWithVoidStar", GetFieldData(Addr, kstrVoidStar, NULL, sizeof(ULONG64), &Pointer));

    return Pointer;
}

inline ULONG64 ReadStructPtrField(IN ULONG64 addrStructBase, IN PCSTR pszStructTypeName, IN PCSTR pszStructFieldName)
{
    ULONG64 addr = 0;

    DBG_LOG(LSA_LOG, ("ReadStructPtrField %s %#I64x %s\n", EasyStr(pszStructTypeName), addrStructBase, EasyStr(pszStructFieldName)));

    HandleIoctlErrors("ReadStructPtrField", GetStructPtrFieldVerbose(addrStructBase, pszStructTypeName, pszStructFieldName, &addr));

    return toPtr(addr);
}

inline ULONG64 ForwardAdjustPtrAddr(IN ULONG64 addr)
{
    static ULONG uPtrSize = ReadPtrSize();

    ULONG64 alignedPtrAddr = addr;

    //
    // Only 64bit machines have alignment requirement
    //
    if (uPtrSize == sizeof(ULONG64)) {

        alignedPtrAddr = ROUND_UP_COUNT(addr, sizeof(ULONG64)); // ((addr + uPtrSize - 1) / uPtrSize) * uPtrSize;

        if (alignedPtrAddr != addr) {

            DBG_LOG(LSA_LOG, ("ForwardAdjustPtrAddr adjusted addr from %#I64x to %#I64x\n", addr, alignedPtrAddr));
        }
    }

    return alignedPtrAddr;
}

inline ULONG64 ReadPtrVar(IN ULONG64 addr)
{
    static uPtrSize = ReadPtrSize();

    ULONG64 value = 0;
    ULONG errorCode = 0;

    //
    // Assume 64 bit machine has alignment requirement for pointers
    //

    if (uPtrSize == sizeof(ULONG64)) {

        ULONG64 alignedAddr = addr;

        alignedAddr = ForwardAdjustPtrAddr(addr);

        if (alignedAddr != addr) {

            DBG_LOG(LSA_WARN, ("Adjust pointer address %#I64x to 8 byte boundary %#I64x\n", addr, alignedAddr));
        }

        addr = alignedAddr;
    }

    HandleIoctlErrors("ReadPtrVar", GetPtrWithVoidStar(addr, &value));

    return toPtr(value);
}

inline UCHAR ReadUCHARVar(IN ULONG64 addr)
{
    UCHAR value = 0;

    if (!ReadMemory(addr, &value, sizeof(value), NULL)) {

        DBG_LOG(LSA_ERROR, ("Can not ReadUCHARVar at %#I64x\n", addr));

        throw "ReadUCHARVar failed";
    }

    return value;
}

inline USHORT ReadUSHORTVar(IN ULONG64 addr)
{
    USHORT value = 0;

    if (!ReadMemory(addr, &value, sizeof(value), NULL)) {

        DBG_LOG(LSA_ERROR, ("Can not ReadUSHORTVar at %#I64x\n", addr));

        throw "ReadUSHORTVar failed";
    }

    return value;
}

inline ULONG ReadULONGVar(IN ULONG64 addr)
{
    ULONG value = 0;

    if (!ReadMemory(addr, &value, sizeof(value), NULL)) {

        DBG_LOG(LSA_ERROR, ("Can not ReadULONGVar at %#I64x\n", addr));

        throw "ReadULONGVar failed";
    }

    return value;
}

inline ULONG64 ReadULONG64Var(IN ULONG64 addr)
{
    ULONG64 value = 0;

    if (!ReadMemory(addr, &value, sizeof(value), NULL)) {

        DBG_LOG(LSA_ERROR, ("Can not ReadULONG64Var at %#I64x\n", addr));

        throw "ReadULONG64Var failed";
    }

    return value;
}

inline ULONG ReadTypeSize(IN PCSTR pszTypeName)
{
    ULONG typeSize = 0;

    typeSize = GetTypeSize(pszTypeName);

    DBG_LOG(LSA_LOG, ("Read type size of \"%s\"\n", EasyStr(pszTypeName)));

    if (!typeSize) {

        DBG_LOG(LSA_ERROR, ("Can not GetTypeSize on %s\n", EasyStr(pszTypeName)));

        throw "ReadTypeSize failed\n";
    }

    return typeSize;
}

inline ULONG ReadFieldOffset(IN PCSTR pszTypeName, IN PCSTR pszFieldName)
{
    ULONG fieldOffset = 0;

    DBG_LOG(LSA_LOG, ("Read field offset of \"%s\" from \"%s\"\n", EasyStr(pszFieldName), EasyStr(pszTypeName)));

    HandleIoctlErrors("ReadFieldOffset", GetFieldOffset(pszTypeName, pszFieldName, &fieldOffset));

    return fieldOffset;
}

inline void ReadStructField(IN ULONG64 addrStructBase, IN PCSTR pszStructTypeName, IN PCSTR pszStructFieldName, IN ULONG fieldSize, OUT void* pFieldValue)
{
    DBG_LOG(LSA_LOG, ("ReadStructField %s %#I64x %s\n", EasyStr(pszStructTypeName), addrStructBase, EasyStr(pszStructFieldName)));

    HandleIoctlErrors("ReadStructField", GetStructFieldVerbose(addrStructBase, pszStructTypeName, pszStructFieldName, fieldSize, pFieldValue));
}

inline void LsaReadMemory(IN ULONG64 addr, IN ULONG cbBuffer, OUT void* buffer)
{
    if (!ReadMemory(addr, buffer, cbBuffer, NULL)) {

        DBG_LOG(LSA_ERROR, ("Unable to read %#x bytes from memory location %#I64x\n", cbBuffer, toPtr(addr)));

        throw "LsaReadMemory failed";
    }
}

//
// Used to read in value of a short (<= 8 bytes) fields
//
// This is essentially the same as GetShortField with one difference that
// it throws exceptions on failures
//

inline ULONG64 ReadShortField(IN ULONG64 TypeAddress, IN PCSTR pszName, IN USHORT  StoreAddress)
{
    static ULONG64 SavedAddress = 0;
    static PCSTR pszSavedName = NULL;
    static ULONG ReadPhysical = 0;

    ULONG Err = 0;

    FIELD_INFO flds = {reinterpret_cast<UCHAR*>(const_cast<PSTR>(pszName)), NULL, 0, DBG_DUMP_FIELD_FULL_NAME, 0, NULL};
    SYM_DUMP_PARAM Sym = {
       sizeof (SYM_DUMP_PARAM), reinterpret_cast<UCHAR*>(const_cast<PSTR>(pszSavedName)), DBG_DUMP_NO_PRINT | ((StoreAddress & 2) ? DBG_DUMP_READ_PHYSICAL : 0),
       SavedAddress, NULL, NULL, NULL, 1, &flds
    };

    if (StoreAddress) {

        Sym.sName = reinterpret_cast<UCHAR*>(const_cast<PSTR>(pszName));
        Sym.nFields = 0;
        pszSavedName = pszName;
        Sym.addr = SavedAddress = TypeAddress;
        ReadPhysical = (StoreAddress & 2);

        if (!SavedAddress) {

            throw "Invalid arguments to ReadShortField";
        }

        Err = Ioctl(IG_DUMP_SYMBOL_INFO, &Sym, Sym.size);

        DBG_LOG(LSA_LOG, ("Save address %s %#I64x\n", EasyStr(pszName), TypeAddress));

        HandleIoctlErrors("ReadShortField (save address) failed", Err);

        return 0; // zero on success

    } else {

        Sym.Options |= ReadPhysical ? DBG_DUMP_READ_PHYSICAL : 0;
    }

    Err = Ioctl(IG_DUMP_SYMBOL_INFO, &Sym, Sym.size);

    DBG_LOG(LSA_LOG, ("Read %s %#I64x %s\n", EasyStr(pszSavedName), SavedAddress, EasyStr(pszName)));

    HandleIoctlErrors("ReadShortField (read value) failed", Err);

    return flds.address;
}

inline ULONG ReadTypeSizeInArray(IN PCSTR pszType)
{
    ULONG cbOneItem = 0;
    ULONG cbTwoItems = 0;
    CHAR szTypeTmp[256] = {0};

    DBG_LOG(LSA_LOG, ("ReadTypeSizeInArray %s\n", pszType));

    cbOneItem = GetTypeSize(pszType);

    if (_snprintf(szTypeTmp, sizeof(szTypeTmp) - 1, "%s[2]", pszType) < 0) {

        throw "ReadTypeSizeInArray failed with insufficient buffer";
    }

    cbTwoItems = GetTypeSize(szTypeTmp);
    cbOneItem = cbTwoItems - cbOneItem;

    if (!cbOneItem) {

        dprintf("Unable to read type size in array for %s\n", pszType);
        throw "ReadTypeSizeInArray failed";
    }

    return cbOneItem;
}

inline ULONG64 ReadPtrField(IN PCSTR Field)
{
    return toPtr(ReadShortField(0, Field, 0));
}

inline void ExitIfControlC(void)
{
    if (CheckControlC()) {

       throw kExitOnControlC;
    }
}

//
// This func is not thread safe
//

inline PCSTR PtrToStr(IN ULONG64 addr)
{
    static CHAR szBuffer[64] = {0};

    if (!addr) {

        return kstrNullPtrA;
    }

    _snprintf(szBuffer, sizeof(szBuffer) - 1, "%#I64x", addr);

    return szBuffer;
}

//
// This func is not thread safe because it calls PtrToStr which is not
// thread safe
//

inline PCSTR GetSymbolStr(IN ULONG64 addr, IN PCSTR pszBuffer)
{
    ExitIfControlC();

    //
    // null
    //
    if (!addr) {

        return kstrNullPtrA;
    }

    //
    // No symbols resolved, return address
    //
    if (!pszBuffer || !*pszBuffer) {

        return PtrToStr(addr);
    }

    //
    //  Return symbols
    //
    return pszBuffer;
}

inline void PrintPtrWithSymbolsLn(IN PCSTR pszBanner, IN ULONG64 addr)
{
    CHAR szBuffer[MAX_PATH] = {0};
    ULONG64 Disp = 0;

    GetSymbol(addr, szBuffer, &Disp);
    dprintf(kstr2StrLn, pszBanner, GetSymbolStr(addr, szBuffer));
}

inline void PrintSpaces(IN LONG cSpaces)
{
    for (LONG i = 0; i < cSpaces; i++) {

        dprintf(kstrSpace);
    }
}

} // LSA_NS

#endif