windows-nt/Source/XPSP1/NT/sdktools/debuggers/windbg/callswin.cpp
2020-09-26 16:20:57 +08:00

581 lines
14 KiB
C++

/*++
Copyright (c) 1992-2001 Microsoft Corporation
Module Name:
callswin.cpp
Abstract:
This module contains the main line code for display of calls window.
Environment:
Win32, User Mode
--*/
#include "precomp.hxx"
#pragma hdrstop
#define MIN_FRAMES 10
#define MORE_LESS 10
#define CALLS_CONTEXT_ID_BASE 0x100
#define TBB_MORE 9
#define TBB_LESS 10
// The IDs of the buttons are the bit shift of the
// corresponding flag.
TBBUTTON g_CallsTbButtons[] =
{
TEXT_TB_BTN(0, "Args", BTNS_CHECK),
TEXT_TB_BTN(1, "Func info", BTNS_CHECK),
TEXT_TB_BTN(2, "Source", BTNS_CHECK),
TEXT_TB_BTN(3, "Addrs", BTNS_CHECK),
TEXT_TB_BTN(4, "Headings", BTNS_CHECK),
TEXT_TB_BTN(5, "Nonvolatile regs", BTNS_CHECK),
TEXT_TB_BTN(6, "Frame nums", BTNS_CHECK),
TEXT_TB_BTN(7, "Arg types", BTNS_CHECK),
SEP_TB_BTN(),
TEXT_TB_BTN(TBB_MORE, "More", 0),
TEXT_TB_BTN(TBB_LESS, "Less", 0),
SEP_TB_BTN(),
TEXT_TB_BTN(ID_SHOW_TOOLBAR, "Toolbar", 0),
};
#define NUM_CALLS_MENU_BUTTONS \
(sizeof(g_CallsTbButtons) / sizeof(g_CallsTbButtons[0]))
#define NUM_CALLS_TB_BUTTONS \
(NUM_CALLS_MENU_BUTTONS - 2)
HMENU CALLSWIN_DATA::s_ContextMenu;
//
//
//
CALLSWIN_DATA::CALLSWIN_DATA()
: SINGLE_CHILDWIN_DATA(1024)
{
m_enumType = CALLS_WINDOW;
m_Flags = 0;
m_Frames = 20;
m_FramesFound = 0;
m_TextOffset = 0;
}
void
CALLSWIN_DATA::Validate()
{
SINGLE_CHILDWIN_DATA::Validate();
Assert(CALLS_WINDOW == m_enumType);
}
HRESULT
CALLSWIN_DATA::ReadState(void)
{
HRESULT Status;
Empty();
//
// Record the raw frame data first.
//
// Preallocate space to record the raw frames.
if (AddData(sizeof(DEBUG_STACK_FRAME) * m_Frames) == NULL)
{
return E_OUTOFMEMORY;
}
// Allocate a separate buffer to hold the frames while
// calling OutputStackTrace on them. We can't just pass
// in the state buffer pointer as resizing of the state
// buffer may cause the data pointer to change.
PDEBUG_STACK_FRAME RawFrames = (PDEBUG_STACK_FRAME)malloc(m_DataUsed);
if (RawFrames == NULL)
{
return E_OUTOFMEMORY;
}
Status = g_pDbgControl->GetStackTrace(0, 0, 0, RawFrames, m_Frames,
&m_FramesFound);
if (Status != S_OK)
{
free(RawFrames);
return Status;
}
m_TextOffset = m_DataUsed;
g_OutStateBuf.SetBuffer(this);
if ((Status = g_OutStateBuf.Start(FALSE)) != S_OK)
{
return Status;
}
// If nonvolatile registers were requested we can't use just
// our saved frames as they require full context information.
Status = g_pOutCapControl->
OutputStackTrace(DEBUG_OUTCTL_THIS_CLIENT |
DEBUG_OUTCTL_OVERRIDE_MASK |
DEBUG_OUTCTL_NOT_LOGGED,
(m_Flags & DEBUG_STACK_NONVOLATILE_REGISTERS) ?
NULL : RawFrames, m_FramesFound, m_Flags);
if (Status == S_OK)
{
Status = g_OutStateBuf.End(TRUE);
if (Status == S_OK)
{
g_OutStateBuf.ReplaceChar('\n', 0);
}
}
else
{
g_OutStateBuf.End(TRUE);
}
// Now that the state buffer is stable put the raw frame
// data in.
memcpy(m_Data, RawFrames, m_TextOffset);
free(RawFrames);
return Status;
}
void
CALLSWIN_DATA::Copy()
{
LRESULT Line = SendMessage(m_hwndChild, LB_GETCURSEL, 0, 0);
Assert(Line != LB_ERR);
LRESULT Len = SendMessage(m_hwndChild, LB_GETTEXTLEN, Line, 0);
if (Len <= 0)
{
return;
}
Len++;
PSTR Text = (PSTR)malloc(Len);
if (!Text)
{
return;
}
SendMessage(m_hwndChild, LB_GETTEXT, Line, (LPARAM)Text);
Text[Len - 1] = 0;
CopyToClipboard(Text);
free (Text);
return;
}
HMENU
CALLSWIN_DATA::GetContextMenu(void)
{
ULONG i;
//
// We only keep one menu around for all call windows
// so apply the menu check state for this particular
// window.
// In reality there's only one calls window anyway,
// but this is a good example of how to handle
// multi-instance windows.
//
for (i = 0; i < NUM_CALLS_TB_BUTTONS; i++)
{
CheckMenuItem(s_ContextMenu, i + CALLS_CONTEXT_ID_BASE,
MF_BYCOMMAND | ((m_Flags & (1 << i)) ? MF_CHECKED : 0));
}
CheckMenuItem(s_ContextMenu, ID_SHOW_TOOLBAR + CALLS_CONTEXT_ID_BASE,
MF_BYCOMMAND | (m_ShowToolbar ? MF_CHECKED : 0));
return s_ContextMenu;
}
void
CALLSWIN_DATA::OnContextMenuSelection(UINT Item)
{
Item -= CALLS_CONTEXT_ID_BASE;
switch(Item)
{
case TBB_MORE:
m_Frames += MORE_LESS;
break;
case TBB_LESS:
if (m_Frames >= MIN_FRAMES + MORE_LESS)
{
m_Frames -= MORE_LESS;
}
break;
case ID_SHOW_TOOLBAR:
SetShowToolbar(!m_ShowToolbar);
break;
default:
m_Flags ^= 1 << Item;
SyncUiWithFlags(1 << Item);
break;
}
if (g_Workspace != NULL)
{
g_Workspace->AddDirty(WSPF_DIRTY_WINDOWS);
}
UiRequestRead();
}
BOOL
CALLSWIN_DATA::CodeExprAtCaret(PSTR Expr, PULONG64 Offset)
{
BOOL Succ = FALSE;
LRESULT Line = SendMessage(m_hwndChild, LB_GETCURSEL, 0, 0);
// If the column headers are on ignore that line.
if (m_Flags & DEBUG_STACK_COLUMN_NAMES)
{
if (Line == 0)
{
return Succ;
}
else
{
Line--;
}
}
if (Line >= 0 && (ULONG)Line < m_FramesFound)
{
if (UiLockForRead() == S_OK)
{
PDEBUG_STACK_FRAME RawFrames = (PDEBUG_STACK_FRAME)m_Data;
if (Expr != NULL)
{
sprintf(Expr, "0x%I64x", RawFrames[Line].InstructionOffset);
}
if (Offset != NULL)
{
*Offset = RawFrames[Line].InstructionOffset;
}
Succ = TRUE;
UnlockStateBuffer(this);
}
}
return Succ;
}
HRESULT
CALLSWIN_DATA::
StackFrameAtCaret(PDEBUG_STACK_FRAME pFrame)
{
LRESULT Line = SendMessage(m_hwndChild, LB_GETCURSEL, 0, 0);
// If the column headers are on ignore that line.
if (m_Flags & DEBUG_STACK_COLUMN_NAMES)
{
if (Line == 0)
{
return E_INVALIDARG;
}
else
{
Line--;
}
}
if (Line >= 0 && (ULONG)Line < m_FramesFound && pFrame)
{
if (UiLockForRead() == S_OK)
{
PDEBUG_STACK_FRAME RawFrames = (PDEBUG_STACK_FRAME)m_Data;
*pFrame = RawFrames[Line];
UnlockStateBuffer(this);
}
}
return S_OK;
}
BOOL
CALLSWIN_DATA::OnCreate(void)
{
if (s_ContextMenu == NULL)
{
s_ContextMenu = CreateContextMenuFromToolbarButtons
(NUM_CALLS_MENU_BUTTONS, g_CallsTbButtons, CALLS_CONTEXT_ID_BASE);
if (s_ContextMenu == NULL)
{
return FALSE;
}
}
m_Toolbar = CreateWindowEx(0, TOOLBARCLASSNAME, NULL,
WS_CHILD | WS_VISIBLE |
TBSTYLE_WRAPABLE | TBSTYLE_LIST | CCS_TOP,
0, 0, m_Size.cx, 0, m_Win, (HMENU)ID_TOOLBAR,
g_hInst, NULL);
if (m_Toolbar == NULL)
{
return FALSE;
}
SendMessage(m_Toolbar, TB_BUTTONSTRUCTSIZE, sizeof(TBBUTTON), 0);
SendMessage(m_Toolbar, TB_ADDBUTTONS, NUM_CALLS_TB_BUTTONS,
(LPARAM)&g_CallsTbButtons);
SendMessage(m_Toolbar, TB_AUTOSIZE, 0, 0);
RECT Rect;
GetClientRect(m_Toolbar, &Rect);
m_ToolbarHeight = Rect.bottom - Rect.top;
m_ShowToolbar = TRUE;
m_hwndChild = CreateWindowEx(
WS_EX_CLIENTEDGE, // Extended style
_T("LISTBOX"), // class name
NULL, // title
WS_CHILD | WS_VISIBLE
| WS_MAXIMIZE
| WS_HSCROLL | WS_VSCROLL
| LBS_NOTIFY | LBS_WANTKEYBOARDINPUT
| LBS_NOINTEGRALHEIGHT, // style
0, // x
m_ToolbarHeight, // y
m_Size.cx, // width
m_Size.cy - m_ToolbarHeight, // height
m_Win, // parent
0, // control id
g_hInst, // hInstance
NULL // user defined data
);
if (m_hwndChild != NULL)
{
SetFont( FONT_FIXED );
return TRUE;
}
else
{
return FALSE;
}
}
LRESULT
CALLSWIN_DATA::OnCommand(
WPARAM wParam,
LPARAM lParam
)
{
if (HIWORD(wParam) == LBN_DBLCLK)
{
ULONG64 Offset;
if (CodeExprAtCaret(NULL, &Offset) &&
Offset != DEBUG_INVALID_OFFSET)
{
UIC_DISPLAY_CODE_DATA* DispCode =
StartStructCommand(UIC_DISPLAY_CODE);
if (DispCode != NULL)
{
DispCode->Offset = Offset;
FinishCommand();
}
}
DEBUG_STACK_FRAME StkFrame;
if (StackFrameAtCaret(&StkFrame) == S_OK)
{
UIC_SET_SCOPE_DATA* SetScope =
StartStructCommand(UIC_SET_SCOPE);
if (SetScope != NULL)
{
SetScope->StackFrame = StkFrame;
FinishCommand();
}
}
return 0;
}
if ((HWND)lParam == m_Toolbar)
{
OnContextMenuSelection(LOWORD(wParam) + CALLS_CONTEXT_ID_BASE);
return 0;
}
return 0;
}
LRESULT
CALLSWIN_DATA::OnVKeyToItem(
WPARAM wParam,
LPARAM lParam
)
{
if (LOWORD(wParam) == VK_RETURN)
{
ULONG64 Offset;
if (CodeExprAtCaret(NULL, &Offset) &&
Offset != DEBUG_INVALID_OFFSET)
{
UIC_DISPLAY_CODE_DATA* DispCode =
StartStructCommand(UIC_DISPLAY_CODE);
if (DispCode != NULL)
{
DispCode->Offset = Offset;
FinishCommand();
}
}
DEBUG_STACK_FRAME StkFrame;
if (StackFrameAtCaret(&StkFrame) == S_OK)
{
UIC_SET_SCOPE_DATA* SetScope =
StartStructCommand(UIC_SET_SCOPE);
if (SetScope != NULL)
{
SetScope->StackFrame = StkFrame;
FinishCommand();
}
}
}
else if (_T('G') == LOWORD(wParam))
{
ULONG64 Offset;
if (CodeExprAtCaret(NULL, &Offset) &&
Offset != DEBUG_INVALID_OFFSET)
{
PrintStringCommand(UIC_EXECUTE, "g 0x%I64x", Offset);
}
}
else if (_T('R') == LOWORD(wParam))
{
OnUpdate(UPDATE_BUFFER);
}
else
{
// Default behavior.
return -1;
}
// Keystroke processed.
return -2;
}
void
CALLSWIN_DATA::OnUpdate(
UpdateType Type
)
{
if (Type != UPDATE_BUFFER)
{
return;
}
LRESULT lbItem;
int nFrameCount;
HRESULT Status;
lbItem = SendMessage( m_hwndChild, LB_GETCURSEL, 0, 0 );
SendMessage( m_hwndChild, WM_SETREDRAW, FALSE, 0L );
SendMessage( m_hwndChild, LB_RESETCONTENT, 0, 0 );
Status = UiLockForRead();
if (Status == S_OK)
{
PSTR Buf = (PSTR)m_Data + m_TextOffset;
// Ignore final terminator.
PSTR End = (PSTR)m_Data + m_DataUsed - 1;
ULONG Width = 0;
nFrameCount = 0;
while (Buf < End)
{
ULONG Len = strlen(Buf) + 1;
ULONG StrWidth = (Len - 1) * m_Font->Metrics.tmAveCharWidth;
SendMessage(m_hwndChild, LB_ADDSTRING, 0, (LPARAM)Buf);
Buf += Len;
if (StrWidth > Width)
{
Width = StrWidth;
}
nFrameCount++;
}
SendMessage(m_hwndChild, LB_SETHORIZONTALEXTENT, Width, 0);
UnlockStateBuffer(this);
}
else
{
SendLockStatusMessage(m_hwndChild, LB_ADDSTRING, Status);
nFrameCount = 1;
}
SendMessage( m_hwndChild, LB_SETCURSEL,
(lbItem > nFrameCount) ? 0 : lbItem, 0 );
SendMessage( m_hwndChild, WM_SETREDRAW, TRUE, 0L );
}
ULONG
CALLSWIN_DATA::GetWorkspaceSize(void)
{
return SINGLE_CHILDWIN_DATA::GetWorkspaceSize() + 2 * sizeof(ULONG);
}
PUCHAR
CALLSWIN_DATA::SetWorkspace(PUCHAR Data)
{
Data = SINGLE_CHILDWIN_DATA::SetWorkspace(Data);
*(PULONG)Data = m_Flags;
Data += sizeof(ULONG);
*(PULONG)Data = m_Frames;
Data += sizeof(ULONG);
return Data;
}
PUCHAR
CALLSWIN_DATA::ApplyWorkspace1(PUCHAR Data, PUCHAR End)
{
Data = SINGLE_CHILDWIN_DATA::ApplyWorkspace1(Data, End);
if (End - Data >= 2 * sizeof(ULONG))
{
m_Flags = *(PULONG)Data;
Data += sizeof(ULONG);
m_Frames = *(PULONG)Data;
Data += sizeof(ULONG);
SyncUiWithFlags(0xffffffff);
UiRequestRead();
}
return Data;
}
void
CALLSWIN_DATA::SyncUiWithFlags(ULONG Changed)
{
ULONG i;
//
// Set toolbar button state from flags.
//
for (i = 0; i < NUM_CALLS_TB_BUTTONS; i++)
{
if (Changed & (1 << i))
{
SendMessage(m_Toolbar, TB_SETSTATE, g_CallsTbButtons[i].idCommand,
TBSTATE_ENABLED |
((m_Flags & (1 << i)) ? TBSTATE_CHECKED : 0));
}
}
}