* Copyright (c) 2013-2020 Google, Inc. All rights reserved.
* Copyright (c) 2007-2008 VMware, Inc. All rights reserved.
* **********************************************************/
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of VMware, Inc. nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
#include "stdafx.h"
#include "DynamoRIO.h"
#include "DynamoRIODoc.h"
#include "DynamoRIOView.h"
#include "DynamoRIO.h"
#include "LoggingDlg.h"
#include <assert.h>
#ifdef _DEBUG
# define new DEBUG_NEW
# undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
IMPLEMENT_DYNCREATE(CDynamoRIOView, CFormView)
BEGIN_MESSAGE_MAP(CDynamoRIOView, CFormView)
#ifndef DRSTATS_DEMO
ON_BN_CLICKED(IDC_CHANGE_LOGGING, OnChangeLogging)
ON_BN_CLICKED(IDC_LOGDIR_EXPLORE, OnLogDirExplore)
#endif
ON_COMMAND(ID_EDIT_COPYSTATS, OnEditCopystats)
ON_WM_VSCROLL()
ON_COMMAND(ID_FILE_PRINT, CFormView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_DIRECT, CFormView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_PREVIEW, CFormView::OnFilePrintPreview)
ON_CBN_SELCHANGE(IDC_PROCESS_LIST, OnSelchangeList)
ON_CBN_DROPDOWN(IDC_PROCESS_LIST, OnDropdownList)
END_MESSAGE_MAP()
VOID CALLBACK
TimerProc(HWND ,
UINT ,
UINT_PTR idEvent,
DWORD
)
{
CDynamoRIOView *view = CDynamoRIOApp::GetActiveView();
if (view == NULL) {
KillTimer(NULL, idEvent);
return;
}
BOOL cont = view->Refresh();
if (!cont) {
KillTimer(NULL, idEvent);
}
}
void
CDynamoRIOView::ZeroStrings()
{
m_Exited = _T("");
m_ClientStats = _T("");
#ifndef DRSTATS_DEMO
m_LogLevel = _T("0");
m_LogMask = _T("0x0000");
m_LogDir = _T("");
#endif
}
CDynamoRIOView::CDynamoRIOView()
: CFormView(CDynamoRIOView::IDD)
{
ZeroStrings();
m_stats = NULL;
m_clientMap = NULL;
m_clientView = NULL;
m_clientStats = NULL;
m_list_pos = 0;
m_selected_pid = 0;
m_StatsViewLines = 0;
OSVERSIONINFOW version;
CDynamoRIOApp::GetWindowsVersion(&version);
if (version.dwPlatformId == VER_PLATFORM_WIN32_NT && version.dwMajorVersion == 4)
m_windows_NT = TRUE;
else
m_windows_NT = FALSE;
}
void
CDynamoRIOView::ClearData()
{
if (m_stats != NULL) {
free_dynamorio_stats(m_stats);
m_stats = NULL;
}
if (m_clientMap != NULL) {
assert(m_clientView != NULL);
UnmapViewOfFile(m_clientView);
CloseHandle(m_clientMap);
m_clientMap = m_clientView = m_clientStats = NULL;
}
}
CDynamoRIOView::~CDynamoRIOView()
{
ClearData();
}
void
CDynamoRIOView::DoDataExchange(CDataExchange *pDX)
{
CFormView::DoDataExchange(pDX);
DDX_Control(pDX, IDC_PROCESS_LIST, m_ProcessList);
DDX_Control(pDX, IDC_STATS, m_StatsCtl);
DDX_Control(pDX, IDC_STATS_SCROLLBAR, m_StatsSB);
DDX_Text(pDX, IDC_CLIENTSTATS, m_ClientStats);
DDX_Text(pDX, IDC_EXITED, m_Exited);
#ifndef DRSTATS_DEMO
DDX_Text(pDX, IDC_LOGLEVEL_VALUE, m_LogLevel);
DDX_Text(pDX, IDC_LOGMASK_VALUE, m_LogMask);
DDX_Text(pDX, IDC_LOGDIR, m_LogDir);
#endif
}
BOOL
CDynamoRIOView::PreCreateWindow(CREATESTRUCT &cs)
{
return CFormView::PreCreateWindow(cs);
}
void
CDynamoRIOView::OnInitialUpdate()
{
CFormView::OnInitialUpdate();
GetParentFrame()->RecalcLayout();
ResizeParentToFit();
OnDropdownList();
::SetTimer(NULL, NULL, 200, TimerProc);
}
BOOL
CDynamoRIOView::OnPreparePrinting(CPrintInfo *pInfo)
{
return DoPreparePrinting(pInfo);
}
void
CDynamoRIOView::OnBeginPrinting(CDC * , CPrintInfo * )
{
}
void
CDynamoRIOView::OnEndPrinting(CDC * , CPrintInfo * )
{
}
void
CDynamoRIOView::OnPrint(CDC * , CPrintInfo * )
{
}
#ifdef _DEBUG
void
CDynamoRIOView::AssertValid() const
{
CFormView::AssertValid();
}
void
CDynamoRIOView::Dump(CDumpContext &dc) const
{
CFormView::Dump(dc);
}
CDynamoRIODoc *
CDynamoRIOView::GetDocument()
{
ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CDynamoRIODoc)));
return (CDynamoRIODoc *)m_pDocument;
}
#endif
BOOL
pw_callback_under_dr(process_info_t *pi, void **param)
{
TCHAR *resstr;
TCHAR reschar;
int res;
TCHAR buf[MAXIMUM_PATH];
DWORD version;
BOOL under_dr;
CDynamoRIOView *view = (CDynamoRIOView *)param;
version = 0;
res = under_dynamorio_ex(pi->ProcessID, &version);
switch (res) {
case DLL_PROFILE:
resstr = _T("SC profile");
reschar = _T('P');
break;
case DLL_RELEASE:
resstr = _T("SC release");
reschar = _T('R');
break;
case DLL_DEBUG:
resstr = _T("SC debug");
reschar = _T('D');
break;
case DLL_CUSTOM:
resstr = _T("SC custom");
reschar = _T('C');
break;
case DLL_NONE:
resstr = _T("native");
reschar = _T('N');
break;
case DLL_UNKNOWN:
default: resstr = _T("<error>"); reschar = _T('?');
}
under_dr = !(res == DLL_NONE || res == DLL_UNKNOWN);
if (under_dr) {
_stprintf(buf, _T("%5d %c %") UNICODE_PRINTF, pi->ProcessID, reschar,
pi->ProcessName);
view->m_ProcessList.InsertString(view->m_list_pos, buf);
view->m_ProcessList.SetItemData(view->m_list_pos, pi->ProcessID);
view->m_list_pos++;
}
return TRUE;
}
void
CDynamoRIOView::EnumerateInstances()
{
ClearData();
m_ProcessList.ResetContent();
m_list_pos = 0;
process_walk(&pw_callback_under_dr, (void **)this);
if (m_ProcessList.GetCount() > 0) {
m_ProcessList.InsertString(0, _T("<select an instance>"));
} else {
m_ProcessList.InsertString(0, _T("<no instances to view>"));
}
m_ProcessList.SetItemData(0, 0);
m_ProcessList.SetCurSel(0);
CString pname;
m_ProcessList.GetLBText(m_ProcessList.GetCurSel(), pname);
GetDocument()->SetTitle(pname);
}
void
CDynamoRIOView::OnSelchangeList()
{
TCHAR buf[128];
ClearData();
m_selected_pid = (DWORD)m_ProcessList.GetItemData(m_ProcessList.GetCurSel());
HANDLE statsCountMap = OpenFileMapping(FILE_MAP_READ, FALSE,
m_windows_NT ? TEXT(CLIENT_SHMEM_KEY_NT)
: TEXT(CLIENT_SHMEM_KEY));
if (statsCountMap != NULL) {
PVOID statsCountView = MapViewOfFile(statsCountMap, FILE_MAP_READ, 0, 0, 0);
assert(statsCountView != NULL);
int statsCount = *((int *)statsCountView);
int num = 0;
while (num < statsCount + 20) {
_stprintf(buf, _T("%") ASCII_PRINTF _T(".%03d"),
m_windows_NT ? CLIENT_SHMEM_KEY_NT : CLIENT_SHMEM_KEY, num);
m_clientMap = OpenFileMapping(FILE_MAP_READ, FALSE, buf);
if (m_clientMap != NULL) {
m_clientView = MapViewOfFile(m_clientMap, FILE_MAP_READ, 0, 0, 0);
assert(m_clientView != NULL);
m_clientStats = (client_stats *)m_clientView;
if (m_clientStats->pid == m_selected_pid)
break;
UnmapViewOfFile(m_clientView);
CloseHandle(m_clientMap);
m_clientMap = m_clientView = m_clientStats = NULL;
}
num++;
}
UnmapViewOfFile(statsCountView);
CloseHandle(statsCountMap);
}
m_StatsSB.SetScrollPos(0);
Refresh();
}
void
CDynamoRIOView::OnDropdownList()
{
EnumerateInstances();
}
BOOL
CDynamoRIOView::SelectProcess(int pid)
{
EnumerateInstances();
TCHAR prefix[16];
_stprintf(prefix, _T("%5d"), pid);
int num = m_ProcessList.FindString(-1, prefix);
if (num == CB_ERR)
return FALSE;
m_ProcessList.SetCurSel(num);
OnSelchangeList();
return TRUE;
}
BOOL
CDynamoRIOView::UpdateProcessList()
{
EnumerateInstances();
CString first;
m_ProcessList.GetLBText(0, first);
if (first.Compare(_T("<select an instance>")) != 0)
return FALSE;
return TRUE;
}
#ifdef X64
* really shrink the names). Instead we add 3 more digits which gets us
* up to hundreds of billions, and we shrunk stat names by 3 to 47.
*/
# define CLIENT_STAT_PFMT _T("%13") _T(INT64_FORMAT) _T("u")
# define DR_STAT_PFMT _T("%10") _T(INT64_FORMAT) _T("u")
#else
# define CLIENT_STAT_PFMT _T("%13") _T(SZFC)
# define DR_STAT_PFMT _T("%10") _T(SZFC)
#endif
uint
CDynamoRIOView::PrintStat(TCHAR *c, uint i, BOOL filter)
{
if (filter) {
#if 0
if (strncmp(m_stats->stats[i].name, "Persisted caches",
strlen("Persisted caches")) == 0)
return 0;
#endif
}
return _stprintf(c, _T("%*.*") ASCII_PRINTF _T(" = ") DR_STAT_PFMT _T("\r\n"),
(int)BUFFER_SIZE_ELEMENTS(m_stats->stats[i].name),
(int)BUFFER_SIZE_ELEMENTS(m_stats->stats[i].name),
m_stats->stats[i].name, m_stats->stats[i].value);
}
uint
CDynamoRIOView::PrintClientStats(TCHAR *c, TCHAR *max)
{
if (m_clientStats == NULL)
return 0;
#define CLIENTSTAT_NAME_MAX_SHOW CLIENTSTAT_NAME_MAX_LEN
uint i;
TCHAR *start = c;
char(*names)[CLIENTSTAT_NAME_MAX_LEN] =
(char(*)[CLIENTSTAT_NAME_MAX_LEN])m_clientStats->data;
stats_int_t *vals = (stats_int_t *)((char *)m_clientStats->data +
m_clientStats->num_stats *
CLIENTSTAT_NAME_MAX_LEN * sizeof(char));
vals = (stats_int_t *)ALIGN_FORWARD(vals, sizeof(stats_int_t));
for (i = 0; i < m_clientStats->num_stats; i++) {
if (c >= max - CLIENTSTAT_NAME_MAX_SHOW * 2 - 3)
break;
c += _stprintf(c, _T("%*.*") ASCII_PRINTF _T(" = ") CLIENT_STAT_PFMT _T("\r\n"),
CLIENTSTAT_NAME_MAX_SHOW, CLIENTSTAT_NAME_MAX_SHOW, names[i],
vals[i]);
assert(c < max);
}
return (uint)(c - start);
}
BOOL
CDynamoRIOView::Refresh()
{
if (m_selected_pid <= 0) {
ZeroStrings();
return TRUE;
}
dr_statistics_t *new_stats = get_dynamorio_stats(m_selected_pid);
if (new_stats == NULL) {
if (m_stats != NULL) {
} else
return TRUE;
} else {
if (m_stats != NULL)
free_dynamorio_stats(m_stats);
m_stats = new_stats;
}
uint i;
#define MAX_VISIBLE_STATS 75
#define STATS_BUFSZ \
(MAX_VISIBLE_STATS * (sizeof(single_stat_t) * 2
etc.*/))
TCHAR buf[STATS_BUFSZ];
TCHAR *c = buf;
buf[0] = _T('\0');
if (m_StatsViewLines == 0) {
for (i = 0; i < m_stats->num_stats &&
i < MAX_VISIBLE_STATS ;
i++) {
if (c >= &buf[STATS_BUFSZ - STAT_NAME_MAX_LEN * 2])
break;
c += PrintStat(c, i, FALSE );
assert(c < &buf[STATS_BUFSZ - 1]);
}
m_StatsCtl.SetWindowText(buf);
UpdateData(FALSE);
RECT rect;
m_StatsCtl.GetRect(&rect);
CPoint pos(rect.right, rect.bottom);
m_StatsViewLines = HIWORD(m_StatsCtl.CharFromPos(pos));
assert(m_StatsViewLines > 0);
m_StatsSB.SetScrollRange(0, m_stats->num_stats - 1, TRUE );
c = buf;
SCROLLINFO info;
info.cbSize = sizeof(SCROLLINFO);
info.fMask = SIF_PAGE;
info.nPage = m_StatsViewLines;
m_StatsSB.SetScrollInfo(&info);
}
int scroll_pos = m_StatsSB.GetScrollPos();
DWORD shown = 0;
uint printed;
for (i = scroll_pos; i < m_stats->num_stats && shown < m_StatsViewLines; i++) {
if (c >= &buf[STATS_BUFSZ - STAT_NAME_MAX_LEN * 2])
break;
printed = PrintStat(c, i, TRUE );
c += printed;
assert(c < &buf[STATS_BUFSZ - 1]);
if (printed > 0)
shown++;
}
m_StatsCtl.SetWindowText(buf);
m_StatsSB.SetScrollRange(0, m_stats->num_stats - 1, TRUE );
if (new_stats == NULL)
m_Exited = _T(" Exited");
else
m_Exited = _T("Running");
#ifndef DRSTATS_DEMO
m_LogLevel.Format(_T("%d"), m_stats->loglevel);
m_LogMask.Format(_T("0x%05X"), m_stats->logmask);
m_LogDir.Format(_T("%") ASCII_PRINTF, m_stats->logdir);
#endif
if (m_clientStats != NULL) {
#define CLIENTSTATS_BUFSZ USHRT_MAX
TCHAR clientstats_buf[CLIENTSTATS_BUFSZ];
PrintClientStats(clientstats_buf, &clientstats_buf[CLIENTSTATS_BUFSZ - 1]);
m_ClientStats.Format(_T("%s"), clientstats_buf);
} else
m_ClientStats.Format(_T(""));
UpdateData(FALSE);
return TRUE;
}
#ifndef DRSTATS_DEMO
void
CDynamoRIOView::OnChangeLogging()
{
if (m_stats == NULL) {
MessageBox(_T("No instance is selected"), _T("Error"), MB_OK | MYMBFLAGS);
return;
}
if (m_stats->loglevel == 0) {
MessageBox(_T("If the application began with log level 0, its logging\n")
_T("cannot be changed.\n"),
_T("Notification"), MB_OK | MYMBFLAGS);
return;
}
CLoggingDlg dlg(m_stats->loglevel, m_stats->logmask);
int res = dlg.DoModal();
if (res == IDCANCEL)
return;
int level = dlg.GetLevel();
int mask = dlg.GetMask();
m_stats->loglevel = level;
m_stats->logmask = mask;
m_LogLevel.Format(_T("%d"), m_stats->loglevel);
m_LogMask.Format(_T("0x%04X"), m_stats->logmask);
UpdateData(FALSE);
}
void
CDynamoRIOView::OnLogDirExplore()
{
if (m_stats == NULL) {
MessageBox(_T("No instance is selected"), _T("Error"), MB_OK | MYMBFLAGS);
return;
}
UpdateData(TRUE);
if (m_LogDir.Find(_T("<none")) == 0) {
MessageBox(_T("There is no log dir because the loglevel was 0 when the ")
_T("application started"),
_T("Error"), MB_OK | MYMBFLAGS);
return;
}
HINSTANCE res =
ShellExecute(m_hWnd, _T("explore"), m_LogDir, NULL, NULL, SW_SHOWNORMAL);
if ((int)res <= 32) {
TCHAR msg[MAX_PATH * 2];
_stprintf(msg, _T("Error exploring %s"), m_LogDir);
MessageBox(msg, _T("Error"), MB_OK | MYMBFLAGS);
}
}
#endif
void
CDynamoRIOView::OnEditCopystats()
{
if (m_ProcessList.GetCurSel() == 0) {
MessageBox(_T("No instance selected"), _T("Error"), MB_OK | MYMBFLAGS);
return;
}
if (!OpenClipboard()) {
MessageBox(_T("Error opening clipboard"), _T("Error"), MB_OK | MYMBFLAGS);
return;
}
EmptyClipboard();
#define CLIPBOARD_BUFSZ USHRT_MAX
TCHAR buf[CLIPBOARD_BUFSZ];
TCHAR *pos = buf;
if (m_selected_pid > 0) {
if (m_stats != NULL) {
pos += _stprintf(pos, _T("Process id = " PIDFMT "\r\n"),
m_stats->process_id);
pos += _stprintf(
pos, _T("Process name = %") ASCII_PRINTF _T("\r\n"),
m_stats->process_name);
pos += _stprintf(pos, _T("Status = %s\r\n"),
m_Exited.GetBuffer(0));
#ifndef DRSTATS_DEMO
pos += _stprintf(pos, _T("Log mask = %s\r\n"),
m_LogMask.GetBuffer(0));
pos += _stprintf(pos, _T("Log level = %s\r\n"),
m_LogLevel.GetBuffer(0));
pos += _stprintf(pos, _T("Log file = %s\r\n"),
m_LogDir.GetBuffer(0));
#endif
pos += _stprintf(pos, _T("\r\nSTATS\r\n"));
uint i;
for (i = 0; i < m_stats->num_stats; i++) {
if (pos >= &buf[STATS_BUFSZ - STAT_NAME_MAX_LEN * 2])
break;
pos += PrintStat(pos, i, TRUE );
assert(pos < &buf[STATS_BUFSZ - 1]);
}
} else {
CString pname;
m_ProcessList.GetLBText(m_ProcessList.GetCurSel(), pname);
_stprintf(pos, _T("%s"), pname.GetString());
pos += _tcslen(pos);
}
}
if (m_clientStats != NULL) {
pos += PrintClientStats(pos, &buf[STATS_BUFSZ - 1]);
}
size_t len = _tcslen(buf);
HGLOBAL hglbCopy = GlobalAlloc(GMEM_DDESHARE, (len + 1) * sizeof(TCHAR));
if (hglbCopy == NULL) {
CloseClipboard();
return;
}
LPTSTR lptstrCopy = (LPTSTR)GlobalLock(hglbCopy);
memcpy(lptstrCopy, buf, (len + 1) * sizeof(TCHAR));
lptstrCopy[len] = (TCHAR)0;
GlobalUnlock(hglbCopy);
SetClipboardData(
#ifdef UNICODE
CF_UNICODETEXT,
#else
CF_TEXT,
#endif
hglbCopy);
CloseClipboard();
}
void
CDynamoRIOView::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar *pScrollBar)
{
int minpos;
int maxpos;
if (pScrollBar == NULL)
return;
pScrollBar->GetScrollRange(&minpos, &maxpos);
maxpos = pScrollBar->GetScrollLimit();
int curpos = pScrollBar->GetScrollPos();
switch (nSBCode) {
case SB_TOP:
curpos = minpos;
break;
case SB_BOTTOM:
curpos = maxpos;
break;
case SB_ENDSCROLL:
break;
case SB_LINEUP:
if (curpos > minpos)
curpos--;
break;
case SB_LINEDOWN:
if (curpos < maxpos)
curpos++;
break;
case SB_PAGEUP:
{
SCROLLINFO info;
pScrollBar->GetScrollInfo(&info, SIF_ALL);
if (curpos > minpos)
curpos = max(minpos, curpos - (int)info.nPage);
} break;
case SB_PAGEDOWN:
{
SCROLLINFO info;
pScrollBar->GetScrollInfo(&info, SIF_ALL);
if (curpos < maxpos)
curpos = min(maxpos, curpos + (int)info.nPage);
} break;
case SB_THUMBPOSITION:
curpos = nPos;
break;
case SB_THUMBTRACK:
curpos = nPos;
break;
}
pScrollBar->SetScrollPos(curpos);
CView::OnVScroll(nSBCode, nPos, pScrollBar);
Refresh();
}