[NTOSKRNL]

Minas Abrahamyan (minas \dot/ subs \at/ gmail \dot/ com):
- Allow to view already shown, logged debug messages on the screen (bug #6018).

svn path=/trunk/; revision=52556
This commit is contained in:
Dmitry Gorbachev 2011-07-07 19:18:16 +00:00
parent 5b90950500
commit e462a7a6d3
2 changed files with 535 additions and 5 deletions

View file

@ -33,7 +33,15 @@ ULONG KdpPort;
CHAR KdpScreenLineBuffer[KdpScreenLineLenght + 1] = "";
ULONG KdpScreenLineBufferPos = 0, KdpScreenLineLength = 0;
/* DEBUG LOG FUNCTIONS *******************************************************/
const ULONG KdpDmesgBufferSize = 128 * 1024; // 512*1024; // 5*1024*1024;
PCHAR KdpDmesgBuffer = NULL;
volatile ULONG KdpDmesgCurrentPosition = 0;
volatile ULONG KdpDmesgFreeBytes = 0;
volatile ULONG KdbDmesgTotalWritten = 0;
KSPIN_LOCK KdpDmesgLogSpinLock;
extern volatile BOOLEAN KdbpIsInDmesgMode;
/* FILE DEBUG LOG FUNCTIONS **************************************************/
VOID
NTAPI
@ -49,8 +57,14 @@ KdpLoggerThread(PVOID Context)
KeWaitForSingleObject(&KdpLoggerThreadEvent, 0, KernelMode, FALSE, NULL);
/* Bug */
/* Keep KdpCurrentPosition and KdpFreeBytes values in local
* variables to avoid their possible change from Producer part,
* KdpPrintToLogFile function
*/
end = KdpCurrentPosition;
num = KdpFreeBytes;
/* Now securely calculate values, based on local variables */
beg = (end + num) % KdpBufferSize;
num = KdpBufferSize - num;
@ -148,6 +162,7 @@ KdpInitDebugLog(PKD_DISPATCH_TABLE DispatchTable,
IO_STATUS_BLOCK Iosb;
HANDLE ThreadHandle;
KPRIORITY Priority;
SIZE_T MemSizeMBs;
if (!KdpDebugMode.File) return;
@ -175,6 +190,8 @@ KdpInitDebugLog(PKD_DISPATCH_TABLE DispatchTable,
/* Display separator + ReactOS version at start of the debug log */
DPRINT1("---------------------------------------------------------------\n");
DPRINT1("ReactOS "KERNEL_VERSION_STR" (Build "KERNEL_VERSION_BUILD_STR")\n");
MemSizeMBs = MmNumberOfPhysicalPages * PAGE_SIZE / 1024 / 1024;
DPRINT1("%u System Processor [%u MB Memory]\n", KeNumberProcessors, MemSizeMBs);
}
else if (BootPhase == 2)
{
@ -276,6 +293,7 @@ NTAPI
KdpSerialInit(PKD_DISPATCH_TABLE DispatchTable,
ULONG BootPhase)
{
SIZE_T MemSizeMBs;
if (!KdpDebugMode.Serial) return;
if (BootPhase == 0)
@ -301,6 +319,8 @@ KdpSerialInit(PKD_DISPATCH_TABLE DispatchTable,
/* Display separator + ReactOS version at start of the debug log */
DPRINT1("-----------------------------------------------------\n");
DPRINT1("ReactOS "KERNEL_VERSION_STR" (Build "KERNEL_VERSION_BUILD_STR")\n");
MemSizeMBs = MmNumberOfPhysicalPages * PAGE_SIZE / 1024 / 1024;
DPRINT1("%u System Processor [%u MB Memory]\n", KeNumberProcessors, MemSizeMBs);
DPRINT1("Command Line: %s\n", KeLoaderBlock->LoadOptions);
DPRINT1("ARC Paths: %s %s %s %s\n", KeLoaderBlock->ArcBootDeviceName,
KeLoaderBlock->NtHalPathName,
@ -315,11 +335,19 @@ KdpSerialInit(PKD_DISPATCH_TABLE DispatchTable,
/* SCREEN FUNCTIONS **********************************************************/
/*
* Screen debug logger function KdpScreenPrint() writes text messages into
* KdpDmesgBuffer, using it as a circular buffer. KdpDmesgBuffer contents could
* be later (re)viewed using dmesg command of kdbg. KdpScreenPrint() protects
* KdpDmesgBuffer from simultaneous writes by use of KdpDmesgLogSpinLock.
*/
VOID
NTAPI
KdpScreenPrint(LPSTR Message,
ULONG Length)
{
ULONG beg, end, num;
KIRQL OldIrql;
PCHAR pch = (PCHAR) Message;
while (*pch)
@ -354,16 +382,74 @@ KdpScreenPrint(LPSTR Message,
KdpScreenLineBuffer[0] = '\0';
KdpScreenLineLength = KdpScreenLineBufferPos = 0;
}
++pch;
}
/* Print buffered characters */
if(KdpScreenLineBufferPos != KdpScreenLineLength)
{
HalDisplayString(KdpScreenLineBuffer + KdpScreenLineBufferPos);
KdpScreenLineBufferPos = KdpScreenLineLength;
}
/* Dmesg: store Message in the buffer to show it later */
if (KdbpIsInDmesgMode)
return;
if (KdpDmesgBuffer == NULL)
return;
/* Acquire the printing spinlock without waiting at raised IRQL */
while (TRUE)
{
/* Wait when the spinlock becomes available */
while (!KeTestSpinLock(&KdpDmesgLogSpinLock));
/* Spinlock was free, raise IRQL */
KeRaiseIrql(HIGH_LEVEL, &OldIrql);
/* Try to get the spinlock */
if (KeTryToAcquireSpinLockAtDpcLevel(&KdpDmesgLogSpinLock))
break;
/* Someone else got the spinlock, lower IRQL back */
KeLowerIrql(OldIrql);
}
/* Invariant: always_true(KdpDmesgFreeBytes == KdpDmesgBufferSize);
* set num to min(KdpDmesgFreeBytes, Length).
*/
num = (Length < KdpDmesgFreeBytes) ? Length : KdpDmesgFreeBytes;
beg = KdpDmesgCurrentPosition;
if (num != 0)
{
end = (beg + num) % KdpDmesgBufferSize;
if (end > beg)
{
RtlCopyMemory(KdpDmesgBuffer + beg, Message, Length);
}
else
{
RtlCopyMemory(KdpDmesgBuffer + beg, Message, KdpDmesgBufferSize - beg);
RtlCopyMemory(KdpDmesgBuffer, Message + (KdpDmesgBufferSize - beg), end);
}
KdpDmesgCurrentPosition = end;
/* Counting the total bytes written */
KdbDmesgTotalWritten += num;
}
/* Release spinlock */
KiReleaseSpinLock(&KdpDmesgLogSpinLock);
/* Lower IRQL */
KeLowerIrql(OldIrql);
/* Optional step(?): find out a way to notify about buffer exhaustion,
* and possibly fall into kbd to use dmesg command: user will read
* debug messages before they will be wiped over by next writes.
*/
}
VOID
@ -371,6 +457,7 @@ NTAPI
KdpScreenInit(PKD_DISPATCH_TABLE DispatchTable,
ULONG BootPhase)
{
SIZE_T MemSizeMBs;
if (!KdpDebugMode.Screen) return;
if (BootPhase == 0)
@ -382,6 +469,30 @@ KdpScreenInit(PKD_DISPATCH_TABLE DispatchTable,
/* Register as a Provider */
InsertTailList(&KdProviders, &DispatchTable->KdProvidersList);
}
else if (BootPhase == 1)
{
/* Allocate a buffer for dmesg log buffer. +1 for terminating null,
* see kdbp_cli.c:KdbpCmdDmesg()/2
*/
KdpDmesgBuffer = ExAllocatePool(NonPagedPool, KdpDmesgBufferSize + 1);
RtlZeroMemory(KdpDmesgBuffer, KdpDmesgBufferSize + 1);
KdpDmesgFreeBytes = KdpDmesgBufferSize;
KdbDmesgTotalWritten = 0;
/* Initialize spinlock */
KeInitializeSpinLock(&KdpDmesgLogSpinLock);
/* Display separator + ReactOS version at start of the debug log */
DPRINT1("-----------------------------------------------------\n");
DPRINT1("ReactOS "KERNEL_VERSION_STR" (Build "KERNEL_VERSION_BUILD_STR")\n");
MemSizeMBs = MmNumberOfPhysicalPages * PAGE_SIZE / 1024 / 1024;
DPRINT1("%u System Processor [%u MB Memory]\n", KeNumberProcessors, MemSizeMBs);
DPRINT1("Command Line: %s\n", KeLoaderBlock->LoadOptions);
DPRINT1("ARC Paths: %s %s %s %s\n", KeLoaderBlock->ArcBootDeviceName,
KeLoaderBlock->NtHalPathName,
KeLoaderBlock->ArcHalDeviceName,
KeLoaderBlock->NtBootPathName);
}
else if (BootPhase == 2)
{
HalDisplayString("\n Screen debugging enabled\n\n");

View file

@ -42,6 +42,13 @@
#define KEY_SCAN_UP 72
#define KEY_SCAN_DOWN 80
/* Scan codes of keyboard keys: */
#define KEYSC_END 0x004f
#define KEYSC_PAGEUP 0x0049
#define KEYSC_PAGEDOWN 0x0051
#define KEYSC_HOME 0x0047
#define KEYSC_ARROWUP 0x0048
#define KDB_ENTER_CONDITION_TO_STRING(cond) \
((cond) == KdbDoNotEnter ? "never" : \
((cond) == KdbEnterAlways ? "always" : \
@ -81,6 +88,7 @@ static BOOLEAN KdbpCmdBugCheck(ULONG Argc, PCHAR Argv[]);
static BOOLEAN KdbpCmdFilter(ULONG Argc, PCHAR Argv[]);
static BOOLEAN KdbpCmdSet(ULONG Argc, PCHAR Argv[]);
static BOOLEAN KdbpCmdHelp(ULONG Argc, PCHAR Argv[]);
static BOOLEAN KdbpCmdDmesg(ULONG Argc, PCHAR Argv[]);
/* GLOBALS *******************************************************************/
@ -101,6 +109,17 @@ static LONG KdbNumberOfColsTerminal = -1;
PCHAR KdbInitFileBuffer = NULL; /* Buffer where KDBinit file is loaded into during initialization */
BOOLEAN KdbpBugCheckRequested = FALSE;
/* Vars for dmesg */
/* defined here, used in ../kd/kdio.c: */
volatile BOOLEAN KdbpIsInDmesgMode = FALSE;
/* defined in ../kd/kdio.c, declare here: */
extern const ULONG KdpDmesgBufferSize;
extern PCHAR KdpDmesgBuffer;
extern volatile ULONG KdpDmesgCurrentPosition;
extern volatile ULONG KdpDmesgFreeBytes;
extern volatile ULONG KdbDmesgTotalWritten;
static const struct
{
PCHAR Name;
@ -150,6 +169,8 @@ static const struct
{ "bugcheck", "bugcheck", "Bugchecks the system.", KdbpCmdBugCheck },
{ "filter", "filter [error|warning|trace|info|level]+|-[componentname|default]", "Enable/disable debug channels", KdbpCmdFilter },
{ "set", "set [var] [value]", "Sets var to value or displays value of var.", KdbpCmdSet },
{ "dmesg", "dmesg", "Display debug messages on screen, with navigation on pages.", KdbpCmdDmesg },
{ "kmsg", "kmsg", "Kernel dmesg. Alias for dmesg.", KdbpCmdDmesg },
{ "help", "help", "Display help screen.", KdbpCmdHelp }
};
@ -1895,6 +1916,58 @@ KdbpCmdBugCheck(
return FALSE;
}
VOID
KdbpPager(
IN PCHAR Buffer,
IN ULONG BufLength);
/*!\brief Display debug messages on screen, with paging.
*
* Keys for per-page view: Home, End, PageUp, Arrow Up, PageDown,
* all others are as PageDown.
*/
static BOOLEAN
KdbpCmdDmesg(
ULONG Argc,
PCHAR Argv[])
{
ULONG beg, end;
KdbpIsInDmesgMode = TRUE; /* Toggle logging flag */
if (!KdpDmesgBuffer)
{
KdbpPrint("Dmesg: error, buffer is not allocated! /DEBUGPORT=SCREEN kernel param required for dmesg.\n");
return TRUE;
}
KdbpPrint("*** Dmesg *** TotalWritten=%lu, BufferSize=%lu, CurrentPosition=%lu\n",
KdbDmesgTotalWritten, KdpDmesgBufferSize, KdpDmesgCurrentPosition);
// Pass data to the pager:
end = KdpDmesgCurrentPosition;
beg = (end + KdpDmesgFreeBytes) % KdpDmesgBufferSize;
// no roll-overs, and overwritten=lost bytes
if (KdbDmesgTotalWritten <= KdpDmesgBufferSize)
{
// show buffer (KdpDmesgBuffer + beg, num)
KdbpPager(KdpDmesgBuffer, KdpDmesgCurrentPosition);
}
else
{
// show 2 buffers: (KdpDmesgBuffer + beg, KdpDmesgBufferSize - beg)
// and: (KdpDmesgBuffer, end)
KdbpPager(KdpDmesgBuffer + beg, KdpDmesgBufferSize - beg);
KdbpPrint("*** Dmesg: buffer rollup ***\n");
KdbpPager(KdpDmesgBuffer, end);
}
KdbpPrint("*** Dmesg: end of output ***\n");
KdbpIsInDmesgMode = FALSE; /* Toggle logging flag */
return TRUE;
}
/*!\brief Sets or displays a config variables value.
*/
static BOOLEAN
@ -2110,6 +2183,7 @@ KdbpCmdHelp(
*
* \note Doesn't correctly handle \\t and terminal escape sequences when calculating the
* number of lines required to print a single line from the Buffer in the terminal.
* Prints maximum 4096 chars, because of its buffer size.
*/
VOID
KdbpPrint(
@ -2215,12 +2289,12 @@ KdbpPrint(
if (KdbNumberOfRowsTerminal <= 0)
{
/* Set number of rows to the default. */
KdbNumberOfRowsTerminal = 24;
KdbNumberOfRowsTerminal = 23; //24; //Mna.: 23 for SCREEN debugport
}
else if (KdbNumberOfColsTerminal <= 0)
{
/* Set number of cols to the default. */
KdbNumberOfColsTerminal = 80;
KdbNumberOfColsTerminal = 75; //80; //Mna.: 75 for SCREEN debugport
}
}
@ -2256,6 +2330,7 @@ KdbpPrint(
DbgPrint("\n");
DbgPrint("--- Press q to abort, any other key to continue ---");
RowsPrintedByTerminal++; /* added by Mna. */
if (KdbDebugState & KD_DEBUG_KDSERIAL)
c = KdbpGetCharSerial();
@ -2337,6 +2412,350 @@ KdbpPrint(
}
}
/** memrchr(), explicitly defined, since was absent in MinGW of RosBE. */
/*
* Reverse memchr()
* Find the last occurrence of 'c' in the buffer 's' of size 'n'.
*/
void *
memrchr(const void *s, int c, size_t n)
{
const unsigned char *cp;
if (n != 0)
{
cp = (unsigned char *)s + n;
do
{
if (*(--cp) == (unsigned char)c)
return (void *)cp;
} while (--n != 0);
}
return NULL;
}
/*!\brief Calculate pointer position for N lines upper of current position.
*
* \param Buffer Characters buffer to operate on.
* \param BufLength Buffer size.
*
* \note Calculate pointer position for N lines upper of current displaying
* position within the given buffer.
*
* Used by KdbpPager().
* Now N lines count is hardcoded to KdbNumberOfRowsTerminal.
*/
PCHAR
CountOnePageUp(PCHAR Buffer, ULONG BufLength, PCHAR pCurPos)
{
PCHAR p;
// p0 is initial guess of Page Start
ULONG p0len = KdbNumberOfRowsTerminal * KdbNumberOfColsTerminal;
PCHAR p0 = pCurPos - p0len;
PCHAR prev_p = p0, p1;
ULONG j;
if (pCurPos < Buffer)
pCurPos = Buffer;
ASSERT(pCurPos <= Buffer + BufLength);
p = memrchr(p0, '\n', p0len);
if (NULL == p)
p = p0;
for (j = KdbNumberOfRowsTerminal; j--; )
{
p1 = memrchr(p0, '\n', p-p0);
prev_p = p;
p = p1;
if (NULL == p)
{
p = prev_p;
if (NULL == p)
p = p0;
break;
}
int linesCnt = (KdbNumberOfColsTerminal+prev_p-p-2) / KdbNumberOfColsTerminal;
if (linesCnt > 1)
j -= linesCnt-1;
}
ASSERT(p != 0);
++p;
return p;
}
/*!\brief Prints the given string with, page by page.
*
* \param Buffer Characters buffer to print.
* \param BufferLen Buffer size.
*
* \note Doesn't correctly handle \\t and terminal escape sequences when calculating the
* number of lines required to print a single line from the Buffer in the terminal.
* Maximum length of buffer is limited only by memory size.
*
* Note: BufLength should be greater then (KdbNumberOfRowsTerminal * KdbNumberOfColsTerminal).
*
*/
VOID
KdbpPager(
IN PCHAR Buffer,
IN ULONG BufLength)
{
static CHAR InBuffer[4096];
static BOOLEAN TerminalInitialized = FALSE;
static BOOLEAN TerminalConnected = FALSE;
static BOOLEAN TerminalReportsSize = TRUE;
CHAR c = '\0';
PCHAR p, p2;
ULONG Length;
ULONG i, j;
LONG RowsPrintedByTerminal;
ULONG ScanCode;
if( BufLength == 0)
return;
/* Check if the user has aborted output of the current command */
if (KdbOutputAborted)
return;
/* Initialize the terminal */
if (!TerminalInitialized)
{
DbgPrint("\x1b[7h"); /* Enable linewrap */
/* Query terminal type */
/*DbgPrint("\x1b[Z");*/
DbgPrint("\x05");
TerminalInitialized = TRUE;
Length = 0;
KeStallExecutionProcessor(100000);
for (;;)
{
c = KdbpTryGetCharSerial(5000);
if (c == -1)
break;
InBuffer[Length++] = c;
if (Length >= (sizeof (InBuffer) - 1))
break;
}
InBuffer[Length] = '\0';
if (Length > 0)
TerminalConnected = TRUE;
}
/* Get number of rows and columns in terminal */
if ((KdbNumberOfRowsTerminal < 0) || (KdbNumberOfColsTerminal < 0) ||
(KdbNumberOfRowsPrinted) == 0) /* Refresh terminal size each time when number of rows printed is 0 */
{
if ((KdbDebugState & KD_DEBUG_KDSERIAL) && TerminalConnected && TerminalReportsSize)
{
/* Try to query number of rows from terminal. A reply looks like "\x1b[8;24;80t" */
TerminalReportsSize = FALSE;
KeStallExecutionProcessor(100000);
DbgPrint("\x1b[18t");
c = KdbpTryGetCharSerial(5000);
if (c == KEY_ESC)
{
c = KdbpTryGetCharSerial(5000);
if (c == '[')
{
Length = 0;
for (;;)
{
c = KdbpTryGetCharSerial(5000);
if (c == -1)
break;
InBuffer[Length++] = c;
if (isalpha(c) || Length >= (sizeof (InBuffer) - 1))
break;
}
InBuffer[Length] = '\0';
if (InBuffer[0] == '8' && InBuffer[1] == ';')
{
for (i = 2; (i < Length) && (InBuffer[i] != ';'); i++);
if (Buffer[i] == ';')
{
Buffer[i++] = '\0';
/* Number of rows is now at Buffer + 2 and number of cols at Buffer + i */
KdbNumberOfRowsTerminal = strtoul(InBuffer + 2, NULL, 0);
KdbNumberOfColsTerminal = strtoul(InBuffer + i, NULL, 0);
TerminalReportsSize = TRUE;
}
}
}
/* Clear further characters */
while ((c = KdbpTryGetCharSerial(5000)) != -1);
}
}
if (KdbNumberOfRowsTerminal <= 0)
{
/* Set number of rows to the default. */
KdbNumberOfRowsTerminal = 24;
}
else if (KdbNumberOfColsTerminal <= 0)
{
/* Set number of cols to the default. */
KdbNumberOfColsTerminal = 80;
}
}
/* Get the string */
p = Buffer;
while (p[0] != '\0')
{
if ( p > Buffer+BufLength)
{
DbgPrint("Dmesg: error, p > Buffer+BufLength,d=%d", p - (Buffer+BufLength));
return;
}
i = strcspn(p, "\n");
// Are we out of buffer?
if (p + i > Buffer + BufLength)
// Leaving pager function:
break;
/* Calculate the number of lines which will be printed in the terminal
* when outputting the current line
*/
if (i > 0)
RowsPrintedByTerminal = (i + KdbNumberOfColsPrinted - 1) / KdbNumberOfColsTerminal;
else
RowsPrintedByTerminal = 0;
if (p[i] == '\n')
RowsPrintedByTerminal++;
/*DbgPrint("!%d!%d!%d!%d!", KdbNumberOfRowsPrinted, KdbNumberOfColsPrinted, i, RowsPrintedByTerminal);*/
/* Display a prompt if we printed one screen full of text */
if (KdbNumberOfRowsTerminal > 0 &&
(LONG)(KdbNumberOfRowsPrinted + RowsPrintedByTerminal) >= KdbNumberOfRowsTerminal)
{
if (KdbNumberOfColsPrinted > 0)
DbgPrint("\n");
DbgPrint("--- Press q to abort, e/End,h/Home,u/PgUp, other key/PgDn ---");
RowsPrintedByTerminal++;
if (KdbDebugState & KD_DEBUG_KDSERIAL)
c = KdbpGetCharSerial();
else
c = KdbpGetCharKeyboard(&ScanCode);
if (c == '\r')
{
/* Try to read '\n' which might follow '\r' - if \n is not received here
* it will be interpreted as "return" when the next command should be read.
*/
if (KdbDebugState & KD_DEBUG_KDSERIAL)
c = KdbpTryGetCharSerial(5);
else
c = KdbpTryGetCharKeyboard(&ScanCode, 5);
}
//DbgPrint("\n"); //Consize version: don't show pressed key
DbgPrint(" '%c'/scan=%04x\n", c, ScanCode); // Shows pressed key
if (c == 'q')
{
KdbOutputAborted = TRUE;
return;
}
if ( ScanCode == KEYSC_END || c=='e')
{
PCHAR pBufEnd = Buffer + BufLength;
p = CountOnePageUp(Buffer, BufLength, pBufEnd);
i = strcspn(p, "\n");
}
else if (ScanCode == KEYSC_PAGEUP || c=='u')
{
p = CountOnePageUp(Buffer, BufLength, p);
i = strcspn(p, "\n");
}
else if (ScanCode == KEYSC_HOME || c=='h')
{
p = Buffer;
i = strcspn(p, "\n");
}
else if (ScanCode == KEYSC_ARROWUP)
{
p = CountOnePageUp(Buffer, BufLength, p);
i = strcspn(p, "\n");
}
KdbNumberOfRowsPrinted = 0;
KdbNumberOfColsPrinted = 0;
}
/* Insert a NUL after the line and print only the current line. */
if (p[i] == '\n' && p[i + 1] != '\0')
{
c = p[i + 1];
p[i + 1] = '\0';
}
else
{
c = '\0';
}
/* Remove escape sequences from the line if there's no terminal connected */
if (!TerminalConnected)
{
while ((p2 = strrchr(p, '\x1b'))) /* Look for escape character */
{
if (p2[1] == '[')
{
j = 2;
while (!isalpha(p2[j++]));
strcpy(p2, p2 + j);
}
else
{
strcpy(p2, p2 + 1);
}
}
}
// The main printing of the current line:
DbgPrint(p);
// restore not null char with saved:
if (c != '\0')
p[i + 1] = c;
/* Set p to the start of the next line and
* remember the number of rows/cols printed
*/
p += i;
if (p[0] == '\n')
{
p++;
KdbNumberOfColsPrinted = 0;
}
else
{
ASSERT(p[0] == '\0');
KdbNumberOfColsPrinted += i;
}
KdbNumberOfRowsPrinted += RowsPrintedByTerminal;
}
}
/*!\brief Appends a command to the command history
*
* \param Command Pointer to the command to append to the history.