You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3806 lines
110 KiB
3806 lines
110 KiB
//////////////////////////////////////////////////////////////////////////////
|
|
// Copyright (c) 2002 Microsoft Corporation. All rights reserved.
|
|
// Copyright (c) 2002 OSR Open Systems Resources, Inc.
|
|
//
|
|
// DisplayDlg.cpp : implementation file
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "stdafx.h"
|
|
#include <tchar.h>
|
|
#include <wmistr.h>
|
|
#include <initguid.h>
|
|
#include <ks.h>
|
|
extern "C" {
|
|
#include <evntrace.h>
|
|
}
|
|
#include <traceprt.h>
|
|
#include "TraceView.h"
|
|
#include "logsession.h"
|
|
#include "DockDialogBar.h"
|
|
#include "DisplayDlg.h"
|
|
#include "utils.h"
|
|
|
|
// GLOBALS
|
|
|
|
//
|
|
// global event callbacks
|
|
// We need these because there is no context we can
|
|
// pass into the trace event callback routines, thus
|
|
// we must have a unique callback for each instance
|
|
// of this class. We can't use a global hash like
|
|
// we are doing for the buffer callbacks, as we don't
|
|
// get any information in the EVENT_TRACE struct that
|
|
// allows us to lookup a value. This struct is filled
|
|
// in by the call to FormatTraceEvents, and we are
|
|
// supposed to treat this struct as opaque. We must
|
|
// have a unique EventListHead for the call to
|
|
// FormatTraceEvents, so we need these separate callbacks.
|
|
// Yuck!
|
|
//
|
|
PEVENT_CALLBACK g_pDumpEvent[MAX_LOG_SESSIONS];
|
|
|
|
//
|
|
// Global hash table used in the event callbacks
|
|
// to get the proper CDisplayDlg instance.
|
|
//
|
|
CMapWordToPtr g_displayIDToDisplayDlgHash(16);
|
|
|
|
//
|
|
// Global hash table used in the buffer callback
|
|
// to get the proper CDisplayDlg instance.
|
|
//
|
|
CMapStringToPtr g_loggerNameToDisplayDlgHash(16);
|
|
|
|
//
|
|
// Yet another global hash table, this one is used
|
|
// to prevent multiple sessions from starting using
|
|
// the same format GUIDs. Format info as it turns out
|
|
// is stored in a global hash table in traceprt.dll.
|
|
// If multiple sessions in the same process attempt
|
|
// to use the same format GUID, only one entry is
|
|
// entered into the traceprt hash as expected. But,
|
|
// the hash entries are removed when a session ends.
|
|
// So, if multiple sessions in the same process were
|
|
// use the same format GUID, as soon as one of those
|
|
// sessions ended, the other sessions will lose their
|
|
// hash entries.
|
|
//
|
|
CMapStringToPtr g_formatInfoHash(16);
|
|
|
|
//
|
|
// Synchronization event for GetTraceGuids, FormatTraceEvent,
|
|
// and CleanupEventListHead in traceprt.dll. These are not
|
|
// inherently thread safe.
|
|
//
|
|
HANDLE g_hGetTraceEvent = CreateEvent(NULL, FALSE, TRUE, NULL);
|
|
|
|
//
|
|
// Buffer callback prototype
|
|
//
|
|
ULONG WINAPI BufferCallback(PEVENT_TRACE_LOGFILE Buffer);
|
|
|
|
//
|
|
// Memory Tracking
|
|
//
|
|
BOOLEAN RecoveringMemory = FALSE;
|
|
LONG MaxTraceEntries = 50000;
|
|
|
|
// CDisplayDlg dialog
|
|
|
|
IMPLEMENT_DYNAMIC(CDisplayDlg, CDialog)
|
|
CDisplayDlg::CDisplayDlg(CWnd* pParent, LONG DisplayID)
|
|
: CDialog(CDisplayDlg::IDD, pParent)
|
|
{
|
|
ASSERT(DisplayID < MAX_LOG_SESSIONS);
|
|
|
|
//
|
|
// Get a handle to the main frame
|
|
//
|
|
m_hMainWnd = pParent->GetSafeHwnd();
|
|
|
|
//
|
|
// Save the ID for this DisplayDlg
|
|
//
|
|
m_displayID = DisplayID;
|
|
|
|
//
|
|
// Setup the default flags and names for the listing and summary files
|
|
//
|
|
m_bWriteListingFile = FALSE;
|
|
m_bWriteSummaryFile = FALSE;
|
|
m_listingFileName.Format(_T("Output%d.out"), m_displayID);
|
|
m_summaryFileName.Format(_T("Summary%d.sum"), m_displayID);
|
|
|
|
//
|
|
// initialize the column names
|
|
//
|
|
m_columnName.Add("Name");
|
|
m_columnName.Add("File Name");
|
|
m_columnName.Add("Line#");
|
|
m_columnName.Add("Func Name");
|
|
m_columnName.Add("Process ID");
|
|
m_columnName.Add("Thread ID");
|
|
m_columnName.Add("CPU#");
|
|
m_columnName.Add("Sequence#");
|
|
m_columnName.Add("System Time");
|
|
m_columnName.Add("Kernel Time");
|
|
m_columnName.Add("User Time");
|
|
m_columnName.Add("Indent");
|
|
m_columnName.Add("Flags Name");
|
|
m_columnName.Add("Level Name");
|
|
m_columnName.Add("Component Name");
|
|
m_columnName.Add("SubComponent Name");
|
|
m_columnName.Add("Message");
|
|
|
|
//
|
|
// Set the initial column widths
|
|
//
|
|
for(LONG ii = 0; ii < MaxTraceSessionOptions; ii++) {
|
|
m_columnWidth[ii] = 100;
|
|
}
|
|
|
|
//
|
|
// Set the default display flags
|
|
//
|
|
m_displayFlags = TRACEOUTPUT_DISPLAY_PROVIDERNAME |
|
|
TRACEOUTPUT_DISPLAY_MESSAGE |
|
|
TRACEOUTPUT_DISPLAY_FILENAME |
|
|
TRACEOUTPUT_DISPLAY_LINENUMBER |
|
|
TRACEOUTPUT_DISPLAY_FUNCTIONNAME |
|
|
TRACEOUTPUT_DISPLAY_PROCESSID |
|
|
TRACEOUTPUT_DISPLAY_THREADID |
|
|
TRACEOUTPUT_DISPLAY_CPUNUMBER |
|
|
TRACEOUTPUT_DISPLAY_SEQNUMBER |
|
|
TRACEOUTPUT_DISPLAY_SYSTEMTIME;
|
|
|
|
//
|
|
// setup the lookup tables for the column positions
|
|
//
|
|
for(LONG ii = 0; ii < MaxTraceSessionOptions; ii++) {
|
|
//
|
|
// This lookup table allows a retrieval of the current
|
|
// position of a given column like m_retrievalArray[Flags]
|
|
// will return the correct column value for the Flags
|
|
// column
|
|
//
|
|
m_retrievalArray[ii] = ii;
|
|
|
|
//
|
|
// This lookup table allows correct placement of
|
|
// a column being added. So, if the Flags column
|
|
// needed to be inserted, then m_insertionArray[Flags]
|
|
// would give the correct insertion column value
|
|
//
|
|
m_insertionArray[ii] = ii;
|
|
}
|
|
|
|
//
|
|
// initialize the dock dialog bar pointer
|
|
//
|
|
m_pDockDialogBar = NULL;
|
|
|
|
//
|
|
// Show latest event trace entry
|
|
//
|
|
m_bShowLatest = TRUE;
|
|
|
|
//
|
|
// Setup the sort related compare function table
|
|
// There are two functions for each column, an
|
|
// ascending compare and a descending compare.
|
|
//
|
|
m_traceSortRoutine[ProviderName] = CompareOnName;
|
|
m_traceSortRoutine[Message] = CompareOnMessage;
|
|
m_traceSortRoutine[FileName] = CompareOnFileName;
|
|
m_traceSortRoutine[LineNumber] = CompareOnLineNumber;
|
|
m_traceSortRoutine[FunctionName] = CompareOnFunctionName;
|
|
m_traceSortRoutine[ProcessId] = CompareOnProcessId;
|
|
m_traceSortRoutine[ThreadId] = CompareOnThreadId;
|
|
m_traceSortRoutine[CpuNumber] = CompareOnCpuNumber;
|
|
m_traceSortRoutine[SeqNumber] = CompareOnSeqNumber;
|
|
m_traceSortRoutine[SystemTime] = CompareOnSystemTime;
|
|
m_traceSortRoutine[KernelTime] = CompareOnKernelTime;
|
|
m_traceSortRoutine[UserTime] = CompareOnUserTime;
|
|
m_traceSortRoutine[Indent] = CompareOnIndent;
|
|
m_traceSortRoutine[FlagsName] = CompareOnFlagsName;
|
|
m_traceSortRoutine[LevelName] = CompareOnLevelName;
|
|
m_traceSortRoutine[ComponentName] = CompareOnComponentName;
|
|
m_traceSortRoutine[SubComponentName]= CompareOnSubComponentName;
|
|
m_traceReverseSortRoutine[ProviderName] = ReverseCompareOnName;
|
|
m_traceReverseSortRoutine[Message] = ReverseCompareOnMessage;
|
|
m_traceReverseSortRoutine[FileName] = ReverseCompareOnFileName;
|
|
m_traceReverseSortRoutine[LineNumber] = ReverseCompareOnLineNumber;
|
|
m_traceReverseSortRoutine[FunctionName] = ReverseCompareOnFunctionName;
|
|
m_traceReverseSortRoutine[ProcessId] = ReverseCompareOnProcessId;
|
|
m_traceReverseSortRoutine[ThreadId] = ReverseCompareOnThreadId;
|
|
m_traceReverseSortRoutine[CpuNumber] = ReverseCompareOnCpuNumber;
|
|
m_traceReverseSortRoutine[SeqNumber] = ReverseCompareOnSeqNumber;
|
|
m_traceReverseSortRoutine[SystemTime] = ReverseCompareOnSystemTime;
|
|
m_traceReverseSortRoutine[KernelTime] = ReverseCompareOnKernelTime;
|
|
m_traceReverseSortRoutine[UserTime] = ReverseCompareOnUserTime;
|
|
m_traceReverseSortRoutine[Indent] = ReverseCompareOnIndent;
|
|
m_traceReverseSortRoutine[FlagsName] = ReverseCompareOnFlagsName;
|
|
m_traceReverseSortRoutine[LevelName] = ReverseCompareOnLevelName;
|
|
m_traceReverseSortRoutine[ComponentName] = ReverseCompareOnComponentName;
|
|
m_traceReverseSortRoutine[SubComponentName]= ReverseCompareOnSubComponentName;
|
|
|
|
//
|
|
// Zero our column array buffer
|
|
//
|
|
memset(m_columnArray, 0, sizeof(int) * MaxTraceSessionOptions);
|
|
|
|
//
|
|
// Setup the array of event handler pointers
|
|
//
|
|
g_pDumpEvent[0] = DumpEvent0;
|
|
g_pDumpEvent[1] = DumpEvent1;
|
|
g_pDumpEvent[2] = DumpEvent2;
|
|
g_pDumpEvent[3] = DumpEvent3;
|
|
g_pDumpEvent[4] = DumpEvent4;
|
|
g_pDumpEvent[5] = DumpEvent5;
|
|
g_pDumpEvent[6] = DumpEvent6;
|
|
g_pDumpEvent[7] = DumpEvent7;
|
|
g_pDumpEvent[8] = DumpEvent8;
|
|
g_pDumpEvent[9] = DumpEvent9;
|
|
g_pDumpEvent[10] = DumpEvent10;
|
|
g_pDumpEvent[11] = DumpEvent11;
|
|
g_pDumpEvent[12] = DumpEvent12;
|
|
g_pDumpEvent[13] = DumpEvent13;
|
|
g_pDumpEvent[14] = DumpEvent14;
|
|
g_pDumpEvent[15] = DumpEvent15;
|
|
g_pDumpEvent[16] = DumpEvent16;
|
|
g_pDumpEvent[17] = DumpEvent17;
|
|
g_pDumpEvent[18] = DumpEvent18;
|
|
g_pDumpEvent[19] = DumpEvent19;
|
|
g_pDumpEvent[20] = DumpEvent20;
|
|
g_pDumpEvent[21] = DumpEvent21;
|
|
g_pDumpEvent[22] = DumpEvent22;
|
|
g_pDumpEvent[23] = DumpEvent23;
|
|
g_pDumpEvent[24] = DumpEvent24;
|
|
g_pDumpEvent[25] = DumpEvent25;
|
|
g_pDumpEvent[26] = DumpEvent26;
|
|
g_pDumpEvent[27] = DumpEvent27;
|
|
g_pDumpEvent[28] = DumpEvent28;
|
|
g_pDumpEvent[29] = DumpEvent29;
|
|
g_pDumpEvent[30] = DumpEvent30;
|
|
g_pDumpEvent[31] = DumpEvent31;
|
|
g_pDumpEvent[32] = DumpEvent32;
|
|
g_pDumpEvent[33] = DumpEvent33;
|
|
g_pDumpEvent[34] = DumpEvent34;
|
|
g_pDumpEvent[35] = DumpEvent35;
|
|
g_pDumpEvent[36] = DumpEvent36;
|
|
g_pDumpEvent[37] = DumpEvent37;
|
|
g_pDumpEvent[38] = DumpEvent38;
|
|
g_pDumpEvent[39] = DumpEvent39;
|
|
g_pDumpEvent[40] = DumpEvent40;
|
|
g_pDumpEvent[41] = DumpEvent41;
|
|
g_pDumpEvent[42] = DumpEvent42;
|
|
g_pDumpEvent[43] = DumpEvent43;
|
|
g_pDumpEvent[44] = DumpEvent44;
|
|
g_pDumpEvent[45] = DumpEvent45;
|
|
g_pDumpEvent[46] = DumpEvent46;
|
|
g_pDumpEvent[47] = DumpEvent47;
|
|
g_pDumpEvent[48] = DumpEvent48;
|
|
g_pDumpEvent[49] = DumpEvent49;
|
|
g_pDumpEvent[50] = DumpEvent50;
|
|
g_pDumpEvent[51] = DumpEvent51;
|
|
g_pDumpEvent[52] = DumpEvent52;
|
|
g_pDumpEvent[53] = DumpEvent53;
|
|
g_pDumpEvent[54] = DumpEvent54;
|
|
g_pDumpEvent[55] = DumpEvent55;
|
|
g_pDumpEvent[56] = DumpEvent56;
|
|
g_pDumpEvent[57] = DumpEvent57;
|
|
g_pDumpEvent[58] = DumpEvent58;
|
|
g_pDumpEvent[59] = DumpEvent59;
|
|
g_pDumpEvent[60] = DumpEvent60;
|
|
g_pDumpEvent[61] = DumpEvent61;
|
|
g_pDumpEvent[62] = DumpEvent62;
|
|
g_pDumpEvent[63] = DumpEvent63;
|
|
|
|
//
|
|
// Put this session in the global hash table by display ID
|
|
//
|
|
g_displayIDToDisplayDlgHash.SetAt((WORD)m_displayID, this);
|
|
|
|
//
|
|
// Set our event callback
|
|
//
|
|
m_pEventCallback = g_pDumpEvent[m_displayID];
|
|
|
|
//
|
|
// Set our event list head to NULL
|
|
//
|
|
m_pEventListHead = NULL;
|
|
|
|
//
|
|
// initialize the group flags
|
|
//
|
|
m_bGroupActive = FALSE;
|
|
m_bGroupInActive = FALSE;
|
|
|
|
//
|
|
// Initialize the end trace event
|
|
//
|
|
m_hEndTraceEvent = NULL;
|
|
|
|
//
|
|
// Initialize the last sorted column value to be
|
|
// an invalid column so as to not sort, we track
|
|
// the column so sorts can be reversed.
|
|
//
|
|
m_lastSorted = MaxTraceSessionOptions;
|
|
|
|
//
|
|
// Sort order for trace array, TRUE == descending, FALSE == ascending
|
|
//
|
|
m_bOrder = TRUE;
|
|
|
|
//
|
|
// Initialize handles
|
|
//
|
|
m_hRealTimeOutputThread = INVALID_HANDLE_VALUE;
|
|
m_hRealTimeProcessingDoneEvent = NULL;
|
|
m_hRealTimeProcessingStartEvent = NULL;
|
|
m_hRealTimeTerminationEvent = NULL;
|
|
m_hTraceEventMutex = NULL;
|
|
m_hSessionArrayMutex = NULL;
|
|
}
|
|
|
|
CDisplayDlg::~CDisplayDlg()
|
|
{
|
|
ULONG exitCode = STILL_ACTIVE;
|
|
CTraceViewApp *pMainWnd;
|
|
|
|
//
|
|
// Clear the trace log
|
|
//
|
|
|
|
//
|
|
// Get a pointer to the app
|
|
//
|
|
pMainWnd = (CTraceViewApp *)AfxGetApp();
|
|
|
|
//
|
|
// Get our trace event array protection
|
|
//
|
|
WaitForSingleObject(m_hTraceEventMutex,INFINITE);
|
|
|
|
//
|
|
// Send all elements to be freed
|
|
//
|
|
int elCount;
|
|
|
|
elCount = (int)m_traceArray.GetSize();
|
|
|
|
while (elCount > 0 ) {
|
|
|
|
delete m_traceArray.GetAt( --elCount );
|
|
}
|
|
|
|
//
|
|
// Remove the elements from the array
|
|
//
|
|
m_traceArray.RemoveAll();
|
|
|
|
//
|
|
// Release our trace event array protection
|
|
//
|
|
ReleaseMutex(m_hTraceEventMutex);
|
|
|
|
//
|
|
// empty the list control
|
|
//
|
|
m_displayCtrl.DeleteAllItems();
|
|
|
|
//
|
|
// Reset the flag
|
|
//
|
|
exitCode = STILL_ACTIVE;
|
|
|
|
//
|
|
// Terminate the real time thread
|
|
//
|
|
SetEvent(m_hRealTimeTerminationEvent);
|
|
|
|
//
|
|
// Wait on the real time thread to exit
|
|
//
|
|
for(LONG ii = 0; (ii < 200) && (exitCode == STILL_ACTIVE); ii++) {
|
|
if(0 == GetExitCodeThread(m_hRealTimeOutputThread, &exitCode)) {
|
|
break;
|
|
}
|
|
Sleep(100);
|
|
|
|
//
|
|
// set the event again just in case
|
|
//
|
|
SetEvent(m_hRealTimeTerminationEvent);
|
|
}
|
|
|
|
//
|
|
// We waited 20 seconds and the thread didn't die, so kill it
|
|
//
|
|
if(exitCode == STILL_ACTIVE) {
|
|
TerminateThread(m_hRealTimeOutputThread, 0);
|
|
}
|
|
|
|
m_hRealTimeOutputThread = INVALID_HANDLE_VALUE;
|
|
|
|
//
|
|
// Pull this DisplayDlg out of the global hash table
|
|
//
|
|
g_displayIDToDisplayDlgHash.RemoveKey((WORD)m_displayID);
|
|
|
|
//
|
|
// The dialog bar should be deleted after this object,
|
|
// but this pointer better be NULL when we are deleted
|
|
//
|
|
ASSERT(NULL == m_pDockDialogBar);
|
|
|
|
//
|
|
// Close any open handles
|
|
//
|
|
if(m_hRealTimeProcessingDoneEvent != NULL) {
|
|
CloseHandle(m_hRealTimeProcessingDoneEvent);
|
|
}
|
|
|
|
if(m_hRealTimeProcessingStartEvent != NULL) {
|
|
CloseHandle(m_hRealTimeProcessingStartEvent);
|
|
}
|
|
|
|
if(m_hRealTimeTerminationEvent != NULL) {
|
|
CloseHandle(m_hRealTimeTerminationEvent);
|
|
}
|
|
|
|
if(m_hTraceEventMutex != NULL) {
|
|
CloseHandle(m_hTraceEventMutex);
|
|
}
|
|
|
|
if(m_hSessionArrayMutex != NULL) {
|
|
CloseHandle(m_hSessionArrayMutex);
|
|
}
|
|
}
|
|
|
|
void CDisplayDlg::DoDataExchange(CDataExchange* pDX)
|
|
{
|
|
CDialog::DoDataExchange(pDX);
|
|
}
|
|
|
|
BEGIN_MESSAGE_MAP(CDisplayDlg, CDialog)
|
|
//{{AFX_MSG_MAP(CDisplayDlg)
|
|
ON_MESSAGE(WM_USER_TRACE_DONE, OnTraceDone)
|
|
ON_MESSAGE(WM_USER_AUTOSIZECOLUMNS, AutoSizeColumns)
|
|
ON_WM_NCCALCSIZE()
|
|
ON_WM_SIZE()
|
|
ON_WM_TIMER()
|
|
ON_WM_INITMENUPOPUP()
|
|
ON_NOTIFY(LVN_GETDISPINFO, IDC_DISPLAY_LIST, OnGetDispInfo)
|
|
ON_NOTIFY(NM_CLICK, IDC_DISPLAY_LIST, OnNMClickDisplayList)
|
|
ON_NOTIFY(NM_RCLICK, IDC_DISPLAY_LIST, OnNMRClickDisplayList)
|
|
ON_NOTIFY(LVN_BEGINSCROLL, IDC_DISPLAY_LIST, OnLvnBeginScrollDisplayList)
|
|
ON_NOTIFY(HDN_ITEMCLICK, 0, OnDoSort)
|
|
ON_COMMAND(ID__AUTOSIZECOLUMNS, AutoSizeColumns)
|
|
ON_COMMAND(ID__CLEARDISPLAY, OnClearDisplay)
|
|
ON_COMMAND(ID__NAME, OnNameDisplayColumnCheck)
|
|
ON_COMMAND(ID__MESSAGE, OnMessageDisplayColumnCheck)
|
|
ON_COMMAND(ID__FILENAME, OnFileNameDisplayColumnCheck)
|
|
ON_COMMAND(ID__LINENUMBER, OnLineNumberDisplayColumnCheck)
|
|
ON_COMMAND(ID__FUNCTIONNAME, OnFunctionNameDisplayColumnCheck)
|
|
ON_COMMAND(ID__PROCESSID, OnProcessIDDisplayColumnCheck)
|
|
ON_COMMAND(ID__THREADID, OnThreadIDDisplayColumnCheck)
|
|
ON_COMMAND(ID__CPUNUMBER, OnCpuNumberDisplayColumnCheck)
|
|
ON_COMMAND(ID__SEQUENCENUMBER, OnSeqNumberDisplayColumnCheck)
|
|
ON_COMMAND(ID__SYSTEMTIME, OnSystemTimeDisplayColumnCheck)
|
|
ON_COMMAND(ID__KERNELTIME, OnKernelTimeDisplayColumnCheck)
|
|
ON_COMMAND(ID__USERTIME, OnUserTimeDisplayColumnCheck)
|
|
ON_COMMAND(ID__INDENT, OnIndentDisplayColumnCheck)
|
|
ON_COMMAND(ID__FLAGSNAME, OnFlagsNameDisplayColumnCheck)
|
|
ON_COMMAND(ID__LEVELNAME, OnLevelNameDisplayColumnCheck)
|
|
ON_COMMAND(ID__COMPONENTNAME, OnComponentNameDisplayColumnCheck)
|
|
ON_COMMAND(ID__SUBCOMPONENTNAME, OnSubComponentNameDisplayColumnCheck)
|
|
//}}AFX_MSG_MAP
|
|
END_MESSAGE_MAP()
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// CDisplayDlg message handlers
|
|
|
|
BOOL CDisplayDlg::OnInitDialog()
|
|
{
|
|
RECT rc;
|
|
RECT parentRC;
|
|
BOOL retVal;
|
|
CString str;
|
|
|
|
retVal = CDialog::OnInitDialog();
|
|
|
|
//
|
|
// Setup the protection for our trace event array
|
|
//
|
|
m_hTraceEventMutex = CreateMutex(NULL,TRUE,NULL);
|
|
|
|
if(m_hTraceEventMutex == NULL) {
|
|
|
|
DWORD error = GetLastError();
|
|
|
|
str.Format(_T("CreateMutex Error %d %x"),error,error);
|
|
|
|
AfxMessageBox(str);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
ReleaseMutex(m_hTraceEventMutex);
|
|
|
|
|
|
//
|
|
// Setup the protection for our log session array
|
|
//
|
|
m_hSessionArrayMutex = CreateMutex(NULL,TRUE,NULL);
|
|
|
|
if(m_hSessionArrayMutex == NULL) {
|
|
|
|
DWORD error = GetLastError();
|
|
|
|
str.Format(_T("CreateMutex Error %d %x"),error,error);
|
|
|
|
AfxMessageBox(str);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
ReleaseMutex(m_hSessionArrayMutex);
|
|
|
|
//
|
|
// Create the events for the real time thread operation
|
|
//
|
|
m_hRealTimeTerminationEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
if(NULL == m_hRealTimeTerminationEvent) {
|
|
|
|
DWORD error = GetLastError();
|
|
|
|
str.Format(_T("CreateEvent Error %d %x"),error,error);
|
|
|
|
AfxMessageBox(str);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
m_hRealTimeProcessingStartEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
if(NULL == m_hRealTimeProcessingStartEvent) {
|
|
|
|
DWORD error = GetLastError();
|
|
|
|
str.Format(_T("CreateEvent Error %d %x"),error,error);
|
|
|
|
AfxMessageBox(str);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
m_hRealTimeProcessingDoneEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
if(NULL == m_hRealTimeProcessingDoneEvent) {
|
|
|
|
DWORD error = GetLastError();
|
|
|
|
str.Format(_T("CreateEvent Error %d %x"),error,error);
|
|
|
|
AfxMessageBox(str);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// spawn a thread to handle real time events
|
|
//
|
|
m_pRealTimeOutputThread = AfxBeginThread((AFX_THREADPROC)RealTimeEventThread,
|
|
this,
|
|
THREAD_PRIORITY_LOWEST,
|
|
0,
|
|
CREATE_SUSPENDED);
|
|
|
|
//
|
|
// save the thread handle
|
|
//
|
|
DuplicateHandle(GetCurrentProcess(),
|
|
m_pRealTimeOutputThread->m_hThread,
|
|
GetCurrentProcess(),
|
|
&m_hRealTimeOutputThread,
|
|
0,
|
|
FALSE,
|
|
DUPLICATE_SAME_ACCESS);
|
|
|
|
//
|
|
// start the thread
|
|
//
|
|
ResumeThread(m_pRealTimeOutputThread->m_hThread);
|
|
|
|
//
|
|
// get the parent dimensions
|
|
//
|
|
GetParent()->GetParent()->GetClientRect(&parentRC);
|
|
|
|
//
|
|
// get the dialog dimensions
|
|
//
|
|
GetWindowRect(&rc);
|
|
|
|
//
|
|
// adjust the list control dimensions
|
|
//
|
|
rc.right = parentRC.right - parentRC.left - 24;
|
|
rc.bottom = rc.bottom - rc.top;
|
|
rc.left = 0;
|
|
rc.top = 0;
|
|
|
|
//
|
|
// Create the list control
|
|
//
|
|
if(!m_displayCtrl.Create(WS_CHILD|WS_VISIBLE|WS_BORDER|LVS_REPORT|LVS_OWNERDATA,
|
|
rc,
|
|
this,
|
|
IDC_DISPLAY_LIST))
|
|
{
|
|
TRACE(_T("Failed to create logger list control\n"));
|
|
return FALSE;
|
|
}
|
|
|
|
return retVal;
|
|
}
|
|
|
|
void CDisplayDlg::OnNcPaint()
|
|
{
|
|
CRect pRect;
|
|
|
|
GetClientRect(&pRect);
|
|
InvalidateRect(&pRect, TRUE);
|
|
}
|
|
|
|
VOID CDisplayDlg::SetDisplayFlags(LONG DisplayFlags)
|
|
{
|
|
LONG addDisplayFlags = ~m_displayFlags & DisplayFlags;
|
|
LONG removeDisplayFlags = m_displayFlags & ~DisplayFlags;
|
|
LONG updateDisplayFlags = m_displayFlags & DisplayFlags;
|
|
BOOL bChanged = FALSE;
|
|
LONG ii;
|
|
LONG jj;
|
|
LONG kk;
|
|
LONG ll;
|
|
CString str;
|
|
|
|
//
|
|
// Insert any new columns and remove any uneeded
|
|
//
|
|
for(ii = 0; ii < MaxTraceSessionOptions; ii++) {
|
|
//
|
|
// add the columns
|
|
//
|
|
if(addDisplayFlags & (1 << ii)) {
|
|
//
|
|
// add the column
|
|
//
|
|
m_displayCtrl.InsertColumn(m_insertionArray[ii],
|
|
m_columnName[ii],
|
|
LVCFMT_LEFT,
|
|
m_columnWidth[ii]);
|
|
|
|
//
|
|
// update the column positions
|
|
//
|
|
for(kk = 0, ll = 0; kk < MaxTraceSessionOptions; kk++) {
|
|
m_insertionArray[kk] = ll;
|
|
if(DisplayFlags & (1 << kk)) {
|
|
m_retrievalArray[ll] = kk;
|
|
ll++;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// remove the columns
|
|
//
|
|
if(removeDisplayFlags & (1 << ii)) {
|
|
//
|
|
// delete the column
|
|
//
|
|
m_displayCtrl.DeleteColumn(m_insertionArray[ii]);
|
|
|
|
//
|
|
// update the column positions
|
|
//
|
|
for(kk = 0, ll = 0; kk < MaxTraceSessionOptions; kk++) {
|
|
m_insertionArray[kk] = ll;
|
|
if(DisplayFlags & (1 << kk)) {
|
|
m_retrievalArray[ll] = kk;
|
|
ll++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Save the new display flags
|
|
//
|
|
m_displayFlags = DisplayFlags;
|
|
|
|
//
|
|
// Save the new column order array
|
|
//
|
|
memset(m_columnArray, 0, sizeof(int) * MaxTraceSessionOptions);
|
|
m_displayCtrl.GetColumnOrderArray(m_columnArray);
|
|
}
|
|
|
|
VOID CDisplayDlg::AddSession(CLogSession *pLogSession)
|
|
{
|
|
ULONG flags;
|
|
|
|
//
|
|
// Get the array protection
|
|
//
|
|
WaitForSingleObject(m_hSessionArrayMutex, INFINITE);
|
|
|
|
//
|
|
// Add the log session to the list
|
|
//
|
|
m_sessionArray.Add(pLogSession);
|
|
|
|
//
|
|
// Is this the first session?
|
|
//
|
|
if(m_sessionArray.GetSize() == 1) {
|
|
//
|
|
// Force the columns to get updated for first time
|
|
//
|
|
flags = GetDisplayFlags();
|
|
m_displayFlags = 0;
|
|
SetDisplayFlags(flags);
|
|
|
|
//
|
|
// Fix the widths of the columns
|
|
//
|
|
AutoSizeColumns();
|
|
}
|
|
|
|
//
|
|
// Release the array protection
|
|
//
|
|
ReleaseMutex(m_hSessionArrayMutex);
|
|
}
|
|
|
|
VOID CDisplayDlg::RemoveSession(CLogSession *pLogSession)
|
|
{
|
|
LONG traceDisplayFlags;
|
|
PVOID pHashEntry;
|
|
CString hashEntryKey;
|
|
POSITION pos;
|
|
|
|
//
|
|
// Get the array protection
|
|
//
|
|
WaitForSingleObject(m_hSessionArrayMutex, INFINITE);
|
|
|
|
for(LONG ii = (LONG)m_sessionArray.GetSize() - 1; ii >= 0; ii--) {
|
|
if(pLogSession == (CLogSession *)m_sessionArray[ii]) {
|
|
m_sessionArray.RemoveAt(ii);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Release the array protection
|
|
//
|
|
ReleaseMutex(m_hSessionArrayMutex);
|
|
|
|
//
|
|
// Remove TMF info from our global hash, if any.
|
|
//
|
|
for(pos = g_formatInfoHash.GetStartPosition(); pos != NULL; )
|
|
{
|
|
g_formatInfoHash.GetNextAssoc(pos, hashEntryKey, pHashEntry);
|
|
|
|
if(pHashEntry == (PVOID)pLogSession) {
|
|
g_formatInfoHash.RemoveKey(hashEntryKey);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Force an update of the displayed columns
|
|
//
|
|
traceDisplayFlags = GetDisplayFlags();
|
|
|
|
//
|
|
// Update the display flags and thus the display
|
|
//
|
|
SetDisplayFlags(traceDisplayFlags);
|
|
}
|
|
|
|
void CDisplayDlg::OnSize(UINT nType, int cx,int cy)
|
|
{
|
|
CRect rc;
|
|
|
|
if(!IsWindow(m_displayCtrl.GetSafeHwnd()))
|
|
{
|
|
return;
|
|
}
|
|
|
|
GetParent()->GetClientRect(&rc);
|
|
|
|
//
|
|
// reset the size of the dialog to follow the frame
|
|
//
|
|
SetWindowPos(NULL,
|
|
0,
|
|
0,
|
|
rc.right - rc.left,
|
|
rc.bottom - rc.top,
|
|
SWP_NOMOVE|SWP_SHOWWINDOW|SWP_NOZORDER);
|
|
|
|
GetClientRect(&rc);
|
|
|
|
//
|
|
// Reset the size and position of the list control in the dialog
|
|
//
|
|
m_displayCtrl.MoveWindow(rc);
|
|
}
|
|
|
|
|
|
BOOL CDisplayDlg::PreTranslateMessage(MSG* pMsg)
|
|
{
|
|
if(pMsg->message == WM_KEYDOWN)
|
|
{
|
|
//
|
|
// Ignore the escape key, otherwise
|
|
// the client area grays out on escape
|
|
//
|
|
if(pMsg->wParam == VK_ESCAPE) {
|
|
// ignore the key
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// For the return key, we set the list control
|
|
// to always scroll to the last item
|
|
//
|
|
if(pMsg->wParam == VK_RETURN) {
|
|
|
|
//
|
|
// Start showing the latest entries in the list
|
|
//
|
|
m_bShowLatest = TRUE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Fix for key accelerators, otherwise they are never
|
|
// processed
|
|
//
|
|
if (AfxGetMainWnd()->PreTranslateMessage(pMsg)) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return CDialog::PreTranslateMessage(pMsg);
|
|
}
|
|
|
|
void CDisplayDlg::OnNMClickDisplayList(NMHDR *pNMHDR, LRESULT *pResult)
|
|
{
|
|
//
|
|
// Stop automatic scrolling to latest list control entry
|
|
// Hit enter to start auto scrolling again
|
|
//
|
|
m_bShowLatest = FALSE;
|
|
|
|
*pResult = 0;
|
|
}
|
|
|
|
void CDisplayDlg::OnNMRClickDisplayList(NMHDR *pNMHDR, LRESULT *pResult)
|
|
{
|
|
CString str;
|
|
DWORD position;
|
|
int listIndex;
|
|
LVHITTESTINFO lvhti;
|
|
|
|
//
|
|
// Get the position of the mouse when this
|
|
// message posted
|
|
//
|
|
position = ::GetMessagePos();
|
|
|
|
//
|
|
// Get the position in an easy to use format
|
|
//
|
|
CPoint point((int) LOWORD (position), (int)HIWORD(position));
|
|
|
|
//
|
|
// Convert to screen coordinates
|
|
//
|
|
CPoint screenPoint(point);
|
|
|
|
//
|
|
// If there are entries in the event array, then pop-up
|
|
// the menu to allow autosize columns and clear display
|
|
//
|
|
CMenu menu;
|
|
menu.LoadMenu(IDR_TRACE_SESSION_POPUP_MENU);
|
|
CMenu* pPopup = menu.GetSubMenu(0);
|
|
ASSERT(pPopup != NULL);
|
|
|
|
pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, screenPoint.x, screenPoint.y, this);
|
|
|
|
*pResult = 0;
|
|
}
|
|
|
|
void CDisplayDlg::OnLvnBeginScrollDisplayList(NMHDR *pNMHDR, LRESULT *pResult)
|
|
{
|
|
//
|
|
// This feature requires Internet Explorer 5.5 or greater.
|
|
// The symbol _WIN32_IE must be >= 0x0560.
|
|
//
|
|
LPNMLVSCROLL pStateChanged = reinterpret_cast<LPNMLVSCROLL>(pNMHDR);
|
|
|
|
//
|
|
// If someone grabs the scroll handle,
|
|
// stop the list control from scrolling
|
|
//
|
|
m_bShowLatest = FALSE;
|
|
|
|
*pResult = 0;
|
|
}
|
|
|
|
void CDisplayDlg::OnGetDispInfo(NMHDR *pNMHDR, LRESULT *pResult)
|
|
{
|
|
CString str;
|
|
LV_DISPINFO *pDispInfo = (LV_DISPINFO*)pNMHDR;
|
|
LV_ITEM *pItem;
|
|
int index;
|
|
int item = 0;
|
|
int subItem = 0;
|
|
LPWSTR outStr;
|
|
CString tempString;
|
|
CString tempString2;
|
|
CTraceMessage *pTraceMessage;
|
|
|
|
if(pDispInfo == NULL) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// list control cell information
|
|
//
|
|
pItem = &pDispInfo->item;
|
|
|
|
//
|
|
// Get the cell item and subitem locators
|
|
//
|
|
item = pItem->iItem;
|
|
subItem = pItem->iSubItem;
|
|
|
|
//
|
|
// Get the string pointer we need to fill in
|
|
//
|
|
outStr = pItem->pszText;
|
|
|
|
//
|
|
// Check that this is a text buffer request
|
|
//
|
|
if(!(pItem->mask & LVIF_TEXT)) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Make sure the text requested is text we have
|
|
//
|
|
if((m_traceArray.GetSize() == 0) ||
|
|
(item >= m_traceArray.GetSize()) ||
|
|
(item < 0)) {
|
|
_tcscpy(outStr,_T(""));
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Get our trace event array protection
|
|
//
|
|
WaitForSingleObject(m_hTraceEventMutex,INFINITE);
|
|
|
|
//
|
|
// Get the message from the array
|
|
//
|
|
pTraceMessage = m_traceArray[item];
|
|
|
|
//
|
|
// Release our trace event array protection
|
|
//
|
|
ReleaseMutex(m_hTraceEventMutex);
|
|
|
|
if(NULL == pTraceMessage) {
|
|
_tcscpy(outStr,_T(""));
|
|
return;
|
|
}
|
|
|
|
CTime kernelTime(pTraceMessage->m_KernelTime);
|
|
CTime userTime(pTraceMessage->m_UserTime);
|
|
|
|
// if( (pTraceMessage->m_UserTime.dwLowDateTime != 0) && (pTraceMessage->m_UserTime.dwHighDateTime != 0) ) {
|
|
// CTime userTime1(pTraceMessage->m_UserTime);
|
|
// userTime = userTime1;
|
|
// }
|
|
|
|
//
|
|
// Copy the proper portion of the message to the out string
|
|
//
|
|
|
|
switch(m_retrievalArray[subItem]) {
|
|
case ProviderName:
|
|
_tcscpy(outStr, pTraceMessage->m_GuidName);
|
|
break;
|
|
|
|
case Message: {
|
|
|
|
_tcsncpy(outStr, pTraceMessage->m_Message, pItem->cchTextMax-4);
|
|
|
|
outStr[pItem->cchTextMax-4] = 0x00;
|
|
|
|
if( (pItem->cchTextMax-4) <=
|
|
(int)_tcslen(pTraceMessage->m_Message) ) {
|
|
_tcscat(outStr, _T("..."));
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
|
|
case FileName:
|
|
//
|
|
// Filename and line number are combined in a single string
|
|
// so we have to parse them out. The format is generally
|
|
// something like this:
|
|
//
|
|
// myfile_c389
|
|
//
|
|
// Where myfile.c is where the event occurred and on line 389.
|
|
// This field can also signify a type of message, as in the
|
|
// Kernel Logger case, for example, so if there is no underscore
|
|
// we assume this is the case and we just print out the whole
|
|
// field.
|
|
//
|
|
if(pTraceMessage->m_GuidTypeName.Find('_') > 0) {
|
|
tempString = (LPCTSTR)pTraceMessage->m_GuidTypeName.Left(pTraceMessage->m_GuidTypeName.Find('_'));
|
|
if(tempString.IsEmpty()) {
|
|
//
|
|
// Copy the string back to the list control
|
|
//
|
|
_tcscpy(outStr, tempString);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Get the extension
|
|
//
|
|
tempString += ".";
|
|
|
|
tempString2 = (LPCTSTR)pTraceMessage->m_GuidTypeName.Right(pTraceMessage->m_GuidTypeName.GetLength() - pTraceMessage->m_GuidTypeName.Find('_') - 1);
|
|
|
|
tempString += (LPCTSTR)tempString2.Left(tempString2.FindOneOf(_T("0123456789")));
|
|
} else {
|
|
tempString = (LPCTSTR)pTraceMessage->m_GuidTypeName;
|
|
}
|
|
|
|
_tcscpy(outStr, tempString);
|
|
break;
|
|
|
|
case LineNumber:
|
|
//
|
|
// Filename and line number are combined in a single string
|
|
// so we have to parse them out. The format is generally
|
|
// something like this:
|
|
//
|
|
// myfile_c389
|
|
//
|
|
// Where myfile.c is where the event occurred and on line 389.
|
|
// This field can also signify a type of message, as in the
|
|
// Kernel Logger case, for example, so if there is no underscore
|
|
// we assume this is the case and we print nothing for the line
|
|
// number.
|
|
//
|
|
if(pTraceMessage->m_GuidTypeName.Find('_') > 0) {
|
|
tempString2 =
|
|
pTraceMessage->m_GuidTypeName.Right(pTraceMessage->m_GuidTypeName.GetLength() -
|
|
pTraceMessage->m_GuidTypeName.Find('_') - 1);
|
|
|
|
tempString2 =
|
|
tempString2.Right(tempString2.GetLength() -
|
|
tempString2.FindOneOf(_T("0123456789")));
|
|
} else {
|
|
tempString2.Empty();
|
|
}
|
|
|
|
_tcscpy(outStr, tempString2);
|
|
break;
|
|
|
|
case FunctionName:
|
|
_tcscpy(outStr, pTraceMessage->m_FunctionName);
|
|
break;
|
|
|
|
case ProcessId:
|
|
if((LONG)pTraceMessage->m_ProcessId >= 0) {
|
|
tempString.Format(_T("%d"), pTraceMessage->m_ProcessId);
|
|
} else {
|
|
tempString.Empty();
|
|
}
|
|
_tcscpy(outStr, tempString);
|
|
break;
|
|
|
|
case ThreadId:
|
|
if((LONG)pTraceMessage->m_ThreadId >= 0) {
|
|
tempString.Format(_T("%d"), pTraceMessage->m_ThreadId);
|
|
} else {
|
|
tempString.Empty();
|
|
}
|
|
_tcscpy(outStr, tempString);
|
|
break;
|
|
|
|
case CpuNumber:
|
|
if((LONG)pTraceMessage->m_CpuNumber >= 0) {
|
|
tempString.Format(_T("%d"), pTraceMessage->m_CpuNumber);
|
|
} else {
|
|
tempString.Empty();
|
|
}
|
|
_tcscpy(outStr, tempString);
|
|
break;
|
|
|
|
case SeqNumber:
|
|
if((LONG)pTraceMessage->m_SequenceNum >= 0) {
|
|
tempString.Format(_T("%d"), pTraceMessage->m_SequenceNum);
|
|
} else {
|
|
tempString.Empty();
|
|
}
|
|
|
|
_tcscpy(outStr, tempString);
|
|
break;
|
|
|
|
case SystemTime:
|
|
|
|
tempString.Format(_T("%02d\\%02d\\%4d-%02d:%02d:%02d:%02d"),
|
|
pTraceMessage->m_SystemTime.wMonth,
|
|
pTraceMessage->m_SystemTime.wDay,
|
|
pTraceMessage->m_SystemTime.wYear,
|
|
pTraceMessage->m_SystemTime.wHour,
|
|
pTraceMessage->m_SystemTime.wMinute,
|
|
pTraceMessage->m_SystemTime.wSecond,
|
|
pTraceMessage->m_SystemTime.wMilliseconds);
|
|
|
|
_tcscpy(outStr, tempString);
|
|
break;
|
|
|
|
case KernelTime:
|
|
//
|
|
// We have to do an ugly conversion here. If the year is 1969,
|
|
// then the time was blank.
|
|
//
|
|
if( (kernelTime == -1) || (kernelTime.GetYear() == 1969)) {
|
|
tempString.Format(_T(""));
|
|
} else {
|
|
tempString.Format(_T("%s-%02d:%02d:%02d"),
|
|
kernelTime.Format("%m\\%d\\%Y"),
|
|
kernelTime.GetHour() + 5,
|
|
kernelTime.GetMinute(),
|
|
kernelTime.GetSecond());
|
|
}
|
|
_tcscpy(outStr, tempString);
|
|
break;
|
|
|
|
case UserTime:
|
|
//
|
|
// We have to do an ugly conversion here. If the year is 1969 or
|
|
// 1970 then the time was blank.
|
|
//
|
|
if( (userTime == -1) || (userTime.GetYear() == 1969) || (userTime.GetYear() == 1970) ) {
|
|
tempString.Format(_T(""));
|
|
} else {
|
|
tempString.Format(_T("%s-%02d:%02d:%02d"),
|
|
userTime.Format("%m\\%d\\%Y"),
|
|
userTime.GetHour() + 5,
|
|
userTime.GetMinute(),
|
|
userTime.GetSecond());
|
|
}
|
|
_tcscpy(outStr, tempString);
|
|
break;
|
|
|
|
case Indent:
|
|
tempString.Format(_T("%d"), pTraceMessage->m_Indent);
|
|
_tcscpy(outStr, tempString);
|
|
break;
|
|
|
|
case FlagsName:
|
|
_tcscpy(outStr, pTraceMessage->m_FlagsName);
|
|
break;
|
|
|
|
case LevelName:
|
|
_tcscpy(outStr, pTraceMessage->m_LevelName);
|
|
break;
|
|
|
|
case ComponentName:
|
|
_tcscpy(outStr, pTraceMessage->m_ComponentName);
|
|
break;
|
|
|
|
case SubComponentName:
|
|
_tcscpy(outStr, pTraceMessage->m_SubComponentName);
|
|
break;
|
|
|
|
default:
|
|
//
|
|
// Default to empty string
|
|
//
|
|
_tcscpy(outStr,_T(""));
|
|
break;
|
|
}
|
|
}
|
|
|
|
void CDisplayDlg::OnClearDisplay()
|
|
{
|
|
CTraceMessage *pTraceMessage;
|
|
CTraceViewApp *pMainWnd;
|
|
|
|
//
|
|
// Clear the trace log
|
|
//
|
|
|
|
//
|
|
// Get a pointer to the main frame
|
|
//
|
|
pMainWnd = (CTraceViewApp *)AfxGetApp();
|
|
|
|
//
|
|
// Get our trace event array protection
|
|
//
|
|
WaitForSingleObject(m_hTraceEventMutex,INFINITE);
|
|
|
|
int elCount;
|
|
|
|
elCount = (int)m_traceArray.GetSize();
|
|
|
|
while (elCount > 0 ) {
|
|
|
|
delete m_traceArray.GetAt( --elCount );
|
|
}
|
|
|
|
//
|
|
// Remove the elements from the array
|
|
//
|
|
m_traceArray.RemoveAll();
|
|
|
|
//
|
|
// Release our trace event array protection
|
|
//
|
|
ReleaseMutex(m_hTraceEventMutex);
|
|
|
|
//
|
|
// Clear the list control
|
|
//
|
|
m_displayCtrl.DeleteAllItems();
|
|
|
|
//
|
|
// Reset the output to always show latest
|
|
//
|
|
m_bShowLatest = TRUE;
|
|
}
|
|
|
|
void CDisplayDlg::AutoSizeColumns()
|
|
{
|
|
LONG colWidth1;
|
|
LONG colWidth2;
|
|
LONG columnCount;
|
|
CHeaderCtrl *pHeaderCtrl;
|
|
|
|
//
|
|
// Get the list control header
|
|
//
|
|
pHeaderCtrl = m_displayCtrl.GetHeaderCtrl();
|
|
|
|
if (pHeaderCtrl != NULL)
|
|
{
|
|
//
|
|
// Get number of columns
|
|
//
|
|
columnCount = pHeaderCtrl->GetItemCount();
|
|
|
|
for(LONG ii = 0; ii < (columnCount - 1); ii++) {
|
|
//
|
|
// Get the max width of the column entries
|
|
//
|
|
m_displayCtrl.SetColumnWidth(ii, LVSCW_AUTOSIZE);
|
|
colWidth1 = m_displayCtrl.GetColumnWidth(ii);
|
|
|
|
//
|
|
// Get the width of the column header
|
|
//
|
|
m_displayCtrl.SetColumnWidth(ii, LVSCW_AUTOSIZE_USEHEADER);
|
|
colWidth2 = m_displayCtrl.GetColumnWidth(ii);
|
|
|
|
//
|
|
// Set the column width to the max of the two
|
|
// Special case the first column?? Seems to be off
|
|
// a couple of pixels
|
|
//
|
|
if(0 == ii) {
|
|
m_displayCtrl.SetColumnWidth(ii, max(colWidth1,colWidth2) + 2);
|
|
} else {
|
|
m_displayCtrl.SetColumnWidth(ii, max(colWidth1,colWidth2));
|
|
}
|
|
|
|
//
|
|
// Save the column width
|
|
//
|
|
m_columnWidth[m_retrievalArray[ii]] = m_displayCtrl.GetColumnWidth(ii);
|
|
}
|
|
|
|
//
|
|
// Special case the message column. The last column is usually
|
|
// limited to only be as wide as needed. But, if the message
|
|
// column is the last column, then use up all available space.
|
|
//
|
|
if(m_retrievalArray[columnCount - 1] != Message) {
|
|
//
|
|
// Get the column width of the last column
|
|
//
|
|
colWidth2 = m_displayCtrl.GetColumnWidth(columnCount - 1);
|
|
|
|
//
|
|
// Get the max width of the column entries for the last column
|
|
//
|
|
m_displayCtrl.SetColumnWidth(columnCount - 1, LVSCW_AUTOSIZE);
|
|
|
|
colWidth1 = m_displayCtrl.GetColumnWidth(columnCount - 1);
|
|
|
|
//
|
|
// Set the last column width to the max of the two
|
|
//
|
|
m_displayCtrl.SetColumnWidth(columnCount - 1, max(colWidth1,colWidth2));
|
|
|
|
//
|
|
// Save the column width
|
|
//
|
|
m_columnWidth[m_retrievalArray[columnCount - 1]] = m_displayCtrl.GetColumnWidth(columnCount - 1);
|
|
} else {
|
|
//
|
|
// Set the width of the column to use the remaining space
|
|
//
|
|
m_displayCtrl.SetColumnWidth(columnCount - 1,
|
|
LVSCW_AUTOSIZE_USEHEADER);
|
|
|
|
//
|
|
// Save the column width
|
|
//
|
|
m_columnWidth[m_retrievalArray[columnCount - 1]] =
|
|
m_displayCtrl.GetColumnWidth(columnCount - 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CDisplayDlg::OnNameDisplayColumnCheck()
|
|
{
|
|
LONG flags;
|
|
|
|
//
|
|
// Get the current flag values
|
|
//
|
|
flags = GetDisplayFlags();
|
|
|
|
//
|
|
// Toggle the Name flag value
|
|
//
|
|
if(flags & TRACEOUTPUT_DISPLAY_PROVIDERNAME) {
|
|
flags &= ~TRACEOUTPUT_DISPLAY_PROVIDERNAME;
|
|
} else {
|
|
flags |= TRACEOUTPUT_DISPLAY_PROVIDERNAME;
|
|
}
|
|
|
|
//
|
|
// Update the flag values and thus the display
|
|
//
|
|
SetDisplayFlags(flags);
|
|
}
|
|
|
|
void CDisplayDlg::OnMessageDisplayColumnCheck()
|
|
{
|
|
LONG flags;
|
|
|
|
//
|
|
// Get the current flag values
|
|
//
|
|
flags = GetDisplayFlags();
|
|
|
|
//
|
|
// Toggle the Message flag value
|
|
//
|
|
if(flags & TRACEOUTPUT_DISPLAY_MESSAGE) {
|
|
flags &= ~TRACEOUTPUT_DISPLAY_MESSAGE;
|
|
} else {
|
|
flags |= TRACEOUTPUT_DISPLAY_MESSAGE;
|
|
}
|
|
|
|
//
|
|
// Update the flag values and thus the display
|
|
//
|
|
SetDisplayFlags(flags);
|
|
}
|
|
|
|
void CDisplayDlg::OnFileNameDisplayColumnCheck()
|
|
{
|
|
LONG flags;
|
|
|
|
//
|
|
// Get the current flag values
|
|
//
|
|
flags = GetDisplayFlags();
|
|
|
|
//
|
|
// Toggle the FileName flag value
|
|
//
|
|
if(flags & TRACEOUTPUT_DISPLAY_FILENAME) {
|
|
flags &= ~TRACEOUTPUT_DISPLAY_FILENAME;
|
|
} else {
|
|
flags |= TRACEOUTPUT_DISPLAY_FILENAME;
|
|
}
|
|
|
|
//
|
|
// Update the flag values and thus the display
|
|
//
|
|
SetDisplayFlags(flags);
|
|
}
|
|
|
|
void CDisplayDlg::OnLineNumberDisplayColumnCheck()
|
|
{
|
|
LONG flags;
|
|
|
|
//
|
|
// Get the current flag values
|
|
//
|
|
flags = GetDisplayFlags();
|
|
|
|
//
|
|
// Toggle the LineNumber flag value
|
|
//
|
|
if(flags & TRACEOUTPUT_DISPLAY_LINENUMBER) {
|
|
flags &= ~TRACEOUTPUT_DISPLAY_LINENUMBER;
|
|
} else {
|
|
flags |= TRACEOUTPUT_DISPLAY_LINENUMBER;
|
|
}
|
|
|
|
//
|
|
// Update the flag values and thus the display
|
|
//
|
|
SetDisplayFlags(flags);
|
|
}
|
|
|
|
void CDisplayDlg::OnFunctionNameDisplayColumnCheck()
|
|
{
|
|
LONG flags;
|
|
|
|
//
|
|
// Get the current flag values
|
|
//
|
|
flags = GetDisplayFlags();
|
|
|
|
//
|
|
// Toggle the FunctionName flag value
|
|
//
|
|
if(flags & TRACEOUTPUT_DISPLAY_FUNCTIONNAME) {
|
|
flags &= ~TRACEOUTPUT_DISPLAY_FUNCTIONNAME;
|
|
} else {
|
|
flags |= TRACEOUTPUT_DISPLAY_FUNCTIONNAME;
|
|
}
|
|
|
|
//
|
|
// Update the flag values and thus the display
|
|
//
|
|
SetDisplayFlags(flags);
|
|
}
|
|
|
|
void CDisplayDlg::OnProcessIDDisplayColumnCheck()
|
|
{
|
|
LONG flags;
|
|
|
|
//
|
|
// Get the current flag values
|
|
//
|
|
flags = GetDisplayFlags();
|
|
|
|
//
|
|
// Toggle the ProcessID flag value
|
|
//
|
|
if(flags & TRACEOUTPUT_DISPLAY_PROCESSID) {
|
|
flags &= ~TRACEOUTPUT_DISPLAY_PROCESSID;
|
|
} else {
|
|
flags |= TRACEOUTPUT_DISPLAY_PROCESSID;
|
|
}
|
|
|
|
//
|
|
// Update the flag values and thus the display
|
|
//
|
|
SetDisplayFlags(flags);
|
|
}
|
|
|
|
void CDisplayDlg::OnThreadIDDisplayColumnCheck()
|
|
{
|
|
LONG flags;
|
|
|
|
//
|
|
// Get the current flag values
|
|
//
|
|
flags = GetDisplayFlags();
|
|
|
|
//
|
|
// Toggle the Thread ID flag value
|
|
//
|
|
if(flags & TRACEOUTPUT_DISPLAY_THREADID) {
|
|
flags &= ~TRACEOUTPUT_DISPLAY_THREADID;
|
|
} else {
|
|
flags |= TRACEOUTPUT_DISPLAY_THREADID;
|
|
}
|
|
|
|
//
|
|
// Update the flag values and thus the display
|
|
//
|
|
SetDisplayFlags(flags);
|
|
}
|
|
|
|
void CDisplayDlg::OnCpuNumberDisplayColumnCheck()
|
|
{
|
|
LONG flags;
|
|
|
|
//
|
|
// Get the current flag values
|
|
//
|
|
flags = GetDisplayFlags();
|
|
|
|
//
|
|
// Toggle the CPU Number flag value
|
|
//
|
|
if(flags & TRACEOUTPUT_DISPLAY_CPUNUMBER) {
|
|
flags &= ~TRACEOUTPUT_DISPLAY_CPUNUMBER;
|
|
} else {
|
|
flags |= TRACEOUTPUT_DISPLAY_CPUNUMBER;
|
|
}
|
|
|
|
//
|
|
// Update the flag values and thus the display
|
|
//
|
|
SetDisplayFlags(flags);
|
|
}
|
|
|
|
void CDisplayDlg::OnSeqNumberDisplayColumnCheck()
|
|
{
|
|
LONG flags;
|
|
|
|
//
|
|
// Get the current flag values
|
|
//
|
|
flags = GetDisplayFlags();
|
|
|
|
//
|
|
// Toggle the Sequence Number flag value
|
|
//
|
|
if(flags & TRACEOUTPUT_DISPLAY_SEQNUMBER) {
|
|
flags &= ~TRACEOUTPUT_DISPLAY_SEQNUMBER;
|
|
} else {
|
|
flags |= TRACEOUTPUT_DISPLAY_SEQNUMBER;
|
|
}
|
|
|
|
//
|
|
// Update the flag values and thus the display
|
|
//
|
|
SetDisplayFlags(flags);
|
|
}
|
|
|
|
void CDisplayDlg::OnSystemTimeDisplayColumnCheck()
|
|
{
|
|
LONG flags;
|
|
|
|
//
|
|
// Get the current flag values
|
|
//
|
|
flags = GetDisplayFlags();
|
|
|
|
//
|
|
// Toggle the SystemTime flag value
|
|
//
|
|
if(flags & TRACEOUTPUT_DISPLAY_SYSTEMTIME) {
|
|
flags &= ~TRACEOUTPUT_DISPLAY_SYSTEMTIME;
|
|
} else {
|
|
flags |= TRACEOUTPUT_DISPLAY_SYSTEMTIME;
|
|
}
|
|
|
|
//
|
|
// Update the flag values and thus the display
|
|
//
|
|
SetDisplayFlags(flags);
|
|
}
|
|
|
|
void CDisplayDlg::OnKernelTimeDisplayColumnCheck()
|
|
{
|
|
LONG flags;
|
|
|
|
//
|
|
// Get the current flag values
|
|
//
|
|
flags = GetDisplayFlags();
|
|
|
|
//
|
|
// Toggle the KernelTime flag value
|
|
//
|
|
if(flags & TRACEOUTPUT_DISPLAY_KERNELTIME) {
|
|
flags &= ~TRACEOUTPUT_DISPLAY_KERNELTIME;
|
|
} else {
|
|
flags |= TRACEOUTPUT_DISPLAY_KERNELTIME;
|
|
}
|
|
|
|
//
|
|
// Update the flag values and thus the display
|
|
//
|
|
SetDisplayFlags(flags);
|
|
}
|
|
|
|
void CDisplayDlg::OnUserTimeDisplayColumnCheck()
|
|
{
|
|
LONG flags;
|
|
|
|
//
|
|
// Get the current flag values
|
|
//
|
|
flags = GetDisplayFlags();
|
|
|
|
//
|
|
// Toggle the UserTime flag value
|
|
//
|
|
if(flags & TRACEOUTPUT_DISPLAY_USERTIME) {
|
|
flags &= ~TRACEOUTPUT_DISPLAY_USERTIME;
|
|
} else {
|
|
flags |= TRACEOUTPUT_DISPLAY_USERTIME;
|
|
}
|
|
|
|
//
|
|
// Update the flag values and thus the display
|
|
//
|
|
SetDisplayFlags(flags);
|
|
}
|
|
|
|
void CDisplayDlg::OnIndentDisplayColumnCheck()
|
|
{
|
|
LONG flags;
|
|
|
|
//
|
|
// Get the current flag values
|
|
//
|
|
flags = GetDisplayFlags();
|
|
|
|
//
|
|
// Toggle the Indent flag value
|
|
//
|
|
if(flags & TRACEOUTPUT_DISPLAY_INDENT) {
|
|
flags &= ~TRACEOUTPUT_DISPLAY_INDENT;
|
|
} else {
|
|
flags |= TRACEOUTPUT_DISPLAY_INDENT;
|
|
}
|
|
|
|
//
|
|
// Update the flag values and thus the display
|
|
//
|
|
SetDisplayFlags(flags);
|
|
}
|
|
|
|
void CDisplayDlg::OnFlagsNameDisplayColumnCheck()
|
|
{
|
|
LONG flags;
|
|
|
|
//
|
|
// Get the current flag values
|
|
//
|
|
flags = GetDisplayFlags();
|
|
|
|
//
|
|
// Toggle the FlagsName flag value
|
|
//
|
|
if(flags & TRACEOUTPUT_DISPLAY_FLAGSNAME) {
|
|
flags &= ~TRACEOUTPUT_DISPLAY_FLAGSNAME;
|
|
} else {
|
|
flags |= TRACEOUTPUT_DISPLAY_FLAGSNAME;
|
|
}
|
|
|
|
//
|
|
// Update the flag values and thus the display
|
|
//
|
|
SetDisplayFlags(flags);
|
|
}
|
|
|
|
void CDisplayDlg::OnLevelNameDisplayColumnCheck()
|
|
{
|
|
LONG flags;
|
|
|
|
//
|
|
// Get the current flag values
|
|
//
|
|
flags = GetDisplayFlags();
|
|
|
|
//
|
|
// Toggle the LevelName flag value
|
|
//
|
|
if(flags & TRACEOUTPUT_DISPLAY_LEVELNAME) {
|
|
flags &= ~TRACEOUTPUT_DISPLAY_LEVELNAME;
|
|
} else {
|
|
flags |= TRACEOUTPUT_DISPLAY_LEVELNAME;
|
|
}
|
|
|
|
//
|
|
// Update the flag values and thus the display
|
|
//
|
|
SetDisplayFlags(flags);
|
|
}
|
|
|
|
void CDisplayDlg::OnComponentNameDisplayColumnCheck()
|
|
{
|
|
LONG flags;
|
|
|
|
//
|
|
// Get the current flag values
|
|
//
|
|
flags = GetDisplayFlags();
|
|
|
|
//
|
|
// Toggle the ComponentName flag value
|
|
//
|
|
if(flags & TRACEOUTPUT_DISPLAY_COMPNAME) {
|
|
flags &= ~TRACEOUTPUT_DISPLAY_COMPNAME;
|
|
} else {
|
|
flags |= TRACEOUTPUT_DISPLAY_COMPNAME;
|
|
}
|
|
|
|
//
|
|
// Update the flag values and thus the display
|
|
//
|
|
SetDisplayFlags(flags);
|
|
}
|
|
|
|
void CDisplayDlg::OnSubComponentNameDisplayColumnCheck()
|
|
{
|
|
LONG flags;
|
|
|
|
//
|
|
// Get the current flag values
|
|
//
|
|
flags = GetDisplayFlags();
|
|
|
|
//
|
|
// Toggle the SubComponentName flag value
|
|
//
|
|
if(flags & TRACEOUTPUT_DISPLAY_SUBCOMPNAME) {
|
|
flags &= ~TRACEOUTPUT_DISPLAY_SUBCOMPNAME;
|
|
} else {
|
|
flags |= TRACEOUTPUT_DISPLAY_SUBCOMPNAME;
|
|
}
|
|
|
|
//
|
|
// Update the flag values and thus the display
|
|
//
|
|
SetDisplayFlags(flags);
|
|
}
|
|
|
|
BOOL CDisplayDlg::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
|
|
{
|
|
HD_NOTIFY *pHDN = (HD_NOTIFY*)lParam;
|
|
LPNMHDR pNH = (LPNMHDR) lParam;
|
|
|
|
//
|
|
// wParam is zero for Header ctrl
|
|
//
|
|
if(wParam == 0 && pNH->code == NM_RCLICK) {
|
|
//
|
|
// Right button was clicked on header
|
|
//
|
|
|
|
//
|
|
// Determine where the right click occurred and pop-up
|
|
// a menu there
|
|
//
|
|
CPoint pt(GetMessagePos());
|
|
|
|
CHeaderCtrl *pHeader = m_displayCtrl.GetHeaderCtrl();
|
|
|
|
pHeader->ScreenToClient(&pt);
|
|
|
|
CPoint screenPoint(GetMessagePos());
|
|
|
|
CMenu menu;
|
|
menu.LoadMenu(IDR_TRACE_DISPLAY_OPTION_POPUP_MENU);
|
|
CMenu* pPopup = menu.GetSubMenu(0);
|
|
ASSERT(pPopup != NULL);
|
|
|
|
pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON,
|
|
screenPoint.x,
|
|
screenPoint.y,
|
|
this);
|
|
|
|
return TRUE;
|
|
} else if(wParam == 0 && pNH->code == NM_RELEASEDCAPTURE) {
|
|
//
|
|
// Column header was pressed and now released
|
|
//
|
|
|
|
POINT Point;
|
|
CHeaderCtrl *pHeader = m_displayCtrl.GetHeaderCtrl();
|
|
int columnArray[MaxTraceSessionOptions];
|
|
|
|
GetCursorPos (&Point);
|
|
ScreenToClient(&Point);
|
|
|
|
HDHITTESTINFO HitTest;
|
|
|
|
//
|
|
//Offset of right scrolling
|
|
//
|
|
HitTest.pt.x = Point.x + GetScrollPos(SB_HORZ);
|
|
HitTest.pt.y = Point.y;
|
|
|
|
//
|
|
//Send the hit test message
|
|
//
|
|
pHeader->SendMessage(HDM_HITTEST,0,(LPARAM)&HitTest);
|
|
|
|
//
|
|
// We check here if the column order changed. If so, then
|
|
// we don't want to cause a sort of the column items, the user
|
|
// had to press the column header to drag the column around.
|
|
//
|
|
memset(columnArray, 0, sizeof(int) * MaxTraceSessionOptions);
|
|
|
|
m_displayCtrl.GetColumnOrderArray(columnArray);
|
|
|
|
if(memcmp(m_columnArray, columnArray, sizeof(int) * MaxTraceSessionOptions)) {
|
|
//
|
|
// Column order changed, save the new order
|
|
//
|
|
memcpy(m_columnArray, columnArray, sizeof(int) * MaxTraceSessionOptions);
|
|
|
|
//
|
|
// Now call the default handler and return
|
|
//
|
|
return CDialog::OnNotify(wParam, lParam, pResult);
|
|
}
|
|
|
|
if(HitTest.iItem >= 0) {
|
|
//
|
|
// Now check for column resize
|
|
//
|
|
if(m_displayCtrl.GetColumnWidth(HitTest.iItem) != m_columnWidth[m_retrievalArray[HitTest.iItem]]) {
|
|
//
|
|
// Save off the new column width and don't sort the column
|
|
//
|
|
m_columnWidth[m_retrievalArray[HitTest.iItem]] = m_displayCtrl.GetColumnWidth(HitTest.iItem);
|
|
|
|
//
|
|
// Now call the default handler and return
|
|
//
|
|
return CDialog::OnNotify(wParam, lParam, pResult);
|
|
}
|
|
|
|
// //
|
|
// // Sort the table by this column's data
|
|
// //
|
|
// SortTable(HitTest.iItem);
|
|
//
|
|
// //
|
|
// // Force a redraw of the data
|
|
// //
|
|
// m_displayCtrl.RedrawItems(m_displayCtrl.GetTopIndex(),
|
|
// m_displayCtrl.GetTopIndex() + m_displayCtrl.GetCountPerPage());
|
|
//
|
|
// m_displayCtrl.UpdateWindow();
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now call the default handler and return
|
|
//
|
|
return CDialog::OnNotify(wParam, lParam, pResult);
|
|
}
|
|
|
|
void CDisplayDlg::OnInitMenuPopup(CMenu* pPopupMenu, UINT nIndex, BOOL bSysMenu)
|
|
{
|
|
//
|
|
// Call the default handler
|
|
//
|
|
CDialog::OnInitMenuPopup(pPopupMenu, nIndex, bSysMenu);
|
|
|
|
//
|
|
// As CDisplayDlg corresponds to a non-CFrameWnd window, OF COURSE
|
|
// you have to overload this handler and explicitly set the check
|
|
// state of each item in the popup menu. It is utterly,
|
|
// absolutely, and intuitively obvious as to why this is
|
|
// necessary, thus it won't be explained here
|
|
//
|
|
|
|
|
|
//
|
|
// Trace session pop-up menu
|
|
//
|
|
|
|
//
|
|
// Disable the clear display option, if there are no
|
|
// traces displayed
|
|
//
|
|
if(m_traceArray.GetSize() == 0) {
|
|
pPopupMenu->EnableMenuItem(ID__CLEARDISPLAY, MF_GRAYED);
|
|
}
|
|
|
|
|
|
//
|
|
// Trace display option pop-up menu
|
|
//
|
|
|
|
if(GetDisplayFlags() & TRACEOUTPUT_DISPLAY_PROVIDERNAME) {
|
|
pPopupMenu->CheckMenuItem(ID__NAME, MF_CHECKED);
|
|
}
|
|
|
|
if(GetDisplayFlags() & TRACEOUTPUT_DISPLAY_MESSAGE) {
|
|
pPopupMenu->CheckMenuItem(ID__MESSAGE, MF_CHECKED);
|
|
}
|
|
|
|
if(GetDisplayFlags() & TRACEOUTPUT_DISPLAY_FILENAME) {
|
|
pPopupMenu->CheckMenuItem(ID__FILENAME, MF_CHECKED);
|
|
}
|
|
|
|
if(GetDisplayFlags() & TRACEOUTPUT_DISPLAY_LINENUMBER) {
|
|
pPopupMenu->CheckMenuItem(ID__LINENUMBER, MF_CHECKED);
|
|
}
|
|
|
|
if(GetDisplayFlags() & TRACEOUTPUT_DISPLAY_FUNCTIONNAME) {
|
|
pPopupMenu->CheckMenuItem(ID__FUNCTIONNAME, MF_CHECKED);
|
|
}
|
|
|
|
if(GetDisplayFlags() & TRACEOUTPUT_DISPLAY_PROCESSID) {
|
|
pPopupMenu->CheckMenuItem(ID__PROCESSID, MF_CHECKED);
|
|
}
|
|
|
|
if(GetDisplayFlags() & TRACEOUTPUT_DISPLAY_THREADID) {
|
|
pPopupMenu->CheckMenuItem(ID__THREADID, MF_CHECKED);
|
|
}
|
|
|
|
if(GetDisplayFlags() & TRACEOUTPUT_DISPLAY_CPUNUMBER) {
|
|
pPopupMenu->CheckMenuItem(ID__CPUNUMBER, MF_CHECKED);
|
|
}
|
|
|
|
if(GetDisplayFlags() & TRACEOUTPUT_DISPLAY_SEQNUMBER) {
|
|
pPopupMenu->CheckMenuItem(ID__SEQUENCENUMBER, MF_CHECKED);
|
|
}
|
|
|
|
if(GetDisplayFlags() & TRACEOUTPUT_DISPLAY_SYSTEMTIME) {
|
|
pPopupMenu->CheckMenuItem(ID__SYSTEMTIME, MF_CHECKED);
|
|
}
|
|
|
|
if(GetDisplayFlags() & TRACEOUTPUT_DISPLAY_KERNELTIME) {
|
|
pPopupMenu->CheckMenuItem(ID__KERNELTIME, MF_CHECKED);
|
|
}
|
|
|
|
if(GetDisplayFlags() & TRACEOUTPUT_DISPLAY_USERTIME) {
|
|
pPopupMenu->CheckMenuItem(ID__USERTIME, MF_CHECKED);
|
|
}
|
|
|
|
if(GetDisplayFlags() & TRACEOUTPUT_DISPLAY_INDENT) {
|
|
pPopupMenu->CheckMenuItem(ID__INDENT, MF_CHECKED);
|
|
}
|
|
|
|
if(GetDisplayFlags() & TRACEOUTPUT_DISPLAY_FLAGSNAME) {
|
|
pPopupMenu->CheckMenuItem(ID__FLAGSNAME, MF_CHECKED);
|
|
}
|
|
|
|
if(GetDisplayFlags() & TRACEOUTPUT_DISPLAY_LEVELNAME) {
|
|
pPopupMenu->CheckMenuItem(ID__LEVELNAME, MF_CHECKED);
|
|
}
|
|
|
|
if(GetDisplayFlags() & TRACEOUTPUT_DISPLAY_COMPNAME) {
|
|
pPopupMenu->CheckMenuItem(ID__COMPONENTNAME, MF_CHECKED);
|
|
}
|
|
|
|
if(GetDisplayFlags() & TRACEOUTPUT_DISPLAY_SUBCOMPNAME) {
|
|
pPopupMenu->CheckMenuItem(ID__SUBCOMPONENTNAME, MF_CHECKED);
|
|
}
|
|
}
|
|
|
|
void CDisplayDlg::SortTable(int Column)
|
|
{
|
|
CString str;
|
|
|
|
//
|
|
// Use qsort with the proper compare routine. We use qsort
|
|
// versus the list control's own sort functionality as its
|
|
// actually more flexible for this case. We don't have to
|
|
// parse the item data during the sorts this way. The native
|
|
// sort wouldn't buy us anything.
|
|
//
|
|
|
|
//
|
|
// Get our trace event array protection
|
|
//
|
|
WaitForSingleObject(m_hTraceEventMutex,INFINITE);
|
|
|
|
//
|
|
// Check the column to see if its in range, if not just sort
|
|
// by the last selected column
|
|
//
|
|
if(Column < MaxLogSessionOptions) {
|
|
|
|
if(m_lastSorted != m_retrievalArray[Column]) {
|
|
|
|
//
|
|
// If this column has not been used to sort the trace event
|
|
// data before, then sort ascending
|
|
//
|
|
m_bOrder = TRUE;
|
|
|
|
} else {
|
|
//
|
|
// If this column has been used before, then reverse the order
|
|
// of the sort (column header was selected again)
|
|
//
|
|
m_bOrder = (m_bOrder == TRUE ? FALSE : TRUE);
|
|
}
|
|
|
|
//
|
|
// Save the column
|
|
//
|
|
m_lastSorted = m_retrievalArray[Column];
|
|
|
|
} else if(m_lastSorted >= MaxLogSessionOptions) {
|
|
|
|
//
|
|
// Release our trace event array protection
|
|
//
|
|
ReleaseMutex(m_hTraceEventMutex);
|
|
|
|
str.Format(_T("m_lastSorted = %d"), m_lastSorted);
|
|
return;
|
|
|
|
}
|
|
|
|
//
|
|
// Determine whether to sort ascending or descending and call
|
|
// qsort with the proper callback
|
|
//
|
|
if(m_bOrder) {
|
|
qsort((PVOID)&m_traceArray[0], m_traceArray.GetSize(), sizeof(CTraceMessage *), m_traceSortRoutine[m_lastSorted]);
|
|
} else {
|
|
qsort((PVOID)&m_traceArray[0], m_traceArray.GetSize(), sizeof(CTraceMessage *), m_traceReverseSortRoutine[m_lastSorted]);
|
|
}
|
|
|
|
//
|
|
// Release our trace event array protection
|
|
//
|
|
ReleaseMutex(m_hTraceEventMutex);
|
|
}
|
|
|
|
int CDisplayDlg::CompareOnName(const void *pMessage1, const void *pMessage2)
|
|
{
|
|
CTraceMessage *pTraceMessage1 = *(CTraceMessage **)pMessage1;
|
|
CTraceMessage *pTraceMessage2 = *(CTraceMessage **)pMessage2;
|
|
|
|
return(pTraceMessage1->m_GuidName.Compare(pTraceMessage2->m_GuidName));
|
|
}
|
|
|
|
int CDisplayDlg::CompareOnMessage(const void *pMessage1, const void *pMessage2)
|
|
{
|
|
CTraceMessage *pTraceMessage1 = *(CTraceMessage **)pMessage1;
|
|
CTraceMessage *pTraceMessage2 = *(CTraceMessage **)pMessage2;
|
|
|
|
return(pTraceMessage1->m_Message.Compare(pTraceMessage2->m_Message));
|
|
}
|
|
|
|
int CDisplayDlg::CompareOnFileName(const void *pMessage1, const void *pMessage2)
|
|
{
|
|
CTraceMessage *pTraceMessage1 = *(CTraceMessage **)pMessage1;
|
|
CTraceMessage *pTraceMessage2 = *(CTraceMessage **)pMessage2;
|
|
|
|
return(pTraceMessage1->m_GuidTypeName.Compare(pTraceMessage2->m_GuidTypeName));
|
|
}
|
|
|
|
int CDisplayDlg::CompareOnLineNumber(const void *pMessage1, const void *pMessage2)
|
|
{
|
|
CTraceMessage *pTraceMessage1 = *(CTraceMessage **)pMessage1;
|
|
CTraceMessage *pTraceMessage2 = *(CTraceMessage **)pMessage2;
|
|
CString string1;
|
|
CString string2;
|
|
DWORD val1, val2;
|
|
|
|
//
|
|
// Get Line number string for the first element
|
|
//
|
|
string1 =
|
|
pTraceMessage1->m_GuidTypeName.Right(pTraceMessage1->m_GuidTypeName.GetLength() -
|
|
pTraceMessage1->m_GuidTypeName.Find('_') - 1);
|
|
|
|
string1 =
|
|
string1.Right(string1.GetLength() -
|
|
string1.FindOneOf(_T("0123456789")));
|
|
|
|
string2 =
|
|
pTraceMessage2->m_GuidTypeName.Right(pTraceMessage2->m_GuidTypeName.GetLength() -
|
|
pTraceMessage2->m_GuidTypeName.Find('_') - 1);
|
|
|
|
//
|
|
// Get the line number string for the second element
|
|
//
|
|
string2 =
|
|
string2.Right(string2.GetLength() -
|
|
string2.FindOneOf(_T("0123456789")));
|
|
|
|
TCHAR * end;
|
|
|
|
val1 = _tcstoul(string1, &end, 10);
|
|
val2 = _tcstoul(string2, &end,10);
|
|
|
|
if(val1 == val2) {
|
|
return(0);
|
|
}
|
|
|
|
if(val1 < val2) {
|
|
return(-1);
|
|
}
|
|
|
|
return(1);
|
|
}
|
|
|
|
int CDisplayDlg::CompareOnFunctionName(const void *pMessage1, const void *pMessage2)
|
|
{
|
|
CTraceMessage *pTraceMessage1 = *(CTraceMessage **)pMessage1;
|
|
CTraceMessage *pTraceMessage2 = *(CTraceMessage **)pMessage2;
|
|
|
|
return(pTraceMessage1->m_FunctionName.Compare(pTraceMessage2->m_FunctionName));
|
|
}
|
|
|
|
int CDisplayDlg::CompareOnProcessId(const void *pMessage1, const void *pMessage2)
|
|
{
|
|
CTraceMessage *pTraceMessage1 = *(CTraceMessage **)pMessage1;
|
|
CTraceMessage *pTraceMessage2 = *(CTraceMessage **)pMessage2;
|
|
|
|
return((LONG)pTraceMessage1->m_ProcessId - (LONG)pTraceMessage2->m_ProcessId);
|
|
}
|
|
|
|
int CDisplayDlg::CompareOnThreadId(const void *pMessage1, const void *pMessage2)
|
|
{
|
|
CTraceMessage *pTraceMessage1 = *(CTraceMessage **)pMessage1;
|
|
CTraceMessage *pTraceMessage2 = *(CTraceMessage **)pMessage2;
|
|
|
|
return((LONG)pTraceMessage1->m_ThreadId - (LONG)pTraceMessage2->m_ThreadId);
|
|
}
|
|
|
|
int CDisplayDlg::CompareOnCpuNumber(const void *pMessage1, const void *pMessage2)
|
|
{
|
|
CTraceMessage *pTraceMessage1 = *(CTraceMessage **)pMessage1;
|
|
CTraceMessage *pTraceMessage2 = *(CTraceMessage **)pMessage2;
|
|
|
|
return((LONG)pTraceMessage1->m_CpuNumber - (LONG)pTraceMessage2->m_CpuNumber);
|
|
}
|
|
|
|
int CDisplayDlg::CompareOnSeqNumber(const void *pMessage1, const void *pMessage2)
|
|
{
|
|
CTraceMessage *pTraceMessage1 = *(CTraceMessage **)pMessage1;
|
|
CTraceMessage *pTraceMessage2 = *(CTraceMessage **)pMessage2;
|
|
|
|
return((LONG)pTraceMessage1->m_SequenceNum - (LONG)pTraceMessage2->m_SequenceNum);
|
|
}
|
|
|
|
int CDisplayDlg::CompareOnSystemTime(const void *pMessage1, const void *pMessage2)
|
|
{
|
|
CTraceMessage *pTraceMessage1 = *(CTraceMessage **)pMessage1;
|
|
CTraceMessage *pTraceMessage2 = *(CTraceMessage **)pMessage2;
|
|
FILETIME timea, timeb;
|
|
BOOL res;
|
|
|
|
res = SystemTimeToFileTime(&pTraceMessage1->m_SystemTime, &timea);
|
|
res = SystemTimeToFileTime(&pTraceMessage2->m_SystemTime, &timeb);
|
|
|
|
return(CompareFileTime(&timea, &timeb));
|
|
}
|
|
|
|
int CDisplayDlg::CompareOnKernelTime(const void *pMessage1, const void *pMessage2)
|
|
{
|
|
CTraceMessage *pTraceMessage1 = *(CTraceMessage **)pMessage1;
|
|
CTraceMessage *pTraceMessage2 = *(CTraceMessage **)pMessage2;
|
|
|
|
return(pTraceMessage1->m_KernelTime - pTraceMessage2->m_KernelTime);
|
|
// return (pTraceMessage1->m_KernelTime.dwHighDateTime - pTraceMessage2->m_KernelTime.dwHighDateTime) ?
|
|
// (pTraceMessage1->m_KernelTime.dwHighDateTime - pTraceMessage2->m_KernelTime.dwHighDateTime) :
|
|
// (pTraceMessage1->m_KernelTime.dwLowDateTime - pTraceMessage2->m_KernelTime.dwLowDateTime);
|
|
}
|
|
|
|
int CDisplayDlg::CompareOnUserTime(const void *pMessage1, const void *pMessage2)
|
|
{
|
|
CTraceMessage *pTraceMessage1 = *(CTraceMessage **)pMessage1;
|
|
CTraceMessage *pTraceMessage2 = *(CTraceMessage **)pMessage2;
|
|
|
|
return(pTraceMessage1->m_UserTime - pTraceMessage2->m_UserTime);
|
|
|
|
// return (pTraceMessage1->m_UserTime.dwHighDateTime - pTraceMessage2->m_UserTime.dwHighDateTime) ?
|
|
// (pTraceMessage1->m_UserTime.dwHighDateTime - pTraceMessage2->m_UserTime.dwHighDateTime) :
|
|
// (pTraceMessage1->m_UserTime.dwLowDateTime - pTraceMessage2->m_UserTime.dwLowDateTime);
|
|
}
|
|
|
|
int CDisplayDlg::CompareOnIndent(const void *pMessage1, const void *pMessage2)
|
|
{
|
|
CTraceMessage *pTraceMessage1 = *(CTraceMessage **)pMessage1;
|
|
CTraceMessage *pTraceMessage2 = *(CTraceMessage **)pMessage2;
|
|
|
|
return((LONG)pTraceMessage1->m_Indent - (LONG)pTraceMessage2->m_Indent);
|
|
}
|
|
|
|
int CDisplayDlg::CompareOnFlagsName(const void *pMessage1, const void *pMessage2)
|
|
{
|
|
CTraceMessage *pTraceMessage1 = *(CTraceMessage **)pMessage1;
|
|
CTraceMessage *pTraceMessage2 = *(CTraceMessage **)pMessage2;
|
|
|
|
return(pTraceMessage1->m_FlagsName.Compare(pTraceMessage2->m_FlagsName));
|
|
}
|
|
|
|
int CDisplayDlg::CompareOnLevelName(const void *pMessage1, const void *pMessage2)
|
|
{
|
|
CTraceMessage *pTraceMessage1 = *(CTraceMessage **)pMessage1;
|
|
CTraceMessage *pTraceMessage2 = *(CTraceMessage **)pMessage2;
|
|
|
|
return(pTraceMessage1->m_LevelName.Compare(pTraceMessage2->m_LevelName));
|
|
}
|
|
|
|
int CDisplayDlg::CompareOnComponentName(const void *pMessage1, const void *pMessage2)
|
|
{
|
|
CTraceMessage *pTraceMessage1 = *(CTraceMessage **)pMessage1;
|
|
CTraceMessage *pTraceMessage2 = *(CTraceMessage **)pMessage2;
|
|
|
|
return(pTraceMessage1->m_ComponentName.Compare(pTraceMessage2->m_ComponentName));
|
|
}
|
|
|
|
int CDisplayDlg::CompareOnSubComponentName(const void *pMessage1, const void *pMessage2)
|
|
{
|
|
CTraceMessage *pTraceMessage1 = *(CTraceMessage **)pMessage1;
|
|
CTraceMessage *pTraceMessage2 = *(CTraceMessage **)pMessage2;
|
|
|
|
return(pTraceMessage1->m_SubComponentName.Compare(pTraceMessage2->m_SubComponentName));
|
|
}
|
|
|
|
int CDisplayDlg::ReverseCompareOnName(const void *pMessage1, const void *pMessage2)
|
|
{
|
|
CTraceMessage *pTraceMessage1 = *(CTraceMessage **)pMessage1;
|
|
CTraceMessage *pTraceMessage2 = *(CTraceMessage **)pMessage2;
|
|
|
|
return(pTraceMessage2->m_GuidName.Compare(pTraceMessage1->m_GuidName));
|
|
}
|
|
|
|
int CDisplayDlg::ReverseCompareOnMessage(const void *pMessage1, const void *pMessage2)
|
|
{
|
|
CTraceMessage *pTraceMessage1 = *(CTraceMessage **)pMessage1;
|
|
CTraceMessage *pTraceMessage2 = *(CTraceMessage **)pMessage2;
|
|
|
|
return(pTraceMessage2->m_Message.Compare(pTraceMessage1->m_Message));
|
|
}
|
|
|
|
int CDisplayDlg::ReverseCompareOnFileName(const void *pMessage1, const void *pMessage2)
|
|
{
|
|
CTraceMessage *pTraceMessage1 = *(CTraceMessage **)pMessage1;
|
|
CTraceMessage *pTraceMessage2 = *(CTraceMessage **)pMessage2;
|
|
|
|
return(pTraceMessage2->m_GuidTypeName.Compare(pTraceMessage1->m_GuidTypeName));
|
|
}
|
|
|
|
int CDisplayDlg::ReverseCompareOnLineNumber(const void *pMessage1, const void *pMessage2)
|
|
{
|
|
CTraceMessage *pTraceMessage1 = *(CTraceMessage **)pMessage1;
|
|
CTraceMessage *pTraceMessage2 = *(CTraceMessage **)pMessage2;
|
|
CString string1;
|
|
CString string2;
|
|
DWORD val1, val2;
|
|
|
|
//
|
|
// Get Line number string for the first element
|
|
//
|
|
string1 =
|
|
pTraceMessage1->m_GuidTypeName.Right(pTraceMessage1->m_GuidTypeName.GetLength() -
|
|
pTraceMessage1->m_GuidTypeName.Find('_') - 1);
|
|
|
|
string1 =
|
|
string1.Right(string1.GetLength() -
|
|
string1.FindOneOf(_T("0123456789")));
|
|
|
|
string2 =
|
|
pTraceMessage2->m_GuidTypeName.Right(pTraceMessage2->m_GuidTypeName.GetLength() -
|
|
pTraceMessage2->m_GuidTypeName.Find('_') - 1);
|
|
|
|
//
|
|
// Get the line number string for the second element
|
|
//
|
|
string2 =
|
|
string2.Right(string2.GetLength() -
|
|
string2.FindOneOf(_T("0123456789")));
|
|
|
|
TCHAR * end;
|
|
|
|
val1 = _tcstoul(string1, &end, 10);
|
|
val2 = _tcstoul(string2, &end,10);
|
|
|
|
if(val1 == val2) {
|
|
return(0);
|
|
}
|
|
|
|
if(val1 > val2) {
|
|
return(-1);
|
|
}
|
|
|
|
return(1);
|
|
}
|
|
|
|
int CDisplayDlg::ReverseCompareOnFunctionName(const void *pMessage1, const void *pMessage2)
|
|
{
|
|
CTraceMessage *pTraceMessage1 = *(CTraceMessage **)pMessage1;
|
|
CTraceMessage *pTraceMessage2 = *(CTraceMessage **)pMessage2;
|
|
|
|
return(pTraceMessage2->m_FunctionName.Compare(pTraceMessage1->m_FunctionName));
|
|
}
|
|
|
|
int CDisplayDlg::ReverseCompareOnProcessId(const void *pMessage1, const void *pMessage2)
|
|
{
|
|
CTraceMessage *pTraceMessage1 = *(CTraceMessage **)pMessage1;
|
|
CTraceMessage *pTraceMessage2 = *(CTraceMessage **)pMessage2;
|
|
|
|
return((LONG)pTraceMessage2->m_ProcessId - (LONG)pTraceMessage1->m_ProcessId);
|
|
}
|
|
|
|
int CDisplayDlg::ReverseCompareOnThreadId(const void *pMessage1, const void *pMessage2)
|
|
{
|
|
CTraceMessage *pTraceMessage1 = *(CTraceMessage **)pMessage1;
|
|
CTraceMessage *pTraceMessage2 = *(CTraceMessage **)pMessage2;
|
|
|
|
return((LONG)pTraceMessage2->m_ThreadId - (LONG)pTraceMessage1->m_ThreadId);
|
|
}
|
|
|
|
int CDisplayDlg::ReverseCompareOnCpuNumber(const void *pMessage1, const void *pMessage2)
|
|
{
|
|
CTraceMessage *pTraceMessage1 = *(CTraceMessage **)pMessage1;
|
|
CTraceMessage *pTraceMessage2 = *(CTraceMessage **)pMessage2;
|
|
|
|
return((LONG)pTraceMessage2->m_CpuNumber - (LONG)pTraceMessage1->m_CpuNumber);
|
|
}
|
|
|
|
int CDisplayDlg::ReverseCompareOnSeqNumber(const void *pMessage1, const void *pMessage2)
|
|
{
|
|
CTraceMessage *pTraceMessage1 = *(CTraceMessage **)pMessage1;
|
|
CTraceMessage *pTraceMessage2 = *(CTraceMessage **)pMessage2;
|
|
|
|
return((LONG)pTraceMessage2->m_SequenceNum - (LONG)pTraceMessage1->m_SequenceNum);
|
|
}
|
|
|
|
int CDisplayDlg::ReverseCompareOnSystemTime(const void *pMessage1, const void *pMessage2)
|
|
{
|
|
CTraceMessage *pTraceMessage1 = *(CTraceMessage **)pMessage1;
|
|
CTraceMessage *pTraceMessage2 = *(CTraceMessage **)pMessage2;
|
|
FILETIME timea, timeb;
|
|
BOOL res;
|
|
|
|
res = SystemTimeToFileTime(&pTraceMessage1->m_SystemTime, &timea);
|
|
res = SystemTimeToFileTime(&pTraceMessage2->m_SystemTime, &timeb);
|
|
|
|
return(CompareFileTime(&timeb, &timea));
|
|
}
|
|
|
|
int CDisplayDlg::ReverseCompareOnKernelTime(const void *pMessage1, const void *pMessage2)
|
|
{
|
|
CTraceMessage *pTraceMessage1 = *(CTraceMessage **)pMessage1;
|
|
CTraceMessage *pTraceMessage2 = *(CTraceMessage **)pMessage2;
|
|
|
|
return(pTraceMessage2->m_KernelTime - pTraceMessage1->m_KernelTime);
|
|
|
|
// return (pTraceMessage2->m_KernelTime.dwHighDateTime - pTraceMessage1->m_KernelTime.dwHighDateTime) ?
|
|
// (pTraceMessage2->m_KernelTime.dwHighDateTime - pTraceMessage1->m_KernelTime.dwHighDateTime) :
|
|
// (pTraceMessage2->m_KernelTime.dwLowDateTime - pTraceMessage1->m_KernelTime.dwLowDateTime);
|
|
}
|
|
|
|
int CDisplayDlg::ReverseCompareOnUserTime(const void *pMessage1, const void *pMessage2)
|
|
{
|
|
CTraceMessage *pTraceMessage1 = *(CTraceMessage **)pMessage1;
|
|
CTraceMessage *pTraceMessage2 = *(CTraceMessage **)pMessage2;
|
|
|
|
return(pTraceMessage2->m_UserTime - pTraceMessage1->m_UserTime);
|
|
// return (pTraceMessage2->m_UserTime.dwHighDateTime - pTraceMessage1->m_UserTime.dwHighDateTime) ?
|
|
// (pTraceMessage2->m_UserTime.dwHighDateTime - pTraceMessage1->m_UserTime.dwHighDateTime) :
|
|
// (pTraceMessage2->m_UserTime.dwLowDateTime - pTraceMessage1->m_UserTime.dwLowDateTime);
|
|
}
|
|
|
|
int CDisplayDlg::ReverseCompareOnIndent(const void *pMessage1, const void *pMessage2)
|
|
{
|
|
CTraceMessage *pTraceMessage1 = *(CTraceMessage **)pMessage1;
|
|
CTraceMessage *pTraceMessage2 = *(CTraceMessage **)pMessage2;
|
|
|
|
return((LONG)pTraceMessage2->m_Indent - (LONG)pTraceMessage1->m_Indent);
|
|
}
|
|
|
|
int CDisplayDlg::ReverseCompareOnFlagsName(const void *pMessage1, const void *pMessage2)
|
|
{
|
|
CTraceMessage *pTraceMessage1 = *(CTraceMessage **)pMessage1;
|
|
CTraceMessage *pTraceMessage2 = *(CTraceMessage **)pMessage2;
|
|
|
|
return(pTraceMessage2->m_FlagsName.Compare(pTraceMessage1->m_FlagsName));
|
|
}
|
|
|
|
int CDisplayDlg::ReverseCompareOnLevelName(const void *pMessage1, const void *pMessage2)
|
|
{
|
|
CTraceMessage *pTraceMessage1 = *(CTraceMessage **)pMessage1;
|
|
CTraceMessage *pTraceMessage2 = *(CTraceMessage **)pMessage2;
|
|
|
|
return(pTraceMessage2->m_LevelName.Compare(pTraceMessage1->m_LevelName));
|
|
}
|
|
|
|
int CDisplayDlg::ReverseCompareOnComponentName(const void *pMessage1, const void *pMessage2)
|
|
{
|
|
CTraceMessage *pTraceMessage1 = *(CTraceMessage **)pMessage1;
|
|
CTraceMessage *pTraceMessage2 = *(CTraceMessage **)pMessage2;
|
|
|
|
return(pTraceMessage2->m_ComponentName.Compare(pTraceMessage1->m_ComponentName));
|
|
}
|
|
|
|
int CDisplayDlg::ReverseCompareOnSubComponentName(const void *pMessage1, const void *pMessage2)
|
|
{
|
|
CTraceMessage *pTraceMessage1 = *(CTraceMessage **)pMessage1;
|
|
CTraceMessage *pTraceMessage2 = *(CTraceMessage **)pMessage2;
|
|
|
|
return(pTraceMessage2->m_SubComponentName.Compare(pTraceMessage1->m_SubComponentName));
|
|
}
|
|
|
|
BOOL CDisplayDlg::BeginTrace(BOOL bUseExisting)
|
|
{
|
|
CTraceSession *pTraceSession;
|
|
CLogSession *pLogSession;
|
|
LONG ii;
|
|
LONG jj;
|
|
CString str;
|
|
BOOL bGroupActive = FALSE;
|
|
|
|
//
|
|
// If the group is already started or being started, just return
|
|
// false
|
|
//
|
|
if(TRUE == SetGroupActive(TRUE)) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// The list head should always be NULL here
|
|
//
|
|
ASSERT(m_pEventListHead == NULL);
|
|
|
|
//
|
|
// Setup the event timer
|
|
//
|
|
m_eventTimer = SetTimer(1, EVENT_TIME_LIMIT, 0);
|
|
|
|
//
|
|
// zero our counts
|
|
//
|
|
m_totalBuffersRead = 0;
|
|
m_totalEventsLost = 0;
|
|
m_totalEventCount = 0;
|
|
|
|
//
|
|
// We set to use the StructuredFormat method in traceprt.dll
|
|
// and we use no prefix on the message. We have to specify no
|
|
// prefix as we cannot otherwise predictably setup the data we
|
|
// get in our event callback.
|
|
//
|
|
SetTraceFormatParameter(ParameterStructuredFormat,UlongToPtr(1));
|
|
SetTraceFormatParameter(ParameterUsePrefix,UlongToPtr(0));
|
|
|
|
//
|
|
// open the output file if writing output
|
|
//
|
|
if(m_bWriteListingFile) {
|
|
m_bListingFileOpen = FALSE;
|
|
if(!m_listingFile.Open(
|
|
m_listingFileName,
|
|
CFile::modeCreate | CFile::modeReadWrite)) {
|
|
AfxMessageBox(_T("Unable To Create Listing File"));
|
|
} else {
|
|
m_bListingFileOpen = TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get the array protection
|
|
//
|
|
WaitForSingleObject(m_hSessionArrayMutex, INFINITE);
|
|
|
|
//
|
|
// Setup the log sessions
|
|
//
|
|
for(ii = 0; ii < m_sessionArray.GetSize(); ii++) {
|
|
|
|
//
|
|
// Get the next CLogSession pointer
|
|
//
|
|
pLogSession = (CLogSession *)m_sessionArray[ii];
|
|
|
|
if(NULL == pLogSession) {
|
|
continue;
|
|
}
|
|
|
|
if(!pLogSession->BeginTrace(bUseExisting)) {
|
|
if(pLogSession->m_bTraceActive) {
|
|
//BUGBUG -- should we be doing this??
|
|
pLogSession->EndTrace();
|
|
}
|
|
|
|
continue;
|
|
}
|
|
bGroupActive = TRUE;
|
|
}
|
|
|
|
//
|
|
// Release the array protection
|
|
//
|
|
ReleaseMutex(m_hSessionArrayMutex);
|
|
|
|
//
|
|
// Show the sessions running
|
|
//
|
|
::PostMessage(m_hMainWnd, WM_USER_UPDATE_LOGSESSION_LIST, 0, 0);
|
|
|
|
//
|
|
// If none of the log sessions started successfully,
|
|
// then return FALSE
|
|
//
|
|
//BUGBUG -- maybe we want to fail if ANY of the sessions didn't start?
|
|
// Also, might need a pop-up here?
|
|
if(!bGroupActive) {
|
|
SetGroupActive(FALSE);
|
|
|
|
SetState(Stopped);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Prepare the format GUIDs and such
|
|
//
|
|
if(!SetupTraceSessions()) {
|
|
AfxMessageBox(_T("Failed To Open Handles For Any Log Sessions In Group"));
|
|
SetGroupActive(FALSE);
|
|
|
|
SetState(Stopped);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
UINT CDisplayDlg::RealTimeEventThread(LPVOID pParam)
|
|
{
|
|
CString str;
|
|
CDisplayDlg *pDisplayDlg = (CDisplayDlg *)pParam;
|
|
CLogSession *pLogSession;
|
|
LONG status;
|
|
CTraceSession *pTraceSession;
|
|
DWORD handleIndex;
|
|
HANDLE handleArray[2];
|
|
|
|
//
|
|
// Setup the handle array, the termination event
|
|
// must always be element zero, so that it's index
|
|
// will be the index returned in case multiple
|
|
// events are set
|
|
//
|
|
handleArray[0] = pDisplayDlg->m_hRealTimeTerminationEvent;
|
|
handleArray[1] = pDisplayDlg->m_hRealTimeProcessingStartEvent;
|
|
|
|
//
|
|
// Our master control loop, loop until we get a termination event
|
|
// signal. The start event signals the loop to call ProcessTrace,
|
|
// the done event is signalled and the WM_USER_TRACE_DONE message
|
|
// posted to signal that CloseTrace was called
|
|
//
|
|
while(1) {
|
|
//
|
|
// Wait on start or termination event
|
|
//
|
|
handleIndex = WaitForMultipleObjects(2,
|
|
handleArray,
|
|
FALSE,
|
|
INFINITE) - WAIT_OBJECT_0;
|
|
//
|
|
// Did we get killed?
|
|
//
|
|
if(0 == handleIndex) {
|
|
//
|
|
// Signal that we are done.
|
|
//
|
|
::PostMessage(pDisplayDlg->GetSafeHwnd(), WM_USER_TRACE_DONE, 0, 0);
|
|
|
|
//
|
|
// Free our string buffer (threads seem to leak if this isn't done??)
|
|
//
|
|
str.Empty();
|
|
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// Reset done event for UpdateSession
|
|
//
|
|
ResetEvent(pDisplayDlg->m_hRealTimeProcessingDoneEvent);
|
|
|
|
//
|
|
// Process the trace events
|
|
//
|
|
status = ProcessTrace(pDisplayDlg->m_traceHandleArray,
|
|
pDisplayDlg->m_traceHandleCount,
|
|
NULL,
|
|
NULL);
|
|
if(ERROR_SUCCESS != status){
|
|
str.Format(_T("ProcessTrace returned error %d, GetLastError() = %d\n"), status, GetLastError());
|
|
AfxMessageBox(str);
|
|
}
|
|
|
|
//
|
|
// We are done, close the trace sessions
|
|
//
|
|
for(LONG ii = 0; ii < pDisplayDlg->m_traceHandleCount; ii++) {
|
|
status = CloseTrace(pDisplayDlg->m_traceHandleArray[ii]);
|
|
if(ERROR_SUCCESS != status){
|
|
str.Format(_T("CloseTrace returned error %d\n"), status);
|
|
AfxMessageBox(str);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Set done event for UpdateSession
|
|
//
|
|
SetEvent(pDisplayDlg->m_hRealTimeProcessingDoneEvent);
|
|
|
|
//
|
|
// Indicate that we are done.
|
|
//
|
|
::PostMessage(pDisplayDlg->GetSafeHwnd(), WM_USER_TRACE_DONE, 0, 0);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
BOOL CDisplayDlg::EndTrace(HANDLE DoneEvent)
|
|
{
|
|
CString str;
|
|
CLogSession *pLogSession;
|
|
LONG ii;
|
|
BOOL bWasActive = FALSE;
|
|
BOOL bWasRealTime = FALSE;
|
|
|
|
//
|
|
// If the group is already stopped, or is stopping, just
|
|
// return FALSE.
|
|
//
|
|
if(TRUE == SetGroupInActive(TRUE)) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Save the caller's event to be set when stopped
|
|
//
|
|
m_hEndTraceEvent = DoneEvent;
|
|
|
|
//
|
|
// Get the array protection
|
|
//
|
|
WaitForSingleObject(m_hSessionArrayMutex, INFINITE);
|
|
|
|
for(ii = 0; ii < m_sessionArray.GetSize(); ii++) {
|
|
pLogSession = (CLogSession *)m_sessionArray[ii];
|
|
|
|
if(NULL == pLogSession) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// If the session is active, stop it
|
|
//
|
|
if(pLogSession->m_bTraceActive) {
|
|
pLogSession->EndTrace();
|
|
|
|
bWasActive = TRUE;
|
|
|
|
if(pLogSession->m_bRealTime) {
|
|
bWasRealTime = TRUE;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// Release the array protection
|
|
//
|
|
ReleaseMutex(m_hSessionArrayMutex);
|
|
|
|
if(bWasActive) {
|
|
//
|
|
// Update the log session state
|
|
//
|
|
::PostMessage(m_hMainWnd, WM_USER_UPDATE_LOGSESSION_LIST, 0, 0);
|
|
|
|
if(!bWasRealTime) {
|
|
//
|
|
// We need to explicitly call OnTraceDone here
|
|
//
|
|
OnTraceDone(NULL, NULL);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
void CDisplayDlg::OnTraceDone(WPARAM wParam, LPARAM lParam)
|
|
{
|
|
CString str;
|
|
CLogSession *pLogSession = NULL;
|
|
|
|
//
|
|
// Set the group state
|
|
//
|
|
SetState(Stopping);
|
|
|
|
//
|
|
// Write the summary file if requested
|
|
//
|
|
if(m_bWriteSummaryFile) {
|
|
WriteSummaryFile();
|
|
}
|
|
|
|
//
|
|
// close the output file if opened
|
|
//
|
|
if(m_bWriteListingFile && m_bListingFileOpen) {
|
|
|
|
m_listingFile.Close();
|
|
|
|
m_bListingFileOpen = FALSE;
|
|
}
|
|
|
|
//
|
|
// Cleanup the event list
|
|
//
|
|
WaitForSingleObject(g_hGetTraceEvent, INFINITE);
|
|
|
|
//
|
|
// The event list head
|
|
//
|
|
CleanupTraceEventList(m_pEventListHead);
|
|
|
|
SetEvent(g_hGetTraceEvent);
|
|
|
|
m_pEventListHead = NULL;
|
|
|
|
//
|
|
// Remove the keys from the hash table
|
|
//
|
|
for(LONG ii = 0; ii < m_sessionArray.GetSize(); ii++) {
|
|
pLogSession = (CLogSession *)m_sessionArray[ii];
|
|
|
|
if(NULL == pLogSession) {
|
|
continue;
|
|
}
|
|
|
|
g_loggerNameToDisplayDlgHash.RemoveKey(pLogSession->GetDisplayName());
|
|
}
|
|
|
|
//
|
|
// Kill the event timer
|
|
//
|
|
KillTimer(m_eventTimer);
|
|
|
|
//
|
|
// Force one last update of the event display
|
|
//
|
|
OnTimer(1);
|
|
|
|
//
|
|
// Set the group as not active
|
|
//
|
|
SetGroupActive(FALSE);
|
|
SetGroupInActive(FALSE);
|
|
|
|
//
|
|
// Set the group state
|
|
//
|
|
SetState(Stopped);
|
|
|
|
if(m_hEndTraceEvent != NULL) {
|
|
SetEvent(m_hEndTraceEvent);
|
|
|
|
m_hEndTraceEvent = NULL;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Updates an active tracing session. Real-Time mode, log file name,
|
|
// flush-time, flags, and maximum buffers can be updated for active
|
|
// sessions.
|
|
//
|
|
BOOL CDisplayDlg::UpdateSession(CLogSession *pLogSession)
|
|
{
|
|
CLogSession *pLog;
|
|
PEVENT_TRACE_PROPERTIES pQueryProperties;
|
|
ULONG sizeNeeded;
|
|
LPTSTR pLoggerName;
|
|
LPTSTR pCurrentLogFileName;
|
|
LPTSTR pLogFileName;
|
|
ULONG flags;
|
|
ULONG level;
|
|
ULONG status;
|
|
CString logFileName;
|
|
CString str;
|
|
|
|
//
|
|
// setup our buffer size for the properties struct for our query
|
|
//
|
|
sizeNeeded = sizeof(EVENT_TRACE_PROPERTIES) + (2 * 1024 * sizeof(TCHAR));
|
|
|
|
//
|
|
// allocate our memory
|
|
//
|
|
pQueryProperties = (PEVENT_TRACE_PROPERTIES) new char[sizeNeeded];
|
|
if(NULL == pQueryProperties) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// zero our structures
|
|
//
|
|
memset(pQueryProperties, 0, sizeNeeded);
|
|
|
|
//
|
|
// Set the size
|
|
//
|
|
pQueryProperties->Wnode.BufferSize = sizeNeeded;
|
|
|
|
//
|
|
// Set the GUID
|
|
//
|
|
pQueryProperties->Wnode.Flags = WNODE_FLAG_TRACED_GUID;
|
|
|
|
//
|
|
// Set the logger name offset
|
|
//
|
|
pQueryProperties->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES);
|
|
|
|
//
|
|
// Set the logger name for the query
|
|
//
|
|
pLoggerName = (LPTSTR)((char*)pQueryProperties + pQueryProperties->LoggerNameOffset);
|
|
_tcscpy(pLoggerName, pLogSession->GetDisplayName());
|
|
|
|
//
|
|
// Set the log file name offset
|
|
//
|
|
pQueryProperties->LogFileNameOffset = sizeof(EVENT_TRACE_PROPERTIES) + (500 * sizeof(TCHAR));
|
|
|
|
//
|
|
// Get the log file name pointers
|
|
//
|
|
pCurrentLogFileName = (LPTSTR)((char*)pQueryProperties + pQueryProperties->LogFileNameOffset);
|
|
|
|
//
|
|
// Query the log session
|
|
//
|
|
status = ControlTrace(0,
|
|
pLogSession->GetDisplayName(),
|
|
pQueryProperties,
|
|
EVENT_TRACE_CONTROL_QUERY);
|
|
|
|
if(ERROR_SUCCESS != status) {
|
|
str.Format(_T("Query Failed For Log Session, %d\nSession Not Updated"), status);
|
|
AfxMessageBox(str);
|
|
}
|
|
|
|
//
|
|
// Call the log session's UpdateSession function
|
|
//
|
|
if(!pLogSession->UpdateSession(pQueryProperties)) {
|
|
delete [] pQueryProperties;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Get the array protection
|
|
//
|
|
WaitForSingleObject(m_hSessionArrayMutex, INFINITE);
|
|
|
|
for(LONG ii = 0; ii < m_sessionArray.GetSize(); ii++) {
|
|
pLog = (CLogSession *)m_sessionArray[ii];
|
|
|
|
if(pLog == pLogSession) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// In our real-time thread we call ProcessTrace. If all sessions
|
|
// related to that call update to be non-real-time sessions, then
|
|
// ProcessTrace will exit and CloseTrace will get called. We have
|
|
// to track this and restart the real-time processing if a log session
|
|
// subsequently updates to be real-time again. So, we check here if
|
|
// any other attached sessions are real-time, and if so, we don't
|
|
// have to bother with waiting on or setting the events below.
|
|
//
|
|
if(pLog->m_bRealTime) {
|
|
delete [] pQueryProperties;
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Release the array protection
|
|
//
|
|
ReleaseMutex(m_hSessionArrayMutex);
|
|
|
|
//
|
|
// If real-time is specified and the session was not real-time before,
|
|
// then we need to set the start event.
|
|
//
|
|
if(pLogSession->m_bRealTime &&
|
|
!(pQueryProperties->LogFileMode & EVENT_TRACE_REAL_TIME_MODE)) {
|
|
//
|
|
// We have updated a session to be real-time and it is the only session in
|
|
// its group to be real-time. We need to restart the trace in this case
|
|
//
|
|
|
|
//
|
|
// Start the real-time thread processing again
|
|
//
|
|
BeginTrace(TRUE);
|
|
}
|
|
|
|
//
|
|
// If real-time is not set and it was before, then we need to wait on
|
|
// the real-time thread to catch the fact that the session is no longer
|
|
// tracing real time. This will cause the ProcessTrace call to
|
|
// return in the real-time thread.
|
|
//
|
|
if(!pLogSession->m_bRealTime &&
|
|
(pQueryProperties->LogFileMode & EVENT_TRACE_REAL_TIME_MODE)) {
|
|
//
|
|
// Make sure our thread catches the stop
|
|
//
|
|
WaitForSingleObject(m_hRealTimeProcessingDoneEvent, INFINITE);
|
|
}
|
|
|
|
delete [] pQueryProperties;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CDisplayDlg::SetupTraceSessions()
|
|
{
|
|
CTraceSession *pTraceSession;
|
|
CLogSession *pLogSession;
|
|
LONG jj;
|
|
CString str;
|
|
LPTSTR tmfFile;
|
|
PVOID pHashEntry;
|
|
|
|
//
|
|
// Initialize our handle count
|
|
//
|
|
m_traceHandleCount = 0;
|
|
|
|
//
|
|
// Get the array protection
|
|
//
|
|
WaitForSingleObject(m_hSessionArrayMutex, INFINITE);
|
|
|
|
//
|
|
// Setup the log sessions
|
|
//
|
|
for(LONG ii = 0; ii < m_sessionArray.GetSize(); ii++) {
|
|
|
|
//
|
|
// Get the next CLogSession pointer
|
|
//
|
|
pLogSession = (CLogSession *)m_sessionArray[ii];
|
|
|
|
if(NULL == pLogSession) {
|
|
continue;
|
|
}
|
|
|
|
|
|
//
|
|
// If the log session is not real-time and we are not dumping an existing
|
|
// logfile, then no need to call OpenTrace
|
|
//
|
|
if((!pLogSession->m_bRealTime) && (!pLogSession->m_bDisplayExistingLogFileOnly)) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// zero the EVENT_TRACE_LOGFILE buffer
|
|
//
|
|
memset(&pLogSession->m_evmFile, 0, sizeof(EVENT_TRACE_LOGFILE));
|
|
|
|
//
|
|
// setup the trace parameters
|
|
//
|
|
|
|
//
|
|
// For real-time we set the logger name only, for existing logfiles we
|
|
// also set the logfile name.
|
|
//
|
|
if(pLogSession->m_bDisplayExistingLogFileOnly) {
|
|
pLogSession->m_evmFile.LogFileName = (LPTSTR)(LPCTSTR)pLogSession->m_logFileName;
|
|
pLogSession->m_evmFile.LoggerName = (LPTSTR)(LPCTSTR)pLogSession->GetDisplayName();
|
|
} else {
|
|
pLogSession->m_evmFile.LoggerName = (LPTSTR)(LPCTSTR)pLogSession->GetDisplayName();
|
|
}
|
|
pLogSession->m_evmFile.Context = NULL;
|
|
pLogSession->m_evmFile.BufferCallback = (PEVENT_TRACE_BUFFER_CALLBACK)BufferCallback;
|
|
pLogSession->m_evmFile.BuffersRead = 0;
|
|
pLogSession->m_evmFile.CurrentTime = 0;
|
|
pLogSession->m_evmFile.EventCallback = m_pEventCallback;
|
|
if(pLogSession->m_bDisplayExistingLogFileOnly) {
|
|
pLogSession->m_evmFile.LogFileMode = EVENT_TRACE_FILE_MODE_SEQUENTIAL;
|
|
} else {
|
|
pLogSession->m_evmFile.LogFileMode = EVENT_TRACE_REAL_TIME_MODE;
|
|
}
|
|
|
|
//
|
|
// We call GetTraceGuids for each TMF file specified, or once if
|
|
// a TMF path is used.
|
|
//
|
|
for(jj = 0; jj < pLogSession->m_traceSessionArray.GetSize(); jj++) {
|
|
pTraceSession = (CTraceSession *)pLogSession->m_traceSessionArray[jj];
|
|
|
|
if((NULL != pTraceSession) && (0 != pTraceSession->m_tmfFile.GetSize())) {
|
|
//
|
|
// TMFs were selected get the format info from each one.
|
|
//
|
|
for(LONG kk = 0; kk < pTraceSession->m_tmfFile.GetSize(); kk++) {
|
|
|
|
//
|
|
// Make sure the format info isn't already in use
|
|
// This actually isn't foolproof, as it counts on
|
|
// no one changing the TMF filename and thus two TMFs
|
|
// with different names having the same format GUID.
|
|
//
|
|
if(g_formatInfoHash.Lookup(pTraceSession->m_tmfFile[kk].Right(40), pHashEntry)) {
|
|
//
|
|
// Need to clean out any unique entries from the hash that
|
|
// were added by this session
|
|
//
|
|
for(; kk > 0; kk--) {
|
|
g_formatInfoHash.RemoveKey(pTraceSession->m_tmfFile[kk - 1].Right(40));
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Not there, so add it to the hash
|
|
//
|
|
g_formatInfoHash.SetAt(pTraceSession->m_tmfFile[kk].Right(40), pLogSession);
|
|
|
|
//
|
|
// get the protection
|
|
//
|
|
WaitForSingleObject(g_hGetTraceEvent, INFINITE);
|
|
|
|
//
|
|
// Get the format GUIDs from the TMF file(s)
|
|
//
|
|
if(0 == GetTraceGuids((LPTSTR)(LPCTSTR)pTraceSession->m_tmfFile[kk],
|
|
(PLIST_ENTRY *)&m_pEventListHead)) {
|
|
str.Format(_T("Failed To Get Format Information For Logger %ls"), pLogSession->m_evmFile.LoggerName);
|
|
AfxMessageBox(str);
|
|
}
|
|
|
|
//
|
|
// Release the protection
|
|
//
|
|
SetEvent(g_hGetTraceEvent);
|
|
}
|
|
} else {
|
|
|
|
CString defaultPath;
|
|
|
|
if((GetModuleFileName(NULL, defaultPath.GetBuffer(500), 500)) == 0) {
|
|
defaultPath.Empty();
|
|
} else {
|
|
defaultPath = (LPCTSTR)defaultPath.Left(defaultPath.ReverseFind('\\') + 1);
|
|
}
|
|
defaultPath +=_T("default.tmf");
|
|
|
|
//
|
|
// If path is empty, the TMF path environment variable will
|
|
// be used as-is by default.
|
|
//
|
|
if(!pTraceSession->m_tmfPath.IsEmpty()){
|
|
//
|
|
// If the path is set we set the TMF path
|
|
// environment variable
|
|
//
|
|
SetTraceFormatParameter(ParameterTraceFormatSearchPath, (PVOID)(LPCTSTR)pTraceSession->m_tmfPath);
|
|
}
|
|
|
|
//
|
|
// Still have to call GetTraceGuids
|
|
//
|
|
WaitForSingleObject(g_hGetTraceEvent, INFINITE);
|
|
|
|
//
|
|
// Get the format info
|
|
//
|
|
GetTraceGuids((LPTSTR)(LPCTSTR)defaultPath,
|
|
(PLIST_ENTRY *)&m_pEventListHead);
|
|
|
|
//
|
|
// Release the protection
|
|
//
|
|
SetEvent(g_hGetTraceEvent);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Open and get a handle to the trace session
|
|
//
|
|
pLogSession->m_traceHandle = OpenTrace(&pLogSession->m_evmFile);
|
|
|
|
if(INVALID_HANDLE_VALUE == (HANDLE)pLogSession->m_traceHandle) {
|
|
str.Format(_T("OpenTrace failed for logger %ls\nError returned: %d"),
|
|
pLogSession->GetDisplayName(),
|
|
GetLastError());
|
|
AfxMessageBox(str);
|
|
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Save the handle in our array for OpenTrace
|
|
//
|
|
m_traceHandleArray[m_traceHandleCount++] = pLogSession->m_traceHandle;
|
|
|
|
//
|
|
// Add this displayDlg to the global hash table keyed
|
|
// by log session name
|
|
//
|
|
g_loggerNameToDisplayDlgHash.SetAt(pLogSession->GetDisplayName(), this);
|
|
}
|
|
|
|
//
|
|
// Release the array protection
|
|
//
|
|
ReleaseMutex(m_hSessionArrayMutex);
|
|
|
|
//
|
|
// If we have at least one valid handle start
|
|
// our real-time thread processing events
|
|
//
|
|
if(m_traceHandleCount != 0) {
|
|
//
|
|
// Set the real-time processing start event and thus kick
|
|
// the real-time thread into processing events
|
|
//
|
|
SetEvent(m_hRealTimeProcessingStartEvent);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// This function handles writing the summary file. Heavily borrowed code
|
|
// from TraceFmt here.
|
|
//
|
|
VOID CDisplayDlg::WriteSummaryFile()
|
|
{
|
|
CLogSession *pLogSession;
|
|
CString str;
|
|
CStdioFile summaryFile;
|
|
TCHAR *pSummaryBlock;
|
|
__int64 elapsedTime;
|
|
LONG ii;
|
|
|
|
//
|
|
// Attempt to open the summary file
|
|
//
|
|
if(!summaryFile.Open(m_summaryFileName, CFile::modeCreate |
|
|
CFile::modeReadWrite)) {
|
|
AfxMessageBox(_T("Unable To Create Summary File"));
|
|
return;
|
|
}
|
|
|
|
//
|
|
// allocate some memory
|
|
//
|
|
pSummaryBlock = new TCHAR[SIZESUMMARYBLOCK];
|
|
if(NULL == pSummaryBlock) {
|
|
AfxMessageBox(_T("Unable To Create Summary File"));
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Write the header
|
|
//
|
|
str.Format(_T("Files Processed:\n"));
|
|
summaryFile.WriteString(str);
|
|
|
|
//
|
|
// Get the array protection
|
|
//
|
|
WaitForSingleObject(m_hSessionArrayMutex, INFINITE);
|
|
|
|
for(ii = 0; ii < m_sessionArray.GetSize(); ii++) {
|
|
pLogSession = (CLogSession *)m_sessionArray[ii];
|
|
|
|
if(NULL == pLogSession) {
|
|
continue;
|
|
}
|
|
|
|
str.Format(_T("\t%s\n"),pLogSession->m_logFileName);
|
|
summaryFile.WriteString(str);
|
|
}
|
|
|
|
//
|
|
// Release the array protection
|
|
//
|
|
ReleaseMutex(m_hSessionArrayMutex);
|
|
|
|
//
|
|
// Get the duration of the session
|
|
//
|
|
GetTraceElapseTime(&elapsedTime);
|
|
|
|
//
|
|
// Write out the counts
|
|
//
|
|
str.Format(
|
|
_T("Total Buffers Processed %d\n")
|
|
_T("Total Events Processed %d\n")
|
|
_T("Total Events Lost %d\n")
|
|
_T("Elapsed Time %I64d sec\n"),
|
|
m_totalBuffersRead,
|
|
m_totalEventCount,
|
|
m_totalEventsLost,
|
|
(elapsedTime / 10000000) );
|
|
summaryFile.WriteString(str);
|
|
|
|
str.Format(
|
|
_T("+--------------------------------------------------------------------------------------+\n")
|
|
_T("|%10s %-20s %-10s %-36s|\n")
|
|
_T("+--------------------------------------------------------------------------------------+\n"),
|
|
_T("EventCount"),
|
|
_T("EventName"),
|
|
_T("EventType"),
|
|
_T("Guid"));
|
|
summaryFile.WriteString(str);
|
|
|
|
//
|
|
// Write out the summary block
|
|
//
|
|
SummaryTraceEventList(pSummaryBlock, SIZESUMMARYBLOCK, m_pEventListHead);
|
|
str.Format(_T("%s+--------------------------------------------------------------------------------------+\n"),
|
|
pSummaryBlock);
|
|
summaryFile.WriteString(str);
|
|
|
|
//
|
|
// Close the file
|
|
//
|
|
summaryFile.Close();
|
|
|
|
//
|
|
// Free our memory
|
|
//
|
|
delete [] pSummaryBlock;
|
|
}
|
|
|
|
ULONG WINAPI BufferCallback(PEVENT_TRACE_LOGFILE pLog)
|
|
{
|
|
CDisplayDlg *pDisplayDlg;
|
|
CString str;
|
|
|
|
//
|
|
// Get the logger name from the passed in struct
|
|
//
|
|
str.Format(_T("%s"), pLog->LoggerName);
|
|
|
|
//
|
|
// Get our logsession from the global hash table using the logger name
|
|
//
|
|
g_loggerNameToDisplayDlgHash.Lookup(str, (void*&)pDisplayDlg);
|
|
|
|
//
|
|
// If we got our displayDlg, update the counts
|
|
//
|
|
if(NULL != pDisplayDlg) {
|
|
pDisplayDlg->m_totalBuffersRead++;
|
|
pDisplayDlg->m_totalEventsLost += pLog->EventsLost;
|
|
|
|
::PostMessage(pDisplayDlg->m_hMainWnd, WM_USER_UPDATE_LOGSESSION_LIST, 0, 0);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Sort handler
|
|
VOID CDisplayDlg::OnDoSort(NMHDR * pNmhdr, LRESULT * pResult)
|
|
{
|
|
NMLISTVIEW *pLV = (NMLISTVIEW *)pNmhdr;
|
|
|
|
//
|
|
// Sort the table by the selected column's data
|
|
//
|
|
SortTable(pLV->iItem);
|
|
|
|
//
|
|
// Force a redraw of the data
|
|
//
|
|
m_displayCtrl.RedrawItems(m_displayCtrl.GetTopIndex(),
|
|
m_displayCtrl.GetTopIndex() + m_displayCtrl.GetCountPerPage());
|
|
|
|
m_displayCtrl.UpdateWindow();
|
|
}
|
|
|
|
|
|
//
|
|
// EventHandler() is only a wrapper, it calls FormatTraceEvent() in TracePrt.
|
|
//
|
|
VOID CDisplayDlg::EventHandler(PEVENT_TRACE pEvent)
|
|
{
|
|
CString str;
|
|
CString tempString;
|
|
GUID guidValue;
|
|
CString guidString;
|
|
PSTRUCTUREDMESSAGE pStructuredMessage;
|
|
CTraceMessage *pTraceMessage;
|
|
DWORD itemCount;
|
|
DWORD time;
|
|
CTraceViewApp *pMainWnd;
|
|
|
|
//
|
|
// Make sure we got an event
|
|
//
|
|
if (NULL == pEvent) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Get traceprt protection
|
|
//
|
|
WaitForSingleObject(g_hGetTraceEvent, INFINITE);
|
|
|
|
//
|
|
// FormatTraceEvent fills in our event buffer and updates some
|
|
// info in the event list for the summary file
|
|
//
|
|
if(FormatTraceEvent(m_pEventListHead,
|
|
pEvent,
|
|
m_pEventBuf,
|
|
EVENT_BUFFER_SIZE,
|
|
NULL) <= 0) {
|
|
|
|
//
|
|
// Release traceprt protection
|
|
//
|
|
SetEvent(g_hGetTraceEvent);
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Release traceprt protection
|
|
//
|
|
SetEvent(g_hGetTraceEvent);
|
|
|
|
//
|
|
// Get the structured message struct from the event buffer
|
|
//
|
|
pStructuredMessage = (PSTRUCTUREDMESSAGE)&m_pEventBuf[0];
|
|
|
|
//
|
|
// Get a pointer to the main wnd
|
|
//
|
|
pMainWnd = (CTraceViewApp *)AfxGetApp();
|
|
|
|
if(NULL == pMainWnd) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Allocate a message struct from the main frame
|
|
//
|
|
pTraceMessage = pMainWnd->AllocateTraceEventBlock();
|
|
|
|
if(NULL == pTraceMessage) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Get the message from the event buffer
|
|
//
|
|
memcpy(&pTraceMessage->m_TraceGuid,
|
|
&pStructuredMessage->TraceGuid,
|
|
sizeof(GUID));
|
|
|
|
//
|
|
// Copy the message struct to our own struct
|
|
//
|
|
pTraceMessage->m_GuidName =
|
|
(pStructuredMessage->GuidName ? &m_pEventBuf[pStructuredMessage->GuidName/sizeof(TCHAR)] :_T(""));
|
|
|
|
pTraceMessage->m_GuidTypeName.Format(_T("%s"),
|
|
(pStructuredMessage->GuidTypeName ? &m_pEventBuf[pStructuredMessage->GuidTypeName/sizeof(TCHAR)] :_T("")));
|
|
|
|
pTraceMessage->m_ThreadId = pStructuredMessage->ThreadId;
|
|
|
|
memcpy(&pTraceMessage->m_SystemTime,
|
|
&pStructuredMessage->SystemTime,
|
|
sizeof(SYSTEMTIME));
|
|
|
|
memcpy(&pTraceMessage->m_UserTime,
|
|
&pStructuredMessage->UserTime,
|
|
sizeof(ULONG));
|
|
|
|
memcpy(&pTraceMessage->m_KernelTime,
|
|
&pStructuredMessage->KernelTime,
|
|
sizeof(ULONG));
|
|
|
|
pTraceMessage->m_SequenceNum = pStructuredMessage->SequenceNum;
|
|
|
|
pTraceMessage->m_ProcessId = pStructuredMessage->ProcessId;
|
|
|
|
pTraceMessage->m_CpuNumber = pStructuredMessage->CpuNumber;
|
|
|
|
pTraceMessage->m_Indent = pStructuredMessage->Indent;
|
|
|
|
pTraceMessage->m_FlagsName.Format(_T("%s"),
|
|
(pStructuredMessage->FlagsName ? &m_pEventBuf[pStructuredMessage->FlagsName/sizeof(TCHAR)] :_T("")));
|
|
|
|
pTraceMessage->m_LevelName.Format(_T("%s"),
|
|
(pStructuredMessage->LevelName ? &m_pEventBuf[pStructuredMessage->LevelName/sizeof(TCHAR)] :_T("")));
|
|
|
|
pTraceMessage->m_FunctionName.Format(_T("%s"),
|
|
(pStructuredMessage->FunctionName ? &m_pEventBuf[pStructuredMessage->FunctionName/sizeof(TCHAR)] :_T("")));
|
|
|
|
pTraceMessage->m_ComponentName.Format(_T("%s"),
|
|
(pStructuredMessage->ComponentName ? &m_pEventBuf[pStructuredMessage->ComponentName/sizeof(TCHAR)] :_T("")));
|
|
|
|
pTraceMessage->m_SubComponentName.Format(_T("%s"),
|
|
(pStructuredMessage->SubComponentName ? &m_pEventBuf[pStructuredMessage->SubComponentName/sizeof(TCHAR)] :_T("")));
|
|
|
|
pTraceMessage->m_Message.Format(_T("%s"),
|
|
(pStructuredMessage->FormattedString ? &m_pEventBuf[pStructuredMessage->FormattedString/sizeof(TCHAR)] :_T("")));
|
|
|
|
//
|
|
// Get our trace event array protection
|
|
//
|
|
WaitForSingleObject(m_hTraceEventMutex,INFINITE);
|
|
|
|
//
|
|
// If we're in a low memory situation, drop one entry for every one we add
|
|
//
|
|
if( m_traceArray.GetSize() > MaxTraceEntries) {
|
|
|
|
delete m_traceArray.GetAt(0);
|
|
|
|
//
|
|
// Slide 'em all down...
|
|
//
|
|
m_traceArray.RemoveAt(0);
|
|
}
|
|
|
|
//
|
|
// Add the event to the array
|
|
//
|
|
m_traceArray.Add(pTraceMessage);
|
|
|
|
//
|
|
// Update the item count for the list control so that
|
|
// the display gets updated
|
|
//
|
|
itemCount = (LONG)m_traceArray.GetSize();
|
|
|
|
//
|
|
// Release our trace event array protection
|
|
//
|
|
ReleaseMutex(m_hTraceEventMutex);
|
|
|
|
//
|
|
// dump the event to the output file if any
|
|
//
|
|
if(m_bWriteListingFile &&
|
|
m_bListingFileOpen) {
|
|
//
|
|
// Glorious hack to get FILETIME back into string format
|
|
// Not sure this works for different time zones or daylight savings
|
|
//
|
|
CTime kernelTime(pStructuredMessage->KernelTime);
|
|
CTime userTime(pStructuredMessage->UserTime);
|
|
|
|
CString system;
|
|
CString kernel;
|
|
CString user;
|
|
|
|
system.Format(_T("%02d\\%02d\\%4d-%02d:%02d:%02d"),
|
|
pStructuredMessage->SystemTime.wMonth,
|
|
pStructuredMessage->SystemTime.wDay,
|
|
pStructuredMessage->SystemTime.wYear,
|
|
pStructuredMessage->SystemTime.wHour,
|
|
pStructuredMessage->SystemTime.wMinute,
|
|
pStructuredMessage->SystemTime.wSecond,
|
|
pStructuredMessage->SystemTime.wMilliseconds);
|
|
|
|
if(kernelTime.GetYear() == 1969) {
|
|
kernel.Format(_T(" "));
|
|
} else {
|
|
kernel.Format(_T("%s-%02d:%02d:%02d"),
|
|
kernelTime.Format("%m\\%d\\%Y"),
|
|
kernelTime.GetHour() + 5,
|
|
kernelTime.GetMinute(),
|
|
kernelTime.GetSecond());
|
|
}
|
|
|
|
if(userTime.GetYear() == 1969) {
|
|
user.Format(_T(" "));
|
|
} else {
|
|
user.Format(_T("%s-%02d:%02d:%02d"),
|
|
userTime.Format("%m\\%d\\%Y"),
|
|
userTime.GetHour() + 5,
|
|
userTime.GetMinute(),
|
|
userTime.GetSecond());
|
|
}
|
|
|
|
//
|
|
// Need to reformat with good timestamps for the output file.
|
|
// Output Order --> Name FileName_Line# FunctionName ProcessID ThreadID
|
|
// CPU# Seq# SystemTime KernelTime UserTime Indent
|
|
// FlagsName LevelName ComponentName SubComponentName
|
|
// Message at end
|
|
//
|
|
str.Format(_T("%s %s %s %X %X %d %u %s %s %s %s %s %s %s %s %s"),
|
|
(pStructuredMessage->GuidName ? &m_pEventBuf[pStructuredMessage->GuidName/sizeof(TCHAR)] :_T("")),
|
|
(pStructuredMessage->GuidTypeName ? &m_pEventBuf[pStructuredMessage->GuidTypeName/sizeof(TCHAR)] :_T("")),
|
|
(pStructuredMessage->FunctionName ? &m_pEventBuf[pStructuredMessage->FunctionName/sizeof(TCHAR)] :_T("")),
|
|
pStructuredMessage->ProcessId,
|
|
pStructuredMessage->ThreadId,
|
|
pStructuredMessage->CpuNumber,
|
|
pStructuredMessage->SequenceNum,
|
|
system,
|
|
kernel,
|
|
user,
|
|
(pStructuredMessage->Indent ? &m_pEventBuf[pStructuredMessage->Indent/sizeof(TCHAR)] :_T("")),
|
|
(pStructuredMessage->FlagsName ? &m_pEventBuf[pStructuredMessage->FlagsName/sizeof(TCHAR)] :_T("")),
|
|
(pStructuredMessage->LevelName ? &m_pEventBuf[pStructuredMessage->LevelName/sizeof(TCHAR)] :_T("")),
|
|
(pStructuredMessage->ComponentName ? &m_pEventBuf[pStructuredMessage->ComponentName/sizeof(TCHAR)] :_T("")),
|
|
(pStructuredMessage->SubComponentName ? &m_pEventBuf[pStructuredMessage->SubComponentName/sizeof(TCHAR)] :_T("")),
|
|
(pStructuredMessage->FormattedString ? &m_pEventBuf[pStructuredMessage->FormattedString/sizeof(TCHAR)] :_T("")));
|
|
|
|
str +=_T("\n");
|
|
m_listingFile.WriteString(str);
|
|
}
|
|
|
|
//
|
|
// Update the event count for the summary file
|
|
//
|
|
m_totalEventCount++;
|
|
|
|
//
|
|
// Empty the string buffer (not strictly necessary)
|
|
//
|
|
str.Empty();
|
|
}
|
|
|
|
VOID CDisplayDlg::OnTimer(UINT nIDEvent)
|
|
{
|
|
int itemCount;
|
|
static int previousCount = 0;
|
|
static BOOL bAutoSizeOnce = TRUE;
|
|
static CTraceMessage * msgLastSeen=0;
|
|
|
|
//
|
|
// This is a really bad hack.
|
|
// Couldn't figure out anyway to get notified when
|
|
// the DisplayDlg window has actually been displayed.
|
|
// Until it is displayed, then AutoSizeColumns won't
|
|
// work correctly for the last column. Calling it here
|
|
// seems to fix the problem. There has to be a better way.
|
|
//
|
|
if(bAutoSizeOnce) {
|
|
AutoSizeColumns();
|
|
bAutoSizeOnce = FALSE;
|
|
}
|
|
|
|
//
|
|
// Get our trace event array protection
|
|
//
|
|
WaitForSingleObject(m_hTraceEventMutex,INFINITE);
|
|
|
|
//
|
|
// Update the item count for the list control so that
|
|
// the display gets updated
|
|
//
|
|
itemCount = (LONG)m_traceArray.GetSize();
|
|
|
|
//
|
|
// Release our trace event array protection
|
|
//
|
|
ReleaseMutex(m_hTraceEventMutex);
|
|
|
|
if(itemCount == 0) {
|
|
msgLastSeen = NULL;
|
|
return;
|
|
}
|
|
|
|
if( itemCount <= MaxTraceEntries) {
|
|
|
|
if( itemCount == previousCount ) {
|
|
return;
|
|
}
|
|
|
|
} else {
|
|
|
|
if(m_traceArray.GetAt(0) == msgLastSeen) {
|
|
return;
|
|
}
|
|
|
|
msgLastSeen = m_traceArray.GetAt(0);
|
|
}
|
|
|
|
previousCount = itemCount;
|
|
|
|
//
|
|
// Sort the table of event messages
|
|
//
|
|
SortTable();
|
|
|
|
//
|
|
// Adjust the trace display item count
|
|
//
|
|
m_displayCtrl.SetItemCountEx(itemCount,
|
|
LVSICF_NOINVALIDATEALL|LVSICF_NOSCROLL);
|
|
|
|
m_displayCtrl.RedrawItems(m_displayCtrl.GetTopIndex(),
|
|
m_displayCtrl.GetTopIndex() + m_displayCtrl.GetCountPerPage());
|
|
|
|
m_displayCtrl.UpdateWindow();
|
|
|
|
//
|
|
// Ensure that the last item is visible if requested.
|
|
//
|
|
if(m_bShowLatest) {
|
|
m_displayCtrl.EnsureVisible(itemCount - 1,
|
|
FALSE);
|
|
}
|
|
|
|
//
|
|
// Force an update of the event count in the display
|
|
//
|
|
::PostMessage(m_hMainWnd, WM_USER_UPDATE_LOGSESSION_LIST, 0, 0);
|
|
}
|
|
|
|
|
|
|
|
VOID CDisplayDlg::SetState(LOG_SESSION_STATE StateValue)
|
|
{
|
|
CLogSession *pLogSession = NULL;
|
|
|
|
//
|
|
// Get the array protection
|
|
//
|
|
WaitForSingleObject(m_hSessionArrayMutex, INFINITE);
|
|
|
|
for(LONG ii = 0; ii < m_sessionArray.GetSize(); ii++) {
|
|
pLogSession = (CLogSession *)m_sessionArray[ii];
|
|
|
|
if(NULL == pLogSession) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Set the log session's state
|
|
//
|
|
pLogSession->SetState(StateValue);
|
|
}
|
|
|
|
//
|
|
// Release the array protection
|
|
//
|
|
ReleaseMutex(m_hSessionArrayMutex);
|
|
|
|
::PostMessage(m_hMainWnd, WM_USER_UPDATE_LOGSESSION_LIST, 0, 0);
|
|
}
|
|
|
|
//
|
|
// Macro to create our event callback functions. This is only
|
|
// necessary as there is no context we can pass into our event
|
|
// callback function. So, we setup a unique event callback for
|
|
// each log session. We have 64 here, but the number will have
|
|
// to increase to match the total number of possible loggers
|
|
//
|
|
#define MAKE_EVENT_CALLBACK(n) \
|
|
VOID WINAPI DumpEvent##n(PEVENT_TRACE pEvent) \
|
|
{ \
|
|
CDisplayDlg *pDisplayDlg; \
|
|
g_displayIDToDisplayDlgHash.Lookup(n, (void *&)pDisplayDlg);\
|
|
if(NULL != pDisplayDlg) { \
|
|
pDisplayDlg->EventHandler(pEvent); \
|
|
} \
|
|
}
|
|
|
|
//
|
|
// Make 64 of them initially
|
|
//
|
|
MAKE_EVENT_CALLBACK(0);
|
|
MAKE_EVENT_CALLBACK(1);
|
|
MAKE_EVENT_CALLBACK(2);
|
|
MAKE_EVENT_CALLBACK(3);
|
|
MAKE_EVENT_CALLBACK(4);
|
|
MAKE_EVENT_CALLBACK(5);
|
|
MAKE_EVENT_CALLBACK(6);
|
|
MAKE_EVENT_CALLBACK(7);
|
|
MAKE_EVENT_CALLBACK(8);
|
|
MAKE_EVENT_CALLBACK(9);
|
|
MAKE_EVENT_CALLBACK(10);
|
|
MAKE_EVENT_CALLBACK(11);
|
|
MAKE_EVENT_CALLBACK(12);
|
|
MAKE_EVENT_CALLBACK(13);
|
|
MAKE_EVENT_CALLBACK(14);
|
|
MAKE_EVENT_CALLBACK(15);
|
|
MAKE_EVENT_CALLBACK(16);
|
|
MAKE_EVENT_CALLBACK(17);
|
|
MAKE_EVENT_CALLBACK(18);
|
|
MAKE_EVENT_CALLBACK(19);
|
|
MAKE_EVENT_CALLBACK(20);
|
|
MAKE_EVENT_CALLBACK(21);
|
|
MAKE_EVENT_CALLBACK(22);
|
|
MAKE_EVENT_CALLBACK(23);
|
|
MAKE_EVENT_CALLBACK(24);
|
|
MAKE_EVENT_CALLBACK(25);
|
|
MAKE_EVENT_CALLBACK(26);
|
|
MAKE_EVENT_CALLBACK(27);
|
|
MAKE_EVENT_CALLBACK(28);
|
|
MAKE_EVENT_CALLBACK(29);
|
|
MAKE_EVENT_CALLBACK(30);
|
|
MAKE_EVENT_CALLBACK(31);
|
|
MAKE_EVENT_CALLBACK(32);
|
|
MAKE_EVENT_CALLBACK(33);
|
|
MAKE_EVENT_CALLBACK(34);
|
|
MAKE_EVENT_CALLBACK(35);
|
|
MAKE_EVENT_CALLBACK(36);
|
|
MAKE_EVENT_CALLBACK(37);
|
|
MAKE_EVENT_CALLBACK(38);
|
|
MAKE_EVENT_CALLBACK(39);
|
|
MAKE_EVENT_CALLBACK(40);
|
|
MAKE_EVENT_CALLBACK(41);
|
|
MAKE_EVENT_CALLBACK(42);
|
|
MAKE_EVENT_CALLBACK(43);
|
|
MAKE_EVENT_CALLBACK(44);
|
|
MAKE_EVENT_CALLBACK(45);
|
|
MAKE_EVENT_CALLBACK(46);
|
|
MAKE_EVENT_CALLBACK(47);
|
|
MAKE_EVENT_CALLBACK(48);
|
|
MAKE_EVENT_CALLBACK(49);
|
|
MAKE_EVENT_CALLBACK(50);
|
|
MAKE_EVENT_CALLBACK(51);
|
|
MAKE_EVENT_CALLBACK(52);
|
|
MAKE_EVENT_CALLBACK(53);
|
|
MAKE_EVENT_CALLBACK(54);
|
|
MAKE_EVENT_CALLBACK(55);
|
|
MAKE_EVENT_CALLBACK(56);
|
|
MAKE_EVENT_CALLBACK(57);
|
|
MAKE_EVENT_CALLBACK(58);
|
|
MAKE_EVENT_CALLBACK(59);
|
|
MAKE_EVENT_CALLBACK(60);
|
|
MAKE_EVENT_CALLBACK(61);
|
|
MAKE_EVENT_CALLBACK(62);
|
|
MAKE_EVENT_CALLBACK(63);
|