581 lines
14 KiB
C++
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));
|
|
}
|
|
}
|
|
}
|