From 58da176c8bf51eac4dc11ab854888b38a394318a Mon Sep 17 00:00:00 2001 From: "andrus.aaslaid" Date: Thu, 5 Dec 2013 13:29:54 +0000 Subject: [PATCH] v1.36 05.07.2013 - Preliminary TX8M support v1.37 17.11.2013 - Added SetRegistryKey("Satrian") in InitInstance() to maintain registry access compatibility with UAC - ExtIO DLL is now a default data method (was audio interfaces earlier) - ExtIORegistryUpdateTask() now writes all changes down after every second and checks update flag in 100ms (was 10sec and 1sec) - ExtIODialog stuff modified to use HWType field, not fetching value from registry. - Debug console initialization and removal moved to Init/ExitInstance() code v1.38 21.11.2013 - Re-layouted GUI to acommodate RF gain control controls - LibUSB-win32 version checking is now relaxed to 1.2.2.0, since we can live with this and Atmel FLIP utility is reverting us to old version. Not nice, but could still be used, so lets not complain. - Replaced all _beginthread stuff with AfxBeginThread() to be more MCF compatible - Panadapter speed slider made to work - OnTimer() disables scroll timer temporarily to avoid any possibility for recursion (although there probably wasnt any) - UI Controls conditional enabling and disabling logic cleaned up. - Included memwatch 2.71 to the build from http://www.linkdata.se/sourcecode/memwatch/ Old, but functional, since our dynamic memory allocation is using ANSI memory functions anyway. - Fixed task scheduling issues related to panadapter and ExtIODataTask() - Eliminated ExtIOCallbackTask() associated double buffering. HDSDR does not block on data callback and therefore callback is possible to initiate straight from ExtIODataTask() v1.39 5.12.2013 - UI converted to tabbed dialog (Although only ExtIO tab is implemented) - Dialog initialization and cleanup revisited and (most of the) leaks fixed --- AdvancedDialog.cpp | 33 + AdvancedDialog.h | 21 + AdvancedDialog.htm | 19 + DialogClasses.cpp | 22 +- DialogClasses.h | 3 + ExtIODialog.cpp | 453 ++++-- ExtIODialog.h | 13 +- ExtIODll.cpp | 100 +- ExtIODll.h | 4 + ExtIODll.rc | 151 +- ExtIODll.vcproj | 61 +- ExtIOFunctions.cpp | 348 +++-- ExtIOFunctions.h | 13 +- ExtIOIQData.cpp | 459 ++---- ExtIO_SDR_MK15.dll | Bin 333312 -> 370176 bytes MainDialog.cpp | 132 ++ MainDialog.h | 33 + PanadapterDialog.cpp | 256 ++-- PanadapterDialog.h | 2 - StdAfx.h | 3 + memwatch-2.71/FAQ | 133 ++ memwatch-2.71/README | 99 ++ memwatch-2.71/USING | 213 +++ memwatch-2.71/gpl.txt | 340 +++++ memwatch-2.71/memwatch.cpp | 2712 ++++++++++++++++++++++++++++++++++++ memwatch-2.71/memwatch.h | 711 ++++++++++ resource.h | 17 +- 27 files changed, 5555 insertions(+), 796 deletions(-) create mode 100644 AdvancedDialog.cpp create mode 100644 AdvancedDialog.h create mode 100644 AdvancedDialog.htm create mode 100644 MainDialog.cpp create mode 100644 MainDialog.h create mode 100644 memwatch-2.71/FAQ create mode 100644 memwatch-2.71/README create mode 100644 memwatch-2.71/USING create mode 100644 memwatch-2.71/gpl.txt create mode 100644 memwatch-2.71/memwatch.cpp create mode 100644 memwatch-2.71/memwatch.h diff --git a/AdvancedDialog.cpp b/AdvancedDialog.cpp new file mode 100644 index 0000000..3df474c --- /dev/null +++ b/AdvancedDialog.cpp @@ -0,0 +1,33 @@ +// AdvancedDialog.cpp : implementation file +// + +#include "stdafx.h" +#include "ExtIODll.h" +#include "AdvancedDialog.h" + + +// CAdvancedDialog dialog + +IMPLEMENT_DYNAMIC(CAdvancedDialog, CDialog) + +CAdvancedDialog::CAdvancedDialog(CWnd* pParent /*=NULL*/) + : CDialog(CAdvancedDialog::IDD, pParent) +{ + +} + +CAdvancedDialog::~CAdvancedDialog() +{ +} + +void CAdvancedDialog::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); +} + + +BEGIN_MESSAGE_MAP(CAdvancedDialog, CDialog) +END_MESSAGE_MAP() + + +// CAdvancedDialog message handlers diff --git a/AdvancedDialog.h b/AdvancedDialog.h new file mode 100644 index 0000000..d8a640e --- /dev/null +++ b/AdvancedDialog.h @@ -0,0 +1,21 @@ +#pragma once + + +// CAdvancedDialog dialog + +class CAdvancedDialog : public CDialog +{ + DECLARE_DYNAMIC(CAdvancedDialog) + +public: + CAdvancedDialog(CWnd* pParent = NULL); // standard constructor + virtual ~CAdvancedDialog(); + +// Dialog Data + enum { IDD = IDD_ADVANCEDDIALOG }; + +protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + + DECLARE_MESSAGE_MAP() +}; diff --git a/AdvancedDialog.htm b/AdvancedDialog.htm new file mode 100644 index 0000000..becc42a --- /dev/null +++ b/AdvancedDialog.htm @@ -0,0 +1,19 @@ + + + + + + + + + + +
+
+ +
+TODO: Place controls here. +
+ + + \ No newline at end of file diff --git a/DialogClasses.cpp b/DialogClasses.cpp index 2870207..dabc9fb 100644 --- a/DialogClasses.cpp +++ b/DialogClasses.cpp @@ -6,21 +6,28 @@ #include "ExtIODialog.h" -// Our empty class definition template. While there are likely more beutiful ways for this +// Our empty class definition template. While there are likely more beautiful ways for this // in c++, this one works, so there are no experiments with templates. -// All the classes are left empty, because we are communicating using messages with the -// controls on dialog screen. All the setup for the controls is performed at the +// This approach is useful for getting the DDX variablesset up for objects, so we +// can use DDX method for communication. + +// All the classes are left empty, since we really need these only as a variables +// of the right type. All the setup for the controls is performed at the // ExtIODlg.cpp OnInitDialog() to keep all control initializations at the same place // // Basically, what you need to do is following: // // - Create control on the resource screen -// - Add class definition with the macro here and ExtIODialogClasses.h, be sure to use appropriate public class +// - Add class definition with the macro here and DialogClasses.h, be sure to use appropriate public class // - Add class to the CExtIODialog class definition at ExtIODialog.h // - Add DDX message handler to the ExtIODialog.cpp CExtIODialog::DoDataExchange() -// - If the main dialog has to receive specific events from control, add appropriate -// messaging definitions to ExtIODialog.cpp message map. +// +// See also: http://msdn.microsoft.com/en-us/library/xwz5tb1x%28v=vs.110%29.asp + + +// NB! If the main dialog has to receive specific events from control, appropriate messaging definitions +// still have to be added to message map at ExtIODialog.cpp // #define DialogClass(ClassName, PublicClass) \ @@ -49,6 +56,9 @@ DialogClass(CPhaseInfo, CEdit) DialogClass(CTransparencySlider, CSliderCtrl) DialogClass(CCheckBoxDllIQ, CButton) DialogClass(CCheckBoxDebugConsole, CButton) +DialogClass(CCheckBoxAGC, CButton) +DialogClass(CRFGainASlider, CSliderCtrl) +DialogClass(CRFGainBSlider, CSliderCtrl) DialogClass(CDataRateInfo, CEdit) DialogClass(CButton1, CButton) diff --git a/DialogClasses.h b/DialogClasses.h index 9aebf04..bbf4c93 100644 --- a/DialogClasses.h +++ b/DialogClasses.h @@ -30,6 +30,9 @@ DialogClassDef(CPhaseInfo, CEdit) DialogClassDef(CTransparencySlider, CSliderCtrl) DialogClassDef(CCheckBoxDllIQ, CButton) DialogClassDef(CCheckBoxDebugConsole, CButton) +DialogClassDef(CCheckBoxAGC, CButton) +DialogClassDef(CRFGainASlider, CSliderCtrl) +DialogClassDef(CRFGainBSlider, CSliderCtrl) DialogClassDef(CDataRateInfo, CEdit) DialogClassDef(CButton1, CButton) diff --git a/ExtIODialog.cpp b/ExtIODialog.cpp index 5e107b4..590670c 100644 --- a/ExtIODialog.cpp +++ b/ExtIODialog.cpp @@ -4,6 +4,7 @@ #include "stdafx.h" #include "ExtIODll.h" #include "ExtIODialog.h" +#include "MainDialog.h" #include "SelectComPort\ComPortCombo.h" #include "serial\serial.h" #include "libusb\lusb0_usb.h" @@ -25,10 +26,12 @@ extern int SyncGain; extern unsigned long lo_freq; extern unsigned long lastlo_freqA; extern unsigned long lastlo_freqB; +extern unsigned long lastlo_freqC; extern unsigned long tune_freq; extern unsigned long lasttune_freqA; extern unsigned long lasttune_freqB; +extern unsigned long lasttune_freqC; volatile bool channelmode_changed=false; // set true to have datatask changing the channel mode (by sending USB control message to radio) volatile bool samplerate_changed=false; // set true to ask datatask to change sample rate for radio @@ -36,22 +39,33 @@ volatile bool update_lo=false; // trigger LO change on datatask extern volatile bool update_registry; extern volatile bool update_phaseA; -extern volatile bool update_gain; +extern volatile bool update_IFgain; +extern volatile bool update_RFgain; extern volatile bool do_callback105; extern long IQSampleRate; long last_samplerate; -extern int GainA, GainB; +extern int IFGainA, IFGainB; +extern int RFGainA, RFGainB; + +extern int AGC; + extern int PhaseCoarse, PhaseFine; +unsigned long phaseword=0; extern int DebugConsole; extern const struct usb_version* libver; CPanadapterDialog* m_pmodelessPanadapter = NULL; +extern CMainDialog* m_pmodeless; extern CWnd* MainWindow; +int Transparency=0; +int fw_major=-1, fw_minor=0, extio_major=DLLVER_MAJOR, extio_minor=DLLVER_MINOR; +char verinfo[128]; + // CExtIODialog dialog IMPLEMENT_DYNAMIC(CExtIODialog, CDialog) @@ -70,8 +84,8 @@ void CExtIODialog::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); DDX_Control(pDX, IDC_COMBO1, m_comboPorts); - DDX_Control(pDX, IDC_SLIDER1, m_GainSliderA); - DDX_Control(pDX, IDC_SLIDER2, m_GainSliderB); + DDX_Control(pDX, IDC_SLIDER1, m_IFGainSliderA); + DDX_Control(pDX, IDC_SLIDER2, m_IFGainSliderB); DDX_Control(pDX, IDC_SLIDER3, m_PhaseSliderCoarse); DDX_Control(pDX, IDC_SLIDER4, m_PhaseSliderFine); DDX_Control(pDX, IDC_EDIT1, m_PhaseInfo); @@ -81,6 +95,9 @@ void CExtIODialog::DoDataExchange(CDataExchange* pDX) DDX_Radio(pDX, IDC_RADIO_CMODE1, m_nChannelMode); DDX_Control(pDX, IDC_CHECK2, m_SyncGainCheck); DDX_Control(pDX, IDC_CHECK3, m_SyncTuneCheck); + DDX_Control(pDX, IDC_CHECK5, m_AGCCheck); + DDX_Control(pDX, IDC_SLIDER6, m_RFGainSliderA); + DDX_Control(pDX, IDC_SLIDER7, m_RFGainSliderB); DDX_Control(pDX, IDC_CHECK4, m_DebugConsoleCheck); DDX_Control(pDX, IDC_BUTTON1, m_Button1); } @@ -95,6 +112,7 @@ BEGIN_MESSAGE_MAP(CExtIODialog, CDialog) ON_BN_CLICKED(IDC_CHECK2, &CExtIODialog::OnBnClickedCheck2) ON_BN_CLICKED(IDC_CHECK3, &CExtIODialog::OnBnClickedCheck3) ON_BN_CLICKED(IDC_CHECK4, &CExtIODialog::OnBnClickedCheck4) + ON_BN_CLICKED(IDC_CHECK5, &CExtIODialog::OnBnClickedCheck5) ON_WM_HSCROLL() ON_WM_TIMER() // we need timer to do periodic updates on dialog window. Doing it by accessing contols from different task directly corrupts something ON_BN_CLICKED(IDC_RADIO_CMODE1, &CExtIODialog::OnBnClickedRadioCmode1) @@ -104,19 +122,122 @@ BEGIN_MESSAGE_MAP(CExtIODialog, CDialog) ON_BN_CLICKED(IDC_RADIO_CMODE5, &CExtIODialog::OnBnClickedRadioCmode5) ON_BN_CLICKED(IDC_RADIO_CMODE6, &CExtIODialog::OnBnClickedRadioCmode6) ON_BN_CLICKED(IDC_RADIO_CMODE7, &CExtIODialog::OnBnClickedRadioCmode7) + ON_BN_CLICKED(IDC_RADIO_CMODE8, &CExtIODialog::OnBnClickedRadioCmode8) END_MESSAGE_MAP() -// CExtIODialog message handlers +// +// "Decision Engine" for enabling and disabling the controls dependednt on the UI selections +// -int Transparency=0; -int fw_major=-1, fw_minor=0, extio_major=DLLVER_MAJOR, extio_minor=DLLVER_MINOR; -char verinfo[128]; +void CExtIODialog::EnableDisableControls(void) +{ + if (m_DllIQ.GetCheck()) + { + // in IQ mode we could have all possible controls active, so enable all! + GetDlgItem(IDC_RADIO_CMODE1)->EnableWindow(true); + GetDlgItem(IDC_RADIO_CMODE2)->EnableWindow(true); + GetDlgItem(IDC_RADIO_CMODE3)->EnableWindow(true); + GetDlgItem(IDC_RADIO_CMODE4)->EnableWindow(true); + GetDlgItem(IDC_RADIO_CMODE5)->EnableWindow(true); + GetDlgItem(IDC_RADIO_CMODE6)->EnableWindow(true); + GetDlgItem(IDC_RADIO_CMODE7)->EnableWindow(true); + GetDlgItem(IDC_RADIO_CMODE8)->EnableWindow(true); + } + else + { + // override global channel mode if data is not supplied by ExtIO DLL + if ((m_nChannelMode != CHMODE_A)&&(m_nChannelMode!=CHMODE_AMB)) + m_nChannelMode=CHMODE_A; + + //in non-extio mode disable modes we can not use + GetDlgItem(IDC_RADIO_CMODE1)->EnableWindow(true); + GetDlgItem(IDC_RADIO_CMODE2)->EnableWindow(false); + GetDlgItem(IDC_RADIO_CMODE3)->EnableWindow(false); + GetDlgItem(IDC_RADIO_CMODE4)->EnableWindow(true); + GetDlgItem(IDC_RADIO_CMODE5)->EnableWindow(false); + GetDlgItem(IDC_RADIO_CMODE6)->EnableWindow(false); + GetDlgItem(IDC_RADIO_CMODE7)->EnableWindow(false); + GetDlgItem(IDC_RADIO_CMODE8)->EnableWindow(false); + } + + // only enable panadapter if firmware version supports it + if ((fw_major < MIN_FW_MAJOR_PAN)||(fw_minor < MIN_FW_MINOR_PAN) || (fw_major == 256)) //1.58 had wrong endian for version info, so check for 256 major + { + if ((m_nChannelMode == CHMODE_ABPAN)||(m_nChannelMode == CHMODE_BAPAN)) + m_nChannelMode=CHMODE_A; + + GetDlgItem(IDC_RADIO_CMODE6)->EnableWindow(false); + GetDlgItem(IDC_RADIO_CMODE7)->EnableWindow(false); + GetDlgItem(IDC_RADIO_CMODE8)->EnableWindow(false); + } + + //enable all buttons and sliders by default + + m_SyncGainCheck.EnableWindow(true); + m_SyncTuneCheck.EnableWindow(true); + m_AGCCheck.EnableWindow(true); + m_IFGainSliderA.EnableWindow(true); + m_IFGainSliderB.EnableWindow(true); + m_RFGainSliderA.EnableWindow(true); + m_RFGainSliderB.EnableWindow(true); + m_PhaseSliderCoarse.EnableWindow(true); + m_PhaseSliderFine.EnableWindow(true); + m_Button1.EnableWindow(true); + + switch(m_nChannelMode) + { + case CHMODE_A: + case CHMODE_B: + m_Button1.EnableWindow(false); + break; + + case CHMODE_APB: + case CHMODE_AMB: + case CHMODE_BMA: + m_SyncTuneCheck.EnableWindow(false); // always fix tuning for diversity modes, so no selection + m_Button1.EnableWindow(false); // no panadapter in diversity mode + break; + + case CHMODE_ABPAN: + case CHMODE_BAPAN: + m_SyncTuneCheck.EnableWindow(false); // second channel tuning is done internally + //m_SyncGainCheck.EnableWindow(false); // This is relly irrleevant and we COULD sync gain, although it does not really make sense + m_Button1.EnableWindow(true); // Panadapter can be selected + break; + + case CHMODE_IABQ: + m_SyncTuneCheck.EnableWindow(false); // Both channels have to be same frequency, so disable selection + m_SyncGainCheck.EnableWindow(false); // Both channels have to be same gain, so disable channel B gain + m_RFGainSliderB.EnableWindow(false); + m_IFGainSliderB.EnableWindow(false); + m_PhaseSliderCoarse.EnableWindow(false); + m_PhaseSliderFine.EnableWindow(false); + + break; + } + + if (m_AGCCheck.GetCheck()) + { + m_RFGainSliderA.EnableWindow(false); //AGC, so no manual RF gain adjustment + m_RFGainSliderB.EnableWindow(false); + } + + if (m_SyncGainCheck.GetCheck()) + { + m_IFGainSliderB.EnableWindow(false); //AGC, so no manual RF gain adjustment + m_RFGainSliderB.EnableWindow(false); + } + + UpdateData(false); //update radio buttons +} + + +// CExtIODialog message handlers BOOL CExtIODialog::OnInitDialog() { int ret; -int hardwaretype; CDialog::OnInitDialog(); @@ -145,13 +266,21 @@ int hardwaretype; // m_listPorts.InitList(strPort.GetString()); // init sliders - m_GainSliderA.SetRange(0, 15); - m_GainSliderA.SetPos(GainA); - m_GainSliderA.SetTicFreq(1); + m_IFGainSliderA.SetRange(0, 15); + m_IFGainSliderA.SetPos(IFGainA); + m_IFGainSliderA.SetTicFreq(1); + + m_IFGainSliderB.SetRange(0, 15); + m_IFGainSliderB.SetPos(IFGainB); + m_IFGainSliderB.SetTicFreq(1); + + m_RFGainSliderA.SetRange(0, 7); + m_RFGainSliderA.SetPos(RFGainA); + m_RFGainSliderA.SetTicFreq(1); - m_GainSliderB.SetRange(0, 15); - m_GainSliderB.SetPos(GainB); - m_GainSliderB.SetTicFreq(1); + m_RFGainSliderB.SetRange(0, 7); + m_RFGainSliderB.SetPos(RFGainB); + m_RFGainSliderB.SetTicFreq(1); m_PhaseSliderCoarse.SetRange(0, 359); m_PhaseSliderCoarse.SetTicFreq(10); @@ -170,11 +299,20 @@ int hardwaretype; m_PhaseInfo.SetWindowText("0.0"); m_DataRateInfo.SetWindowText("0 Mbit/s"); - hardwaretype=AfxGetApp()->GetProfileInt(_T("Config"), _T("HardwareType"), -1); - if (hardwaretype == 3) + m_nChannelMode=AfxGetApp()->GetProfileInt(_T("Config"), _T("ChannelMode"), 0); + + if (HWType == 3) m_DllIQ.SetCheck(BST_CHECKED); - else - m_DllIQ.SetCheck(BST_UNCHECKED); + else + m_DllIQ.SetCheck(BST_UNCHECKED); + + SyncTuning=AfxGetApp()->GetProfileInt(_T("Config"), _T("SyncTuning"), 0); + SyncGain=AfxGetApp()->GetProfileInt(_T("Config"), _T("SyncGain"), 0); + + m_SyncGainCheck.SetCheck(SyncGain); + m_SyncTuneCheck.SetCheck(SyncTuning); + + m_AGCCheck.SetCheck(AGC); SetTimer(ID_CLOCK_TIMER, 500, NULL); //500ms timer, null show put it to task queue @@ -228,86 +366,38 @@ int hardwaretype; sprintf_s(verinfo, 128, "No LibUSB-win32 Version Info"); GetDlgItem(IDC_STATIC_LIBUSBVER)->SendMessage(WM_SETTEXT, 0, (LPARAM)verinfo); + + m_DebugConsoleCheck.SetCheck(DebugConsole); - m_nChannelMode=AfxGetApp()->GetProfileInt(_T("Config"), _T("ChannelMode"), 0); - - // override channel mode if data is not supplied by ExtIO DLL - if (hardwaretype != 3) - { - if ((m_nChannelMode != CHMODE_A)&&(m_nChannelMode!=CHMODE_AMB)) - m_nChannelMode=CHMODE_A; - - // disable radio buttons what should not be used - - GetDlgItem(IDC_RADIO_CMODE2)->EnableWindow(false); - GetDlgItem(IDC_RADIO_CMODE3)->EnableWindow(false); - GetDlgItem(IDC_RADIO_CMODE5)->EnableWindow(false); - GetDlgItem(IDC_RADIO_CMODE6)->EnableWindow(false); - GetDlgItem(IDC_RADIO_CMODE7)->EnableWindow(false); - } - - // only enable panadapter if firmware version supports it - if ((fw_major < MIN_FW_MAJOR_PAN)||(fw_minor < MIN_FW_MINOR_PAN) || (fw_major == 256)) //1.58 had wrong endian for version info, so check for 256 major - { - if ((m_nChannelMode >=5)&&(m_nChannelMode <=6)) - m_nChannelMode=CHMODE_A; - - GetDlgItem(IDC_RADIO_CMODE6)->EnableWindow(false); - GetDlgItem(IDC_RADIO_CMODE7)->EnableWindow(false); - } - - m_SyncGainCheck.SetCheck(AfxGetApp()->GetProfileInt(_T("Config"), _T("SyncGain"), 0)); - - if ((m_nChannelMode > 1)&&(m_nChannelMode < 5)) // fix tuning for diversity modes - { - m_SyncTuneCheck.SetCheck(1); - m_SyncTuneCheck.EnableWindow(false); - m_Button1.EnableWindow(false); - } - else if (m_nChannelMode <= 1) - { - m_SyncTuneCheck.SetCheck(AfxGetApp()->GetProfileInt(_T("Config"), _T("SyncTuning"), 0)); - m_Button1.EnableWindow(false); - } - else if ((m_nChannelMode >= 5)&&(m_nChannelMode <= 6)) - { - m_SyncTuneCheck.SetCheck(0); - m_SyncTuneCheck.EnableWindow(false); - m_SyncGainCheck.SetCheck(0); - m_SyncGainCheck.EnableWindow(false); - - SyncTuning=0; - SyncGain=0; - - m_Button1.EnableWindow(true); - } - - UpdateData(false); //update radio buttons + EnableDisableControls(); last_samplerate=IQSampleRate; - m_DebugConsoleCheck.SetCheck(DebugConsole); - return true; } void CExtIODialog::OnBnClickedOk() { - update_registry=true; // ask ExtIORegistryUpdate() to perform update - OnOK(); + update_registry=true; // ask ExtIORegistryUpdate() to perform update + //OnOK(); + //Ask main window to close + + m_pmodeless->PostMessage(WM_CLOSE, 0, 0); } -extern CExtIODialog* m_pmodeless; +//extern CExtIODialog* m_pmodeless; +//extern CMainDialog* m_pmodeless; void CExtIODialog::PostNcDestroy() { CDialog::PostNcDestroy(); - m_pmodeless = NULL; + //m_pmodeless = NULL; //xx delete this; } +/* // We have to handle close event, as we do not have parent object what would perform destruction for us void CExtIODialog::OnClose() { @@ -322,8 +412,10 @@ void CExtIODialog::OnClose() if (m_pmodeless) { m_pmodeless->DestroyWindow(); + m_pmodeless=NULL; //xx } } +*/ // WM_DEVICECHANGE handler. // Used here to detect plug-in and -out of devices providing virtual COM ports. @@ -386,13 +478,17 @@ void CExtIODialog::OnBnClickedCheck1() { if (m_DllIQ.GetCheck()) { - AfxGetApp()->WriteProfileInt(_T("Config"), _T("HardwareType"), 3); // 16-bit ExtIODll managed I/Q source + AfxGetApp()->WriteProfileInt(_T("Config"), _T("HardwareType"), 3); // 16-bit ExtIODll managed I/Q source + HWType=3; } else { AfxGetApp()->WriteProfileInt(_T("Config"), _T("HardwareType"), 4); // Audio card managed by HDSDR + HWType=4; } + EnableDisableControls(); + AfxMessageBox("I/Q Data Source Changed, Restart Application!", MB_ICONEXCLAMATION); } @@ -408,13 +504,15 @@ void CExtIODialog::OnBnClickedCheck2() // if sync gain is checked, sync both gains to Ch A gain if (SyncGain) { - GainB=GainA; - m_GainSliderB.EnableWindow(false); - m_GainSliderB.SetPos(GainB); - update_gain=true; // ask datatask to update gain + IFGainB=IFGainA; + RFGainB=RFGainA; + m_IFGainSliderB.SetPos(IFGainB); + m_RFGainSliderB.SetPos(RFGainB); + update_IFgain=true; // ask datatask to update gain + update_RFgain=true; } - else - m_GainSliderB.EnableWindow(true); + + EnableDisableControls(); } void CExtIODialog::OnBnClickedCheck3() @@ -431,6 +529,8 @@ void CExtIODialog::OnBnClickedCheck3() update_lo=true; do_callback105=true; } + + EnableDisableControls(); } void CExtIODialog::OnBnClickedCheck4() @@ -442,114 +542,143 @@ void CExtIODialog::OnBnClickedCheck4() AfxMessageBox("Restart Application to Disable Debug Console!", MB_ICONEXCLAMATION); } +void CExtIODialog::OnBnClickedCheck5() +{ + AGC=m_AGCCheck.GetCheck(); + + AfxGetApp()->WriteProfileInt(_T("Config"), _T("AGC"), AGC); + + EnableDisableControls(); //enable and isable appropriate controls dependent on new state of buttons + + //disable AGC and set gain according to gain sliders + + update_RFgain=true; +} + void CExtIODialog::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) { -unsigned long phaseword; +//unsigned long phaseword; CSliderCtrl* pSld; pSld = (CSliderCtrl*)pScrollBar; - if (*pSld == m_GainSliderA) + if (*pSld == m_RFGainSliderA) + { + RFGainA = m_RFGainSliderA.GetPos(); + if (SyncGain) + { + RFGainB = RFGainA; + m_RFGainSliderB.SetPos(RFGainB); + } + + update_RFgain=true; + } + else if (*pSld == m_RFGainSliderB) + { + RFGainB = m_RFGainSliderB.GetPos(); + update_RFgain=true; + } + else if (*pSld == m_IFGainSliderA) { - GainA = m_GainSliderA.GetPos(); + IFGainA = m_IFGainSliderA.GetPos(); if (HWType != 3) { - sprintf_s(serbuff, 128, "_ga %d\n\r", GainA); + sprintf_s(serbuff, 128, "_ga %d\n\r", IFGainA); serial.Write(serbuff, strlen(serbuff)); } else - update_gain=true; + update_IFgain=true; if (SyncGain) { - GainB = GainA; - m_GainSliderB.SetPos(GainB); + IFGainB = IFGainA; + m_IFGainSliderB.SetPos(IFGainB); } else { - if (GainA > 7) + if (IFGainA > 7) { //gains higher than 7 mean, that exponent is going to be fixed for both channels, so move the //channel B slider accordingly - if (m_GainSliderB.GetPos() < 8) + if (m_IFGainSliderB.GetPos() < 8) { - GainB=8; + IFGainB=8; if (HWType !=3) { sprintf_s(serbuff, 128, "_gb 8\n\r"); serial.Write(serbuff, strlen(serbuff)); } else - update_gain=true; + update_IFgain=true; - m_GainSliderB.SetPos(GainB); + m_IFGainSliderB.SetPos(IFGainB); } } else { - if (m_GainSliderB.GetPos() > 7) + if (m_IFGainSliderB.GetPos() > 7) { - GainB=7; + IFGainB=7; if (HWType != 3) { sprintf_s(serbuff, 128, "_gb 7\n\r"); serial.Write(serbuff, strlen(serbuff)); } else - update_gain=true; + update_IFgain=true; - m_GainSliderB.SetPos(GainB); + m_IFGainSliderB.SetPos(IFGainB); } } } } - else if ((*pSld == m_GainSliderB) || (SyncGain)) + else if ((*pSld == m_IFGainSliderB) || ((*pSld == m_IFGainSliderA)&&(SyncGain))) { - GainB = m_GainSliderB.GetPos(); + IFGainB = m_IFGainSliderB.GetPos(); if (HWType != 3) { - sprintf_s(serbuff, 128, "_gb %d\n\r", GainB); + sprintf_s(serbuff, 128, "_gb %d\n\r", IFGainB); serial.Write(serbuff, strlen(serbuff)); } else - update_gain=true; + update_IFgain=true; - if (GainB > 7) + if (IFGainB > 7) { //gains higher than 7 mean that exponent is going to be fixed for both channels, so move the //channel B slider accordingly - if (m_GainSliderA.GetPos() < 8) + if (m_IFGainSliderA.GetPos() < 8) { - GainA=8; + IFGainA=8; if (HWType != 3) { sprintf_s(serbuff, 128, "_ga 8\n\r"); serial.Write(serbuff, strlen(serbuff)); } else - update_gain=true; + update_IFgain=true; - m_GainSliderA.SetPos(GainA); + m_IFGainSliderA.SetPos(IFGainA); } } else { - if (m_GainSliderA.GetPos() > 7) + if (m_IFGainSliderA.GetPos() > 7) { - GainA=7; + IFGainA=7; if (HWType != 3) { sprintf_s(serbuff, 128, "_ga 7\n\r"); serial.Write(serbuff, strlen(serbuff)); } else - update_gain=true; + update_IFgain=true; - m_GainSliderA.SetPos(GainA); + m_IFGainSliderA.SetPos(IFGainA); } } } @@ -561,14 +690,15 @@ CSliderCtrl* pSld; sprintf_s(serbuff, 128, "%d.%d", PhaseCoarse+(PhaseFine/100), PhaseFine%100); m_PhaseInfo.SetWindowText(serbuff); - // calculate phase word from the sliders + // update global phaseword + // calculate phase word from the sliders. All math is in deg*100 + + phaseword=18204; //65535=2pi rad = 360deg; 65535/360*100 + phaseword*=(PhaseCoarse*100)+PhaseFine; + phaseword/=100*100; if (HWType != 3) { - phaseword=1820; //65535=2pi rad = 360deg; 65535/360*1000 - phaseword*=(PhaseCoarse*100)+PhaseFine; - phaseword/=1000; - sprintf_s(serbuff, 128, "_pa %ld\n\r", phaseword); serial.Write(serbuff, strlen(serbuff)); } @@ -620,25 +750,22 @@ unsigned long tunefreq; SyncTuning=AfxGetApp()->GetProfileInt(_T("Config"), _T("SyncTuning"), 0); m_SyncTuneCheck.SetCheck(SyncTuning); - m_SyncTuneCheck.EnableWindow(true); m_SyncGainCheck.SetCheck(SyncGain); - m_SyncGainCheck.EnableWindow(true); + if (HWType == 3) IQSampleRate=IQSAMPLERATE_FULL; - m_Button1.EnableWindow(false); break; case CHMODE_APB: case CHMODE_AMB: case CHMODE_BMA: // fix tuning for diversity modes - m_SyncTuneCheck.SetCheck(1); - m_SyncTuneCheck.EnableWindow(false); - m_SyncGainCheck.SetCheck(SyncGain); - m_SyncGainCheck.EnableWindow(true); - SyncTuning=1; + + m_SyncTuneCheck.SetCheck(SyncTuning); + m_SyncGainCheck.SetCheck(SyncGain); + if (HWType == 3) IQSampleRate=IQSAMPLERATE_DIVERSITY; @@ -648,33 +775,62 @@ unsigned long tunefreq; case CHMODE_ABPAN: case CHMODE_BAPAN: - m_SyncTuneCheck.SetCheck(0); - m_SyncTuneCheck.EnableWindow(false); - m_SyncGainCheck.SetCheck(0); - m_SyncGainCheck.EnableWindow(false); - SyncTuning=0; SyncGain=0; + m_SyncTuneCheck.SetCheck(SyncTuning); + m_SyncGainCheck.SetCheck(SyncGain); + if (HWType == 3) IQSampleRate=IQSAMPLERATE_PANADAPTER; - m_Button1.EnableWindow(true); + break; + + case CHMODE_IABQ: + + SyncTuning=1; + SyncGain=1; + PhaseCoarse=0; + PhaseFine=0; + + IFGainB=IFGainA; + RFGainB=RFGainA; + + m_SyncTuneCheck.SetCheck(1); + m_SyncGainCheck.SetCheck(1); + + m_IFGainSliderB.SetPos(IFGainA); + m_RFGainSliderB.SetPos(RFGainA); + m_PhaseSliderCoarse.SetPos(PhaseCoarse); + m_PhaseSliderFine.SetPos(PhaseFine); + + if (HWType == 3) + IQSampleRate=IQSAMPLERATE_FULL; + + m_Button1.EnableWindow(false); break; default: // should not get here break; } - - if (SyncTuning) // override frequencies with channel A if synchronous tuning is selected! + + if (ChannelMode == CHMODE_IABQ) { - lofreq=lastlo_freqA; - tunefreq=lasttune_freqA; + lofreq=_lofreq; + tunefreq=_tunefreq; } else { - lofreq=_lofreq; - tunefreq=_tunefreq; + if (SyncTuning) // override frequencies with channel A if synchronous tuning is selected! + { + lofreq=lastlo_freqA; + tunefreq=lasttune_freqA; + } + else + { + lofreq=_lofreq; + tunefreq=_tunefreq; + } } if ((long)lofreq !=-1) @@ -692,7 +848,7 @@ unsigned long tunefreq; if (HWType != 3) { - if (ChannelMode == 0) + if (ChannelMode == CHMODE_A) //ChA / soundcard sprintf_s(serbuff, 128, "diversity 0\n\r"); else sprintf_s(serbuff, 128, "diversity 1\n\r"); @@ -712,6 +868,13 @@ unsigned long tunefreq; last_samplerate=IQSampleRate; samplerate_changed=true; // indicate datatask, that sample rate must be changed } + + // update phase and gain settings after mode change to whatever they should be + update_IFgain=true; + update_RFgain=true; + update_phaseA=true; + + EnableDisableControls(); } void CExtIODialog::OnBnClickedRadioCmode1() //A @@ -749,6 +912,11 @@ void CExtIODialog::OnBnClickedRadioCmode7() //B+Panadapter ChangeMode(lastlo_freqB, lasttune_freqB); } +void CExtIODialog::OnBnClickedRadioCmode8() //A=I, B=Q +{ + ChangeMode(lastlo_freqC, lasttune_freqC); +} + void CExtIODialog::OnBnClickedButton1() { if(m_pmodelessPanadapter) @@ -762,7 +930,7 @@ void CExtIODialog::OnBnClickedButton1() if (m_pmodelessPanadapter) { - m_pmodelessPanadapter->Create(/*CGenericMFCDlg*/CPanadapterDialog::IDD, CWnd::GetActiveWindow() /*MainWindow*/ /*GetDesktopWindow()*/); + m_pmodelessPanadapter->Create(/*CGenericMFCDlg*/CPanadapterDialog::IDD, /*CWnd::GetActiveWindow()*/ MainWindow /*GetDesktopWindow()*/); m_pmodelessPanadapter->ShowWindow(SW_SHOW); } else @@ -771,3 +939,4 @@ void CExtIODialog::OnBnClickedButton1() } } } + diff --git a/ExtIODialog.h b/ExtIODialog.h index fa8c267..274f481 100644 --- a/ExtIODialog.h +++ b/ExtIODialog.h @@ -25,15 +25,15 @@ class CExtIODialog : public CDialog //{{AFX_MSG(CGenericMFCDlg) virtual BOOL OnInitDialog(); virtual void PostNcDestroy(); - afx_msg void OnClose(); + //afx_msg void OnClose(); afx_msg BOOL OnDeviceChange(UINT nEventType, DWORD_PTR dwData); //}}AFX_MSG virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support CComPortCombo m_comboPorts; - CGainSliderCHA m_GainSliderA; - CGainSliderCHB m_GainSliderB; + CGainSliderCHA m_IFGainSliderA; + CGainSliderCHB m_IFGainSliderB; CPhaseSliderCoarse m_PhaseSliderCoarse; CPhaseSliderFine m_PhaseSliderFine; CCheckBoxDiversity m_DiversityCheck; @@ -44,6 +44,9 @@ class CExtIODialog : public CDialog CCheckBoxDllIQ m_DllIQ; CDataRateInfo m_DataRateInfo; CCheckBoxDebugConsole m_DebugConsoleCheck; + CCheckBoxAGC m_AGCCheck; + CRFGainASlider m_RFGainSliderA; + CRFGainBSlider m_RFGainSliderB; CButton1 m_Button1; DECLARE_MESSAGE_MAP() @@ -52,6 +55,8 @@ class CExtIODialog : public CDialog int m_nChannelMode; afx_msg void OnBnClickedOk(); + void EnableDisableControls(void); + private: CLayeredWindowHelperST m_Layered; @@ -70,5 +75,7 @@ class CExtIODialog : public CDialog afx_msg void OnBnClickedRadioCmode5(); afx_msg void OnBnClickedRadioCmode6(); afx_msg void OnBnClickedRadioCmode7(); + afx_msg void OnBnClickedRadioCmode8(); afx_msg void OnBnClickedButton1(); + afx_msg void OnBnClickedCheck5(); }; diff --git a/ExtIODll.cpp b/ExtIODll.cpp index 2d7b0a7..a2375bc 100644 --- a/ExtIODll.cpp +++ b/ExtIODll.cpp @@ -1,6 +1,11 @@ #include "stdafx.h" #include "ExtIODll.h" +#include "ExtIOFunctions.h" + +#include // gives _cprintf() + +bool hasconsole=false; #ifdef _DEBUG #define new DEBUG_NEW @@ -51,7 +56,100 @@ END_MESSAGE_MAP() CExtIODllApp::CExtIODllApp() { // TODO: add construction code here, - // Place all significant initialization in InitInstance + // Place all significant initialization in InitInstance +} + +HANDLE sleepevent; + +BOOL CExtIODllApp::InitInstance() +{ +int debugcon, transp; + + CWinApp::InitInstance(); + + SetRegistryKey("Satrian"); + + //not nice to fetch these parameters here from the registry, but we eant this to be done before anything else starts, so there is no way really. + //All other registry loading is at the InitHW() + + debugcon = AfxGetApp()->GetProfileInt(_T("Config"), _T("DebugConsole"), 0); + transp = AfxGetApp()->GetProfileInt(_T("Config"), _T("Transparency"), 90); //default transparency is 90% + + //-------------- + // Create console. This is convenient for _cprintf() debugging, but as we will + // set up this as a transparent window, it may also be of use for other things. + //-------------- + if (debugcon) + { + if (!AllocConsole()) + { + hasconsole=false; + AfxMessageBox("Failed to create the console!", MB_ICONEXCLAMATION); + } + else + { + HWND hWnd; + + hasconsole=true; + HANDLE nConsole = GetStdHandle(STD_OUTPUT_HANDLE); + SetConsoleTextAttribute(nConsole, 0x0002|0x0008); //green|white to get the bright color + SetConsoleTitle("ExtIO DLL Console"); + + hWnd=GetConsoleWindow(); + SetWindowLong(hWnd, GWL_EXSTYLE, ::GetWindowLong(hWnd, GWL_EXSTYLE) | WS_EX_LAYERED); // add layered attribute + SetLayeredWindowAttributes(hWnd, 0, 255 * transp /*percent*//100, LWA_ALPHA); // set transparency + + //Have to disable close button, as this will kill the application instance with no questions asked! + //Note, that application is still terminated when the consle is closed from taskbar. + DeleteMenu(GetSystemMenu(hWnd, false), SC_CLOSE, MF_BYCOMMAND); + + _cprintf("InitInstance(): Console initialized\n"); + } + } +/* + { + DWORD dwError, dwThreadPri; + + dwThreadPri = GetThreadPriority(); + _cprintf("Current thread priority is %d\n", dwThreadPri); + + if(!SetThreadPriority(THREAD_MODE_BACKGROUND_BEGIN)) + { + dwError = GetLastError(); + if( ERROR_THREAD_MODE_ALREADY_BACKGROUND == dwError) + _cprintf("Already in background mode\n"); + else + _cprintf("Failed to enter background mode (%d)\n", dwError); + } + } + + dwThreadPri = GetThreadPriority(); + _cprintf("Thread priority is set to %d\n", dwThreadPri); +*/ + sleepevent = CreateEvent(NULL, FALSE, FALSE, NULL); // we are using that instead of sleep(), as it is more kind to overall system resources + + _cprintf("Initializing memwatch ..\n"); + mwInit(); + + return TRUE; +} + +int CExtIODllApp::ExitInstance() +{ + mwTerm(); + + // deallocate console + if (hasconsole) + { + AfxMessageBox("All done. Check the console now or press OK to continue closing application", MB_ICONINFORMATION); + + if (!FreeConsole()) + AfxMessageBox("Could not free the console!"); + + hasconsole=false; //just in case update flag + } + + return CWinApp::ExitInstance(); } ///////////////////////////////////////////////////////////////////////////// diff --git a/ExtIODll.h b/ExtIODll.h index 2d8b528..d8d3895 100644 --- a/ExtIODll.h +++ b/ExtIODll.h @@ -25,6 +25,10 @@ class CExtIODllApp : public CWinApp CExtIODllApp(); // Overrides + +public: + virtual BOOL InitInstance(); + virtual int ExitInstance(); // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CMyDllApp) //}}AFX_VIRTUAL diff --git a/ExtIODll.rc b/ExtIODll.rc index d8abbfd..ced96db 100644 --- a/ExtIODll.rc +++ b/ExtIODll.rc @@ -104,57 +104,62 @@ END // Dialog // -IDD_DIALOG1 DIALOGEX 0, 0, 296, 338 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "SDR MK1.5 'Andrus' ExtIO DLL Window" +IDD_EXTIODIALOG DIALOGEX 0, 0, 281, 377 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN - COMBOBOX IDC_COMBO1,17,18,144,30,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP - CONTROL "",IDC_SLIDER5,"msctls_trackbar32",TBS_AUTOTICKS | WS_TABSTOP,197,30,77,15 - CONTROL "ExtIO DLL I/Q Souce",IDC_CHECK1,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,23,42,82,10 - CONTROL "A / AB Audio",IDC_RADIO_CMODE1,"Button",BS_AUTORADIOBUTTON | WS_GROUP,67,63,56,10 - CONTROL "B",IDC_RADIO_CMODE2,"Button",BS_AUTORADIOBUTTON,67,74,20,10 - CONTROL "A+ B",IDC_RADIO_CMODE3,"Button",BS_AUTORADIOBUTTON,67,85,32,10 - CONTROL "A - B",IDC_RADIO_CMODE4,"Button",BS_AUTORADIOBUTTON,67,96,31,10 - CONTROL "B - A",IDC_RADIO_CMODE5,"Button",BS_AUTORADIOBUTTON,67,107,31,10 - CONTROL "A+ Panadapter",IDC_RADIO_CMODE6,"Button",BS_AUTORADIOBUTTON,67,118,65,10 - CONTROL "B+ Panadapter",IDC_RADIO_CMODE7,"Button",BS_AUTORADIOBUTTON,67,129,65,10 - PUSHBUTTON "Panadapter",IDC_BUTTON1,219,135,60,13,WS_DISABLED - CONTROL "",IDC_SLIDER1,"msctls_trackbar32",TBS_AUTOTICKS | WS_TABSTOP,175,60,100,15 - CONTROL "",IDC_SLIDER2,"msctls_trackbar32",TBS_AUTOTICKS | WS_TABSTOP,175,79,100,15 - CONTROL "Sync Gain",IDC_CHECK2,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,149,101,47,10 - CONTROL "Sync Tuning",IDC_CHECK3,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,149,114,55,10 - CONTROL "",IDC_SLIDER3,"msctls_trackbar32",TBS_AUTOTICKS | WS_TABSTOP,21,157,250,15 - CONTROL "",IDC_SLIDER4,"msctls_trackbar32",TBS_AUTOTICKS | WS_TABSTOP,21,184,250,15 - DEFPUSHBUTTON "OK",IDOK,222,308,59,14 - LTEXT "Transparency",IDC_STATIC_TEST,201,19,45,8 - LTEXT "Uses LayeredWindowHelper class by Davide Calabro.",IDC_STATIC,18,278,171,8 - LTEXT "See LayeredWindowHelperST.h for license details.",IDC_STATIC,18,289,162,8 - LTEXT "SDR MK1.5 'Andrus' ExtIO DLL by Andrus Aaslaid (ES1UVB)",IDC_STATIC,75,254,189,8 - LTEXT "http://uvb-76.net",IDC_STATIC,75,263,58,8 - CONTROL 5003,IDC_STATIC,"Static",SS_BITMAP,19,206,44,61 - LTEXT "COM Port",IDC_STATIC,17,7,32,8 - LTEXT "Gain A",IDC_STATIC,149,65,22,8 - LTEXT "Gain B",IDC_STATIC,149,82,21,8 - LTEXT "COM Ports enumeration class by Jochen Arndt.",IDC_STATIC,18,305,152,8 - LTEXT "Licensed as The Code Project Open License (CPOL)",IDC_STATIC,18,315,164,8 - LTEXT "Phase A Coarse",IDC_STATIC,25,150,52,8 - LTEXT "Phase A Fine",IDC_STATIC,25,177,42,8 - EDITTEXT IDC_EDIT1,225,201,40,14,ES_AUTOHSCROLL | WS_DISABLED | NOT WS_BORDER | NOT WS_TABSTOP - LTEXT "Ch A Phase:",IDC_STATIC,183,201,43,8 - EDITTEXT IDC_EDIT2,107,42,54,14,ES_RIGHT | ES_AUTOHSCROLL | WS_DISABLED | NOT WS_BORDER | NOT WS_TABSTOP - LTEXT "Radio FW Ver.",IDC_STATIC_FWVER,75,214,188,8 - LTEXT "ExtIO DLL Ver.",IDC_STATIC_EXTIOVER,75,223,60,8 - GROUPBOX "Channel",IDC_STATIC,22,57,109,85 - LTEXT "0",IDC_STATIC,18,159,8,8 - RTEXT "0.0",IDC_STATIC,7,185,13,8 - LTEXT "359",IDC_STATIC,272,158,17,8 - LTEXT "1.0",IDC_STATIC,271,185,18,8 - CONTROL "Show Debug Console",IDC_CHECK4,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,200,272,89,10 - LTEXT "LibUSB DLL Version Driver Version",IDC_STATIC_LIBUSBVER,75,232,193,8 + COMBOBOX IDC_COMBO1,10,19,144,30,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + CONTROL "",IDC_SLIDER5,"msctls_trackbar32",TBS_AUTOTICKS | WS_TABSTOP,186,26,77,15 + CONTROL "ExtIO DLL I/Q Souce",IDC_CHECK1,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,43,82,10 + CONTROL "A / AB Audio",IDC_RADIO_CMODE1,"Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,21,69,56,10 + CONTROL "B",IDC_RADIO_CMODE2,"Button",BS_AUTORADIOBUTTON,21,80,20,10 + CONTROL "A+ B",IDC_RADIO_CMODE3,"Button",BS_AUTORADIOBUTTON,21,91,32,10 + CONTROL "A - B",IDC_RADIO_CMODE4,"Button",BS_AUTORADIOBUTTON,21,103,31,10 + CONTROL "B - A",IDC_RADIO_CMODE5,"Button",BS_AUTORADIOBUTTON,21,114,31,10 + CONTROL "A+ Panadapter",IDC_RADIO_CMODE6,"Button",BS_AUTORADIOBUTTON,21,125,65,10 + CONTROL "B+ Panadapter",IDC_RADIO_CMODE7,"Button",BS_AUTORADIOBUTTON,21,136,65,10 + CONTROL "UHF (TX8M ChA=I ChB=Q)",IDC_RADIO_CMODE8,"Button",BS_AUTORADIOBUTTON,21,147,103,10 + PUSHBUTTON "Panadapter",IDC_BUTTON1,212,237,60,13,WS_DISABLED + CONTROL "",IDC_SLIDER1,"msctls_trackbar32",TBS_AUTOTICKS | WS_TABSTOP,164,56,100,15 + CONTROL "",IDC_SLIDER2,"msctls_trackbar32",TBS_AUTOTICKS | WS_TABSTOP,164,75,100,15 + CONTROL "Sync A/B Gain",IDC_CHECK2,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,185,97,61,10 + CONTROL "Sync A/B Tuning",IDC_CHECK3,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,185,110,68,10 + CONTROL "",IDC_SLIDER3,"msctls_trackbar32",TBS_AUTOTICKS | WS_TABSTOP,14,177,250,15 + CONTROL "",IDC_SLIDER4,"msctls_trackbar32",TBS_AUTOTICKS | WS_TABSTOP,14,204,250,15 + DEFPUSHBUTTON "OK",IDOK,213,354,59,14 + LTEXT "Transparency",IDC_STATIC_TEST,192,15,45,8 + LTEXT "Uses LayeredWindowHelper class by Davide Calabro.",IDC_STATIC,11,305,171,8 + LTEXT "See LayeredWindowHelperST.h for license details.",IDC_STATIC,11,316,162,8 + LTEXT "SDR MK1.5 'Andrus' ExtIO DLL by Andrus Aaslaid (ES1UVB)",IDC_STATIC,68,276,189,8 + LTEXT "http://uvb-76.net",IDC_STATIC,68,285,58,8 + CONTROL 5003,IDC_STATIC,"Static",SS_BITMAP,12,226,44,61 + LTEXT "COM Port",IDC_STATIC,10,8,32,8 + LTEXT "IF Gain A",IDC_STATIC,135,58,30,8 + LTEXT "IF Gain B",IDC_STATIC,135,76,30,8 + LTEXT "COM Ports enumeration class by Jochen Arndt.",IDC_STATIC,11,332,152,8 + LTEXT "Licensed as The Code Project Open License (CPOL)",IDC_STATIC,11,342,164,8 + LTEXT "Phase A Coarse",IDC_STATIC,18,170,52,8 + LTEXT "Phase A Fine",IDC_STATIC,18,197,42,8 + EDITTEXT IDC_EDIT1,218,221,40,14,ES_AUTOHSCROLL | WS_DISABLED | NOT WS_BORDER | NOT WS_TABSTOP + LTEXT "Ch A Phase:",IDC_STATIC,176,221,43,8 + EDITTEXT IDC_EDIT2,100,43,54,14,ES_RIGHT | ES_AUTOHSCROLL | WS_DISABLED | NOT WS_BORDER | NOT WS_TABSTOP + LTEXT "Radio FW Ver.",IDC_STATIC_FWVER,67,253,214,8 + LTEXT "ExtIO DLL Ver.",IDC_STATIC_EXTIOVER,68,244,107,8 + GROUPBOX "Channel",IDC_STATIC,15,58,109,102 + LTEXT "0",IDC_STATIC,11,179,8,8 + RTEXT "0.0",IDC_STATIC,0,204,13,8 + LTEXT "359",IDC_STATIC,264,178,17,8 + LTEXT "1.0",IDC_STATIC,263,205,18,8 + CONTROL "Show Debug Console",IDC_CHECK4,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,192,294,89,10 + LTEXT "LibUSB DLL Version Driver Version",IDC_STATIC_LIBUSBVER,67,263,214,8 + CONTROL "AGC ON",IDC_CHECK5,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,185,123,42,10 + CONTROL "",IDC_SLIDER6,"msctls_trackbar32",TBS_AUTOTICKS | WS_TABSTOP,164,139,100,15 + CONTROL "",IDC_SLIDER7,"msctls_trackbar32",TBS_AUTOTICKS | WS_TABSTOP,164,157,100,15 + LTEXT "RF Gain A",IDC_STATIC,132,141,32,8 + LTEXT "RF Gain B",IDC_STATIC,132,159,32,8 END -IDD_DIALOG2 DIALOGEX 0, 0, 913, 237 +IDD_PANADAPTERDIALOG DIALOGEX 0, 0, 913, 237 STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU EXSTYLE WS_EX_STATICEDGE CAPTION "Panadapter" @@ -182,6 +187,21 @@ BEGIN LTEXT "RangeSlider Class by Jens Scheidtmann (Creative Commons BY-SA 3.0 License)",IDC_STATIC,606,220,290,9 END +IDD_MAINDIALOG DIALOGEX 0, 0, 292, 402 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "SDR MK1.5 'Andrus' ExtIO DLL Window" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "",IDC_TAB1,"SysTabControl32",0x0,7,2,278,13 +END + +IDD_ADVANCEDDIALOG DIALOGEX 0, 0, 282, 387 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_SYSMENU +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "Advanced Settings are not yet Implemented",IDC_STATIC,63,130,144,8 +END + ///////////////////////////////////////////////////////////////////////////// // @@ -191,20 +211,26 @@ END #ifdef APSTUDIO_INVOKED GUIDELINES DESIGNINFO BEGIN - IDD_DIALOG1, DIALOG + IDD_PANADAPTERDIALOG, DIALOG BEGIN LEFTMARGIN, 7 - RIGHTMARGIN, 289 + RIGHTMARGIN, 906 TOPMARGIN, 7 - BOTTOMMARGIN, 331 + BOTTOMMARGIN, 229 END - IDD_DIALOG2, DIALOG + IDD_MAINDIALOG, DIALOG BEGIN LEFTMARGIN, 7 - RIGHTMARGIN, 906 + RIGHTMARGIN, 285 TOPMARGIN, 7 - BOTTOMMARGIN, 229 + BOTTOMMARGIN, 395 + END + + IDD_ADVANCEDDIALOG, DIALOG + BEGIN + VERTGUIDE, 281 + BOTTOMMARGIN, 377 END END #endif // APSTUDIO_INVOKED @@ -220,6 +246,25 @@ IDB_BITMAP1 BITMAP "extio_small.bmp" ///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// +// Estonian resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ETI) +#ifdef _WIN32 +LANGUAGE LANG_ESTONIAN, SUBLANG_DEFAULT +#pragma code_page(1257) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// HTML +// + +IDR_HTML_ADVANCEDDIALOG HTML "AdvancedDialog.htm" +#endif // Estonian resources +///////////////////////////////////////////////////////////////////////////// + + #ifndef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// diff --git a/ExtIODll.vcproj b/ExtIODll.vcproj index 6146634..f9c1713 100644 --- a/ExtIODll.vcproj +++ b/ExtIODll.vcproj @@ -1,7 +1,7 @@ + + @@ -290,6 +294,10 @@ RelativePath=".\Fourier.h" > + + @@ -337,6 +345,10 @@ Name="Header Files" Filter="h;hpp;hxx;hm;inl" > + + @@ -353,6 +365,10 @@ RelativePath=".\ExtIOFunctions.h" > + + @@ -459,6 +475,43 @@ + + + + + + + + + + + + + + + + + + diff --git a/ExtIOFunctions.cpp b/ExtIOFunctions.cpp index 41f8bb5..d316715 100644 --- a/ExtIOFunctions.cpp +++ b/ExtIOFunctions.cpp @@ -19,7 +19,7 @@ This DLL provides an empty core for implementing hardware support functionality for the Winrad Software Defined Radio (SDR) application from Alberto Di Bene (http://www.weaksignals.com/) - and its offsprings supporting the same ExtIO DLL format, most notably the outstanding HDSDR software (http://hdsdr.org) + and its offsprings supporting the same ExtIO DLL format, most notably the outstanding HDSDR software (http://hdsdr.de) As the Winrad source is written on Borland C-Builder environment, there has been very little information available of how the ExtIO DLL should be implemented on Microsoft Visual Studio 2008 @@ -70,18 +70,89 @@ v1.35 04.01.2013 - Test version - Lots of stuff fixed in panadapter. Does not work perfectly, but is more or less usable. - Fixes for tuning sync and A/B frequeny switching and storing + + v1.36 05.07.2013 - Preliminary TX8M support + + v1.37 17.11.2013 - Added SetRegistryKey("Satrian") in InitInstance() to maintain registry access compatibility + with UAC + - ExtIO DLL is now a default data method (was audio interfaces earlier) + - ExtIORegistryUpdateTask() now writes all changes down after every second and checks update flag in 100ms + (was 10sec and 1sec) + - ExtIODialog stuff modified to use HWType field, not fetching value from registry. + - Debug console initialization and removal moved to Init/ExitInstance() code + + v1.38 21.11.2013 - Re-layouted GUI to acommodate RF gain control controls + - LibUSB-win32 version checking is now relaxed to 1.2.2.0, since we can live with this and Atmel FLIP utility is + reverting us to old version. Not nice, but could still be used, so lets not complain. + - Replaced all _beginthread stuff with AfxBeginThread() to be more MCF compatible + - Panadapter speed slider made to work + - OnTimer() disables scroll timer temporarily to avoid any possibility for recursion (although there probably wasnt any) + - UI Controls conditional enabling and disabling logic cleaned up. + - Included memwatch 2.71 to the build from http://www.linkdata.se/sourcecode/memwatch/ + Old, but functional, since our dynamic memory allocation is using ANSI memory functions anyway. + - Fixed task scheduling issues related to panadapter and ExtIODataTask() + - Eliminated ExtIOCallbackTask() associated double buffering. HDSDR does not block on data callback + and therefore callback is possible to initiate straight from ExtIODataTask() + + v1.40 5.12.2013 - UI converted to tabbed dialog (Although only ExtIO tab i simplemented) + - Dialog initialization and cleanup revisited and (most of the) leaks fixed + To Do: - Make diversity mode work so that both channels could be tweaked, so will get 2x360deg span - - Diversity and gain selection for ExtIO mode + * Diversity and gain selection for ExtIO mode - Sample rate switching for ExtIO mode x Cache size adjustment control for used form - A/B/HDSDR channel selection with LO and freq feedback through callback - - Synchronous tuning - - Last freq etc. parameters update for registry + * Synchronous tuning + * Last freq etc. parameters update for registry * LO frequency following when tuning reaches the border + * Make Extio DLL as data source a default selection + - Manual gain control dialog implementation (Add firmware command as well)(for older HDSDR versions with no gain slider built-in) + - Rework registry updates, so the local stub could be used when UAC is enabled! + - Fix frequency storing in registry, so each mode will store its own frequency value correctly (A, A+pan = same; B, b+pan=sam etc., TX8m etc.) + * Do something about the fact that FLIP installs 1.2.2.0 libusb driver instead of 1.2.2.6 and ExtIO complains. Are we actually compatible with + 1.2.2.0 as well? + - Create twodimensional/class array of enabled/disabled items, frequencys etc., indexed by mode, so we know where the stuff is stored! + * For UI, put all the controls enabling and disabling logic onto ExtIODialog::EnableDisableControls() + - At startup, use single place to read all globals from registry (ExtIODll::InitInstance()? InitHW()?), then use values to + set states at InitDialog() etc. after that. + * replace all _beginthread stuff with AfxBeginThread() + * Fix bug where closing the ExtIO dialog while panadapter is open (what also causes panadapter to close erratically!) + and then re-opening the dialog and panadapter will crash the dll and extio + - clean up thread and other function declarations to .h files + - panadapter shows some banding at some resolutions. + * panadapter speed slider not working + - panadapter speed slider last value not stored in registry + * panadapter speed slider is working backwards + - ExtIO button has to be pressed twice to get the Extio UI showing + * ShowDebug button seems broken (not always in sync with registry) + - See if something can be done about sound breakup when phase is changed (not to reset entire buffer or something..) + - Put globals in separate .cpp and .h file + - Deal with startup error recovery if radio is not connected + - Try to do something no HDSDR restart would be needed if radio disconnected and re-connected. + ? Could the scrolltimer variable modification at OnHscroll() cause fatal race condition when OnTimer() tries to re-enable timer? + - Adjust gain for Panadapter dependent on width, so the intensity of the picture stays the same. + - Make the Panadapter intensity slider default less than maximum and reduce gain, so the intensity could also be turned down, if needed. + * Panadapter takes too much resource from CPU + - See if there is a better way dealing with memwatch output redirection to _cprintf then just #ifndef ENABLEMWFILEWRITES + * AGC setting is not saved in registry + * RF gain sliders not saved in registry + - RF gain and AGC functions are not working if extio is used in serial mode. (rfga, rfgb, agc commans not initiated) + + + To Do v2.x: + + - Full rework for new HDSDR ExtioDLL example from LC + - Update google code with new/up to date DLL example code from LC (maintain two branches -- legacy and new) + - Implement firmware update function through ExtIO dll, including automated FLIP and Atmel driver installation. + Have Exe what allows invoking this feature from external app as well for non-hdsdr users. + - Fix panadapter + - Network support! + UDP discovery + UDP IP address renewal for radio (for peer-to-peer with direct cable connections) + - Port to libusbK for Win8 compatibility (http://code.google.com/p/usb-travis/downloads/list) + - See if void CMainDialog::ShowTabDlg(int tabSel) handles multiple monitors right (do we have screen coordinates really?) */ @@ -90,15 +161,17 @@ #include "stdafx.h" #include "ExtIODll.h" -#include "ExtIODialog.h" +//#include "ExtIODialog.h" +#include "MainDialog.h" #include "ExtIOFunctions.h" +#include "PanadapterDialog.h" #include "libusb\lusb0_usb.h" //serial.h is included from ExtIOFunctions.h #include // gives _cprintf() -#include // gives _beginthread() +#include // gives threading #ifdef _DEBUG #undef THIS_FILE @@ -106,9 +179,10 @@ static char THIS_FILE[]=__FILE__; #define new DEBUG_NEW #endif +UINT ExtIODataTask(LPVOID dummy); +UINT ExtIOCallbackTask(LPVOID dummy); -void ExtIODataTask(void* dummy); -void ExtIOCallbackTask(void* dummy); +extern HANDLE sleepevent; /* @@ -128,8 +202,10 @@ Some notes: #error SERIAL_NO_OVERLAPPED has to be defined #endif -CExtIODialog* m_pmodeless; -bool hasconsole=false; +//CExtIODialog* m_pmodeless; +extern CPanadapterDialog* m_pmodelessPanadapter; +CMainDialog* m_pmodeless; +//bool hasconsole=false; unsigned long lo_freq=4624000L; unsigned long tune_freq=0; @@ -166,21 +242,26 @@ int SyncGain; unsigned long lasttune_freqA=-1; // init so, that HDSDR would update immediately on startup unsigned long lasttune_freqB=-1; +unsigned long lasttune_freqC=-1; extern volatile bool update_lo; unsigned long lastlo_freqA=-1; unsigned long lastlo_freqB=-1; +unsigned long lastlo_freqC=-1; extern int Transparency; long IQSampleRate; -int GainA, GainB; +int IFGainA, IFGainB; +int RFGainA, RFGainB; +int AGC; int PhaseCoarse, PhaseFine; int DebugConsole; extern volatile bool update_phaseA; -extern volatile bool update_gain; +extern volatile bool update_IFgain; +extern volatile bool update_RFgain; usb_dev_handle *dev = NULL; // device handle to be used by libusb const struct usb_version* libver = NULL; // libusb version information @@ -188,30 +269,6 @@ const struct usb_version* libver = NULL; // libusb version information CWnd* MainWindow; -/* -This entry is the first called by Winrad at startup time, and it is used both to tell to the DLL that it is -time to initialize the hardware, and to get back a descriptive name and model (or Serial Number) of the HW, -together with a type code. - -Parameters : - - name - descriptive name of the hardware. Preferably not longer than about 16 characters, as it will be used in a Winrad menu. - model - model code of the hardware, or its Serial Number. Keep also this field not too long, for the same reason of the previous one. - type - this is an index code that Winrad uses to identify the hardware type supported by the DLL. - Please use one the following values : - 3 the hardware does its own digitization and the audio data are returned to Winrad via the callback device. Data must be in 16-bit (short) format, little endian. - 4 The audio data are returned via the sound card managed by Winrad. The external hardware just controls the LO, and possibly a preselector, under DLL control. - 5 the hardware does its own digitization and the audio data are returned to Winrad via the callback device. Data are in 24-bit integer format, little endian. - 6 the hardware does its own digitization and the audio data are returned to Winrad via the callback device. Data are in 32-bit integer format, little endian. - 7 the hardware does its own digitization and the audio data are returned to Winrad via the callback device. Data are in 32-bit float format, little endian. - - Please ask me (i2phd@weaksignals.com) for the assignment of an index code for cases different from the above. -Return value : - - true - everything went well, the HW did initialize, and the return parameters have been filled. - false - the HW did not initialize (error, or powered off, or other reasons). -*/ - usb_dev_handle *open_dev(void) { struct usb_bus *bus; @@ -234,26 +291,31 @@ struct usb_device *dev; /* -Monitors the LO and Tuning frequency for channels and if changed, writes new value to registry +Monitors the LO and Tuning frequency for channels and if changed, writes new value to registry. + +NB! Only to be used for data what changes rapidly (sliders, frequency etc.) and can not be written therefore in real-time. +All checkboxes, lists etc. have to be written down at the UI task immediately. + */ -void ExtIORegistryUpdateTask(void* dummy) +UINT ExtIORegistryUpdateTask(LPVOID dummy) { -HANDLE sleepevent = CreateEvent(NULL, FALSE, FALSE, NULL); // we are using that instead of sleep(), as it is more kind to overall system resources -static long lasttuna=-1, lasttunb=-1, lastloa=-1, lastlob=-1; -static long lasttransparency=-1, lastgaina=-1, lastgainb=-1, lastcoarse=-1, lastfine=-1; +static long lasttuna=-1, lasttunb=-1, lasttunc=-1, lastloa=-1, lastlob=-1, lastloc=-1; +static long lasttransparency=-1, lastifgaina=-1, lastifgainb=-1, lastrfgaina=-1, lastrfgainb=-1; +static long lastcoarse=-1, lastfine=-1; +static int lasthwtype=-1; int i; // Wait for fifo to be filled with data before returning to program while(!globalshutdown) { - for (i=0; i<10; i++) + for (i=0; i<10; i++) //check every second for variable updates (10x100ms) { if (update_registry) { update_registry=false; // should be in critical section really, but works without as well break; } - WaitForSingleObject(sleepevent, 1000); // do it only after every second + WaitForSingleObject(sleepevent, 100); // do it only after every 100ms } if (lastlo_freqA != lastloa) @@ -268,6 +330,12 @@ int i; lastlob=lastlo_freqB; } + if (lastlo_freqC != lastloc) + { + AfxGetApp()->WriteProfileInt(_T("Config"), _T("LastLO_C"), lastlo_freqC); + lastloc=lastlo_freqC; + } + if (lasttune_freqA != lasttuna) { AfxGetApp()->WriteProfileInt(_T("Config"), _T("LastTune_A"), lasttune_freqA); @@ -280,6 +348,12 @@ int i; lasttunb=lasttune_freqB; } + if (lasttune_freqC != lasttunc) + { + AfxGetApp()->WriteProfileInt(_T("Config"), _T("LastTune_C"), lasttune_freqC); + lasttunc=lasttune_freqC; + } + if (Transparency != lasttransparency) { AfxGetApp()->WriteProfileInt(_T("Config"), _T("Transparency"), Transparency); @@ -298,40 +372,80 @@ int i; lastfine=PhaseFine; } - if (GainA != lastgaina) + if (IFGainA != lastifgaina) + { + AfxGetApp()->WriteProfileInt(_T("Config"), _T("IFGainA"), IFGainA); + lastifgaina=IFGainA; + } + + if (IFGainB != lastifgainb) + { + AfxGetApp()->WriteProfileInt(_T("Config"), _T("IFGainB"), IFGainB); + lastifgainb=IFGainB; + } + + if (RFGainA != lastrfgaina) { - AfxGetApp()->WriteProfileInt(_T("Config"), _T("GainA"), GainA); - lastgaina=GainA; + AfxGetApp()->WriteProfileInt(_T("Config"), _T("RFGainA"), RFGainA); + lastrfgaina=RFGainA; } - if (GainB != lastgainb) + if (RFGainB != lastrfgainb) { - AfxGetApp()->WriteProfileInt(_T("Config"), _T("GainB"), GainB); - lastgainb=GainB; + AfxGetApp()->WriteProfileInt(_T("Config"), _T("RFGainB"), RFGainB); + lastrfgainb=RFGainB; } } + + return 1; } +/* +This entry is the first called by Winrad at startup time, and it is used both to tell to the DLL that it is +time to initialize the hardware, and to get back a descriptive name and model (or Serial Number) of the HW, +together with a type code. + +Parameters : + + name - descriptive name of the hardware. Preferably not longer than about 16 characters, as it will be used in a Winrad menu. + model - model code of the hardware, or its Serial Number. Keep also this field not too long, for the same reason of the previous one. + type - this is an index code that Winrad uses to identify the hardware type supported by the DLL. + Please use one the following values : + 3 the hardware does its own digitization and the audio data are returned to Winrad via the callback device. Data must be in 16-bit (short) format, little endian. + 4 The audio data are returned via the sound card managed by Winrad. The external hardware just controls the LO, and possibly a preselector, under DLL control. + 5 the hardware does its own digitization and the audio data are returned to Winrad via the callback device. Data are in 24-bit integer format, little endian. + 6 the hardware does its own digitization and the audio data are returned to Winrad via the callback device. Data are in 32-bit integer format, little endian. + 7 the hardware does its own digitization and the audio data are returned to Winrad via the callback device. Data are in 32-bit float format, little endian. + + Please ask me (i2phd@weaksignals.com) for the assignment of an index code for cases different from the above. +Return value : + + true - everything went well, the HW did initialize, and the return parameters have been filled. + false - the HW did not initialize (error, or powered off, or other reasons). +*/ extern "C" bool __stdcall InitHW(char *name, char *model, int& type) { //static bool first = true; char errstring[128]; -int consoletransparency; unsigned long long libvernum; DebugConsole = AfxGetApp()->GetProfileInt(_T("Config"), _T("DebugConsole"), 0); - Transparency = AfxGetApp()->GetProfileInt(_T("Config"), _T("Transparency"), -1); + Transparency = AfxGetApp()->GetProfileInt(_T("Config"), _T("Transparency"), DEFAULTTRANSPARENCY); - consoletransparency=Transparency; + //consoletransparency=Transparency; - if (consoletransparency == -1) - consoletransparency = DEFAULTTRANSPARENCY; + //if (consoletransparency == -1) + // consoletransparency = DEFAULTTRANSPARENCY; //-------------- // Create console. This is convenient for _cprintf() debugging, but as we will // set up this as a transparent window, it may also be of use for other things. //-------------- + /* + + Moved to InitInstance() + if (DebugConsole) { if (!AllocConsole()) @@ -350,7 +464,7 @@ unsigned long long libvernum; hWnd=GetConsoleWindow(); SetWindowLong(hWnd, GWL_EXSTYLE, ::GetWindowLong(hWnd, GWL_EXSTYLE) | WS_EX_LAYERED); // add layered attribute - SetLayeredWindowAttributes(hWnd, 0, 255 * consoletransparency /*percent*//100, LWA_ALPHA); // set transparency + SetLayeredWindowAttributes(hWnd, 0, 255 * consoletransparency / 100, LWA_ALPHA); // set transparency in percents //Have to disable close button, as this will kill the application instance with no questions asked! //Note, that application is still terminated when the consle is closed from taskbar. @@ -359,29 +473,34 @@ unsigned long long libvernum; _cprintf("InitHW(): Console initialized\n"); } } + */ InitializeCriticalSectionAndSpinCount(&CriticalSection, 0x00000400); m_pmodeless=NULL; // reset GUI so we will be able to track the state - HWType = AfxGetApp()->GetProfileInt(_T("Config"), _T("HardwareType"), -1); + HWType = AfxGetApp()->GetProfileInt(_T("Config"), _T("HardwareType"), 3); // get channel mode and tuning sync setting ChannelMode=AfxGetApp()->GetProfileInt(_T("Config"), _T("ChannelMode"), CHMODE_A); SyncTuning=AfxGetApp()->GetProfileInt(_T("Config"), _T("SyncTuning"), 0); SyncGain=AfxGetApp()->GetProfileInt(_T("Config"), _T("SyncGain"), 0); - GainA=AfxGetApp()->GetProfileInt(_T("Config"), _T("GainA"), 7); - GainB=AfxGetApp()->GetProfileInt(_T("Config"), _T("GainB"), 7); + IFGainA=AfxGetApp()->GetProfileInt(_T("Config"), _T("IFGainA"), 7); + IFGainB=AfxGetApp()->GetProfileInt(_T("Config"), _T("IFGainB"), 7); + RFGainA=AfxGetApp()->GetProfileInt(_T("Config"), _T("RFGainA"), 7); + RFGainB=AfxGetApp()->GetProfileInt(_T("Config"), _T("RFGainB"), 7); PhaseCoarse=AfxGetApp()->GetProfileInt(_T("Config"), _T("PhaseCoarse"), 0); PhaseFine=AfxGetApp()->GetProfileInt(_T("Config"), _T("PhaseFine"), 0); + AGC=AfxGetApp()->GetProfileInt(_T("Config"), _T("AGC"), 1); + /* // for diversity modes, force tune syncing! if ((ChannelMode > 1)&&(ChannelMode < 5)) { IQSampleRate = IQSAMPLERATE_DIVERSITY; SyncTuning=1; } - else if (ChannelMode <=1) + else if ((ChannelMode <=1)||(ChannelMode == 7)) { IQSampleRate = IQSAMPLERATE_FULL; } @@ -389,23 +508,54 @@ unsigned long long libvernum; { IQSampleRate = IQSAMPLERATE_PANADAPTER; } - - if (HWType != 3) - IQSampleRate = IQSAMPLERATE_AUDIO; - +*/ // for all other modes than ExtIO DLL we are supporting only standard mode and diversity mode, which is the same as A-B if ((HWType != 3) && (!(ChannelMode == CHMODE_A)||(ChannelMode == CHMODE_AMB))) ChannelMode=CHMODE_A; _cprintf("ChannelMode = %d\n", ChannelMode); + if (HWType != 3) + { + IQSampleRate = IQSAMPLERATE_AUDIO; + } + else + { + switch(ChannelMode) + { + case CHMODE_A: + case CHMODE_B: + IQSampleRate = IQSAMPLERATE_FULL; + break; + + case CHMODE_APB: + case CHMODE_AMB: + case CHMODE_BMA: + IQSampleRate = IQSAMPLERATE_DIVERSITY; + SyncTuning=1; + break; + + case CHMODE_ABPAN: + case CHMODE_BAPAN: + IQSampleRate = IQSAMPLERATE_PANADAPTER; + break; + + case CHMODE_IABQ: + default: + IQSampleRate = IQSAMPLERATE_FULL; + break; + } + } + // There is not really a good way for fetching the LO and Tune frequency from HDSDR when noone has actually touched // the frequency controls. Therefore, we are mirroring those ourselves and init the HDSDR to appropriate values ... lastlo_freqA=AfxGetApp()->GetProfileInt(_T("Config"), _T("LastLO_A"), 0); lastlo_freqB=AfxGetApp()->GetProfileInt(_T("Config"), _T("LastLO_B"), 0); + lastlo_freqC=AfxGetApp()->GetProfileInt(_T("Config"), _T("LastLO_C"), 0); lasttune_freqA=AfxGetApp()->GetProfileInt(_T("Config"), _T("LastTune_A"), 0); lasttune_freqB=AfxGetApp()->GetProfileInt(_T("Config"), _T("LastTune_B"), 0); + lasttune_freqC=AfxGetApp()->GetProfileInt(_T("Config"), _T("LastTune_C"), 0); switch(ChannelMode) { @@ -416,13 +566,18 @@ unsigned long long libvernum; tune_freq=lasttune_freqB; break; + case CHMODE_IABQ: + lo_freq=lastlo_freqC; + tune_freq=lasttune_freqC; + break; + default: lo_freq=lastlo_freqA; tune_freq=lasttune_freqA; break; } - _beginthread(ExtIORegistryUpdateTask, 0, NULL); + AfxBeginThread((AFX_THREADPROC)ExtIORegistryUpdateTask, NULL); if ((HWType == -1)||(HWType < 3) || (HWType > 7)) HWType = 4; //4 ==> data returned via the sound card @@ -576,7 +731,6 @@ char errstring[128]; long lLastError; char sdrport[16]; char freqstring[32]; -HANDLE sleepevent = CreateEvent(NULL, FALSE, FALSE, NULL); // we are using that instead of sleep(), as it is more kind to overall system resources unsigned long phaseword; char modetmp[16]; @@ -629,16 +783,18 @@ char modetmp[16]; //libusb_ok=true; fifo_loaded=false; - _beginthread(ExtIOCallbackTask, 0, NULL); - _beginthread(ExtIODataTask, 0, NULL); + //AfxBeginThread((AFX_THREADPROC)ExtIOCallbackTask, NULL); + AfxBeginThread((AFX_THREADPROC)ExtIODataTask, NULL); // Wait for fifo to be filled with data before returning to program + /* while(!globalshutdown) { WaitForSingleObject(sleepevent, 0); // give away timeslice if (fifo_loaded==true) break; } + */ } else { @@ -737,9 +893,9 @@ char modetmp[16]; if (HWType != 3) { - sprintf_s(freqstring, 32, "_ga %d\n\r", GainA); + sprintf_s(freqstring, 32, "_ga %d\n\r", IFGainA); serial.Write(freqstring, strlen(freqstring)); - sprintf_s(freqstring, 32, "_gb %d\n\r", GainB); + sprintf_s(freqstring, 32, "_gb %d\n\r", IFGainB); serial.Write(freqstring, strlen(freqstring)); phaseword=1820; //65535=2pi rad = 360deg; 65535/360*1000 @@ -753,7 +909,8 @@ char modetmp[16]; } else { - update_gain=true; + update_IFgain=true; + update_RFgain=true; update_phaseA=true; channelmode_changed=true; lo_changed=true; @@ -775,7 +932,6 @@ It has no parameters and no return value. extern "C" void __stdcall StopHW(void) { -HANDLE sleepevent = CreateEvent(NULL, FALSE, FALSE, NULL); // we are using that instead of sleep(), as it is more kind to overall system resources char modetmp[16]; _cprintf("StopHW() called\n"); @@ -793,7 +949,7 @@ char modetmp[16]; while (!datatask_done) { - WaitForSingleObject(sleepevent, 0); // give away timeslice + WaitForSingleObject(sleepevent, 1); // give away timeslice } _cprintf("StopHW: data thread finished (threadcount=%d)\n", threadcount); } @@ -836,8 +992,26 @@ extern "C" void __stdcall CloseHW(void) if (m_pmodeless) { - m_pmodeless->DestroyWindow(); + //does the following actually leak resources? + //we cant send close messages here, because + //app is getting terminated before the message gets processed. + + if (m_pmodeless) + { + m_pmodeless->DestroyWindow(); + m_pmodeless=NULL; //xx + } + + if (m_pmodelessPanadapter) + { + m_pmodelessPanadapter->DestroyWindow(); + m_pmodelessPanadapter=NULL; //xx + } + + //m_pmodeless->PostMessage(WM_CLOSE, 0, 0); } +/* + Moved to ExitInstance() code if (hasconsole) { @@ -845,7 +1019,7 @@ extern "C" void __stdcall CloseHW(void) if (!FreeConsole()) AfxMessageBox("Could not free the console!"); } - +*/ if (dev) { usb_release_interface(dev, 0); @@ -891,6 +1065,10 @@ char freqstring[32]; lastlo_freqB=LOfreq; break; + case CHMODE_IABQ: + lastlo_freqC=LOfreq; + break; + default: lastlo_freqA=LOfreq; break; @@ -1045,14 +1223,15 @@ extern "C" void __stdcall ShowGUI(void) } else { - m_pmodeless = new CExtIODialog; + m_pmodeless = new CMainDialog; //CExtIODialog; if (m_pmodeless) { MainWindow=CWnd::GetActiveWindow(); - m_pmodeless->Create(/*CGenericMFCDlg*/CExtIODialog::IDD, MainWindow /*GetDesktopWindow()*/); + m_pmodeless->Create(/*CGenericMFCDlg*//*CExtIODialog*/CMainDialog::IDD, MainWindow /*GetDesktopWindow()*/); m_pmodeless->ShowWindow(SW_SHOW); + } else { @@ -1074,12 +1253,13 @@ extern "C" void __stdcall HideGUI(void) { // ...... If the DLL has a GUI, now you have to hide it - // Noone seems to call this function, but it seems that it really _should_ be called during the exit. - // Lets just implement GUI cleanup here for future compatibility when it starts to be called! - if (m_pmodeless) { - m_pmodeless->DestroyWindow(); + //does the following actually leak resources? + //m_pmodeless->DestroyWindow(); + //m_pmodeless=NULL; //xx + + m_pmodeless->PostMessage(WM_CLOSE, 0, 0); } return; @@ -1126,9 +1306,6 @@ It has no return value. extern "C" void __stdcall TuneChanged(long freq) { -unsigned long increment; - - // store channel-specific switch(ChannelMode) { @@ -1138,6 +1315,10 @@ unsigned long increment; lasttune_freqB=freq; break; + case CHMODE_IABQ: + lasttune_freqC=freq; + break; + default: lasttune_freqA=freq; break; @@ -1339,7 +1520,6 @@ extern "C" void __stdcall RawDataReady(long samprate, int *Ldata, int *Rdata, in void ExtIODllCleanup(void) { -HANDLE sleepevent = CreateEvent(NULL, FALSE, FALSE, NULL); // we are using that instead of sleep(), as it is more kind to overall system resources int i; globalshutdown=true; // ask all threads to exit diff --git a/ExtIOFunctions.h b/ExtIOFunctions.h index 3283ec2..9a6a354 100644 --- a/ExtIOFunctions.h +++ b/ExtIOFunctions.h @@ -31,7 +31,7 @@ extern void (* ExtIOCallback)(int, int, float, void *); #define DLLVER_MAJOR 1 -#define DLLVER_MINOR 30 +#define DLLVER_MINOR 39 #define MIN_FW_MAJOR 1 #define MIN_FW_MINOR 70 @@ -39,9 +39,13 @@ extern void (* ExtIOCallback)(int, int, float, void *); #define MIN_FW_MAJOR_PAN 1 #define MIN_FW_MINOR_PAN 95 // minimum version needed to run panadapter +#define MIN_FW_MAJOR_TX8M 1 +#define MIN_FW_MINOR_TX8M 99 // minimum version needed to run TX8M + #define LIB_MIN_MAJOR 1 #define LIB_MIN_MINOR 2 -#define LIB_MIN_MICRO 6 +//#define LIB_MIN_MICRO 6 +#define LIB_MIN_MICRO 2 #define LIB_MIN_NANO 0 #define DATAMASK 0x3FFFF @@ -113,7 +117,8 @@ extern int HWType; #define LIBMODE_16BMA 4 #define LIBMODE_16ABPAN 5 //A, B=panscan #define LIBMODE_16BAPAN 6 //A=panscan, B -#define LIBMODE_16AB 7 +//#define LIBMODE_16AB 7 +#define LIBMODE_16IABQ 7 #define LIBMODE_OFF 10 #define LIBMODE_SPEEDTEST 11 @@ -133,7 +138,7 @@ extern int HWType; #define CHMODE_BMA LIBMODE_16BMA // B-A #define CHMODE_ABPAN LIBMODE_16ABPAN // A, B=panscan #define CHMODE_BAPAN LIBMODE_16BAPAN // A=panscan, B - +#define CHMODE_IABQ LIBMODE_16IABQ // ChA_I=I, ChB_I=Q // structure for running the panoramic scan by typedef struct diff --git a/ExtIOIQData.cpp b/ExtIOIQData.cpp index ef2e867..fe88655 100644 --- a/ExtIOIQData.cpp +++ b/ExtIOIQData.cpp @@ -14,14 +14,17 @@ void (* ExtIOCallback)(int, int, float, void *) = NULL; // Task for receiving samples from radio and communicating back to HDSDR -// note the triple buffering here. -unsigned char tmpData[60*1024]; //Bulk RX buffer. Keep huge to allow USB receive loop to fetch whatever is there, so radio side of USB can transmit as fast as it could! - //this buffer will be copied off immediately after bulk receive function returns. Note, that it has to be less than int/2 - //as we are indexing it with signed int and cant get negative idx -unsigned char SDRData[DATAMASK+1]; //this buffer is where we copy the tmpData to after each rx attempt. Note, that it is exactly an int long, so - //read and write indexes will automatically form a ring pointer - //It is a ring buffer and 20kbytes will be cashed here just in case. IQData buffer content will be copied off from here. -unsigned char IQData[SDRIQDATASIZE]; //take this as global. This buffer is given to HDSDR for samples +// Our static data buffers. We need them all the time, so no need to allocate memory dynamically. + +unsigned char tmpData[60*1024]; //Bulk RX buffer. Keep huge to allow USB receive loop to fetch whatever is there, + //so radio side of USB can transmit as fast as it could. This buffer will be copied off immediately + //after bulk receive function returns. +unsigned char SDRData[SDRIQDATASIZE]; //This buffer is where we copy the tmpData to after each rx attempt. Once it has enough data to call ExtIOCallback, it will be + //copied off and the write pointer reset. + +unsigned char PANData[PANDATAMASK+1]; //If panadapter is active, ExtIODatatask will mux between panadapter and SDR data, so panadapter + //dat ais put here. PANDATAMASK is used for ring buffer masking, so it has to be contignous + //bits, like 00111111b, //unsigned long iqdata_wptr; @@ -40,31 +43,38 @@ extern volatile bool do_callback101; extern volatile bool channelmode_changed; // indicate, that GUI has requested channel mode change extern volatile bool update_lo; extern volatile bool samplerate_changed; // indicates, that someone somewhere has requested sample rate change -extern volatile bool gainA_changed; -extern volatile bool gainB_changed; +extern volatile bool IFgainA_changed; +extern volatile bool IFgainB_changed; extern volatile bool do_pantableupdate; extern volatile bool panadaptertask_exited, run_panadaptertask; extern PANENTRY panentry; +bool pancaching=true; + volatile bool update_phaseA=false; -volatile bool update_gain=false; +volatile bool update_IFgain=false; +volatile bool update_RFgain=false; extern int ChannelMode; extern int SyncTuning; extern int SyncGain; -extern unsigned long lastlo_freqA; -extern unsigned long lastlo_freqB; -extern int GainA, GainB; +extern int IFGainA, IFGainB; +extern int RFGainA, RFGainB; extern int PhaseCoarse, PhaseFine; +extern int AGC; extern long IQSampleRate; +extern unsigned long phaseword; + double callbackinterval; // note, that interval calculations are done with floating point, because callback interval is not integer ms unsigned int pandata_wptr = 0; -unsigned char* PANData=NULL; +//unsigned char* PANData=NULL; + +extern HANDLE sleepevent; /* usb_dev_handle *open_dev(void) @@ -88,27 +98,23 @@ struct usb_device *dev; } */ -void ExtIOCallbackTask(void* dummy); // see at the end of file +UINT ExtIOCallbackTask(LPVOID dummy); // see at the end of file unsigned long sdr_wptr, sdr_readptr; bool buffer_filled; extern char global_dataratestr[64]; -void ExtIODataTask(void* dummy) +UINT ExtIODataTask(LPVOID dummy) { -HANDLE sleepevent = CreateEvent(NULL, FALSE, FALSE, NULL); // we are using that instead of sleep(), as it is more kind to overall system resources int ret; unsigned long i, j, k; unsigned long starttime, millis, totalbytes, lasttotalbytes, buffered; double mbits; int last_synctuning; -unsigned long phaseword; +//unsigned long phaseword; unsigned int iterations=0; unsigned long bytecounter=0; - -//char errstring[128]; -//unsigned long sdr_rptr_local, cached_len; -//usb_dev_handle *dev = NULL; // device handle to be used by libusb +unsigned long samplescached=0; char tmp[128]; EnterCriticalSection(&CriticalSection); @@ -117,57 +123,10 @@ char tmp[128]; datatask_running=true; LeaveCriticalSection(&CriticalSection); - PANData=(unsigned char*)malloc(PANDATAMASK+1); // allocate memory for panoramic data - - /* - As the USB communication task is synchronous, we have to have another task what feeds the data with callback to software. - There is no throttling mechanism prescribed by Winrad nor HDSDR, so we have to keep track ourselves after how many milliseconds - the callback has to be invoked. - */ - callbackinterval=((double)((SDRIQDATASIZE*1000)/(IQSampleRate*BYTESPERSAMPLE*2))); //=callback interval ms - /* - 192000 bytes/sec - /8192=23.4375 x/sec - 1000ms/23.4375=42.6(6) msec interval - */ - _cprintf("ExtIODataTask() thread started\n"); last_synctuning=SyncTuning; -/* - //////////// - // init libusb - usb_init(); // initialize the library - usb_find_busses(); // find all busses - usb_find_devices(); // find all connected devices - - if (!(dev = open_dev())) - { - sprintf_s(errstring, 128, "Error opening device: \n%s\n", usb_strerror()); - AfxMessageBox(errstring, MB_ICONEXCLAMATION); - _cprintf(errstring); - //return(-10); - do_datatask=false; - } - else if (usb_set_configuration(dev, SDR_CONFIG) < 0) - { - sprintf_s(errstring, 128, "Error setting config #%d: %s\n", SDR_CONFIG, usb_strerror()); - AfxMessageBox(errstring, MB_ICONEXCLAMATION); - _cprintf(errstring); - usb_close(dev); - //return(-11); - do_datatask=false; - } - else if (usb_claim_interface(dev, SDR_BULKIF) < 0) // check, if we can claim the bulk interface from radio? - { - sprintf_s(errstring, 128, "Error claiming interface #%d:\n%s\n", SDR_BULKIF, usb_strerror()); - AfxMessageBox(errstring, MB_ICONEXCLAMATION); - _cprintf(errstring); - usb_close(dev); - //return(-12); - do_datatask=false; - } -*/ + totalbytes=0; lasttotalbytes=0; buffered=0; @@ -177,23 +136,9 @@ char tmp[128]; //iqdata_wptr=0; buffer_filled=false; -/* - if (do_datatask) - { - _cprintf("libusb started successfully (0x%04X:0x%04X_%02d)\n", SDR_VID, SDR_PID, SDR_BULKIF); - do_callbacktask=true; - _beginthread(ExtIOCallbackTask, 0, NULL); - } - else - do_callbacktask=false; -*/ - // reset whatever is going on at the endpoint usb_clear_halt(dev, SDREP_IN); - //////////// - - // disable bulk interface usb_control_msg(dev, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_IN, LIBUSB_MODE, @@ -201,18 +146,28 @@ char tmp[128]; SDR_BULKIF, tmp, 1, 1000); - // set sample mode now, to conserve time later! - if (ChannelMode < 2) + switch(ChannelMode) { + case CHMODE_A: + case CHMODE_B: *(unsigned long*)&tmp[0]=IQSAMPLERATE_FULL; - } - else if ((ChannelMode >=5) && (ChannelMode <=6)) - { - *(unsigned long*)&tmp[0]=IQSAMPLERATE_PANADAPTER; - } - else - { + break; + + case CHMODE_APB: + case CHMODE_AMB: + case CHMODE_BMA: *(unsigned long*)&tmp[0]=IQSAMPLERATE_DIVERSITY; + break; + + case CHMODE_ABPAN: + case CHMODE_BAPAN: + *(unsigned long*)&tmp[0]=IQSAMPLERATE_PANADAPTER; + break; + + case CHMODE_IABQ: + default: + *(unsigned long*)&tmp[0]=IQSAMPLERATE_FULL; + break; } tmp[4]=16; @@ -222,19 +177,7 @@ char tmp[128]; SDR_BULKIF, tmp, 5, 1000); - /* - StartHW() will set loop flag for us to perform gain and phase updates by datatask! - // reset phase - *(__int16*)&tmp[0]=(__int16)phaseword; - usb_control_msg(dev, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_OUT, - LIBUSB_SETPHASE, - LIBUSB_CHA, - SDR_BULKIF, - tmp, 2, 1000); - */ - _cprintf("purge ..\n"); - //EnterCriticalSection(&CriticalSection); // fetch all data in buffer to establish I/Q sync while ((do_datatask)&&(!globalshutdown)) { @@ -242,7 +185,6 @@ char tmp[128]; break; WaitForSingleObject(sleepevent, 0); // give away timeslice } - //LeaveCriticalSection(&CriticalSection); if (ret < 0) _cprintf("usb_bulk_read() status %d: %s\n", ret, usb_strerror()); @@ -259,8 +201,6 @@ char tmp[128]; tmp, 1, 1000); // we must now be able to fetch the data before first double-buffered endpoint transfer would fail, otherwise sync may be lost! - - buffer_filled=false; while ((do_datatask)&&(!globalshutdown)) { @@ -272,46 +212,43 @@ char tmp[128]; //if there was any data, put it to staging ring buffer if (ret > 0) { - for (i=0, j=0, k=0; i<(unsigned int)ret; i++) - { - //WaitForSingleObject(sleepevent, 0); // give away timeslice +#pragma message(" ------- do another magic check here!") + + for (i=0, j=0, sdr_wptr=0; i<(unsigned int)ret; i++) + { // multiplex off data for panadapter - if ((ChannelMode == 5)&&((bytecounter+i)&0x4)) //A + panadapter. sdr_wptr+pandata_wptr+i is a method to calculate byte count since the beginning + if ((ChannelMode == CHMODE_ABPAN)&&((bytecounter+i)&0x4)) //A + panadapter. sdr_wptr+pandata_wptr+i is a method to calculate byte count since the beginning { PANData[(pandata_wptr+j)&PANDATAMASK]=tmpData[i]; // 256K ring buffer j++; } - else if ((ChannelMode == 6)&&(!((bytecounter+i)&0x4))) //B + panadapter + else if ((ChannelMode == CHMODE_BAPAN)&&(!((bytecounter+i)&0x4))) //B + panadapter { PANData[(pandata_wptr+j)&PANDATAMASK]=tmpData[i]; // 256K ring buffer j++; } else { - SDRData[(sdr_wptr+k)&DATAMASK]=tmpData[i]; // somewhat slow but straightforward, as memmove() will likely not roll over at 64K - k++; + SDRData[sdr_wptr++]=tmpData[i]; // + + if (sdr_wptr==SDRIQDATASIZE) + { + (*ExtIOCallback)(IQPAIRS, 0, 0, SDRData); // IQPAIRS parameter is not used really by system for nothing else than checking if its negative + sdr_wptr=0; + } } } - EnterCriticalSection(&CriticalSection); - sdr_wptr+=k; // copy current write offset from data pump task + EnterCriticalSection(&CriticalSection); pandata_wptr+=j; LeaveCriticalSection(&CriticalSection); totalbytes+=ret; bytecounter+=ret; - - if (buffered < (SDRIQDATASIZE*DATATASK2CACHE)) // cache some amount of full buffers (we are going to write down immediately most of them to internal cache of HDSDR) - { - buffered+=ret; - WaitForSingleObject(sleepevent, 0); // give away timeslice - _cprintf("v"); - continue; - } - else - buffer_filled=true; } + //stop and exit from task if there is any errors + if (ret < 0) // if we havent received data for 5 seconds, it is quite likely that radio is not responding, so quit data alltogether { _cprintf("ExtIODataTask() error %d retrieving data: %s\n", ret, usb_strerror()); @@ -338,6 +275,8 @@ char tmp[128]; continue; // resume loop, so while() will pop out with next loop } + //update bitrate counter + if (totalbytes > (120000*8)) { lasttotalbytes=totalbytes; @@ -346,29 +285,12 @@ char tmp[128]; mbits/=millis; mbits/=1000; mbits*=8; - /* - EnterCriticalSection(&CriticalSection); - sdr_rptr_local=sdr_readptr; // copy current read offset from callback task - LeaveCriticalSection(&CriticalSection); - if ((sdr_rptr_local&DATAMASK)<(sdr_wptr&DATAMASK)) // read ptr has to catch write ptr - cached_len=(sdr_wptr&DATAMASK)-(sdr_rptr_local&DATAMASK); - else // rptr has to wrap over to catch wptr - cached_len=(sdr_wptr&DATAMASK)+((DATAMASK+1)-(sdr_rptr_local&DATAMASK)); - - cached_len/=SDRIQDATASIZE; - */ // global_dataratebuff is actually outputted by CExtIODialog::OnTimer() EnterCriticalSection(&CriticalSection); sprintf_s(global_dataratestr, 64, "%.3f Mbit/s", mbits); LeaveCriticalSection(&CriticalSection); - //NB! all methods below screw up the GUI (task will not shut down), so use only for testing purposes! - //m_pmodeless->m_DataRateInfo.SetWindowText(ratebuff); - //m_pmodeless->UpdateDataRate(ratebuff); // ratebuff has to be global? - //SendMessage(GetDlgItem(m_pmodeless->/*m_hWnd*/GetSafeHwnd(), IDC_EDIT2/*IDC_STATIC_TEST*/), WM_SETTEXT, 0, (LPARAM)ratebuff); - //SendMessage(GetDlgItem(m_hWnd, IDC_STATIC_TEST), WM_SETTEXT, 0, (LPARAM)ratebuff); - starttime=GetTickCount(); totalbytes=0; lasttotalbytes=0; @@ -376,11 +298,7 @@ char tmp[128]; //WaitForSingleObject(sleepevent, 0); // give away timeslice - if (/*((ChannelMode == CHMODE_A)&&(lastlo_freqA != lo_freq)) || - ((ChannelMode == CHMODE_B)&&(lastlo_freqB != lo_freq)) || - ((SyncTuning)&&(lastlo_freqA != lo_freq)) || // synchronous tuning is tracked by chA frequency - (SyncTuning != last_synctuning)||*/ - (update_lo)) + if (update_lo) { *(unsigned long*)&tmp[0]=lo_freq; last_synctuning=SyncTuning; @@ -534,11 +452,7 @@ char tmp[128]; update_phaseA=false; // clear change request flag LeaveCriticalSection(&CriticalSection); - phaseword=1820; //65535=2pi rad = 360deg; 65535/360*1000 - phaseword*=(PhaseCoarse*100)+PhaseFine; - phaseword/=1000; - - *(__int16*)&tmp[0]=(__int16)phaseword; + *(unsigned __int16*)&tmp[0]=(unsigned __int16)phaseword; // global phaseword is calculated in slider dialog code usb_control_msg(dev, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_OUT, LIBUSB_SETPHASE, @@ -546,16 +460,16 @@ char tmp[128]; SDR_BULKIF, tmp, 2, 1000); - _cprintf("Phase for Ch A set to %ld\n", phaseword); + _cprintf("Phase for Ch A requested to set %u\n", *(unsigned __int16*)&tmp[0]); } - if (update_gain) + if (update_IFgain) { EnterCriticalSection(&CriticalSection); - update_gain=false; // clear gain update request flag + update_IFgain=false; // clear gain update request flag LeaveCriticalSection(&CriticalSection); - *(__int16*)&tmp[0]=(__int16)GainA; + *(__int16*)&tmp[0]=(__int16)IFGainA; usb_control_msg(dev, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_OUT, LIBUSB_SETGAIN, @@ -563,7 +477,7 @@ char tmp[128]; SDR_BULKIF, tmp, 2, 1000); - *(__int16*)&tmp[0]=(__int16)GainB; + *(__int16*)&tmp[0]=(__int16)IFGainB; usb_control_msg(dev, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_OUT, LIBUSB_SETGAIN, @@ -571,15 +485,68 @@ char tmp[128]; SDR_BULKIF, tmp, 2, 1000); - _cprintf("Gains set: ChA=%d ChB=%d\n", GainA, GainB); + _cprintf("IFGains set: ChA=%d ChB=%d\n", IFGainA, IFGainB); + + } + + if (update_RFgain) + { + EnterCriticalSection(&CriticalSection); + update_RFgain=false; // clear gain update request flag + LeaveCriticalSection(&CriticalSection); + + _cprintf("AGC %s\n", (AGC)?"ON":"OFF"); + + //doc sais that in order to go for manual gain, initial conditions must be programmed BEFORE agc loop is open, so we + //will enabling AGC as a first thing always + + //read register with AGC integrator + ret=usb_control_msg(dev, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_IN, + LIBUSB_READREG, + 20, + SDR_BULKIF, + tmp, 1, 1000); + + _cprintf("Reg(20): 0x%02X (retcode %d)\n", tmp[0], ret); + + if (AGC) + tmp[0]&=0xF7; //integrator in closed loop + else + tmp[0]|=0x8; //disable AGC + + usb_control_msg(dev, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_OUT, + LIBUSB_WRITEREG, + 20, + SDR_BULKIF, + tmp, 1, 1000); + + if (!AGC) + { + tmp[1]=RFGainA; + tmp[2]=RFGainB; + + tmp[1]<<=5; + tmp[2]<<=5; + tmp[1]|=0x1F; + tmp[2]|=0x1F; + tmp[1]=~tmp[1]; + tmp[2]=~tmp[2]; + + usb_control_msg(dev, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_OUT, + LIBUSB_WRITEREG, + 23, + SDR_BULKIF, + tmp+1, 2, 1000); + _cprintf("RFGain values set: ChA=%d(reg23=0x%02X) ChB=%d(reg24=%02.2X)\n", RFGainA, (unsigned char)tmp[1], RFGainB, (unsigned char)tmp[2]); + } } + //theoretically not needed, as we are calling extio callback and this seems to be taking care of timeslice. + //However, if we have no data to give the load will likely peak so give away time forcefully every once and a while iterations++; if (!(iterations%10000)) WaitForSingleObject(sleepevent, 1); // give away timeslice - else - WaitForSingleObject(sleepevent, 0); // give away timeslice } if (dev) @@ -604,30 +571,6 @@ char tmp[128]; */ } - _cprintf("Waiting for callback task to exit ..\n"); - // shut down callback data pump - if (do_callbacktask) - { - do_callbacktask=false; - - for (i=0; i<200; i++) // wait alltogether two seconds - { - if (!callbacktask_running) - break; - WaitForSingleObject(sleepevent, 10); // give away timeslice - } - } - - // have to wait for panadapter task to finish, otherwise PANData array gets lost before task finishes and HDSDR ends with exception - _cprintf("Waiting for Panadapter task to exit ..\n"); - run_panadaptertask=false; - for (i=0; i<200; i++) // wait alltogether two seconds - { - if (panadaptertask_exited) - break; - WaitForSingleObject(sleepevent, 10); // give away timeslice - } - _cprintf("ExtIODataTask() now leaving!\n"); // indicate to main, that thread is done EnterCriticalSection(&CriticalSection); @@ -636,144 +579,6 @@ char tmp[128]; datatask_done=true; sprintf_s(global_dataratestr, 64, "0 Mbit/s"); // reset datarate buffer, as we have stopped data pumps LeaveCriticalSection(&CriticalSection); -} - - -void ExtIOCallbackTask(void* dummy) -{ -HANDLE sleepevent = CreateEvent(NULL, FALSE, FALSE, NULL); // we are using that instead of sleep(), as it is more kind to overall system resources -double nextpass; -int cachedblocks, iqdata_wptr; -unsigned long sdr_wptr_local; -unsigned int iterations=0; - - EnterCriticalSection(&CriticalSection); - threadcount++; - callbacktask_running=true; - LeaveCriticalSection(&CriticalSection); - - _cprintf("ExtIOCallbackTask() thread started\n"); - - cachedblocks=0; - sdr_readptr=0; - iqdata_wptr=0; - - // wait for data task to cache enough data - while((do_callbacktask)&&(!globalshutdown)&&(!buffer_filled)) - { - WaitForSingleObject(sleepevent, 1); // give away timeslice - } - - nextpass=GetTickCount(); // start counting time - - while((do_callbacktask)&&(!globalshutdown)) - { - // Oddly enough, if we are measuring time ourselves and just give time away with WaitForSingleObject(xx, 0) - // the CPU gets 100% utilization. So we are now using WaitForSingleObject() for timings, what also gives away time then. - //if ((cachedblocks < WINRAD2CACHE) || (nextpass <= GetTickCount())) - { - - EnterCriticalSection(&CriticalSection); - sdr_wptr_local=sdr_wptr; // copy current write offset from data pump task - LeaveCriticalSection(&CriticalSection); - - //fill IQ data buffer - if (iqdata_wptr < SDRIQDATASIZE) - { - /* - // If we are in panadapter mode, we have to split the stream in two between actual channel data and panadapter data - if (ChannelMode == 5) //A + panadapter - { - while (((sdr_readptr&DATAMASK) != (sdr_wptr_local&DATAMASK))&&(iqdata_wptr < SDRIQDATASIZE)&&(do_datatask)&&(!globalshutdown)) - { - if (!(sdr_readptr&0x4)) - { - IQData[iqdata_wptr]=SDRData[sdr_readptr&DATAMASK]; - iqdata_wptr++; - } - - else - { - //data for panadapter - //PANData[pandata_wptr&PANDATAMASK]=SDRData[sdr_readptr&DATAMASK]; // 256K ring buffer - //pandata_wptr++; - } - - - sdr_readptr++; - - //WaitForSingleObject(sleepevent, 0); // give away timeslice - } - } - else if (ChannelMode == 6) //B + panadapter - { - while (((sdr_readptr&DATAMASK) != (sdr_wptr_local&DATAMASK))&&(iqdata_wptr < SDRIQDATASIZE)&&(do_datatask)&&(!globalshutdown)) - { - if (sdr_readptr&0x4) - { - IQData[iqdata_wptr]=SDRData[sdr_readptr&DATAMASK]; - iqdata_wptr++; - } - - else - { - //data for panadapter - //PANData[pandata_wptr&PANDATAMASK]=SDRData[sdr_readptr&DATAMASK]; // 256K ring buffer - //pandata_wptr++; - } - - sdr_readptr++; - - //WaitForSingleObject(sleepevent, 0); // give away timeslice - } - } - else - { - */ - while (((sdr_readptr&DATAMASK) != (sdr_wptr_local&DATAMASK))&&(iqdata_wptr < SDRIQDATASIZE)&&(do_datatask)&&(!globalshutdown)) - { - IQData[iqdata_wptr]=SDRData[sdr_readptr&DATAMASK]; // note, that as sdr_rptr was initialized to -1, we must increment it _before_ usage. - sdr_readptr++; - iqdata_wptr++; - //WaitForSingleObject(sleepevent, 0); // give away timeslice - } - //} - } - - if (iqdata_wptr == SDRIQDATASIZE) - { - (*ExtIOCallback)(IQPAIRS, 0, 0, IQData); // IQPAIRS parameter is not used really by system for nothing else than checking if its negative - iqdata_wptr=0; - - if (!fifo_loaded) - { - cachedblocks++; - _cprintf("^"); - } - //else - // nextpass+=callbackinterval; // get time for next pass - } - } - - if ((!fifo_loaded)&&(cachedblocks == WINRAD2CACHE)) - { - fifo_loaded=true; - } - - if (!fifo_loaded) - WaitForSingleObject(sleepevent, 0); // give away timeslice - else - { - // just waiting by callbackinterval ms will give improper timing, as - // our time granularity is around 55ms in windows and we may end up starving - nextpass+=callbackinterval; - WaitForSingleObject(sleepevent, max(0, (nextpass-GetTickCount()))); - } - } - - EnterCriticalSection(&CriticalSection); - threadcount--; - callbacktask_running=false; - LeaveCriticalSection(&CriticalSection); -} \ No newline at end of file + return 1; +} diff --git a/ExtIO_SDR_MK15.dll b/ExtIO_SDR_MK15.dll index 9d7e5410180ec09dde3451fffe7e72954eb848a2..bb0a1d14eb1aa01f6d65a35465c856dfffb3ca18 100644 GIT binary patch delta 166064 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, this)); + VERIFY(m_dlg[1]->Create(IDD_ADVANCEDDIALOG, this)); + + CenterWindow(); + + return TRUE; +} + +void CMainDialog::OnShowWindow(BOOL bShow, UINT nStatus) +{ + // initialize tabbed view - show first dialog + ShowTabDlg(m_tab.GetCurSel()); +} + +void CMainDialog::ShowTabDlg(int tabSel) +{ + // hide all tab dialogs + for(int idx = 0; idx < myARRAYSIZE(m_dlg); idx++) + m_dlg[idx]->ShowWindow(SW_HIDE); + + // show selected one + CRect rc; + m_tab.GetClientRect(rc); + m_tab.AdjustRect(FALSE, rc); + //m_tab.ClientToScreen(rc); Seems that we do not need this for modeless dialogs + // as they are already operating with screen coordinates. + m_dlg[tabSel]->SetWindowPos(NULL, rc.left+2, rc.top+2, 0, 0, SWP_NOZORDER|SWP_NOSIZE|SWP_SHOWWINDOW|SWP_NOACTIVATE); +} + + +void CMainDialog::OnTabChange(NMHDR* pnmhdr, LRESULT* pResult) +{ + ShowTabDlg(m_tab.GetCurSel()); + *pResult = 0; +} + +/* +void CMainDialog::OnDestroy() +{ + AfxMessageBox("OnDestroy()"); + + for(int idx = 0; idx < myARRAYSIZE(m_dlg); idx++) + delete m_dlg[idx]; +} +*/ + +//This is needed when we are closing the window using the ExtIO button. + +void CMainDialog::PostNcDestroy() +{ + CDialog::PostNcDestroy(); + + for(int idx = 0; idx < myARRAYSIZE(m_dlg); idx++) + { + if (m_dlg[idx]) + { + delete m_dlg[idx]; + m_dlg[idx]=0; + } + } + + delete this; + m_pmodeless=NULL; //xx +} + +// This is needed on a case we are closing window using the [X] at the corner + +void CMainDialog::OnClose() +{ + for(int idx = 0; idx < myARRAYSIZE(m_dlg); idx++) + { + if (m_dlg[idx]) + m_dlg[idx]->ShowWindow(SW_HIDE); + } + + CDialog::OnClose(); +} diff --git a/MainDialog.h b/MainDialog.h new file mode 100644 index 0000000..791b2d4 --- /dev/null +++ b/MainDialog.h @@ -0,0 +1,33 @@ +#pragma once + + +// CMainDialog dialog + +class CMainDialog : public CDialog +{ + DECLARE_DYNAMIC(CMainDialog) + +public: + + CMainDialog(CWnd* pParent = NULL); // standard constructor + virtual ~CMainDialog(); + +// Dialog Data + enum { IDD = IDD_DIALOG3 }; + +protected: + + CTabCtrl m_tab; + CDialog* m_dlg[2]; + + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + virtual BOOL OnInitDialog(); + void ShowTabDlg(int tabSel); + void OnTabChange(NMHDR* pnmhdr, LRESULT* pResult); + virtual void PostNcDestroy(); + + afx_msg void OnClose(); + afx_msg void OnShowWindow(BOOL bShow, UINT nStatus); + + DECLARE_MESSAGE_MAP() +}; diff --git a/PanadapterDialog.cpp b/PanadapterDialog.cpp index e218bfa..7b94b18 100644 --- a/PanadapterDialog.cpp +++ b/PanadapterDialog.cpp @@ -8,23 +8,14 @@ #include #include +#include #include "Fourier.h" -//#include "ExtIODll.h" -//#include "ExtIODialog.h" -//#include "SelectComPort\ComPortCombo.h" -//#include "serial\serial.h" -//#include "libusb\lusb0_usb.h" -//#include "ExtIOFunctions.h" - -//#include // Equates for WM_DEVICECHANGE and BroadcastSystemMessage -//#include // gives _cprintf() - // CPanadapterDialog dialog extern int Transparency; extern unsigned int pandata_wptr; -extern unsigned char* PANData; +extern unsigned char PANData[]; extern unsigned long iflimit_high; //ExtIOFunctions.cpp extern unsigned long iflimit_low; @@ -35,6 +26,11 @@ extern volatile bool do_callback105; extern volatile bool do_callback101; volatile bool do_pantableupdate=false; +extern bool pancaching; +extern HANDLE sleepevent; + +int scrolltimer=50; //NB! this value is an actual timer value, NOT the slider value (we have slider reversed!) + PANENTRY panentry; int fftbands=0; int fftbandsneeded=0; @@ -47,6 +43,9 @@ int skipzone_new=8; long long _lastrangemin=32000000; // cause immdiate update on pass 1 long long _lastrangemax=0; +long long bytesprocessed=0; +long long lastmagicfound=0; + double dbrangemin=0, dbrangemax=1100; CBitmap *pOldBitmap; @@ -80,22 +79,6 @@ void CPanadapterDialog::DoDataExchange(CDataExchange* pDX) DDX_Control(pDX, IDC_EDIT2, m_ActiveInfo); DDX_Control(pDX, IDC_CUSTOM1, c_FreqRangeSlider); DDX_Control(pDX, IDC_CUSTOM2, c_ColorRangeSlider); - - /* - DDX_Control(pDX, IDC_COMBO1, m_comboPorts); - DDX_Control(pDX, IDC_SLIDER1, m_GainSliderA); - DDX_Control(pDX, IDC_SLIDER2, m_GainSliderB); - DDX_Control(pDX, IDC_SLIDER3, m_PhaseSliderCoarse); - DDX_Control(pDX, IDC_SLIDER4, m_PhaseSliderFine); - DDX_Control(pDX, IDC_EDIT1, m_PhaseInfo); - DDX_Control(pDX, IDC_SLIDER5, m_TransparencySlider); - DDX_Control(pDX, IDC_CHECK1, m_DllIQ); - DDX_Control(pDX, IDC_EDIT2, m_DataRateInfo); - DDX_Radio(pDX, IDC_RADIO_CMODE1, m_nChannelMode); - DDX_Control(pDX, IDC_CHECK2, m_SyncGainCheck); - DDX_Control(pDX, IDC_CHECK3, m_SyncTuneCheck); - DDX_Control(pDX, IDC_CHECK4, m_DebugConsoleCheck); - */ } @@ -108,28 +91,9 @@ BEGIN_MESSAGE_MAP(CPanadapterDialog, CDialog) ON_WM_ERASEBKGND() ON_REGISTERED_MESSAGE(RANGE_CHANGED, OnRangeChange) ON_WM_CLOSE() -/* - ON_BN_CLICKED(IDOK, &CExtIODialog::OnBnClickedOk) - ON_BN_CLICKED(IDC_BUTTON1, &CExtIODialog::OnBnClickedButton1) - ON_WM_DEVICECHANGE() - ON_CBN_SELCHANGE(IDC_COMBO1, &CExtIODialog::OnCbnSelchangeCombo1) - ON_BN_CLICKED(IDC_CHECK1, &CExtIODialog::OnBnClickedCheck1) - ON_BN_CLICKED(IDC_CHECK2, &CExtIODialog::OnBnClickedCheck2) - ON_BN_CLICKED(IDC_CHECK3, &CExtIODialog::OnBnClickedCheck3) - ON_BN_CLICKED(IDC_CHECK4, &CExtIODialog::OnBnClickedCheck4) - ON_WM_HSCROLL() - ON_WM_TIMER() // we need timer to do periodic updates on dialog window. Doing it by accessing contols from different task directly corrupts something - ON_BN_CLICKED(IDC_RADIO_CMODE1, &CExtIODialog::OnBnClickedRadioCmode1) - ON_BN_CLICKED(IDC_RADIO_CMODE2, &CExtIODialog::OnBnClickedRadioCmode2) - ON_BN_CLICKED(IDC_RADIO_CMODE3, &CExtIODialog::OnBnClickedRadioCmode3) - ON_BN_CLICKED(IDC_RADIO_CMODE4, &CExtIODialog::OnBnClickedRadioCmode4) - ON_BN_CLICKED(IDC_RADIO_CMODE5, &CExtIODialog::OnBnClickedRadioCmode5) - ON_BN_CLICKED(IDC_RADIO_CMODE5, &CExtIODialog::OnBnClickedRadioCmode6) - ON_BN_CLICKED(IDC_RADIO_CMODE5, &CExtIODialog::OnBnClickedRadioCmode7) -*/ END_MESSAGE_MAP() -void PanadapterTask(void* dummy); +UINT PanadapterTask(LPVOID dummy); CDC *MemDC, *pDC; CRect rcWaterFall; @@ -193,6 +157,13 @@ int i, j; // Set "Visual" range. c_ColorRangeSlider.SetVisualMinMax(0, 0); + m_SpeedSlider.SetRange(1, 120); + //reversing the slider control is real pain, so we are interpreting the value bacwkards. + //Note, that we are writing the actual timer value to registry, not the slider value! + m_SpeedSlider.SetPos(m_SpeedSlider.GetRangeMax()+m_SpeedSlider.GetRangeMin()-scrolltimer); + + m_SpeedSlider.SetTicFreq(10); + // build waterfall bitblt structures now GetDlgItem(IDC_WATERFALL)->GetClientRect(rcWaterFall); @@ -229,9 +200,9 @@ int i, j; run_panadaptertask=true; panadaptertask_exited=false; - _beginthread(PanadapterTask, 0, NULL); + AfxBeginThread((AFX_THREADPROC)PanadapterTask, NULL/*, THREAD_PRIORITY_IDLE*/); // set timer only after DC stuff is initialized - SetTimer(ID_CLOCK_TIMER, 50, NULL); //50ms timer, null show put it to task queue + SetTimer(ID_CLOCK_TIMER, scrolltimer, NULL); //50ms timer, null show put it to task queue return true; } @@ -250,49 +221,6 @@ float shift=10.0f; // just brighten the overall picture by x dB double multiplier; - /* - for (i=0; i<_fftbands; i++) - { - fftpool[i]-=(dbrangemin/10); // crop from below; - if (fftpool[i] < 0) - fftpool[i]=0; - } - */ - - /* - for (i=0; i<_fftbands; i++) - { - // expander - fftpool[i]*=(fftpool[i]/knee)+(100-(100*knee)); - //fftpool[i]*=fftpool[i]; //make a little more "logarithmic" to filter noise - fftpool[i]/=expander; //100=no expansion; below 100 will cause expansion - fftpool[i]+=shift; - - if (fftpool[i] > 110.0f) - fftpool[i]=110.0f; // set limiter to 100dB - - } - */ - - /* - // now create expander - - multiplier=(110-(dbrangemin/10))/(dbrangemax/10); - - for (i=0; i<_fftbands; i++) - { - fftpool[i]-=(dbrangemin/10); // crop from below; - if (fftpool[i] < 0) - fftpool[i]=0; - - //now multiply, so dbrangemax == 110, clip the rest to 110 - - fftpool[i]*=multiplier; - if (fftpool[i]>110) - fftpool[i]=110; - } - */ - multiplier=110/((dbrangemax-dbrangemin)/10); for (i=0; i<_fftbands; i++) @@ -314,20 +242,17 @@ unsigned int pandata_rptr=0; int magicsfound=0; unsigned char magic[12]={0xA0+0, 0x55, 0xF0+1, 0xF0, 0xA0+2, 0x55, 0xF0+3, 0xF0, 0xA0+4, 0x55, 0xF0+5, 0xF0}; int panstate=PAN_MAGIC; - int minfreq, maxfreq; long long _freqrangemin=0, _freqrangemax=32000000, _freqspan=32000000; - float dbminmaxpool[0xFFF+1]; -void PanadapterTask(void* dummy) + +UINT PanadapterTask(LPVOID dummy) { int i=0, j=0, k; unsigned int l_pandata_wptr; -//char prntxt[64]; char freqbuff[8]; // four last bytes are used for checking the trailer magic (supposed to be AA 55 F0 F0) -HANDLE sleepevent = CreateEvent(NULL, FALSE, FALSE, NULL); // we are using that instead of sleep(), as it is more kind to overall system resources -HANDLE hArray[2]; + //HANDLE hSnapShot; unsigned int pandatalen=0; @@ -345,13 +270,15 @@ double dummydbl; double* window; unsigned int iterations=0; +double avgval=0; +int avgcnt=0; + +bool magicgone=false; + float re, im; unsigned int dbpooloffset=0; - hArray[0] = sleepevent; - //hArray[1] = hParent; - //hSnapShot = lpfCreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0 ); for (k=0; k<(0xFFF+1); k++) @@ -379,8 +306,13 @@ unsigned int dbpooloffset=0; while(run_panadaptertask) { - //MsgWaitForMultipleObjects(1, hArray, FALSE, 0, QS_ALLINPUT); // give away timeslice - WaitForSingleObject(sleepevent, 1); + WaitForSingleObject(sleepevent, 1); //give away timeslice + + if ((bytesprocessed-lastmagicfound)>1024) + { + magicgone=true; + _cprintf("Magic loss!\n"); + } if (fftbands_new > fftbands) { @@ -456,38 +388,31 @@ unsigned int dbpooloffset=0; l_pandata_wptr=pandata_wptr; while (((pandata_rptr&PANDATAMASK)!=(l_pandata_wptr&PANDATAMASK))&&(run_panadaptertask)) - { - //WaitForSingleObject(sleepevent, 0); // give away timeslice - //MsgWaitForMultipleObjects(1, hArray, FALSE, 0, QS_ALLINPUT); // give away timeslice - - iterations++; - if (!(iterations%10000)) - WaitForSingleObject(sleepevent, 1); // give away timeslice - else - WaitForSingleObject(sleepevent, 0); // give away timeslice - + { switch(panstate) { case PAN_MAGIC: case PAN_DATA: while (((pandata_rptr&PANDATAMASK)!=(l_pandata_wptr&PANDATAMASK))&&(run_panadaptertask)) - { - iterations++; - if (!(iterations%1000)) - WaitForSingleObject(sleepevent, 1); // give away timeslice - else - WaitForSingleObject(sleepevent, 0); // give away timeslice - - //MsgWaitForMultipleObjects(1, hArray, FALSE, 0, QS_ALLINPUT); // give away timeslice - + { for (; i<8; i++) { + bytesprocessed++; + if (PANData[(pandata_rptr+i)&PANDATAMASK] == magic[i]) { if (((pandata_rptr+i)&PANDATAMASK)==(l_pandata_wptr&PANDATAMASK)) break; // buffer end, so break, but do not reset scan pointer else + { + if (magicgone) + { + _cprintf("Magic reappeared after %ld\n", (bytesprocessed-lastmagicfound)); + magicgone=false; + } + lastmagicfound=bytesprocessed; //internal debug only continue; // magic matches and there is still data, so continue scan + } } else { @@ -498,17 +423,16 @@ unsigned int dbpooloffset=0; if (i==8) // all magic done? { - if ((panstate == PAN_DATA)&&(pandatalen >=((fftbands*4)+(skipzone*4)))) // we need to have at least enough samples for FFT (16-bit I, 16-bit Q and 8-IQsample ignore zone what is captured while frequency was changed) + if ((panstate == PAN_DATA)&&(pandatalen >=(unsigned int)((fftbands*4)+(skipzone*4)))) // we need to have at least enough samples for FFT (16-bit I, 16-bit Q and 8-IQsample ignore zone what is captured while frequency was changed) { //new magic found, so we have to process the current pandata buffer and inject new FFT! //note, that (int)(*(int*)&freqbuff[0]) contains current starting frequency! // copy last IQ pairs to FFT source buffer for (i=0, j=skipzone*4; iSetPixelV(freqpos+j, rcWaterFall.bottom-1, Intensity(avgval)); - j++; - avgval=0; - avgcnt=0; - } + avgval/=avgcnt; + if ((freqpos+j)<=(rcWaterFall.right-1)) + MemDC->SetPixelV(freqpos+j, rcWaterFall.bottom-1, Intensity(avgval)); + j++; + avgval=0; + avgcnt=0; } } @@ -664,9 +572,8 @@ unsigned int dbpooloffset=0; { //copy panadapter data to the buffer if (pandatalen < (PANDATAMASK+1)) - { - //WaitForSingleObject(sleepevent, 0); // give away timeslice - //MsgWaitForMultipleObjects(1, hArray, FALSE, 0, QS_ALLINPUT); // give away timeslice + { +#pragma message(" ------- Why do we double-buffer here?") pandata[pandatalen]=PANData[pandata_rptr&PANDATAMASK]; pandatalen++; } @@ -678,26 +585,25 @@ unsigned int dbpooloffset=0; case PAN_FREQ: while ((pandata_rptr&PANDATAMASK)!=(l_pandata_wptr&PANDATAMASK)) - { - WaitForSingleObject(sleepevent, 0); // give away timeslice - //MsgWaitForMultipleObjects(1, hArray, FALSE, 0, QS_ALLINPUT); // give away timeslice - + { freqbuff[i]=PANData[pandata_rptr&PANDATAMASK]; i++; pandata_rptr++; + bytesprocessed++; + if (i==8) { + i=0; + if (!memcmp(freqbuff+4, magic+8, 4)) { - i=0; panstate=PAN_DATA; magicsfound++; // increment successful tokens count } else { // trailer failure, so discard current header and start over - i=0; panstate=PAN_MAGIC; } @@ -724,6 +630,8 @@ unsigned int dbpooloffset=0; free(fftmap16); panadaptertask_exited=true; + + return 1; } long lastclickX=0; @@ -746,6 +654,8 @@ unsigned int steps, fftpoints, fftpointsP2, skip; if (nIDEvent == ID_CLOCK_TIMER) { + KillTimer(ID_CLOCK_TIMER); //50ms should be enough, but just to be sure to avoid recursion + CBitmap *pxOldBitmap = (CBitmap*) MemDC->SelectObject(MemBitmap); pDC->BitBlt(0,0,rcWaterFall.right,rcWaterFall.bottom,MemDC,0,0,SRCCOPY); // Copies the bitmap from the memory dc to the pdc using a fast bitblt function call. //bitblt internally by one line and add a new scanline at the bottom @@ -768,7 +678,7 @@ unsigned int steps, fftpoints, fftpointsP2, skip; if (!_freqspan) _freqspan=1; // avoid divide by 0 - sprintf_s(prntxt, 256, "%.3f-%.3f MHz (%d-%d) [%d+%d*%d %dsps]", freqrangemin/1000000, freqrangemax/1000000, minfreq, maxfreq, panentry.startfreq, panentry.steps, panentry.stepfreq, panentry.samples); + sprintf_s(prntxt, 256, "%.3f-%.3f MHz (%d-%d) [%d+%d*%d %dsps] [%d]", freqrangemin/1000000, freqrangemax/1000000, minfreq, maxfreq, panentry.startfreq, panentry.steps, panentry.stepfreq, panentry.samples, _selectedfreq); m_RangeInfo.SetWindowText(prntxt); GetDlgItem(IDC_WATERFALL)->GetWindowRect(&rect); @@ -811,7 +721,7 @@ unsigned int steps, fftpoints, fftpointsP2, skip; //make fftpoints power of two now for (i=0, fftpointsP2=2; i<16; i++) { - if ((1<=fftpoints) + if ((unsigned int)(1<=fftpoints) { fftpointsP2=1<<(i+1); //multiple by two on the fly, as for output complex FFT both sides (-N and +N) we need double the points break; @@ -879,7 +789,7 @@ unsigned int steps, fftpoints, fftpointsP2, skip; _selectedfreq=lo_freq; } - sprintf_s(prntxt, 256, "%d (%d) [%d]", magicsfound, magicsfound-localmagic, _selectedfreq); + sprintf_s(prntxt, 256, "%d (%d) [%d]", magicsfound, magicsfound-localmagic, bytesprocessed); m_ActiveInfo.SetWindowText(prntxt); lastclickX=0; @@ -894,6 +804,8 @@ unsigned int steps, fftpoints, fftpointsP2, skip; freqrangemax+=(iflimit_high-iflimit_low)/2; c_FreqRangeSlider.SetVisualMinMax(freqrangemin, freqrangemax); + + SetTimer(ID_CLOCK_TIMER, scrolltimer, NULL); //restore our 50ms timer } CDialog::OnTimer(nIDEvent); @@ -927,8 +839,15 @@ CSliderCtrl* pSld; pSld = (CSliderCtrl*)pScrollBar; + // It seems, that having a reversed slider is a real pain, so we have to interpret + // the result backwards ourselves. Note, that we also write backwards value to the registry + // for easing up initialization. + if (*pSld == m_SpeedSlider) { + //could this generate race condition when the OnTimer() is tryng to re-enable timer? + scrolltimer=m_SpeedSlider.GetPos(); + scrolltimer=m_SpeedSlider.GetRangeMax()+m_SpeedSlider.GetRangeMin()-scrolltimer; } CDialog::OnHScroll(nSBCode, nPos, pScrollBar); @@ -949,8 +868,11 @@ LRESULT CPanadapterDialog::OnRangeChange(WPARAM wParam, LPARAM /* lParam */) extern CPanadapterDialog* m_pmodelessPanadapter; void CPanadapterDialog::PostNcDestroy() -{ +{ + AfxMessageBox("PostNcDestroy()"); + CDialog::PostNcDestroy(); + m_pmodelessPanadapter = NULL; delete this; } @@ -958,7 +880,7 @@ void CPanadapterDialog::PostNcDestroy() // We have to handle close event, as we do not have parent object what would perform destruction for us void CPanadapterDialog::OnClose() { -HANDLE sleepevent = CreateEvent(NULL, FALSE, FALSE, NULL); // we are using that instead of sleep(), as it is more kind to overall system resources + AfxMessageBox("OnClose()"); run_panadaptertask=false; diff --git a/PanadapterDialog.h b/PanadapterDialog.h index 3005dae..7a063c6 100644 --- a/PanadapterDialog.h +++ b/PanadapterDialog.h @@ -20,8 +20,6 @@ class CPanadapterDialog : public CDialog CRangeSlider c_FreqRangeSlider; CRangeSlider c_ColorRangeSlider; - - HMODULE hParent; //void filterfft(float* fftpool, int fftbands); diff --git a/StdAfx.h b/StdAfx.h index 8c0dae0..7767fd2 100644 --- a/StdAfx.h +++ b/StdAfx.h @@ -35,6 +35,9 @@ #include // MFC support for Windows Common Controls #endif // _AFX_NO_AFXCMN_SUPPORT +//stdafx.h is sure to be loaded for all source files, so this is a good place to have this. +#include "memwatch-2.71\\memwatch.h" + //{{AFX_INSERT_LOCATION}} // Microsoft Visual C++ will insert additional declarations immediately before the previous line. diff --git a/memwatch-2.71/FAQ b/memwatch-2.71/FAQ new file mode 100644 index 0000000..efcec58 --- /dev/null +++ b/memwatch-2.71/FAQ @@ -0,0 +1,133 @@ +Frequently Asked Questions for memwatch + +Q. I'm not getting any log file! What's wrong?? + +A. Did you define MEMWATCH when compiling all files? + Did you include memwatch.h in all the files? + If you did, then...: + + Memwatch creates the file when it initializes. If you're not + getting the log file, it's because a) memwatch is not + initializing or b) it's initializing, but can't create the + file. + + Memwatch has two functions, mwInit() and mwTerm(), that + initialize and terminate memwatch, respectively. They are + nestable. You USUALLY don't need to call mwInit() and + mwTerm(), since memwatch will auto-initialize on the first + call to a memory function, and then add mwTerm() to the + atexit() list. + + You can call mwInit() and mwTerm() manually, if it's not + initializing properly or if your system doesn't support + atexit(). Call mwInit() as soon as you can, and mwTerm() at + the logical no-error ending of your program. Call mwAbort() + if the program is stopping due to an error; this will + terminate memwatch even if more than one call to mwTerm() is + outstanding. + + If you are using C++, remember that global and static C++ + objects constructors execute before main() when considering + where to put mwInit(). Also, their destructors execute after + main(). You may want to create a global object very early + with mwInit() in the constructor and mwTerm() in the + destructor. Too bad C++ does not guarantee initialization + order for global objects. + + If this didn't help, try adding a call to mwDoFlush(1) after + mwInit(). If THAT didn't help, then memwatch is unable to + create the log file. Check write permissions. + + If you can't use a log file, you can still use memwatch by + redirecting the output to a function of your choice. See the + next question. + +Q. I'd like memwatch's output to pipe to my fave debugger! How? + +A. Call mwSetOutFunc() with the address of a "void func(int c)" + function. You should also consider doing something about + the ARI handler, see memwatch.h for more details about that. + +Q. Why isn't there any C++ support? + +A. Because C++ is for sissies! =) Just kidding. + C++ comes with overridable allocation/deallocation + built-in. You can define your own new/delete operators + for any class, and thus circumvent memwatch, or confuse + it to no end. Also, the keywords "new" and "delete" may + appear in declarations in C++, making the preprocessor + replacement approach shaky. You can do it, but it's not + very stable. + + If someone were to write a rock solid new/delete checker + for C++, there is no conflict with memwatch; use them both. + +Q. I'm getting "WILD free" errors, but the code is bug-free! + +A. If memwatch's free() recieves a pointer that wasn't allocated + by memwatch, a "WILD free" message appears. If the source of + the memory buffer is outside of memwatch (a non-standard + library function, for instance), you can use mwFree_() to + release it. mwFree_() calls free() on the pointer given if + memwatch can't recognize it, instead of blocking it. + + Another source of "WILD free" messages is that if memwatch + is terminated before all memory allocated is freed, memwatch + will have forgotten about it, and thus generate the errors. + This is commonly caused by having memwatch auto-initialize, + and then using atexit() to clean up. When auto-initializing, + memwatch registers mwTerm() with atexit(), but if mwTerm() + runs before all memory is freed, then you will get "unfreed" + and "WILD free" messages when your own atexit()-registered + cleanup code runs, and frees the memory. + +Q. I'm getting "unfreed" errors, but the code is bug-free! + +A. You can get erroneous "unfreed" messages if memwatch + terminates before all memory has been freed. Try using + mwInit() and mwTerm() instead of auto-initialization. + + If you _are_ using mwInit() and mwTerm(), it may be that + some code in your program executes before mwInit() or + after mwTerm(). Make sure that mwInit() is the first thing + executed, and mwTerm() the last. + +Q. When compiling memwatch I get these 'might get clobbered' + errors, and something about a longjmp() inside memwatch. + +A. When using gcc or egcs with the optimization to inline + functions, this warning occurs. This is because gcc and + egcs inlines memwatch's functions with setjmp/longjmp, + causing the calling functions to become unstable. + + The gcc/egcs maintainers have been informed of this + problem, but until they modify the inline optimization + so that it leaves setjmp functions alone, make sure to + compile memwatch without inline function optimizations. + + gcc 2.95.2 can be patched for this, and I have been told + it will be fixed in an upcoming version. + +Q. My program crashes with SIGSEGV or alignment errors, but + only when I compile with memwatch enabled! + +A. You are using a 64-bit (or higher) platform, and memwatch + was unable to detect and adjust for this. You'll have to + either compile with a suitable define for mwROUNDALLOC, + I suggest (number of bits / 8), or define mwROUNDALLOC + directly in memwatch.c. + + Also, please check your limits.h file for the relevant + #defines, and tell me what they are. + +Q. When I include string.h after memwatch.h, I get errors + related to strdup(), what gives? + +A. Most, but probably not all, platforms are nice about + including files multiple times, so I could probably + avoid these errors by including string.h from memwatch.h. + But since I want to be on the safe side, I don't. + + To fix this, simply include string.h before memwatch.h, + or modify memwatch.h to include string.h. + diff --git a/memwatch-2.71/README b/memwatch-2.71/README new file mode 100644 index 0000000..aeb86ce --- /dev/null +++ b/memwatch-2.71/README @@ -0,0 +1,99 @@ +README for MEMWATCH 2.69 + + This file should be enough to get you started, and should be + enough for small projects. For more info, see the files USING + and the FAQ. If this is not enough, see memwatch.h, which is + well documented. + + Memwatch is licensed under the GPL from version 2.69 + onwards. Please read the file gpl.txt for more details. + + If you choose to use memwatch to validate your projects, I + would like to hear about it. Please drop me a line at + johan@linkdata.se about the project itself, the hardware, + operating system, compiler and any URL(s) you feel is + appropriate. + +***** To run the test program: + + Look at the source code for test.c first. It does some really + nasty things, and I want you to be aware of that. If memwatch + can't capture SIGSEGV (General Protection Fault for Windoze), + your program will dump core (crash for Windoze). + + Once you've done that, you can build the test program. + + Linux and other *nixes with gcc: + + gcc -o test -DMEMWATCH -DMEMWATCH_STDIO test.c memwatch.c + + Windows 95, Windows NT with MS Visual C++: + + cl -DMEMWATCH -DMEMWATCH_STDIO test.c memwatch.c + + Then simply run the test program. + + ./test + + +***** Quick-start instructions: + + 1. Make sure that memwatch.h is included in all of the + source code files. If you have an include file that + all of the source code uses, you might be able to include + memwatch.h from there. + + 2. Recompile the program with MEMWATCH defined. See your + compiler's documentation if you don't know how to do this. + The usual switch looks like "-DMEMWATCH". To have MEMWATCH + use stderr for some output (like, "Abort, Retry, Ignore?"), + please also define MW_STDIO (or MEMWATCH_STDIO, same thing). + + 3. Run the program and examine the output in the + log file "memwatch.log". If you didn't get a log file, + you probably didn't do step 1 and 2 correctly, or your + program crashed before memwatch flushed the file buffer. + To have memwatch _always_ flush the buffer, add a call + to "mwDoFlush(1)" at the top of your main function. + + 4. There is no fourth step... but remember that there + are limits to what memwatch can do, and that you need + to be aware of them: + +***** Limits to memwatch: + + Memwatch cannot catch all wild pointer writes. It can catch + those it could make itself due to your program trashing + memwatch's internal data structures. It can catch, sort of, + wild writes into No Mans Land buffers (see the header file for + more info). Anything else and you're going to get core dumped, + or data corruption if you're lucky. + + There are other limits of course, but that one is the most + serious one, and the one that you're likely to be suffering + from. + +***** Can use memwatch with XXXXX? + + Probably the answer is yes. It's been tested with several + different platforms and compilers. It may not work on yours + though... but there's only one way to find out. + +***** Need more assistance? + + I don't want e-mail on "how to program in C", or "I've got a + bug, help me". I _do_ want you to send email to me if you + find a bug in memwatch, or if it won't compile cleanly on your + system (assuming it's an ANSI-C compiler of course). + + If you need help with using memwatch, read the header file. + If, after reading the header file, you still can't resolve the + problem, please mail me with the details. + + I can be reached at "johan@linkdata.se". + + The latest version of memwatch should be found at + "http://www.linkdata.se/". + + Johan Lindh + diff --git a/memwatch-2.71/USING b/memwatch-2.71/USING new file mode 100644 index 0000000..3d78e95 --- /dev/null +++ b/memwatch-2.71/USING @@ -0,0 +1,213 @@ +Using memwatch +============== + +What is it? + + Memwatch is primarily a memory leak detector for C. Besides + detecting leaks, it can do a bunch of other stuff, but lets + stay to the basics. If you _really_ want to know all the + gory details, you should check out the header file, + memwatch.h, and the source code. It's actually got some + comments! (Whoa, what a concept!) + +How do I get the latest version? + + http://www.linkdata.se/sourcecode.html + ftp://ftp.linkdata.se/pub/memwatch/ + +How does it work? + + Using the C preprocessor, memwatch replaces all your + programs calls to ANSI C memory allocation functions with + calls to it's own functions, which keeps a record of all + allocations. + + Memwatch is very unobtrusive; unless the define MEMWATCH is + defined, memwatch removes all traces of itself from the + code (using the preprocessor). + + Memwatch normally writes it's data to the file + memwatch.log, but this can be overridden; see the section + on I/O, later. + +Can I use it for my C++ sources? + + You can, but it's not recommended. C++ allows individual + classes to have their own memory management, and the + preprocessor approach used by memwatch can cause havoc + with such class declarations if improperly used. + + If you have no such classes, or have them but still want + to test it, you can give it a try. + + First, re-enable the C++ support code in memwatch. + If you can't find it, you probably shouldn't be using + it. Then, in your source code, after including ALL + header files: + + #define new mwNew + #define delete mwDelete + + This will cause calls to new and delete in that source file + to be directed to memwatch. Also, be sure to read all the + text in memwatch.h regarding C++ support. + +Is this stuff thread-safe? + + I doubt it. As of version 2.66, there is rudimentary support + for threads, if you happen to be using Win32 or if you have + pthreads. Define WIN32 or MW_PTHREADS to signify this fact. + + This will cause a global mutex to be created, and memwatch + will lock it when accessing the global memory chain, but it's + still far from certified threadsafe. + +Initialization and cleanup + + In order to do it's work in a timely fashion, memwatch + needs to do some startup and cleanup work. mwInit() + initializes memwatch and mwTerm() terminates it. Memwatch + can auto-initialize, and will do so if you don't call + mwInit() yourself. If this is the case, memwatch will use + atexit() to register mwTerm() to the atexit-queue. + + The auto-init technique has a caveat; if you are using + atexit() yourself to do cleanup work, memwatch may + terminate before your program is done. To be on the safe + side, use mwInit() and mwTerm(). + + mwInit() and mwTerm() is nestable, so you can call mwInit() + several times, requiring mwTerm() to be called an equal + number of times to terminate memwatch. + + In case of the program aborting in a controlled way, you + may want to call mwAbort() instead of mwTerm(). mwAbort() + will terminate memwatch even if there are outstanding calls + to mwTerm(). + +I/O operations + + During normal operations, memwatch creates a file named + memwatch.log. Sometimes, memwatch.log can't be created; + then memwatch tries to create files name memwatNN.log, + where NN is between 01 and 99. If that fails, no log will + be produced. + + If you can't use a file log, or don't want to, no worry. + Just call mwSetOutFunc() with the address of a "void + func(int c)" function, and all output will be directed + there, character by character. + + Memwatch also has an Abort/Retry/Ignore handler that is + used when an ASSERT or VERIFY fails. The default handler + does no I/O, but automatically aborts the program. You can + use any handler you want; just send the address of a "int + func(const char*)" to mwSetAriFunc(). For more details on + that, see memwatch.h. + +TRACE/ASSERT/VERIFY macros + + Memwatch defines (if not already defined) the macros TRACE, + ASSERT and VERIFY. If you are already using macros with + these names, memwatch 2.61 and later will not override + them. Memwatch 2.61 and later will also always define the + macros mwTRACE, mwASSERT and mwVERIFY, so you can use these + to make sure you're talking to memwatch. Versions prior + to 2.61 will *OVERRIDE* existing TRACE, ASSERT and VERIFY. + + To make sure that existing TRACE, ASSERT and VERIFY macros + are preserved, you can define MW_NOTRACE, MW_NOASSERT and + MW_NOVERIFY. All versions of memwatch will abide by these. + +How slow can you go? + + Memwatch slows things down. Large allocations aren't + affected so that you can measure it, but small allocations + that would utilize a compilers small-allocator function + suddenly cannot, and so gets slowed down alot. As a worst + case, expect it to be 3-5 times slower. + + Free'ing gets hit worse, I'm afraid, as memwatch checks + a lot of stuff when freeing. Expect it to be 5-7 times + slower, no matter what the size of the allocation. + +Stress-testing the application + + You can simulate low-memory conditions using mwLimit(). + mwLimit() takes the maximum number of bytes to be + allocated; when the limit is hit, allocation requests will + fail, and a "limit" message will be logged. + + If you hit a real low-memory situation, memwatch logs that + too. Memwatch itself has some reserve memory tucked away so + it should continue running even in the worst conditions. + +Hunting down wild writes and other Nasty Things + + Wild writes are usually caused by using pointers that arent + initialized, or that were initialized, but then the memory + they points to is moved or freed. The best way to avoid + these kind of problems is to ALWAYS initialize pointers to + NULL, and after freeing a memory buffer, setting all + pointers that pointed to it to NULL. + + To aid in tracking down uninitialized pointers memwatch + zaps all memory with certain values. Recently allocated + memory (unless calloc'd, of course), contains 0xFE. + Recently freed memory contains 0xFD. So if your program + crashes when using memwatch and not without memwatch, it's + most likely because you are not initializing your allocated + buffers, or using the buffers after they've been freed. + + In the event that a wild pointer should damage memwatch's + internal data structures, memwatch employs checksums, + multiple copies of some values, and can also repair it's + own data structures. + + If you are a paranoid person, and as programmer you should + be, you can use memwatch's mwIsReadAddr() and + mwIsSafeAddr() functions to check the accessibility of + memory. These are implemented for both ANSI C systems and + Win32 systems. Just put an mwASSERT() around the check and + forget about it. + +Can I help? + + Well, sure. For instance, I like memwatch to compile + without any warnings or errors. If you are using an ANSI C + compliant compiler, and are getting warnings or errors, + please mail me the details and instructions on how to fix + them, if you can. + + Another thing you can do if you decide to use memwatch is + to mail me the name of the project(s) (and URL, if any), + hardware and operating system, compiler and what user + (organization). I will then post this info on the list of + memwatch users. + (http://www.linkdata.se/memwatchusers.html) + +Top five problems using memwatch + + 5. Passed a non-memwatch allocated pointer to memwatch's + free(). Symtom: Causes an erroneous "WILD free" log + entry to appear. Cure: Either include memwatch.h for + the file that allocates, or use mwFree_() to free it. + + 4. Relied on auto-initialization when using atexit(). + Symptom: Causes incorrect "unfreed" and "WILD free" + messages. Cure: Use mwInit() and mwTerm(). + + 3. Forgot to include memwatch.h in all files. Symptom: + Tends to generate "WILD free" and "unfreed" messages. + Cure: Make sure to include memwatch.h! + + 2. No write permissions in currect directory. Symptom: + Seems like memwatch 'just aint working'. Cure: Use + mwSetOutFunc() to redirect output. + + ...and the number one problem is... + + 1. Didn't define MEMWATCH when compiling. Symptom: + Memwatch dutifully disables itself. Cure: Try adding + -DMEMWATCH to the command line. + diff --git a/memwatch-2.71/gpl.txt b/memwatch-2.71/gpl.txt new file mode 100644 index 0000000..5b6e7c6 --- /dev/null +++ b/memwatch-2.71/gpl.txt @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/memwatch-2.71/memwatch.cpp b/memwatch-2.71/memwatch.cpp new file mode 100644 index 0000000..1832dcf --- /dev/null +++ b/memwatch-2.71/memwatch.cpp @@ -0,0 +1,2712 @@ +/* +** MEMWATCH.C +** Nonintrusive ANSI C memory leak / overwrite detection +** Copyright (C) 1992-2003 Johan Lindh +** All rights reserved. +** Version 2.71 + + This file is part of MEMWATCH. + + MEMWATCH is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + MEMWATCH is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with MEMWATCH; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +** +** 920810 JLI [1.00] +** 920830 JLI [1.10 double-free detection] +** 920912 JLI [1.15 mwPuts, mwGrab/Drop, mwLimit] +** 921022 JLI [1.20 ASSERT and VERIFY] +** 921105 JLI [1.30 C++ support and TRACE] +** 921116 JLI [1.40 mwSetOutFunc] +** 930215 JLI [1.50 modified ASSERT/VERIFY] +** 930327 JLI [1.51 better auto-init & PC-lint support] +** 930506 JLI [1.55 MemWatch class, improved C++ support] +** 930507 JLI [1.60 mwTest & CHECK()] +** 930809 JLI [1.65 Abort/Retry/Ignore] +** 930820 JLI [1.70 data dump when unfreed] +** 931016 JLI [1.72 modified C++ new/delete handling] +** 931108 JLI [1.77 mwSetAssertAction() & some small changes] +** 940110 JLI [1.80 no-mans-land alloc/checking] +** 940328 JLI [2.00 version 2.0 rewrite] +** Improved NML (no-mans-land) support. +** Improved performance (especially for free()ing!). +** Support for 'read-only' buffers (checksums) +** ^^ NOTE: I never did this... maybe I should? +** FBI (free'd block info) tagged before freed blocks +** Exporting of the mwCounter variable +** mwBreakOut() localizes debugger support +** Allocation statistics (global, per-module, per-line) +** Self-repair ability with relinking +** 950913 JLI [2.10 improved garbage handling] +** 951201 JLI [2.11 improved auto-free in emergencies] +** 960125 JLI [X.01 implemented auto-checking using mwAutoCheck()] +** 960514 JLI [2.12 undefining of existing macros] +** 960515 JLI [2.13 possibility to use default new() & delete()] +** 960516 JLI [2.20 suppression of file flushing on unfreed msgs] +** 960516 JLI [2.21 better support for using MEMWATCH with DLL's] +** 960710 JLI [X.02 multiple logs and mwFlushNow()] +** 960801 JLI [2.22 merged X.01 version with current] +** 960805 JLI [2.30 mwIsXXXXAddr() to avoid unneeded GP's] +** 960805 JLI [2.31 merged X.02 version with current] +** 961002 JLI [2.32 support for realloc() + fixed STDERR bug] +** 961222 JLI [2.40 added mwMark() & mwUnmark()] +** 970101 JLI [2.41 added over/underflow checking after failed ASSERT/VERIFY] +** 970113 JLI [2.42 added support for PC-Lint 7.00g] +** 970207 JLI [2.43 added support for strdup()] +** 970209 JLI [2.44 changed default filename to lowercase] +** 970405 JLI [2.45 fixed bug related with atexit() and some C++ compilers] +** 970723 JLI [2.46 added MW_ARI_NULLREAD flag] +** 970813 JLI [2.47 stabilized marker handling] +** 980317 JLI [2.48 ripped out C++ support; wasn't working good anyway] +** 980318 JLI [2.50 improved self-repair facilities & SIGSEGV support] +** 980417 JLI [2.51 more checks for invalid addresses] +** 980512 JLI [2.52 moved MW_ARI_NULLREAD to occur before aborting] +** 990112 JLI [2.53 added check for empty heap to mwIsOwned] +** 990217 JLI [2.55 improved the emergency repairs diagnostics and NML] +** 990224 JLI [2.56 changed ordering of members in structures] +** 990303 JLI [2.57 first maybe-fixit-for-hpux test] +** 990516 JLI [2.58 added 'static' to the definition of mwAutoInit] +** 990517 JLI [2.59 fixed some high-sensitivity warnings] +** 990610 JLI [2.60 fixed some more high-sensitivity warnings] +** 990715 JLI [2.61 changed TRACE/ASSERT/VERIFY macro names] +** 991001 JLI [2.62 added CHECK_BUFFER() and mwTestBuffer()] +** 991007 JLI [2.63 first shot at a 64-bit compatible version] +** 991009 JLI [2.64 undef's strdup() if defined, mwStrdup made const] +** 000704 JLI [2.65 added some more detection for 64-bits] +** 010502 JLI [2.66 incorporated some user fixes] +** [mwRelink() could print out garbage pointer (thanks mac@phobos.ca)] +** [added array destructor for C++ (thanks rdasilva@connecttel.com)] +** [added mutex support (thanks rdasilva@connecttel.com)] +** 010531 JLI [2.67 fix: mwMutexXXX() was declared even if MW_HAVE_MUTEX was not defined] +** 010619 JLI [2.68 fix: mwRealloc() could leave the mutex locked] +** 020918 JLI [2.69 changed to GPL, added C++ array allocation by Howard Cohen] +** 030212 JLI [2.70 mwMalloc() bug for very large allocations (4GB on 32bits)] +** 030520 JLI [2.71 added ULONG_LONG_MAX as a 64-bit detector (thanks Sami Salonen)] +*/ + +#include "stdafx.h" + +#define __MEMWATCH_C 1 + +#ifdef MW_NOCPP +#define MEMWATCH_NOCPP +#endif +#ifdef MW_STDIO +#define MEMWATCH_STDIO +#endif + +/*********************************************************************** +** Include files +***********************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "memwatch.h" + +//Undef stuff we redefined, so it will not be recursive (precompiled headers seem to cause recursion otherwise) + +#undef malloc +#undef strdup +#undef realloc +#undef calloc +#undef free +#undef CHECK +#undef CHECK_THIS +#undef CHECK_BUFFER +#undef MARK +#undef UNMARK + +#ifndef toupper +#include +#endif + +#if defined(WIN32) || defined(__WIN32__) +#define MW_HAVE_MUTEX 1 +#include +#endif + +#if defined(MW_PTHREADS) || defined(HAVE_PTHREAD_H) +#define MW_HAVE_MUTEX 1 +#include +#endif + +/*********************************************************************** +** Defines & other weird stuff +***********************************************************************/ + +/*lint -save -e767 */ +#define VERSION "2.71" /* the current version number */ +#define CHKVAL(mw) (0xFE0180L^(long)mw->count^(long)mw->size^(long)mw->line) +#define FLUSH() mwFlush() +#define TESTS(f,l) if(mwTestAlways) (void)mwTestNow(f,l,1) +#define PRECHK 0x01234567L +#define POSTCHK 0x76543210L +#define mwBUFFER_TO_MW(p) ( (mwData*) (void*) ( ((char*)p)-mwDataSize-mwOverflowZoneSize ) ) +/*lint -restore */ + +#define MW_NML 0x0001 + +#ifdef _MSC_VER +#define COMMIT "c" /* Microsoft C requires the 'c' to perform as desired */ +#else +#define COMMIT "" /* Normal ANSI */ +#endif /* _MSC_VER */ + +#ifdef __cplusplus +#define CPPTEXT "++" +#else +#define CPPTEXT "" +#endif /* __cplusplus */ + +#ifdef MEMWATCH_STDIO +#define mwSTDERR stderr +#else +#define mwSTDERR mwLog +#endif + +#ifdef MW_HAVE_MUTEX +#define MW_MUTEX_INIT() mwMutexInit() +#define MW_MUTEX_TERM() mwMutexTerm() +#define MW_MUTEX_LOCK() mwMutexLock() +#define MW_MUTEX_UNLOCK() mwMutexUnlock() +#else +#define MW_MUTEX_INIT() +#define MW_MUTEX_TERM() +#define MW_MUTEX_LOCK() +#define MW_MUTEX_UNLOCK() +#endif + +/*********************************************************************** +** If you really, really know what you're doing, +** you can predefine these things yourself. +***********************************************************************/ + +#ifndef mwBYTE_DEFINED +# if CHAR_BIT != 8 +# error need CHAR_BIT to be 8! +# else +typedef unsigned char mwBYTE; +# define mwBYTE_DEFINED 1 +# endif +#endif + +#if defined(ULONGLONG_MAX) || defined(ULLONG_MAX) || defined(_UI64_MAX) || defined(ULONG_LONG_MAX) +# define mw64BIT 1 +# define mwROUNDALLOC_DEFAULT 8 +#else +# if UINT_MAX <= 0xFFFFUL +# define mw16BIT 1 +# define mwROUNDALLOC_DEFAULT 2 +# else +# if ULONG_MAX > 0xFFFFFFFFUL +# define mw64BIT 1 +# define mwROUNDALLOC_DEFAULT 8 +# else +# define mw32BIT 1 +# define mwROUNDALLOC_DEFAULT 4 +# endif +# endif +#endif + +/* mwROUNDALLOC is the number of bytes to */ +/* round up to, to ensure that the end of */ +/* the buffer is suitable for storage of */ +/* any kind of object */ +#ifndef mwROUNDALLOC +# define mwROUNDALLOC mwROUNDALLOC_DEFAULT +#endif + +#ifndef mwDWORD_DEFINED +#if ULONG_MAX == 0xFFFFFFFFUL +typedef unsigned long mwDWORD; +#define mwDWORD_DEFINED "unsigned long" +#endif +#endif + +#ifndef mwDWORD_DEFINED +#if UINT_MAX == 0xFFFFFFFFUL +typedef unsigned int mwDWORD; +#define mwDWORD_DEFINED "unsigned int" +#endif +#endif + +#ifndef mwDWORD_DEFINED +#if USHRT_MAX == 0xFFFFFFFFUL +typedef unsigned short mwDWORD; +#define mwDWORD_DEFINED "unsigned short" +#endif +#endif + +#ifndef mwBYTE_DEFINED +#error "can't find out the correct type for a 8 bit scalar" +#endif + +#ifndef mwDWORD_DEFINED +#error "can't find out the correct type for a 32 bit scalar" +#endif + +/*********************************************************************** +** Typedefs & structures +***********************************************************************/ + +/* main data holding area, precedes actual allocation */ +typedef struct mwData_ mwData; +struct mwData_ { + mwData* prev; /* previous allocation in chain */ + mwData* next; /* next allocation in chain */ + const char* file; /* file name where allocated */ + long count; /* action count */ + long check; /* integrity check value */ +#if 0 + long crc; /* data crc value */ +#endif + size_t size; /* size of allocation */ + int line; /* line number where allocated */ + unsigned flag; /* flag word */ + }; + +/* statistics structure */ +typedef struct mwStat_ mwStat; +struct mwStat_ { + mwStat* next; /* next statistic buffer */ + const char* file; + long total; /* total bytes allocated */ + long num; /* total number of allocations */ + long max; /* max allocated at one time */ + long curr; /* current allocations */ + int line; + }; + +/* grabbing structure, 1K in size */ +typedef struct mwGrabData_ mwGrabData; +struct mwGrabData_ { + mwGrabData* next; + int type; + char blob[ 1024 - sizeof(mwGrabData*) - sizeof(int) ]; + }; + +typedef struct mwMarker_ mwMarker; +struct mwMarker_ { + void *host; + char *text; + mwMarker *next; + int level; + }; + +#if defined(WIN32) || defined(__WIN32__) +typedef HANDLE mwMutex; +#endif + +#if defined(MW_PTHREADS) || defined(HAVE_PTHREAD_H) +#error "Whould not get here with WIN32 and no PTHREAD" +typedef pthread_mutex_t mwMutex; +#endif + +/*********************************************************************** +** Static variables +***********************************************************************/ + +static int mwInited = 0; +static int mwInfoWritten = 0; +static int mwUseAtexit = 0; +static FILE* mwLog = NULL; +static int mwFlushing = 0; +static int mwStatLevel = MW_STAT_DEFAULT; +static int mwNML = MW_NML_DEFAULT; +static int mwFBI = 0; +static long mwAllocLimit = 0L; +static int mwUseLimit = 0; + +static long mwNumCurAlloc = 0L; +static mwData* mwHead = NULL; +static mwData* mwTail = NULL; +static int mwDataSize = 0; +static unsigned char mwOverflowZoneTemplate[] = "mEmwAtch"; +static int mwOverflowZoneSize = mwROUNDALLOC; + +static void (*mwOutFunction)(int) = NULL; +static int (*mwAriFunction)(const char*) = NULL; +static int mwAriAction = MW_ARI_ABORT; + +static char mwPrintBuf[MW_TRACE_BUFFER+8]; + +static unsigned long mwCounter = 0L; +static long mwErrors = 0L; + +static int mwTestFlags = 0; +static int mwTestAlways = 0; + +static FILE* mwLogB1 = NULL; +static int mwFlushingB1 = 0; + +static mwStat* mwStatList = NULL; +static long mwStatTotAlloc = 0L; +static long mwStatMaxAlloc = 0L; +static long mwStatNumAlloc = 0L; +static long mwStatCurAlloc = 0L; +static long mwNmlNumAlloc = 0L; +static long mwNmlCurAlloc = 0L; + +static mwGrabData* mwGrabList = NULL; +static long mwGrabSize = 0L; + +static void * mwLastFree[MW_FREE_LIST]; +static const char *mwLFfile[MW_FREE_LIST]; +static int mwLFline[MW_FREE_LIST]; +static int mwLFcur = 0; + +static mwMarker* mwFirstMark = NULL; + +static FILE* mwLogB2 = NULL; +static int mwFlushingB2 = 0; + +#ifdef MW_HAVE_MUTEX +mwMutex mwGlobalMutex; +#endif + +/*********************************************************************** +** Static function declarations +***********************************************************************/ + +static void mwAutoInit( void ); +static FILE* mwLogR( void ); +static void mwLogW( FILE* ); +static int mwFlushR( void ); +static void mwFlushW( int ); +static void mwFlush( void ); +static void mwIncErr( void ); +static void mwUnlink( mwData*, const char* file, int line ); +static int mwRelink( mwData*, const char* file, int line ); +static int mwIsHeapOK( mwData *mw ); +static int mwIsOwned( mwData* mw, const char* file, int line ); +static int mwTestBuf( mwData* mw, const char* file, int line ); +static void mwDefaultOutFunc( int ); +static void mwWrite( const char* format, ... ); +static void mwLogFile( const char* name ); +static size_t mwFreeUp( size_t, int ); +static const void *mwTestMem( const void *, unsigned, int ); +static int mwStrCmpI( const char *s1, const char *s2 ); +static int mwTestNow( const char *file, int line, int always_invoked ); +static void mwDropAll( void ); +static const char *mwGrabType( int type ); +static unsigned mwGrab_( unsigned kb, int type, int silent ); +static unsigned mwDrop_( unsigned kb, int type, int silent ); +static int mwARI( const char* text ); +static void mwStatReport( void ); +static mwStat* mwStatGet( const char*, int, int ); +static void mwStatAlloc( size_t, const char*, int ); +static void mwStatFree( size_t, const char*, int ); +static int mwCheckOF( const void * p ); +static void mwWriteOF( void * p ); +static char mwDummy( char c ); +#ifdef MW_HAVE_MUTEX +static void mwMutexInit( void ); +static void mwMutexTerm( void ); +static void mwMutexLock( void ); +static void mwMutexUnlock( void ); +#endif + +/*********************************************************************** +** System functions +***********************************************************************/ +void mwInit( void ) { + time_t tid; + + if( mwInited++ > 0 ) return; + + MW_MUTEX_INIT(); + +#ifdef ENABLEMWFILEWRITES + /* start a log if none is running */ + if( mwLogR() == NULL ) mwLogFile( "memwatch.log" ); + if( mwLogR() == NULL ) { + int i; + char buf[32]; + /* oops, could not open it! */ + /* probably because it's already open */ + /* so we try some other names */ + for( i=1; i<100; i++ ) { + sprintf( buf, "memwat%02d.log", i ); + mwLogFile( buf ); + if( mwLogR() != NULL ) break; + } + } +#endif + + /* initialize the statistics */ + mwStatList = NULL; + mwStatTotAlloc = 0L; + mwStatCurAlloc = 0L; + mwStatMaxAlloc = 0L; + mwStatNumAlloc = 0L; + mwNmlCurAlloc = 0L; + mwNmlNumAlloc = 0L; + + /* calculate the buffer size to use for a mwData */ + mwDataSize = sizeof(mwData); + while( mwDataSize % mwROUNDALLOC ) mwDataSize ++; + + /* write informational header if needed */ + if( !mwInfoWritten ) { + mwInfoWritten = 1; + (void) time( &tid ); + mwWrite( + "\n=============" + " MEMWATCH " VERSION " Copyright (C) 1992-1999 Johan Lindh " + "=============\n"); + mwWrite( "\nStarted at %s\n", ctime( &tid ) ); + +/**************************************************************** Generic */ + mwWrite( "Modes: " ); +#ifdef mwNew + mwWrite( "C++ " ); +#endif /* mwNew */ +#ifdef __STDC__ + mwWrite( "__STDC__ " ); +#endif /* __STDC__ */ +#ifdef mw16BIT + mwWrite( "16-bit " ); +#endif +#ifdef mw32BIT + mwWrite( "32-bit " ); +#endif +#ifdef mw64BIT + mwWrite( "64-bit " ); +#endif + mwWrite( "mwDWORD==(" mwDWORD_DEFINED ")\n" ); + mwWrite( "mwROUNDALLOC==%d sizeof(mwData)==%d mwDataSize==%d\n", + mwROUNDALLOC, sizeof(mwData), mwDataSize ); +/**************************************************************** Generic */ + +/************************************************************ Microsoft C */ +#ifdef _MSC_VER + mwWrite( "Compiled using Microsoft C" CPPTEXT + " %d.%02d\n", _MSC_VER / 100, _MSC_VER % 100 ); +#endif /* _MSC_VER */ +/************************************************************ Microsoft C */ + +/************************************************************** Borland C */ +#ifdef __BORLANDC__ + mwWrite( "Compiled using Borland C" +#ifdef __cplusplus + "++ %d.%01d\n", __BCPLUSPLUS__/0x100, (__BCPLUSPLUS__%0x100)/0x10 ); +#else + " %d.%01d\n", __BORLANDC__/0x100, (__BORLANDC__%0x100)/0x10 ); +#endif /* __cplusplus */ +#endif /* __BORLANDC__ */ +/************************************************************** Borland C */ + +/************************************************************** Watcom C */ +#ifdef __WATCOMC__ + mwWrite( "Compiled using Watcom C %d.%02d ", + __WATCOMC__/100, __WATCOMC__%100 ); +#ifdef __FLAT__ + mwWrite( "(32-bit flat model)" ); +#endif /* __FLAT__ */ + mwWrite( "\n" ); +#endif /* __WATCOMC__ */ +/************************************************************** Watcom C */ + + mwWrite( "\n" ); + FLUSH(); + } + + if( mwUseAtexit ) (void) atexit( mwAbort ); + return; + } + +void mwAbort( void ) { + mwData *mw; + mwMarker *mrk; + char *data; + time_t tid; + int c, i, j; + int errors; + + tid = time( NULL ); + mwWrite( "\nStopped at %s\n", ctime( &tid) ); + + if( !mwInited ) + mwWrite( "internal: mwAbort(): MEMWATCH not initialized!\n" ); + + /* release the grab list */ + mwDropAll(); + + /* report mwMarked items */ + while( mwFirstMark ) { + mrk = mwFirstMark->next; + mwWrite( "mark: %p: %s\n", mwFirstMark->host, mwFirstMark->text ); + free( mwFirstMark->text ); + free( mwFirstMark ); + mwFirstMark = mrk; + mwErrors ++; + } + + /* release all still allocated memory */ + errors = 0; + while( mwHead != NULL && errors < 3 ) { + if( !mwIsOwned(mwHead, __FILE__, __LINE__ ) ) { + if( errors < 3 ) + { + errors ++; + mwWrite( "internal: NML/unfreed scan restarting\n" ); + FLUSH(); + mwHead = mwHead; + continue; + } + mwWrite( "internal: NML/unfreed scan aborted, heap too damaged\n" ); + FLUSH(); + break; + } + mwFlushW(0); + if( !(mwHead->flag & MW_NML) ) { + mwErrors++; + data = ((char*)mwHead)+mwDataSize; + mwWrite( "unfreed: <%ld> %s(%d), %ld bytes at %p ", + mwHead->count, mwHead->file, mwHead->line, (long)mwHead->size, data+mwOverflowZoneSize ); + if( mwCheckOF( data ) ) { + mwWrite( "[underflowed] "); + FLUSH(); + } + if( mwCheckOF( (data+mwOverflowZoneSize+mwHead->size) ) ) { + mwWrite( "[overflowed] "); + FLUSH(); + } + mwWrite( " \t{" ); + j = 16; if( mwHead->size < 16 ) j = (int) mwHead->size; + for( i=0;i<16;i++ ) { + if( i 126 ) c = '.'; + mwWrite( "%c", c ); + } + mwWrite( "}\n" ); + mw = mwHead; + mwUnlink( mw, __FILE__, __LINE__ ); + free( mw ); + } + else { + data = ((char*)mwHead) + mwDataSize + mwOverflowZoneSize; + if( mwTestMem( data, mwHead->size, MW_VAL_NML ) ) { + mwErrors++; + mwWrite( "wild pointer: <%ld> NoMansLand %p alloc'd at %s(%d)\n", + mwHead->count, data + mwOverflowZoneSize, mwHead->file, mwHead->line ); + FLUSH(); + } + mwNmlNumAlloc --; + mwNmlCurAlloc -= mwHead->size; + mw = mwHead; + mwUnlink( mw, __FILE__, __LINE__ ); + free( mw ); + } + } + + if( mwNmlNumAlloc ) mwWrite("internal: NoMansLand block counter %ld, not zero\n", mwNmlNumAlloc ); + if( mwNmlCurAlloc ) mwWrite("internal: NoMansLand byte counter %ld, not zero\n", mwNmlCurAlloc ); + + /* report statistics */ + mwStatReport(); + FLUSH(); + + mwInited = 0; + mwHead = mwTail = NULL; + if( mwErrors ) + fprintf(mwSTDERR,"MEMWATCH detected %ld anomalies\n",mwErrors); + mwLogFile( NULL ); + mwErrors = 0; + + MW_MUTEX_TERM(); + + } + +void mwTerm( void ) { + if( mwInited == 1 ) + { + mwAbort(); + return; + } + if( !mwInited ) + mwWrite("internal: mwTerm(): MEMWATCH has not been started!\n"); + else + mwInited --; + } + +void mwStatistics( int level ) +{ + mwAutoInit(); + if( level<0 ) level=0; + if( mwStatLevel != level ) + { + mwWrite( "statistics: now collecting on a %s basis\n", + level<1?"global":(level<2?"module":"line") ); + mwStatLevel = level; + } +} + +void mwAutoCheck( int onoff ) { + mwAutoInit(); + mwTestAlways = onoff; + if( onoff ) mwTestFlags = MW_TEST_ALL; + } + +void mwSetOutFunc( void (*func)(int) ) { + mwAutoInit(); + mwOutFunction = func; + } + +static void mwWriteOF( void *p ) +{ + int i; + unsigned char *ptr; + ptr = (unsigned char*) p; + for( i=0; inext is not always set? +*/ +void * mwMark( void *p, const char *desc, const char *file, unsigned line ) { + mwMarker *mrk; + unsigned n, isnew; + char *buf; + int tot, oflow = 0; + char wherebuf[128]; + + mwAutoInit(); + TESTS(NULL,0); + + if( desc == NULL ) desc = "unknown"; + if( file == NULL ) file = "unknown"; + + tot = sprintf( wherebuf, "%.48s called from %s(%d)", desc, file, line ); + if( tot >= (int)sizeof(wherebuf) ) { wherebuf[sizeof(wherebuf)-1] = 0; oflow = 1; } + + if( p == NULL ) { + mwWrite("mark: %s(%d), no mark for NULL:'%s' may be set\n", file, line, desc ); + return p; + } + + if( mwFirstMark != NULL && !mwIsReadAddr( mwFirstMark, sizeof( mwMarker ) ) ) + { + mwWrite("mark: %s(%d), mwFirstMark (%p) is trashed, can't mark for %s\n", + file, line, mwFirstMark, desc ); + return p; + } + + for( mrk=mwFirstMark; mrk; mrk=mrk->next ) + { + if( mrk->next != NULL && !mwIsReadAddr( mrk->next, sizeof( mwMarker ) ) ) + { + mwWrite("mark: %s(%d), mark(%p)->next(%p) is trashed, can't mark for %s\n", + file, line, mrk, mrk->next, desc ); + return p; + } + if( mrk->host == p ) break; + } + + if( mrk == NULL ) { + isnew = 1; + mrk = (mwMarker*) malloc( sizeof( mwMarker ) ); + if( mrk == NULL ) { + mwWrite("mark: %s(%d), no mark for %p:'%s', out of memory\n", file, line, p, desc ); + return p; + } + mrk->next = NULL; + n = 0; + } + else { + isnew = 0; + n = strlen( mrk->text ); + } + + n += strlen( wherebuf ); + buf = (char*) malloc( n+3 ); + if( buf == NULL ) { + if( isnew ) free( mrk ); + mwWrite("mark: %s(%d), no mark for %p:'%s', out of memory\n", file, line, p, desc ); + return p; + } + + if( isnew ) { + memcpy( buf, wherebuf, n+1 ); + mrk->next = mwFirstMark; + mrk->host = p; + mrk->text = buf; + mrk->level = 1; + mwFirstMark = mrk; + } + else { + strcpy( buf, mrk->text ); + strcat( buf, ", " ); + strcat( buf, wherebuf ); + free( mrk->text ); + mrk->text = buf; + mrk->level ++; + } + + if( oflow ) { + mwIncErr(); + mwTrace( " [WARNING: OUTPUT BUFFER OVERFLOW - SYSTEM UNSTABLE]\n" ); + } + return p; + } + +void* mwUnmark( void *p, const char *file, unsigned line ) { + mwMarker *mrk, *prv; + mrk = mwFirstMark; + prv = NULL; + while( mrk ) { + if( mrk->host == p ) { + if( mrk->level < 2 ) { + if( prv ) prv->next = mrk->next; + else mwFirstMark = mrk->next; + free( mrk->text ); + free( mrk ); + return p; + } + mrk->level --; + return p; + } + prv = mrk; + mrk = mrk->next; + } + mwWrite("mark: %s(%d), no mark found for %p\n", file, line, p ); + return p; + } + + +/*********************************************************************** +** Abort/Retry/Ignore handlers +***********************************************************************/ + +static int mwARI( const char *estr ) { + char inbuf[81]; + int c; + fprintf(mwSTDERR, "\n%s\nMEMWATCH: Abort, Retry or Ignore? ", estr); + (void) fgets(inbuf,sizeof(inbuf),stdin); + for( c=0; inbuf[c] && inbuf[c] <= ' '; c++ ) ; + c = inbuf[c]; + if( c == 'R' || c == 'r' ) { + mwBreakOut( estr ); + return MW_ARI_RETRY; + } + if( c == 'I' || c == 'i' ) return MW_ARI_IGNORE; + return MW_ARI_ABORT; + } + +/* standard ARI handler (exported) */ +int mwAriHandler( const char *estr ) { + mwAutoInit(); + return mwARI( estr ); + } + +/* used to set the ARI function */ +void mwSetAriFunc( int (*func)(const char *) ) { + mwAutoInit(); + mwAriFunction = func; + } + +/*********************************************************************** +** Allocation handlers +***********************************************************************/ + +void* mwMalloc( size_t size, const char* file, int line) { + size_t needed; + mwData *mw; + char *ptr; + void *p; + + mwAutoInit(); + + MW_MUTEX_LOCK(); + + TESTS(file,line); + + mwCounter ++; + needed = mwDataSize + mwOverflowZoneSize*2 + size; + if( needed < size ) + { + /* theoretical case: req size + mw overhead exceeded size_t limits */ + return NULL; + } + + /* if this allocation would violate the limit, fail it */ + if( mwUseLimit && ((long)size + mwStatCurAlloc > mwAllocLimit) ) { + mwWrite( "limit fail: <%ld> %s(%d), %ld wanted %ld available\n", + mwCounter, file, line, (long)size, mwAllocLimit - mwStatCurAlloc ); + mwIncErr(); + FLUSH(); + MW_MUTEX_UNLOCK(); + return NULL; + } + + mw = (mwData*) malloc( needed ); + if( mw == NULL ) { + if( mwFreeUp(needed,0) >= needed ) { + mw = (mwData*) malloc(needed); + if( mw == NULL ) { + mwWrite( "internal: mwFreeUp(%u) reported success, but malloc() fails\n", needed ); + mwIncErr(); + FLUSH(); + } + } + if( mw == NULL ) { + mwWrite( "fail: <%ld> %s(%d), %ld wanted %ld allocated\n", + mwCounter, file, line, (long)size, mwStatCurAlloc ); + mwIncErr(); + FLUSH(); + MW_MUTEX_UNLOCK(); + return NULL; + } + } + + mw->count = mwCounter; + mw->prev = NULL; + mw->next = mwHead; + mw->file = file; + mw->size = size; + mw->line = line; + mw->flag = 0; + mw->check = CHKVAL(mw); + + if( mwHead ) mwHead->prev = mw; + mwHead = mw; + if( mwTail == NULL ) mwTail = mw; + + ptr = ((char*)mw) + mwDataSize; + mwWriteOF( ptr ); /* '*(long*)ptr = PRECHK;' */ + ptr += mwOverflowZoneSize; + p = ptr; + memset( ptr, MW_VAL_NEW, size ); + ptr += size; + mwWriteOF( ptr ); /* '*(long*)ptr = POSTCHK;' */ + + mwNumCurAlloc ++; + mwStatCurAlloc += (long) size; + mwStatTotAlloc += (long) size; + if( mwStatCurAlloc > mwStatMaxAlloc ) + mwStatMaxAlloc = mwStatCurAlloc; + mwStatNumAlloc ++; + + if( mwStatLevel ) mwStatAlloc( size, file, line ); + + MW_MUTEX_UNLOCK(); + return p; + } + +void* mwRealloc( void *p, size_t size, const char* file, int line) { + int oldUseLimit, i; + mwData *mw; + char *ptr; + + mwAutoInit(); + + if( p == NULL ) return mwMalloc( size, file, line ); + if( size == 0 ) { mwFree( p, file, line ); return NULL; } + + MW_MUTEX_LOCK(); + + /* do the quick ownership test */ + mw = (mwData*) mwBUFFER_TO_MW( p ); + if( mwIsOwned( mw, file, line ) ) { + + /* if the buffer is an NML, treat this as a double-free */ + if( mw->flag & MW_NML ) + { + mwIncErr(); + if( *((unsigned char*)(mw)+mwDataSize+mwOverflowZoneSize) != MW_VAL_NML ) + { + mwWrite( "internal: <%ld> %s(%d), no-mans-land MW-%p is corrupted\n", + mwCounter, file, line, mw ); + } + goto check_dbl_free; + } + + /* if this allocation would violate the limit, fail it */ + if( mwUseLimit && ((long)size + mwStatCurAlloc - (long)mw->size > mwAllocLimit) ) { + TESTS(file,line); + mwCounter ++; + mwWrite( "limit fail: <%ld> %s(%d), %ld wanted %ld available\n", + mwCounter, file, line, (unsigned long)size - mw->size, mwAllocLimit - mwStatCurAlloc ); + mwIncErr(); + FLUSH(); + MW_MUTEX_UNLOCK(); + return NULL; + } + + /* fake realloc operation */ + oldUseLimit = mwUseLimit; + mwUseLimit = 0; + ptr = (char*) mwMalloc( size, file, line ); + if( ptr != NULL ) { + if( size < mw->size ) + memcpy( ptr, p, size ); + else + memcpy( ptr, p, mw->size ); + mwFree( p, file, line ); + } + mwUseLimit = oldUseLimit; + MW_MUTEX_UNLOCK(); + return (void*) ptr; + } + + /* Unknown pointer! */ + + /* using free'd pointer? */ +check_dbl_free: + for(i=0;i %s(%d), %p was" + " freed from %s(%d)\n", + mwCounter, file, line, p, + mwLFfile[i], mwLFline[i] ); + FLUSH(); + MW_MUTEX_UNLOCK(); + return NULL; + } + } + + /* some weird pointer */ + mwIncErr(); + mwWrite( "realloc: <%ld> %s(%d), unknown pointer %p\n", + mwCounter, file, line, p ); + FLUSH(); + MW_MUTEX_UNLOCK(); + return NULL; + } + +char *mwStrdup( const char* str, const char* file, int line ) { + size_t len; + char *newstring; + + MW_MUTEX_LOCK(); + + if( str == NULL ) { + mwIncErr(); + mwWrite( "strdup: <%ld> %s(%d), strdup(NULL) called\n", + mwCounter, file, line ); + FLUSH(); + MW_MUTEX_UNLOCK(); + return NULL; + } + + len = strlen( str ) + 1; + newstring = (char*) mwMalloc( len, file, line ); + if( newstring != NULL ) memcpy( newstring, str, len ); + MW_MUTEX_UNLOCK(); + return newstring; + } + +void mwFree( void* p, const char* file, int line ) { + int i; + mwData* mw; + char buffer[ sizeof(mwData) + (mwROUNDALLOC*3) + 64 ]; + + /* this code is in support of C++ delete */ + if( file == NULL ) { + mwFree_( p ); + MW_MUTEX_UNLOCK(); + return; + } + + mwAutoInit(); + + MW_MUTEX_LOCK(); + TESTS(file,line); + mwCounter ++; + + /* on NULL free, write a warning and return */ + if( p == NULL ) { + mwWrite( "NULL free: <%ld> %s(%d), NULL pointer free'd\n", + mwCounter, file, line ); + FLUSH(); + MW_MUTEX_UNLOCK(); + return; + } + + /* do the quick ownership test */ + mw = (mwData*) mwBUFFER_TO_MW( p ); + + if( mwIsOwned( mw, file, line ) ) { + (void) mwTestBuf( mw, file, line ); + + /* if the buffer is an NML, treat this as a double-free */ + if( mw->flag & MW_NML ) + { + if( *(((unsigned char*)mw)+mwDataSize+mwOverflowZoneSize) != MW_VAL_NML ) + { + mwWrite( "internal: <%ld> %s(%d), no-mans-land MW-%p is corrupted\n", + mwCounter, file, line, mw ); + } + goto check_dbl_free; + } + + /* update the statistics */ + mwNumCurAlloc --; + mwStatCurAlloc -= (long) mw->size; + if( mwStatLevel ) mwStatFree( mw->size, mw->file, mw->line ); + + /* we should either free the allocation or keep it as NML */ + if( mwNML ) { + mw->flag |= MW_NML; + mwNmlNumAlloc ++; + mwNmlCurAlloc += (long) mw->size; + memset( ((char*)mw)+mwDataSize+mwOverflowZoneSize, MW_VAL_NML, mw->size ); + } + else { + /* unlink the allocation, and enter the post-free data */ + mwUnlink( mw, file, line ); + memset( mw, MW_VAL_DEL, + mw->size + mwDataSize+mwOverflowZoneSize+mwOverflowZoneSize ); + if( mwFBI ) { + memset( mw, '.', mwDataSize + mwOverflowZoneSize ); + sprintf( buffer, "FBI<%ld>%s(%d)", mwCounter, file, line ); + strncpy( (char*)(void*)mw, buffer, mwDataSize + mwOverflowZoneSize ); + } + free( mw ); + } + + /* add the pointer to the last-free track */ + mwLFfile[ mwLFcur ] = file; + mwLFline[ mwLFcur ] = line; + mwLastFree[ mwLFcur++ ] = p; + if( mwLFcur == MW_FREE_LIST ) mwLFcur = 0; + + MW_MUTEX_UNLOCK(); + return; + } + + /* check for double-freeing */ +check_dbl_free: + for(i=0;i %s(%d), %p was" + " freed from %s(%d)\n", + mwCounter, file, line, p, + mwLFfile[i], mwLFline[i] ); + FLUSH(); + MW_MUTEX_UNLOCK(); + return; + } + } + + /* some weird pointer... block the free */ + mwIncErr(); + mwWrite( "WILD free: <%ld> %s(%d), unknown pointer %p\n", + mwCounter, file, line, p ); + FLUSH(); + MW_MUTEX_UNLOCK(); + return; + } + +void* mwCalloc( size_t a, size_t b, const char *file, int line ) { + void *p; + size_t size = a * b; + p = mwMalloc( size, file, line ); + if( p == NULL ) return NULL; + memset( p, 0, size ); + return p; + } + +void mwFree_( void *p ) { + MW_MUTEX_LOCK(); + TESTS(NULL,0); + MW_MUTEX_UNLOCK(); + free(p); + } + +void* mwMalloc_( size_t size ) { + MW_MUTEX_LOCK(); + TESTS(NULL,0); + MW_MUTEX_UNLOCK(); + return malloc( size ); + } + +void* mwRealloc_( void *p, size_t size ) { + MW_MUTEX_LOCK(); + TESTS(NULL,0); + MW_MUTEX_UNLOCK(); + return realloc( p, size ); + } + +void* mwCalloc_( size_t a, size_t b ) { + MW_MUTEX_LOCK(); + TESTS(NULL,0); + MW_MUTEX_UNLOCK(); + return calloc( a, b ); + } + +void mwFlushNow( void ) { + if( mwLogR() ) fflush( mwLogR() ); + return; + } + +void mwDoFlush( int onoff ) { + mwFlushW( onoff<1?0:onoff ); + if( onoff ) if( mwLogR() ) fflush( mwLogR() ); + return; + } + +void mwLimit( long lim ) { + TESTS(NULL,0); + mwWrite("limit: old limit = "); + if( !mwAllocLimit ) mwWrite( "none" ); + else mwWrite( "%ld bytes", mwAllocLimit ); + mwWrite( ", new limit = "); + if( !lim ) { + mwWrite( "none\n" ); + mwUseLimit = 0; + } + else { + mwWrite( "%ld bytes\n", lim ); + mwUseLimit = 1; + } + mwAllocLimit = lim; + FLUSH(); + } + +void mwSetAriAction( int action ) { + MW_MUTEX_LOCK(); + TESTS(NULL,0); + mwAriAction = action; + MW_MUTEX_UNLOCK(); + return; + } + +int mwAssert( int exp, const char *exps, const char *fn, int ln ) { + int i; + char buffer[MW_TRACE_BUFFER+8]; + if( exp ) { + return 0; + } + mwAutoInit(); + MW_MUTEX_LOCK(); + TESTS(fn,ln); + mwIncErr(); + mwCounter++; + mwWrite( "assert trap: <%ld> %s(%d), %s\n", mwCounter, fn, ln, exps ); + if( mwAriFunction != NULL ) { + sprintf( buffer, "MEMWATCH: assert trap: %s(%d), %s", fn, ln, exps ); + i = (*mwAriFunction)(buffer); + switch( i ) { + case MW_ARI_IGNORE: + mwWrite( "assert trap: <%ld> IGNORED - execution continues\n", mwCounter ); + MW_MUTEX_UNLOCK(); + return 0; + case MW_ARI_RETRY: + mwWrite( "assert trap: <%ld> RETRY - executing again\n", mwCounter ); + MW_MUTEX_UNLOCK(); + return 1; + } + } + else { + if( mwAriAction & MW_ARI_IGNORE ) { + mwWrite( "assert trap: <%ld> AUTO IGNORED - execution continues\n", mwCounter ); + MW_MUTEX_UNLOCK(); + return 0; + } + fprintf(mwSTDERR,"\nMEMWATCH: assert trap: %s(%d), %s\n", fn, ln, exps ); + } + + FLUSH(); + (void) mwTestNow( fn, ln, 1 ); + FLUSH(); + + if( mwAriAction & MW_ARI_NULLREAD ) { + /* This is made in an attempt to kick in */ + /* any debuggers or OS stack traces */ + FLUSH(); + /*lint -save -e413 */ + i = *((int*)NULL); + mwDummy( (char)i ); + /*lint -restore */ + } + + MW_MUTEX_UNLOCK(); + exit(255); + /* NOT REACHED - the return statement is in to keep */ + /* stupid compilers from squeaking about differing return modes. */ + /* Smart compilers instead say 'code unreachable...' */ + /*lint -save -e527 */ + return 0; + /*lint -restore */ + } + +int mwVerify( int exp, const char *exps, const char *fn, int ln ) { + int i; + char buffer[MW_TRACE_BUFFER+8]; + if( exp ) { + return 0; + } + mwAutoInit(); + MW_MUTEX_LOCK(); + TESTS(fn,ln); + mwIncErr(); + mwCounter++; + mwWrite( "verify trap: <%ld> %s(%d), %s\n", mwCounter, fn, ln, exps ); + if( mwAriFunction != NULL ) { + sprintf( buffer, "MEMWATCH: verify trap: %s(%d), %s", fn, ln, exps ); + i = (*mwAriFunction)(buffer); + if( i == 0 ) { + mwWrite( "verify trap: <%ld> IGNORED - execution continues\n", mwCounter ); + MW_MUTEX_UNLOCK(); + return 0; + } + if( i == 1 ) { + mwWrite( "verify trap: <%ld> RETRY - executing again\n", mwCounter ); + MW_MUTEX_UNLOCK(); + return 1; + } + } + else { + if( mwAriAction & MW_ARI_NULLREAD ) { + /* This is made in an attempt to kick in */ + /* any debuggers or OS stack traces */ + FLUSH(); + /*lint -save -e413 */ + i = *((int*)NULL); + mwDummy( (char)i ); + /*lint -restore */ + } + if( mwAriAction & MW_ARI_IGNORE ) { + mwWrite( "verify trap: <%ld> AUTO IGNORED - execution continues\n", mwCounter ); + MW_MUTEX_UNLOCK(); + return 0; + } + fprintf(mwSTDERR,"\nMEMWATCH: verify trap: %s(%d), %s\n", fn, ln, exps ); + } + FLUSH(); + (void) mwTestNow( fn, ln, 1 ); + FLUSH(); + MW_MUTEX_UNLOCK(); + exit(255); + /* NOT REACHED - the return statement is in to keep */ + /* stupid compilers from squeaking about differing return modes. */ + /* Smart compilers instead say 'code unreachable...' */ + /*lint -save -e527 */ + return 0; + /*lint -restore */ + } + +void mwTrace( const char *format, ... ) { + int tot, oflow = 0; + va_list mark; + + mwAutoInit(); + MW_MUTEX_LOCK(); + TESTS(NULL,0); + if( mwOutFunction == NULL ) mwOutFunction = mwDefaultOutFunc; + + va_start( mark, format ); + tot = vsprintf( mwPrintBuf, format, mark ); + va_end( mark ); + if( tot >= MW_TRACE_BUFFER ) { mwPrintBuf[MW_TRACE_BUFFER] = 0; oflow = 1; } +#ifdef ENABLEMWFILEWRITES + for(tot=0;mwPrintBuf[tot];tot++) + (*mwOutFunction)( mwPrintBuf[tot] ); + if( oflow ) { + mwIncErr(); + mwTrace( " [WARNING: OUTPUT BUFFER OVERFLOW - SYSTEM UNSTABLE]\n" ); + } + + FLUSH(); +#else + _cprintf("%s", mwPrintBuf); +#endif + + MW_MUTEX_UNLOCK(); + } + + +/*********************************************************************** +** Grab & Drop +***********************************************************************/ + +unsigned mwGrab( unsigned kb ) { + TESTS(NULL,0); + return mwGrab_( kb, MW_VAL_GRB, 0 ); + } + +unsigned mwDrop( unsigned kb ) { + TESTS(NULL,0); + return mwDrop_( kb, MW_VAL_GRB, 0 ); + } + +static void mwDropAll() { + TESTS(NULL,0); + (void) mwDrop_( 0, MW_VAL_GRB, 0 ); + (void) mwDrop_( 0, MW_VAL_NML, 0 ); + if( mwGrabList != NULL ) + mwWrite( "internal: the grab list is not empty after mwDropAll()\n"); + } + +static const char *mwGrabType( int type ) { + switch( type ) { + case MW_VAL_GRB: + return "grabbed"; + case MW_VAL_NML: + return "no-mans-land"; + default: + /* do nothing */ + ; + } + return ""; + } + +static unsigned mwGrab_( unsigned kb, int type, int silent ) { + unsigned i = kb; + mwGrabData *gd; + if( !kb ) i = kb = 65000U; + + for(;kb;kb--) { + if( mwUseLimit && + (mwStatCurAlloc + mwGrabSize + (long)sizeof(mwGrabData) > mwAllocLimit) ) { + if( !silent ) { + mwWrite("grabbed: all allowed memory to %s (%u kb)\n", + mwGrabType(type), i-kb); + FLUSH(); + } + return i-kb; + } + gd = (mwGrabData*) malloc( sizeof(mwGrabData) ); + if( gd == NULL ) { + if( !silent ) { + mwWrite("grabbed: all available memory to %s (%u kb)\n", + mwGrabType(type), i-kb); + FLUSH(); + } + return i-kb; + } + mwGrabSize += (long) sizeof(mwGrabData); + gd->next = mwGrabList; + memset( gd->blob, type, sizeof(gd->blob) ); + gd->type = type; + mwGrabList = gd; + } + if( !silent ) { + mwWrite("grabbed: %u kilobytes of %s memory\n", i, mwGrabType(type) ); + FLUSH(); + } + return i; + } + +static unsigned mwDrop_( unsigned kb, int type, int silent ) { + unsigned i = kb; + mwGrabData *gd,*tmp,*pr; + const void *p; + + if( mwGrabList == NULL && kb == 0 ) return 0; + if( !kb ) i = kb = 60000U; + + pr = NULL; + gd = mwGrabList; + for(;kb;) { + if( gd == NULL ) { + if( i-kb > 0 && !silent ) { + mwWrite("dropped: all %s memory (%u kb)\n", mwGrabType(type), i-kb); + FLUSH(); + } + return i-kb; + } + if( gd->type == type ) { + if( pr ) pr->next = gd->next; + kb --; + tmp = gd; + if( mwGrabList == gd ) mwGrabList = gd->next; + gd = gd->next; + p = mwTestMem( tmp->blob, sizeof( tmp->blob ), type ); + if( p != NULL ) { + mwWrite( "wild pointer: <%ld> %s memory hit at %p\n", + mwCounter, mwGrabType(type), p ); + FLUSH(); + } + mwGrabSize -= (long) sizeof(mwGrabData); + free( tmp ); + } + else { + pr = gd; + gd = gd->next; + } + } + if( !silent ) { + mwWrite("dropped: %u kilobytes of %s memory\n", i, mwGrabType(type) ); + FLUSH(); + } + return i; + } + +/*********************************************************************** +** No-Mans-Land +***********************************************************************/ + +void mwNoMansLand( int level ) { + mwAutoInit(); + TESTS(NULL,0); + switch( level ) { + case MW_NML_NONE: + (void) mwDrop_( 0, MW_VAL_NML, 0 ); + break; + case MW_NML_FREE: + break; + case MW_NML_ALL: + (void) mwGrab_( 0, MW_VAL_NML, 0 ); + break; + default: + return; + } + mwNML = level; + } + +/*********************************************************************** +** Static functions +***********************************************************************/ + +static void mwAutoInit( void ) +{ + if( mwInited ) return; + mwUseAtexit = 1; + mwInit(); + return; +} + +static FILE *mwLogR() { +#ifdef ENABLEMWFILEWRITES + if( (mwLog == mwLogB1) && (mwLog == mwLogB2) ) return mwLog; + if( mwLog == mwLogB1 ) mwLogB2 = mwLog; + if( mwLog == mwLogB2 ) mwLogB1 = mwLog; + if( mwLogB1 == mwLogB2 ) mwLog = mwLogB1; + if( (mwLog == mwLogB1) && (mwLog == mwLogB2) ) { + mwWrite("internal: log file handle damaged and recovered\n"); + FLUSH(); + return mwLog; + } + fprintf(mwSTDERR,"\nMEMWATCH: log file handle destroyed, using mwSTDERR\n" ); + mwLog = mwLogB1 = mwLogB2 = mwSTDERR; +#else + return mwSTDERR; +#endif + } + +static void mwLogW( FILE *p ) { + mwLog = mwLogB1 = mwLogB2 = p; + } + +static int mwFlushR() { + if( (mwFlushing == mwFlushingB1) && (mwFlushing == mwFlushingB2) ) return mwFlushing; + if( mwFlushing == mwFlushingB1 ) mwFlushingB2 = mwFlushing; + if( mwFlushing == mwFlushingB2 ) mwFlushingB1 = mwFlushing; + if( mwFlushingB1 == mwFlushingB2 ) mwFlushing = mwFlushingB1; + if( (mwFlushing == mwFlushingB1) && (mwFlushing == mwFlushingB2) ) { + mwWrite("internal: flushing flag damaged and recovered\n"); + FLUSH(); + return mwFlushing; + } + mwWrite("internal: flushing flag destroyed, so set to true\n"); + mwFlushing = mwFlushingB1 = mwFlushingB2 = 1; + return 1; + } + +static void mwFlushW( int n ) { + mwFlushing = mwFlushingB1 = mwFlushingB2 = n; + } + +static void mwIncErr() { + mwErrors++; + mwFlushW( mwFlushR()+1 ); + FLUSH(); + } + +static void mwFlush() { + if( mwLogR() == NULL ) return; +#ifdef MW_FLUSH + fflush( mwLogR() ); +#else + if( mwFlushR() ) fflush( mwLogR() ); +#endif + return; + } + +static void mwUnlink( mwData* mw, const char* file, int line ) { + if( mw->prev == NULL ) { + if( mwHead != mw ) + mwWrite( "internal: <%ld> %s(%d), MW-%p: link1 NULL, but not head\n", + mwCounter, file, line, mw ); + mwHead = mw->next; + } + else { + if( mw->prev->next != mw ) + mwWrite( "internal: <%ld> %s(%d), MW-%p: link1 failure\n", + mwCounter, file, line, mw ); + else mw->prev->next = mw->next; + } + if( mw->next == NULL ) { + if( mwTail != mw ) + mwWrite( "internal: <%ld> %s(%d), MW-%p: link2 NULL, but not tail\n", + mwCounter, file, line, mw ); + mwTail = mw->prev; + } + else { + if( mw->next->prev != mw ) + mwWrite( "internal: <%ld> %s(%d), MW-%p: link2 failure\n", + mwCounter, file, line, mw ); + else mw->next->prev = mw->prev; + } + } + +/* +** Relinking tries to repair a damaged mw block. +** Returns nonzero if it thinks it successfully +** repaired the heap chain. +*/ +static int mwRelink( mwData* mw, const char* file, int line ) { + int fails; + mwData *mw1, *mw2; + long count, size; + mwStat *ms; + + if( file == NULL ) file = "unknown"; + + if( mw == NULL ) { + mwWrite("relink: cannot repair MW at NULL\n"); + FLUSH(); + goto emergency; + } + + if( !mwIsSafeAddr(mw, mwDataSize) ) { + mwWrite("relink: MW-%p is a garbage pointer\n", mw); + FLUSH(); + goto emergency; + } + + mwWrite("relink: <%ld> %s(%d) attempting to repair MW-%p...\n", mwCounter, file, line, mw ); + FLUSH(); + fails = 0; + + /* Repair from head */ + if( mwHead != mw ) { + if( !mwIsSafeAddr( mwHead, mwDataSize ) ) { + mwWrite("relink: failed for MW-%p; head pointer destroyed\n", mw ); + FLUSH(); + goto emergency; + } + for( mw1=mwHead; mw1; mw1=mw1->next ) { + if( mw1->next == mw ) { + mw->prev = mw1; + break; + } + if( mw1->next && + ( !mwIsSafeAddr(mw1->next, mwDataSize ) || mw1->next->prev != mw1) ) { + mwWrite("relink: failed for MW-%p; forward chain fragmented at MW-%p: 'next' is %p\n", mw, mw1, mw1->next ); + FLUSH(); + goto emergency; + } + } + if( mw1 == NULL ) { + mwWrite("relink: MW-%p not found in forward chain search\n", mw ); + FLUSH(); + fails ++; + } + } + else + { + mwWrite( "relink: MW-%p is the head (first) allocation\n", mw ); + if( mw->prev != NULL ) + { + mwWrite( "relink: MW-%p prev pointer is non-NULL, you have a wild pointer\n", mw ); + mw->prev = NULL; + } + } + + /* Repair from tail */ + if( mwTail != mw ) { + if( !mwIsSafeAddr( mwTail, mwDataSize ) ) { + mwWrite("relink: failed for MW-%p; tail pointer destroyed\n", mw ); + FLUSH(); + goto emergency; + } + for( mw1=mwTail; mw1; mw1=mw1->prev ) { + if( mw1->prev == mw ) { + mw->next = mw1; + break; + } + if( mw1->prev && (!mwIsSafeAddr(mw1->prev, mwDataSize ) || mw1->prev->next != mw1) ) { + mwWrite("relink: failed for MW-%p; reverse chain fragmented at MW-%p, 'prev' is %p\n", mw, mw1, mw1->prev ); + FLUSH(); + goto emergency; + } + } + if( mw1 == NULL ) { + mwWrite("relink: MW-%p not found in reverse chain search\n", mw ); + FLUSH(); + fails ++; + } + } + else + { + mwWrite( "relink: MW-%p is the tail (last) allocation\n", mw ); + if( mw->next != NULL ) + { + mwWrite( "relink: MW-%p next pointer is non-NULL, you have a wild pointer\n", mw ); + mw->next = NULL; + } + } + + if( fails > 1 ) { + mwWrite("relink: heap appears intact, MW-%p probably garbage pointer\n", mw ); + FLUSH(); + goto verifyok; + } + + /* restore MW info where possible */ + if( mwIsReadAddr( mw->file, 1 ) ) { + ms = mwStatGet( mw->file, -1, 0 ); + if( ms == NULL ) mw->file = ""; + } + mw->check = CHKVAL(mw); + goto verifyok; + + /* Emergency repair */ + emergency: + + if( mwHead == NULL && mwTail == NULL ) + { + if( mwStatCurAlloc == 0 ) + mwWrite("relink: <%ld> %s(%d) heap is empty, nothing to repair\n", mwCounter, file, line ); + else + mwWrite("relink: <%ld> %s(%d) heap damaged beyond repair\n", mwCounter, file, line ); + FLUSH(); + return 0; + } + + mwWrite("relink: <%ld> %s(%d) attempting emergency repairs...\n", mwCounter, file, line ); + FLUSH(); + + if( mwHead == NULL || mwTail == NULL ) + { + if( mwHead == NULL ) mwWrite("relink: mwHead is NULL, but mwTail is %p\n", mwTail ); + else mwWrite("relink: mwTail is NULL, but mwHead is %p\n", mwHead ); + } + + mw1=NULL; + if( mwHead != NULL ) + { + if( !mwIsReadAddr( mwHead, mwDataSize ) || mwHead->check != CHKVAL(mwHead) ) + { + mwWrite("relink: mwHead (MW-%p) is damaged, skipping forward scan\n", mwHead ); + mwHead = NULL; + goto scan_reverse; + } + if( mwHead->prev != NULL ) + { + mwWrite("relink: the mwHead pointer's 'prev' member is %p, not NULL\n", mwHead->prev ); + } + for( mw1=mwHead; mw1; mw1=mw1->next ) + { + if( mw1->next ) + { + if( !mwIsReadAddr(mw1->next,mwDataSize) || + !mw1->next->check != CHKVAL(mw1) || + mw1->next->prev != mw1 ) + { + mwWrite("relink: forward chain's last intact MW is MW-%p, %ld %sbytes at %s(%d)\n", + mw1, mw1->size, (mw->flag & MW_NML)?"NoMansLand ":"", mw1->file, mw1->line ); + if( mwIsReadAddr(mw1->next,mwDataSize ) ) + { + mwWrite("relink: forward chain's first damaged MW is MW-%p, %ld %sbytes at %s(%d)\n", + mw1->next, mw1->size, (mw->flag & MW_NML)?"NoMansLand ":"", + mwIsReadAddr(mw1->file,16)?mw1->file:"", mw1->line ); + } + else + { + mwWrite("relink: the 'next' pointer of this MW points to %p, which is out-of-legal-access\n", + mw1->next ); + } + break; + } + } + } + } + + +scan_reverse: + mw2=NULL; + if( mwTail != NULL ) + { + if( !mwIsReadAddr(mwTail,mwDataSize) || mwTail->check != CHKVAL(mwTail) ) + { + mwWrite("relink: mwTail (%p) is damaged, skipping reverse scan\n", mwTail ); + mwTail = NULL; + goto analyze; + } + if( mwTail->next != NULL ) + { + mwWrite("relink: the mwTail pointer's 'next' member is %p, not NULL\n", mwTail->next ); + } + for( mw2=mwTail; mw2; mw2=mw2->prev ) + { + if( mw2->prev ) + { + if( !mwIsReadAddr(mw2->prev,mwDataSize) || + !mw2->prev->check != CHKVAL(mw2) || + mw2->prev->next != mw2 ) + { + mwWrite("relink: reverse chain's last intact MW is MW-%p, %ld %sbytes at %s(%d)\n", + mw2, mw2->size, (mw->flag & MW_NML)?"NoMansLand ":"", mw2->file, mw2->line ); + if( mwIsReadAddr(mw2->prev,mwDataSize ) ) + { + mwWrite("relink: reverse chain's first damaged MW is MW-%p, %ld %sbytes at %s(%d)\n", + mw2->prev, mw2->size, (mw->flag & MW_NML)?"NoMansLand ":"", + mwIsReadAddr(mw2->file,16)?mw2->file:"", mw2->line ); + } + else + { + mwWrite("relink: the 'prev' pointer of this MW points to %p, which is out-of-legal-access\n", + mw2->prev ); + } + break; + } + } + } + } + +analyze: + if( mwHead == NULL && mwTail == NULL ) + { + mwWrite("relink: both head and tail pointers damaged, aborting program\n"); + mwFlushW(1); + FLUSH(); + abort(); + } + if( mwHead == NULL ) + { + mwHead = mw2; + mwWrite("relink: heap truncated, MW-%p designated as new mwHead\n", mw2 ); + mw2->prev = NULL; + mw1 = mw2 = NULL; + } + if( mwTail == NULL ) + { + mwTail = mw1; + mwWrite("relink: heap truncated, MW-%p designated as new mwTail\n", mw1 ); + mw1->next = NULL; + mw1 = mw2 = NULL; + } + if( mw1 == NULL && mw2 == NULL && + mwHead->prev == NULL && mwTail->next == NULL ) { + mwWrite("relink: verifying heap integrity...\n" ); + FLUSH(); + goto verifyok; + } + if( mw1 && mw2 && mw1 != mw2 ) { + mw1->next = mw2; + mw2->prev = mw1; + mwWrite("relink: emergency repairs successful, assessing damage...\n"); + FLUSH(); + } + else { + mwWrite("relink: heap totally destroyed, aborting program\n"); + mwFlushW(1); + FLUSH(); + abort(); + } + + /* Verify by checking that the number of active allocations */ + /* match the number of entries in the chain */ +verifyok: + if( !mwIsHeapOK( NULL ) ) { + mwWrite("relink: heap verification FAILS - aborting program\n"); + mwFlushW(1); + FLUSH(); + abort(); + } + for( size=count=0, mw1=mwHead; mw1; mw1=mw1->next ) { + count ++; + size += (long) mw1->size; + } + if( count == mwNumCurAlloc ) { + mwWrite("relink: successful, "); + if( size == mwStatCurAlloc ) { + mwWrite("no allocations lost\n"); + } + else { + if( mw != NULL ) { + mwWrite("size information lost for MW-%p\n", mw); + mw->size = 0; + } + } + } + else { + mwWrite("relink: partial, %ld MW-blocks of %ld bytes lost\n", + mwNmlNumAlloc+mwNumCurAlloc-count, mwNmlCurAlloc+mwStatCurAlloc-size ); + return 0; + } + + return 1; + } + +/* +** If mwData* is NULL: +** Returns 0 if heap chain is broken. +** Returns 1 if heap chain is intact. +** If mwData* is not NULL: +** Returns 0 if mwData* is missing or if chain is broken. +** Returns 1 if chain is intact and mwData* is found. +*/ +static int mwIsHeapOK( mwData *includes_mw ) { + int found = 0; + mwData *mw; + + for( mw = mwHead; mw; mw=mw->next ) { + if( includes_mw == mw ) found++; + if( !mwIsSafeAddr( mw, mwDataSize ) ) return 0; + if( mw->prev ) { + if( !mwIsSafeAddr( mw->prev, mwDataSize ) ) return 0; + if( mw==mwHead || mw->prev->next != mw ) return 0; + } + if( mw->next ) { + if( !mwIsSafeAddr( mw->next, mwDataSize ) ) return 0; + if( mw==mwTail || mw->next->prev != mw ) return 0; + } + else if( mw!=mwTail ) return 0; + } + + if( includes_mw != NULL && !found ) return 0; + + return 1; + } + +static int mwIsOwned( mwData* mw, const char *file, int line ) { + int retv; + mwStat *ms; + + /* see if the address is legal according to OS */ + if( !mwIsSafeAddr( mw, mwDataSize ) ) return 0; + + /* make sure we have _anything_ allocated */ + if( mwHead == NULL && mwTail == NULL && mwStatCurAlloc == 0 ) + return 0; + + /* calculate checksum */ + if( mw->check != CHKVAL(mw) ) { + /* may be damaged checksum, see if block is in heap */ + if( mwIsHeapOK( mw ) ) { + /* damaged checksum, repair it */ + mwWrite( "internal: <%ld> %s(%d), checksum for MW-%p is incorrect\n", + mwCounter, file, line, mw ); + mwIncErr(); + if( mwIsReadAddr( mw->file, 1 ) ) { + ms = mwStatGet( mw->file, -1, 0 ); + if( ms == NULL ) mw->file = ""; + } + else mw->file = ""; + mw->size = 0; + mw->check = CHKVAL(mw); + return 1; + } + /* no, it's just some garbage data */ + return 0; + } + + /* check that the non-NULL pointers are safe */ + if( mw->prev && !mwIsSafeAddr( mw->prev, mwDataSize ) ) mwRelink( mw, file, line ); + if( mw->next && !mwIsSafeAddr( mw->next, mwDataSize ) ) mwRelink( mw, file, line ); + + /* safe address, checksum OK, proceed with heap checks */ + + /* see if the block is in the heap */ + retv = 0; + if( mw->prev ) { if( mw->prev->next == mw ) retv ++; } + else { if( mwHead == mw ) retv++; } + if( mw->next ) { if( mw->next->prev == mw ) retv ++; } + else { if( mwTail == mw ) retv++; } + if( mw->check == CHKVAL(mw) ) retv ++; + if( retv > 2 ) return 1; + + /* block not in heap, check heap for corruption */ + + if( !mwIsHeapOK( mw ) ) { + if( mwRelink( mw, file, line ) ) + return 1; + } + + /* unable to repair */ + mwWrite( "internal: <%ld> %s(%d), mwIsOwned fails for MW-%p\n", + mwCounter, file, line, mw ); + mwIncErr(); + + return 0; + } + +/* +** mwTestBuf: +** Checks a buffers links and pre/postfixes. +** Writes errors found to the log. +** Returns zero if no errors found. +*/ +static int mwTestBuf( mwData* mw, const char* file, int line ) { + int retv = 0; + char *p; + + if( file == NULL ) file = "unknown"; + + if( !mwIsSafeAddr( mw, mwDataSize + mwOverflowZoneSize ) ) { + mwWrite( "internal: <%ld> %s(%d): pointer MW-%p is invalid\n", + mwCounter, file, line, mw ); + mwIncErr(); + return 2; + } + + if( mw->check != CHKVAL(mw) ) { + mwWrite( "internal: <%ld> %s(%d), info trashed; relinking\n", + mwCounter, file, line ); + mwIncErr(); + if( !mwRelink( mw, file, line ) ) return 2; + } + + if( mw->prev && mw->prev->next != mw ) { + mwWrite( "internal: <%ld> %s(%d), buffer <%ld> %s(%d) link1 broken\n", + mwCounter,file,line, (long)mw->size, mw->count, mw->file, mw->line ); + mwIncErr(); + if( !mwRelink( mw, file, line ) ) retv = 2; + } + if( mw->next && mw->next->prev != mw ) { + mwWrite( "internal: <%ld> %s(%d), buffer <%ld> %s(%d) link2 broken\n", + mwCounter,file,line, (long)mw->size, mw->count, mw->file, mw->line ); + mwIncErr(); + if( !mwRelink( mw, file, line ) ) retv = 2; + } + + p = ((char*)mw) + mwDataSize; + if( mwCheckOF( p ) ) { + mwWrite( "underflow: <%ld> %s(%d), %ld bytes alloc'd at <%ld> %s(%d)\n", + mwCounter,file,line, (long)mw->size, mw->count, mw->file, mw->line ); + mwIncErr(); + retv = 1; + } + p += mwOverflowZoneSize + mw->size; + if( mwIsReadAddr( p, mwOverflowZoneSize ) && mwCheckOF( p ) ) { + mwWrite( "overflow: <%ld> %s(%d), %ld bytes alloc'd at <%ld> %s(%d)\n", + mwCounter,file,line, (long)mw->size, mw->count, mw->file, mw->line ); + mwIncErr(); + retv = 1; + } + + return retv; + } + +static void mwDefaultOutFunc( int c ) { +#ifndef ENABLEMWFILEWRITES + if( mwLogR() ) fputc( c, mwLogR() ); +#else + _cprintf("%c", c); +#endif + } + + +static void mwWrite( const char *format, ... ) { + int tot, oflow = 0; + va_list mark; + mwAutoInit(); + if( mwOutFunction == NULL ) mwOutFunction = mwDefaultOutFunc; + va_start( mark, format ); + tot = vsprintf( mwPrintBuf, format, mark ); + va_end( mark ); + if( tot >= MW_TRACE_BUFFER ) { mwPrintBuf[MW_TRACE_BUFFER] = 0; oflow = 1; } +#ifdef ENABLEMWFILEWRITE + for(tot=0;mwPrintBuf[tot];tot++) + (*mwOutFunction)( mwPrintBuf[tot] ); + + if( oflow ) { + mwWrite( "\ninternal: mwWrite(): WARNING! OUTPUT EXCEEDED %u CHARS: SYSTEM UNSTABLE\n", MW_TRACE_BUFFER-1 ); + FLUSH(); + } +#else + _cprintf("%s", mwPrintBuf); +#endif + + return; + } + +static void mwLogFile( const char *name ) { +#ifndef ENABLEMWFILEWRITES +//We are using cprintf, so there iw no need to have any files opened. + mwLogW(NULL); +#else + time_t tid; + (void) time( &tid ); + if( mwLogR() != NULL ) { + fclose( mwLogR() ); + mwLogW( NULL ); + } + if( name == NULL ) return; + mwLogW( fopen( name, "a" COMMIT ) ); + if( mwLogR() == NULL ) + mwWrite( "logfile: failed to open/create file '%s'\n", name ); +#endif +} + +/* +** Try to free NML memory until a contiguous allocation of +** 'needed' bytes can be satisfied. If this is not enough +** and the 'urgent' parameter is nonzero, grabbed memory is +** also freed. +*/ +static size_t mwFreeUp( size_t needed, int urgent ) { + void *p; + mwData *mw, *mw2; + char *data; + + /* free grabbed NML memory */ + for(;;) { + if( mwDrop_( 1, MW_VAL_NML, 1 ) == 0 ) break; + p = malloc( needed ); + if( p == NULL ) continue; + free( p ); + return needed; + } + + /* free normal NML memory */ + mw = mwHead; + while( mw != NULL ) { + if( !(mw->flag & MW_NML) ) mw = mw->next; + else { + data = ((char*)mw)+mwDataSize+mwOverflowZoneSize; + if( mwTestMem( data, mw->size, MW_VAL_NML ) ) { + mwIncErr(); + mwWrite( "wild pointer: <%ld> NoMansLand %p alloc'd at %s(%d)\n", + mw->count, data + mwOverflowZoneSize, mw->file, mw->line ); + } + mw2 = mw->next; + mwUnlink( mw, "mwFreeUp", 0 ); + free( mw ); + mw = mw2; + p = malloc( needed ); + if( p == NULL ) continue; + free( p ); + return needed; + } + } + + /* if not urgent (for internal purposes), fail */ + if( !urgent ) return 0; + + /* free grabbed memory */ + for(;;) { + if( mwDrop_( 1, MW_VAL_GRB, 1 ) == 0 ) break; + p = malloc( needed ); + if( p == NULL ) continue; + free( p ); + return needed; + } + + return 0; + } + +static const void * mwTestMem( const void *p, unsigned len, int c ) { + const unsigned char *ptr; + ptr = (const unsigned char *) p; + while( len-- ) { + if( *ptr != (unsigned char)c ) return (const void*)ptr; + ptr ++; + } + return NULL; + } + +static int mwStrCmpI( const char *s1, const char *s2 ) { + if( s1 == NULL || s2 == NULL ) return 0; + while( *s1 ) { + if( toupper(*s2) == toupper(*s1) ) { s1++; s2++; continue; } + return 1; + } + return 0; + } + +#define AIPH() if( always_invoked ) { mwWrite("autocheck: <%ld> %s(%d) ", mwCounter, file, line ); always_invoked = 0; } + +static int mwTestNow( const char *file, int line, int always_invoked ) { + int retv = 0; + mwData *mw; + char *data; + + if( file && !always_invoked ) + mwWrite("check: <%ld> %s(%d), checking %s%s%s\n", + mwCounter, file, line, + (mwTestFlags & MW_TEST_CHAIN) ? "chain ": "", + (mwTestFlags & MW_TEST_ALLOC) ? "alloc ": "", + (mwTestFlags & MW_TEST_NML) ? "nomansland ": "" + ); + + if( mwTestFlags & MW_TEST_CHAIN ) { + for( mw = mwHead; mw; mw=mw->next ) { + if( !mwIsSafeAddr(mw, mwDataSize) ) { + AIPH(); + mwWrite("check: heap corruption detected\n"); + mwIncErr(); + return retv + 1; + } + if( mw->prev ) { + if( !mwIsSafeAddr(mw->prev, mwDataSize) ) { + AIPH(); + mwWrite("check: heap corruption detected\n"); + mwIncErr(); + return retv + 1; + } + if( mw==mwHead || mw->prev->next != mw ) { + AIPH(); + mwWrite("check: heap chain broken, prev link incorrect\n"); + mwIncErr(); + retv ++; + } + } + if( mw->next ) { + if( !mwIsSafeAddr(mw->next, mwDataSize) ) { + AIPH(); + mwWrite("check: heap corruption detected\n"); + mwIncErr(); + return retv + 1; + } + if( mw==mwTail || mw->next->prev != mw ) { + AIPH(); + mwWrite("check: heap chain broken, next link incorrect\n"); + mwIncErr(); + retv ++; + } + } + else if( mw!=mwTail ) { + AIPH(); + mwWrite("check: heap chain broken, tail incorrect\n"); + mwIncErr(); + retv ++; + } + } + } + if( mwTestFlags & MW_TEST_ALLOC ) { + for( mw = mwHead; mw; mw=mw->next ) { + if( mwTestBuf( mw, file, line ) ) retv ++; + } + } + if( mwTestFlags & MW_TEST_NML ) { + for( mw = mwHead; mw; mw=mw->next ) { + if( (mw->flag & MW_NML) ) { + data = ((char*)mw)+mwDataSize+mwOverflowZoneSize; + if( mwTestMem( data, mw->size, MW_VAL_NML ) ) { + mwIncErr(); + mwWrite( "wild pointer: <%ld> NoMansLand %p alloc'd at %s(%d)\n", + mw->count, data + mwOverflowZoneSize, mw->file, mw->line ); + } + } + } + } + + + if( file && !always_invoked && !retv ) + mwWrite("check: <%ld> %s(%d), complete; no errors\n", + mwCounter, file, line ); + return retv; + } + +/********************************************************************** +** Statistics +**********************************************************************/ + +static void mwStatReport() +{ + mwStat* ms, *ms2; + const char *modname; + int modnamelen; + + /* global statistics report */ + mwWrite( "\nMemory usage statistics (global):\n" ); + mwWrite( " N)umber of allocations made: %ld\n", mwStatNumAlloc ); + mwWrite( " L)argest memory usage : %ld\n", mwStatMaxAlloc ); + mwWrite( " T)otal of all alloc() calls: %ld\n", mwStatTotAlloc ); + mwWrite( " U)nfreed bytes totals : %ld\n", mwStatCurAlloc ); + FLUSH(); + + if( mwStatLevel < 1 ) return; + + /* on a per-module basis */ + mwWrite( "\nMemory usage statistics (detailed):\n"); + mwWrite( " Module/Line Number Largest Total Unfreed \n"); + for( ms=mwStatList; ms; ms=ms->next ) + { + if( ms->line == -1 ) + { + if( ms->file == NULL || !mwIsReadAddr(ms->file,22) ) modname = ""; + else modname = ms->file; + modnamelen = strlen(modname); + if( modnamelen > 42 ) + { + modname = modname + modnamelen - 42; + } + + mwWrite(" %-42s %-8ld %-8ld %-8ld %-8ld\n", + modname, ms->num, ms->max, ms->total, ms->curr ); + if( ms->file && mwStatLevel > 1 ) + { + for( ms2=mwStatList; ms2; ms2=ms2->next ) + { + if( ms2->line!=-1 && ms2->file!=NULL && !mwStrCmpI( ms2->file, ms->file ) ) + { + mwWrite( " %-8d %-8ld %-8ld %-8ld %-8ld\n", + ms2->line, ms2->num, ms2->max, ms2->total, ms2->curr ); + } + } + } + } + } +} + +static mwStat* mwStatGet( const char *file, int line, int makenew ) { + mwStat* ms; + + if( mwStatLevel < 2 ) line = -1; + + for( ms=mwStatList; ms!=NULL; ms=ms->next ) { + if( line != ms->line ) continue; + if( file==NULL ) { + if( ms->file == NULL ) break; + continue; + } + if( ms->file == NULL ) continue; + if( !strcmp( ms->file, file ) ) break; + } + + if( ms != NULL ) return ms; + + if( !makenew ) return NULL; + + ms = (mwStat*) malloc( sizeof(mwStat) ); + if( ms == NULL ) { + if( mwFreeUp( sizeof(mwStat), 0 ) < sizeof(mwStat) || + (ms=(mwStat*)malloc(sizeof(mwStat))) == NULL ) { + mwWrite("internal: memory low, statistics incomplete for '%s'\n", file ); + return NULL; + } + } + ms->file = file; + ms->line = line; + ms->total = 0L; + ms->max = 0L; + ms->num = 0L; + ms->curr = 0L; + ms->next = mwStatList; + mwStatList = ms; + return ms; + } + +static void mwStatAlloc( size_t size, const char* file, int line ) { + mwStat* ms; + + /* update the module statistics */ + ms = mwStatGet( file, -1, 1 ); + if( ms != NULL ) { + ms->total += (long) size; + ms->curr += (long) size; + ms->num ++; + if( ms->curr > ms->max ) ms->max = ms->curr; + } + + /* update the line statistics */ + if( mwStatLevel > 1 && line != -1 && file ) { + ms = mwStatGet( file, line, 1 ); + if( ms != NULL ) { + ms->total += (long) size; + ms->curr += (long) size; + ms->num ++; + if( ms->curr > ms->max ) ms->max = ms->curr; + } + } + + } + +static void mwStatFree( size_t size, const char* file, int line ) { + mwStat* ms; + + /* update the module statistics */ + ms = mwStatGet( file, -1, 1 ); + if( ms != NULL ) ms->curr -= (long) size; + + /* update the line statistics */ + if( mwStatLevel > 1 && line != -1 && file ) { + ms = mwStatGet( file, line, 1 ); + if( ms != NULL ) ms->curr -= (long) size; + } + } + +/*********************************************************************** +** Safe memory checkers +** +** Using ifdefs, implement the operating-system specific mechanism +** of identifying a piece of memory as legal to access with read +** and write priviliges. Default: return nonzero for non-NULL pointers. +***********************************************************************/ + +static char mwDummy( char c ) +{ + return c; +} + +#ifndef MW_SAFEADDR +#ifdef WIN32 +#define MW_SAFEADDR +#define WIN32_LEAN_AND_MEAN +#include +int mwIsReadAddr( const void *p, unsigned len ) +{ + if( p == NULL ) return 0; + if( IsBadReadPtr(p,len) ) return 0; + return 1; +} +int mwIsSafeAddr( void *p, unsigned len ) +{ + /* NOTE: For some reason, under Win95 the IsBad... */ + /* can return false for invalid pointers. */ + if( p == NULL ) return 0; + if( IsBadReadPtr(p,len) || IsBadWritePtr(p,len) ) return 0; + return 1; +} +#endif /* WIN32 */ +#endif /* MW_SAFEADDR */ + +#ifndef MW_SAFEADDR +#ifdef SIGSEGV +#define MW_SAFEADDR + +typedef void (*mwSignalHandlerPtr)( int ); +mwSignalHandlerPtr mwOldSIGSEGV = (mwSignalHandlerPtr) 0; +jmp_buf mwSIGSEGVjump; +static void mwSIGSEGV( int n ); + +static void mwSIGSEGV( int n ) +{ + n = n; + longjmp( mwSIGSEGVjump, 1 ); +} + +int mwIsReadAddr( const void *p, unsigned len ) +{ + const char *ptr; + + if( p == NULL ) return 0; + if( !len ) return 1; + + /* set up to catch the SIGSEGV signal */ + mwOldSIGSEGV = signal( SIGSEGV, mwSIGSEGV ); + + if( setjmp( mwSIGSEGVjump ) ) + { + signal( SIGSEGV, mwOldSIGSEGV ); + return 0; + } + + /* read all the bytes in the range */ + ptr = (const char *)p; + ptr += len; + + /* the reason for this rather strange construct is that */ + /* we want to keep the number of used parameters and locals */ + /* to a minimum. if we use len for a counter gcc will complain */ + /* it may get clobbered by longjmp() at high warning levels. */ + /* it's a harmless warning, but this way we don't have to see it. */ + do + { + ptr --; + if( *ptr == 0x7C ) (void) mwDummy( (char)0 ); + } while( (const void*) ptr != p ); + + /* remove the handler */ + signal( SIGSEGV, mwOldSIGSEGV ); + + return 1; +} +int mwIsSafeAddr( void *p, unsigned len ) +{ + char *ptr; + + if( p == NULL ) return 0; + if( !len ) return 1; + + /* set up to catch the SIGSEGV signal */ + mwOldSIGSEGV = signal( SIGSEGV, mwSIGSEGV ); + + if( setjmp( mwSIGSEGVjump ) ) + { + signal( SIGSEGV, mwOldSIGSEGV ); + return 0; + } + + /* read and write-back all the bytes in the range */ + ptr = (char *)p; + ptr += len; + + /* the reason for this rather strange construct is that */ + /* we want to keep the number of used parameters and locals */ + /* to a minimum. if we use len for a counter gcc will complain */ + /* it may get clobbered by longjmp() at high warning levels. */ + /* it's a harmless warning, but this way we don't have to see it. */ + do + { + ptr --; + *ptr = mwDummy( *ptr ); + } while( (void*) ptr != p ); + + /* remove the handler */ + signal( SIGSEGV, mwOldSIGSEGV ); + + return 1; +} +#endif /* SIGSEGV */ +#endif /* MW_SAFEADDR */ + +#ifndef MW_SAFEADDR +int mwIsReadAddr( const void *p, unsigned len ) +{ + if( p == NULL ) return 0; + if( len == 0 ) return 1; + return 1; +} +int mwIsSafeAddr( void *p, unsigned len ) +{ + if( p == NULL ) return 0; + if( len == 0 ) return 1; + return 1; +} +#endif + +/********************************************************************** +** Mutex handling +**********************************************************************/ + +#if defined(WIN32) || defined(__WIN32__) + +static void mwMutexInit( void ) +{ +SECURITY_ATTRIBUTES MutexAttributes; + + MutexAttributes.nLength=sizeof(SECURITY_ATTRIBUTES); + MutexAttributes.lpSecurityDescriptor=NULL; //go with defaults + MutexAttributes.bInheritHandle=true; //allow child processes inherit the handle + + mwGlobalMutex = CreateMutex(&MutexAttributes, FALSE, "memwatch 2.71"); //cant initialize with no ecurity descriptors, as child proceses can not use it otherwize + return; +} + +static void mwMutexTerm( void ) +{ + CloseHandle( mwGlobalMutex ); + return; +} + +static void mwMutexLock( void ) +{ + if( WaitForSingleObject(mwGlobalMutex, 1000 ) == WAIT_TIMEOUT ) + { + mwWrite( "mwMutexLock: timed out, possible deadlock\n" ); + } + return; +} + +static void mwMutexUnlock( void ) +{ + ReleaseMutex( mwGlobalMutex ); + return; +} + +#endif + +#if defined(MW_PTHREADS) || defined(HAVE_PTHREAD_H) + +static void mwMutexInit( void ) +{ + pthread_mutex_init( &mwGlobalMutex, NULL ); + return; +} + +static void mwMutexTerm( void ) +{ + pthread_mutex_destroy( &mwGlobalMutex ); + return; +} + +static void mwMutexLock( void ) +{ + pthread_mutex_lock(&mwGlobalMutex); + return; +} + +static void mwMutexUnlock( void ) +{ + pthread_mutex_unlock(&mwGlobalMutex); + return; +} + +#endif + +/********************************************************************** +** C++ new & delete +**********************************************************************/ + +#if 0 /* 980317: disabled C++ */ + +#ifdef __cplusplus +#ifndef MEMWATCH_NOCPP + +int mwNCur = 0; +const char *mwNFile = NULL; +int mwNLine = 0; + +class MemWatch { +public: + MemWatch(); + ~MemWatch(); + }; + +MemWatch::MemWatch() { + if( mwInited ) return; + mwUseAtexit = 0; + mwInit(); + } + +MemWatch::~MemWatch() { + if( mwUseAtexit ) return; + mwTerm(); + } + +/* +** This global new will catch all 'new' calls where MEMWATCH is +** not active. +*/ +void* operator new( unsigned size ) { + mwNCur = 0; + return mwMalloc( size, "", 0 ); + } + +/* +** This is the new operator that's called when a module uses mwNew. +*/ +void* operator new( unsigned size, const char *file, int line ) { + mwNCur = 0; + return mwMalloc( size, file, line ); + } + +/* +** This is the new operator that's called when a module uses mwNew[]. +** -- hjc 07/16/02 +*/ +void* operator new[] ( unsigned size, const char *file, int line ) { + mwNCur = 0; + return mwMalloc( size, file, line ); + } + +/* +** Since this delete operator will recieve ALL delete's +** even those from within libraries, we must accept +** delete's before we've been initialized. Nor can we +** reliably check for wild free's if the mwNCur variable +** is not set. +*/ +void operator delete( void *p ) { + if( p == NULL ) return; + if( !mwInited ) { + free( p ); + return; + } + if( mwNCur ) { + mwFree( p, mwNFile, mwNLine ); + mwNCur = 0; + return; + } + mwFree_( p ); + } + +void operator delete[]( void *p ) { + if( p == NULL ) return; + if( !mwInited ) { + free( p ); + return; + } + if( mwNCur ) { + mwFree( p, mwNFile, mwNLine ); + mwNCur = 0; + return; + } + mwFree_( p ); + } + +#endif /* MEMWATCH_NOCPP */ +#endif /* __cplusplus */ + +#endif /* 980317: disabled C++ */ + +/* MEMWATCH.C */ diff --git a/memwatch-2.71/memwatch.h b/memwatch-2.71/memwatch.h new file mode 100644 index 0000000..80323a8 --- /dev/null +++ b/memwatch-2.71/memwatch.h @@ -0,0 +1,711 @@ +/* +** MEMWATCH.H +** Nonintrusive ANSI C memory leak / overwrite detection +** Copyright (C) 1992-2002 Johan Lindh +** All rights reserved. +** Version 2.71 +** +************************************************************************ +** +** PURPOSE: +** +** MEMWATCH has been written to allow guys and gals that like to +** program in C a public-domain memory error control product. +** I hope you'll find it's as advanced as most commercial packages. +** The idea is that you use it during the development phase and +** then remove the MEMWATCH define to produce your final product. +** MEMWATCH is distributed in source code form in order to allow +** you to compile it for your platform with your own compiler. +** It's aim is to be 100% ANSI C, but some compilers are more stingy +** than others. If it doesn't compile without warnings, please mail +** me the configuration of operating system and compiler you are using +** along with a description of how to modify the source, and the version +** number of MEMWATCH that you are using. +** +************************************************************************ + + This file is part of MEMWATCH. + + MEMWATCH is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + MEMWATCH is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with MEMWATCH; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +************************************************************************ +** +** REVISION HISTORY: +** +** 920810 JLI [1.00] +** 920830 JLI [1.10 double-free detection] +** 920912 JLI [1.15 mwPuts, mwGrab/Drop, mwLimit] +** 921022 JLI [1.20 ASSERT and VERIFY] +** 921105 JLI [1.30 C++ support and TRACE] +** 921116 JLI [1.40 mwSetOutFunc] +** 930215 JLI [1.50 modified ASSERT/VERIFY] +** 930327 JLI [1.51 better auto-init & PC-lint support] +** 930506 JLI [1.55 MemWatch class, improved C++ support] +** 930507 JLI [1.60 mwTest & CHECK()] +** 930809 JLI [1.65 Abort/Retry/Ignore] +** 930820 JLI [1.70 data dump when unfreed] +** 931016 JLI [1.72 modified C++ new/delete handling] +** 931108 JLI [1.77 mwSetAssertAction() & some small changes] +** 940110 JLI [1.80 no-mans-land alloc/checking] +** 940328 JLI [2.00 version 2.0 rewrite] +** Improved NML (no-mans-land) support. +** Improved performance (especially for free()ing!). +** Support for 'read-only' buffers (checksums) +** ^^ NOTE: I never did this... maybe I should? +** FBI (free'd block info) tagged before freed blocks +** Exporting of the mwCounter variable +** mwBreakOut() localizes debugger support +** Allocation statistics (global, per-module, per-line) +** Self-repair ability with relinking +** 950913 JLI [2.10 improved garbage handling] +** 951201 JLI [2.11 improved auto-free in emergencies] +** 960125 JLI [X.01 implemented auto-checking using mwAutoCheck()] +** 960514 JLI [2.12 undefining of existing macros] +** 960515 JLI [2.13 possibility to use default new() & delete()] +** 960516 JLI [2.20 suppression of file flushing on unfreed msgs] +** 960516 JLI [2.21 better support for using MEMWATCH with DLL's] +** 960710 JLI [X.02 multiple logs and mwFlushNow()] +** 960801 JLI [2.22 merged X.01 version with current] +** 960805 JLI [2.30 mwIsXXXXAddr() to avoid unneeded GP's] +** 960805 JLI [2.31 merged X.02 version with current] +** 961002 JLI [2.32 support for realloc() + fixed STDERR bug] +** 961222 JLI [2.40 added mwMark() & mwUnmark()] +** 970101 JLI [2.41 added over/underflow checking after failed ASSERT/VERIFY] +** 970113 JLI [2.42 added support for PC-Lint 7.00g] +** 970207 JLI [2.43 added support for strdup()] +** 970209 JLI [2.44 changed default filename to lowercase] +** 970405 JLI [2.45 fixed bug related with atexit() and some C++ compilers] +** 970723 JLI [2.46 added MW_ARI_NULLREAD flag] +** 970813 JLI [2.47 stabilized marker handling] +** 980317 JLI [2.48 ripped out C++ support; wasn't working good anyway] +** 980318 JLI [2.50 improved self-repair facilities & SIGSEGV support] +** 980417 JLI [2.51 more checks for invalid addresses] +** 980512 JLI [2.52 moved MW_ARI_NULLREAD to occur before aborting] +** 990112 JLI [2.53 added check for empty heap to mwIsOwned] +** 990217 JLI [2.55 improved the emergency repairs diagnostics and NML] +** 990224 JLI [2.56 changed ordering of members in structures] +** 990303 JLI [2.57 first maybe-fixit-for-hpux test] +** 990516 JLI [2.58 added 'static' to the definition of mwAutoInit] +** 990517 JLI [2.59 fixed some high-sensitivity warnings] +** 990610 JLI [2.60 fixed some more high-sensitivity warnings] +** 990715 JLI [2.61 changed TRACE/ASSERT/VERIFY macro names] +** 991001 JLI [2.62 added CHECK_BUFFER() and mwTestBuffer()] +** 991007 JLI [2.63 first shot at a 64-bit compatible version] +** 991009 JLI [2.64 undef's strdup() if defined, mwStrdup made const] +** 000704 JLI [2.65 added some more detection for 64-bits] +** 010502 JLI [2.66 incorporated some user fixes] +** [mwRelink() could print out garbage pointer (thanks mac@phobos.ca)] +** [added array destructor for C++ (thanks rdasilva@connecttel.com)] +** [added mutex support (thanks rdasilva@connecttel.com)] +** 010531 JLI [2.67 fix: mwMutexXXX() was declared even if MW_HAVE_MUTEX was not defined] +** 010619 JLI [2.68 fix: mwRealloc() could leave the mutex locked] +** 020918 JLI [2.69 changed to GPL, added C++ array allocation by Howard Cohen] +** 030212 JLI [2.70 mwMalloc() bug for very large allocations (4GB on 32bits)] +** 030520 JLI [2.71 added ULONG_LONG_MAX as a 64-bit detector (thanks Sami Salonen)] +** +** To use, simply include 'MEMWATCH.H' as a header file, +** and add MEMWATCH.C to your list of files, and define the macro +** 'MEMWATCH'. If this is not defined, MEMWATCH will disable itself. +** +** To call the standard C malloc / realloc / calloc / free; use mwMalloc_(), +** mwCalloc_() and mwFree_(). Note that mwFree_() will correctly +** free both malloc()'d memory as well as mwMalloc()'d. +** +** 980317: C++ support has been disabled. +** The code remains, but is not compiled. +** +** For use with C++, which allows use of inlining in header files +** and class specific new/delete, you must also define 'new' as +** 'mwNew' and 'delete' as 'mwDelete'. Do this *after* you include +** C++ header files from libraries, otherwise you can mess up their +** class definitions. If you don't define these, the C++ allocations +** will not have source file and line number information. Also note, +** most C++ class libraries implement their own C++ memory management, +** and don't allow anyone to override them. MFC belongs to this crew. +** In these cases, the only thing to do is to use MEMWATCH_NOCPP. +** +** You can capture output from MEMWATCH using mwSetOutFunc(). +** Just give it the adress of a "void myOutFunc(int c)" function, +** and all characters to be output will be redirected there. +** +** A failing ASSERT() or VERIFY() will normally always abort your +** program. This can be changed using mwSetAriFunc(). Give it a +** pointer to a "int myAriFunc(const char *)" function. Your function +** must ask the user whether to Abort, Retry or Ignore the trap. +** Return 2 to Abort, 1 to Retry or 0 to Ignore. Beware retry; it +** causes the expression to be evaluated again! MEMWATCH has a +** default ARI handler. It's disabled by default, but you can enable +** it by calling 'mwDefaultAri()'. Note that this will STILL abort +** your program unless you define MEMWATCH_STDIO to allow MEMWATCH +** to use the standard C I/O streams. Also, setting the ARI function +** will cause MEMWATCH *NOT* to write the ARI error to stderr. The +** error string is passed to the ARI function instead, as the +** 'const char *' parameter. +** +** You can disable MEMWATCH's ASSERT/VERIFY and/or TRACE implementations. +** This can be useful if you're using a debug terminal or smart debugger. +** Disable them by defining MW_NOASSERT, MW_NOVERIFY or MW_NOTRACE. +** +** MEMWATCH fills all allocated memory with the byte 0xFE, so if +** you're looking at erroneous data which are all 0xFE:s, the +** data probably was not initialized by you. The exception is +** calloc(), which will fill with zero's. All freed buffers are +** zapped with 0xFD. If this is what you look at, you're using +** data that has been freed. If this is the case, be aware that +** MEMWATCH places a 'free'd block info' structure immediately +** before the freed data. This block contains info about where +** the block was freed. The information is in readable text, +** in the format "FBIfilename(line)", for example: +** "FBI<267>test.c(12)". Using FBI's slows down free(), so it's +** disabled by default. Use mwFreeBufferInfo(1) to enable it. +** +** To aid in tracking down wild pointer writes, MEMWATCH can perform +** no-mans-land allocations. No-mans-land will contain the byte 0xFC. +** MEMWATCH will, when this is enabled, convert recently free'd memory +** into NML allocations. +** +** MEMWATCH protects it's own data buffers with checksums. If you +** get an internal error, it means you're overwriting wildly, +** or using an uninitialized pointer. +** +************************************************************************ +** +** Note when compiling with Microsoft C: +** - MSC ignores fflush() by default. This is overridden, so that +** the disk log will always be current. +** +** This utility has been tested with: +** PC-lint 7.0k, passed as 100% ANSI C compatible +** Microsoft Visual C++ on Win16 and Win32 +** Microsoft C on DOS +** SAS C on an Amiga 500 +** Gnu C on a PC running Red Hat Linux +** ...and using an (to me) unknown compiler on an Atari machine. +** +************************************************************************ +** +** Format of error messages in MEMWATCH.LOG: +** message: filename(linenumber), information +** +** Errors caught by MemWatch, when they are detected, and any +** actions taken besides writing to the log file MEMWATCH.LOG: +** +** Double-freeing: +** A pointer that was recently freed and has not since been +** reused was freed again. The place where the previous free() +** was executed is displayed. +** Detect: delete or free() using the offending pointer. +** Action: The delete or free() is cancelled, execution continues. +** Underflow: +** You have written just ahead of the allocated memory. +** The size and place of the allocation is displayed. +** Detect: delete or free() of the damaged buffer. +** Action: The buffer is freed, but there may be secondary damage. +** Overflow: +** Like underflow, but you've written after the end of the buffer. +** Detect: see Underflow. +** Action: see Underflow. +** WILD free: +** An unrecognized pointer was passed to delete or free(). +** The pointer may have been returned from a library function; +** in that case, use mwFree_() to force free() of it. +** Also, this may be a double-free, but the previous free was +** too long ago, causing MEMWATCH to 'forget' it. +** Detect: delete or free() of the offending pointer. +** Action: The delete or free() is cancelled, execution continues. +** NULL free: +** It's unclear to me whether or not freeing of NULL pointers +** is legal in ANSI C, therefore a warning is written to the log file, +** but the error counter remains the same. This is legal using C++, +** so the warning does not appear with delete. +** Detect: When you free(NULL). +** Action: The free() is cancelled. +** Failed: +** A request to allocate memory failed. If the allocation is +** small, this may be due to memory depletion, but is more likely +** to be memory fragmentation problems. The amount of memory +** allocated so far is displayed also. +** Detect: When you new, malloc(), realloc() or calloc() memory. +** Action: NULL is returned. +** Realloc: +** A request to re-allocate a memory buffer failed for reasons +** other than out-of-memory. The specific reason is shown. +** Detect: When you realloc() +** Action: realloc() is cancelled, NULL is returned +** Limit fail: +** A request to allocate memory failed since it would violate +** the limit set using mwLimit(). mwLimit() is used to stress-test +** your code under simulated low memory conditions. +** Detect: At new, malloc(), realloc() or calloc(). +** Action: NULL is returned. +** Assert trap: +** An ASSERT() failed. The ASSERT() macro works like C's assert() +** macro/function, except that it's interactive. See your C manual. +** Detect: On the ASSERT(). +** Action: Program ends with an advisory message to stderr, OR +** Program writes the ASSERT to the log and continues, OR +** Program asks Abort/Retry/Ignore? and takes that action. +** Verify trap: +** A VERIFY() failed. The VERIFY() macro works like ASSERT(), +** but if MEMWATCH is not defined, it still evaluates the +** expression, but it does not act upon the result. +** Detect: On the VERIFY(). +** Action: Program ends with an advisory message to stderr, OR +** Program writes the VERIFY to the log and continues, OR +** Program asks Abort/Retry/Ignore? and takes that action. +** Wild pointer: +** A no-mans-land buffer has been written into. MEMWATCH can +** allocate and distribute chunks of memory solely for the +** purpose of trying to catch random writes into memory. +** Detect: Always on CHECK(), but can be detected in several places. +** Action: The error is logged, and if an ARI handler is installed, +** it is executed, otherwise, execution continues. +** Unfreed: +** A memory buffer you allocated has not been freed. +** You are informed where it was allocated, and whether any +** over or underflow has occured. MemWatch also displays up to +** 16 bytes of the data, as much as it can, in hex and text. +** Detect: When MemWatch terminates. +** Action: The buffer is freed. +** Check: +** An error was detected during a CHECK() operation. +** The associated pointer is displayed along with +** the file and line where the CHECK() was executed. +** Followed immediately by a normal error message. +** Detect: When you CHECK() +** Action: Depends on the error +** Relink: +** After a MEMWATCH internal control block has been trashed, +** MEMWATCH tries to repair the damage. If successful, program +** execution will continue instead of aborting. Some information +** about the block may be gone permanently, though. +** Detect: N/A +** Action: Relink successful: program continues. +** Relink fails: program aborts. +** Internal: +** An internal error is flagged by MEMWATCH when it's control +** structures have been damaged. You are likely using an uninitialized +** pointer somewhere in your program, or are zapping memory all over. +** The message may give you additional diagnostic information. +** If possible, MEMWATCH will recover and continue execution. +** Detect: Various actions. +** Action: Whatever is needed +** Mark: +** The program terminated without umarking all marked pointers. Marking +** can be used to track resources other than memory. mwMark(pointer,text,...) +** when the resource is allocated, and mwUnmark(pointer) when it's freed. +** The 'text' is displayed for still marked pointers when the program +** ends. +** Detect: When MemWatch terminates. +** Action: The error is logged. +** +** +************************************************************************ +** +** The author may be reached by e-mail at the address below. If you +** mail me about source code changes in MEMWATCH, remember to include +** MW's version number. +** +** Johan Lindh +** johan@linkdata.se +** +** The latest version of MEMWATCH may be downloaded from +** http://www.linkdata.se/ +*/ + +#ifndef __MEMWATCH_H +#define __MEMWATCH_H + +/* Make sure that malloc(), realloc(), calloc() and free() are declared. */ +/*lint -save -e537 */ +#include +/*lint -restore */ + +//#define ENABLEMWFILEWRITES 1 +#define fprintf(a, fmt, ...) _cprintf(fmt, __VA_ARGS__) + +#ifdef __cplusplus +extern "C" { +#endif + + +/* +** Constants used +** All MEMWATCH constants start with the prefix MW_, followed by +** a short mnemonic which indicates where the constant is used, +** followed by a descriptive text about it. +*/ + +#define MW_ARI_NULLREAD 0x10 /* Null read (to start debugger) */ +#define MW_ARI_ABORT 0x04 /* ARI handler says: abort program! */ +#define MW_ARI_RETRY 0x02 /* ARI handler says: retry action! */ +#define MW_ARI_IGNORE 0x01 /* ARI handler says: ignore error! */ + +#define MW_VAL_NEW 0xFE /* value in newly allocated memory */ +#define MW_VAL_DEL 0xFD /* value in newly deleted memory */ +#define MW_VAL_NML 0xFC /* value in no-mans-land */ +#define MW_VAL_GRB 0xFB /* value in grabbed memory */ + +#define MW_TEST_ALL 0xFFFF /* perform all tests */ +#define MW_TEST_CHAIN 0x0001 /* walk the heap chain */ +#define MW_TEST_ALLOC 0x0002 /* test allocations & NML guards */ +#define MW_TEST_NML 0x0004 /* test all-NML areas for modifications */ + +#define MW_NML_NONE 0 /* no NML */ +#define MW_NML_FREE 1 /* turn FREE'd memory into NML */ +#define MW_NML_ALL 2 /* all unused memory is NML */ +#define MW_NML_DEFAULT 0 /* the default NML setting */ + +#define MW_STAT_GLOBAL 0 /* only global statistics collected */ +#define MW_STAT_MODULE 1 /* collect statistics on a module basis */ +#define MW_STAT_LINE 2 /* collect statistics on a line basis */ +#define MW_STAT_DEFAULT 0 /* the default statistics setting */ + +/* +** MemWatch internal constants +** You may change these and recompile MemWatch to change the limits +** of some parameters. Respect the recommended minimums! +*/ +#define MW_TRACE_BUFFER 2048 /* (min 160) size of TRACE()'s output buffer */ +#define MW_FREE_LIST 64 /* (min 4) number of free()'s to track */ + +/* +** Exported variables +** In case you have to remove the 'const' keyword because your compiler +** doesn't support it, be aware that changing the values may cause +** unpredictable behaviour. +** - mwCounter contains the current action count. You can use this to +** place breakpoints using a debugger, if you want. +*/ +#ifndef __MEMWATCH_C +//extern const unsigned long mwCounter; //delete by Laid, 24.11.2013 +extern unsigned long mwCounter; +#endif + +/* +** System functions +** Normally, it is not nessecary to call any of these. MEMWATCH will +** automatically initialize itself on the first MEMWATCH function call, +** and set up a call to mwAbort() using atexit(). Some C++ implementations +** run the atexit() chain before the program has terminated, so you +** may have to use mwInit() or the MemWatch C++ class to get good +** behaviour. +** - mwInit() can be called to disable the atexit() usage. If mwInit() +** is called directly, you must call mwTerm() to end MemWatch, or +** mwAbort(). +** - mwTerm() is usually not nessecary to call; but if called, it will +** call mwAbort() if it finds that it is cancelling the 'topmost' +** mwInit() call. +** - mwAbort() cleans up after MEMWATCH, reports unfreed buffers, etc. +*/ +void mwInit( void ); +void mwTerm( void ); +void mwAbort( void ); + +/* +** Setup functions +** These functions control the operation of MEMWATCH's protective features. +** - mwFlushNow() causes MEMWATCH to flush it's buffers. +** - mwDoFlush() controls whether MEMWATCH flushes the disk buffers after +** writes. The default is smart flushing: MEMWATCH will not flush buffers +** explicitly until memory errors are detected. Then, all writes are +** flushed until program end or mwDoFlush(0) is called. +** - mwLimit() sets the allocation limit, an arbitrary limit on how much +** memory your program may allocate in bytes. Used to stress-test app. +** Also, in virtual-memory or multitasking environs, puts a limit on +** how much MW_NML_ALL can eat up. +** - mwGrab() grabs up X kilobytes of memory. Allocates actual memory, +** can be used to stress test app & OS both. +** - mwDrop() drops X kilobytes of grabbed memory. +** - mwNoMansLand() sets the behaviour of the NML logic. See the +** MW_NML_xxx for more information. The default is MW_NML_DEFAULT. +** - mwStatistics() sets the behaviour of the statistics collector. See +** the MW_STAT_xxx defines for more information. Default MW_STAT_DEFAULT. +** - mwFreeBufferInfo() enables or disables the tagging of free'd buffers +** with freeing information. This information is written in text form, +** using sprintf(), so it's pretty slow. Disabled by default. +** - mwAutoCheck() performs a CHECK() operation whenever a MemWatch function +** is used. Slows down performance, of course. +** - mwCalcCheck() calculates checksums for all data buffers. Slow! +** - mwDumpCheck() logs buffers where stored & calc'd checksums differ. Slow!! +** - mwMark() sets a generic marker. Returns the pointer given. +** - mwUnmark() removes a generic marker. If, at the end of execution, some +** markers are still in existence, these will be reported as leakage. +** returns the pointer given. +*/ +void mwFlushNow( void ); +void mwDoFlush( int onoff ); +void mwLimit( long bytes ); +unsigned mwGrab( unsigned kilobytes ); +unsigned mwDrop( unsigned kilobytes ); +void mwNoMansLand( int mw_nml_level ); +void mwStatistics( int level ); +void mwFreeBufferInfo( int onoff ); +void mwAutoCheck( int onoff ); +void mwCalcCheck( void ); +void mwDumpCheck( void ); +void * mwMark( void *p, const char *description, const char *file, unsigned line ); +void * mwUnmark( void *p, const char *file, unsigned line ); + +/* +** Testing/verification/tracing +** All of these macros except VERIFY() evaluates to a null statement +** if MEMWATCH is not defined during compilation. +** - mwIsReadAddr() checks a memory area for read privilige. +** - mwIsSafeAddr() checks a memory area for both read & write privilige. +** This function and mwIsReadAddr() is highly system-specific and +** may not be implemented. If this is the case, they will default +** to returning nonzero for any non-NULL pointer. +** - CHECK() does a complete memory integrity test. Slow! +** - CHECK_THIS() checks only selected components. +** - CHECK_BUFFER() checks the indicated buffer for errors. +** - mwASSERT() or ASSERT() If the expression evaluates to nonzero, execution continues. +** Otherwise, the ARI handler is called, if present. If not present, +** the default ARI action is taken (set with mwSetAriAction()). +** ASSERT() can be disabled by defining MW_NOASSERT. +** - mwVERIFY() or VERIFY() works just like ASSERT(), but when compiling without +** MEMWATCH the macro evaluates to the expression. +** VERIFY() can be disabled by defining MW_NOVERIFY. +** - mwTRACE() or TRACE() writes some text and data to the log. Use like printf(). +** TRACE() can be disabled by defining MW_NOTRACE. +*/ +int mwIsReadAddr( const void *p, unsigned len ); +int mwIsSafeAddr( void *p, unsigned len ); +int mwTest( const char *file, int line, int mw_test_flags ); +int mwTestBuffer( const char *file, int line, void *p ); +int mwAssert( int, const char*, const char*, int ); +int mwVerify( int, const char*, const char*, int ); + +/* +** User I/O functions +** - mwTrace() works like printf(), but dumps output either to the +** function specified with mwSetOutFunc(), or the log file. +** - mwPuts() works like puts(), dumps output like mwTrace(). +** - mwSetOutFunc() allows you to give the adress of a function +** where all user output will go. (exeption: see mwSetAriFunc) +** Specifying NULL will direct output to the log file. +** - mwSetAriFunc() gives MEMWATCH the adress of a function to call +** when an 'Abort, Retry, Ignore' question is called for. The +** actual error message is NOT printed when you've set this adress, +** but instead it is passed as an argument. If you call with NULL +** for an argument, the ARI handler is disabled again. When the +** handler is disabled, MEMWATCH will automatically take the +** action specified by mwSetAriAction(). +** - mwSetAriAction() sets the default ARI return value MEMWATCH should +** use if no ARI handler is specified. Defaults to MW_ARI_ABORT. +** - mwAriHandler() is an ANSI ARI handler you can use if you like. It +** dumps output to stderr, and expects input from stdin. +** - mwBreakOut() is called in certain cases when MEMWATCH feels it would +** be nice to break into a debugger. If you feel like MEMWATCH, place +** an execution breakpoint on this function. +*/ +void mwTrace( const char* format_string, ... ); +void mwPuts( const char* text ); +void mwSetOutFunc( void (*func)(int) ); +void mwSetAriFunc( int (*func)(const char*) ); +void mwSetAriAction( int mw_ari_value ); +int mwAriHandler( const char* cause ); +void mwBreakOut( const char* cause ); + +/* +** Allocation/deallocation functions +** These functions are the ones actually to perform allocations +** when running MEMWATCH, for both C and C++ calls. +** - mwMalloc() debugging allocator +** - mwMalloc_() always resolves to a clean call of malloc() +** - mwRealloc() debugging re-allocator +** - mwRealloc_() always resolves to a clean call of realloc() +** - mwCalloc() debugging allocator, fills with zeros +** - mwCalloc_() always resolves to a clean call of calloc() +** - mwFree() debugging free. Can only free memory which has +** been allocated by MEMWATCH. +** - mwFree_() resolves to a) normal free() or b) debugging free. +** Can free memory allocated by MEMWATCH and malloc() both. +** Does not generate any runtime errors. +*/ +void* mwMalloc( size_t, const char*, int ); +void* mwMalloc_( size_t ); +void* mwRealloc( void *, size_t, const char*, int ); +void* mwRealloc_( void *, size_t ); +void* mwCalloc( size_t, size_t, const char*, int ); +void* mwCalloc_( size_t, size_t ); +void mwFree( void*, const char*, int ); +void mwFree_( void* ); +char* mwStrdup( const char *, const char*, int ); + +/* +** Enable/disable precompiler block +** This block of defines and if(n)defs make sure that references +** to MEMWATCH is completely removed from the code if the MEMWATCH +** manifest constant is not defined. +*/ +#ifndef __MEMWATCH_C +#ifdef MEMWATCH + +#define mwASSERT(exp) while(mwAssert((int)(exp),#exp,__FILE__,__LINE__)) +#ifndef MW_NOASSERT +#ifndef ASSERT +#define ASSERT mwASSERT +#endif /* !ASSERT */ +#endif /* !MW_NOASSERT */ +#define mwVERIFY(exp) while(mwVerify((int)(exp),#exp,__FILE__,__LINE__)) +#ifndef MW_NOVERIFY +#ifndef VERIFY +#define VERIFY mwVERIFY +#endif /* !VERIFY */ +#endif /* !MW_NOVERIFY */ +#define mwTRACE mwTrace +#ifndef MW_NOTRACE +#ifndef TRACE +#define TRACE mwTRACE +#endif /* !TRACE */ +#endif /* !MW_NOTRACE */ + +/* some compilers use a define and not a function */ +/* for strdup(). */ +#ifdef strdup +#undef strdup +#endif + +#define malloc(n) mwMalloc(n,__FILE__,__LINE__) +#define strdup(p) mwStrdup(p,__FILE__,__LINE__) +#define realloc(p,n) mwRealloc(p,n,__FILE__,__LINE__) +#define calloc(n,m) mwCalloc(n,m,__FILE__,__LINE__) +#define free(p) mwFree(p,__FILE__,__LINE__) +#define CHECK() mwTest(__FILE__,__LINE__,MW_TEST_ALL) +#define CHECK_THIS(n) mwTest(__FILE__,__LINE__,n) +#define CHECK_BUFFER(b) mwTestBuffer(__FILE__,__LINE__,b) +#define MARK(p) mwMark(p,#p,__FILE__,__LINE__) +#define UNMARK(p) mwUnmark(p,__FILE__,__LINE__) + +#else /* MEMWATCH */ + +#define mwASSERT(exp) +#ifndef MW_NOASSERT +#ifndef ASSERT +#define ASSERT mwASSERT +#endif /* !ASSERT */ +#endif /* !MW_NOASSERT */ + +#define mwVERIFY(exp) exp +#ifndef MW_NOVERIFY +#ifndef VERIFY +#define VERIFY mwVERIFY +#endif /* !VERIFY */ +#endif /* !MW_NOVERIFY */ + +/*lint -esym(773,mwTRACE) */ +#define mwTRACE /*lint -save -e506 */ 1?(void)0:mwDummyTraceFunction /*lint -restore */ +#ifndef MW_NOTRACE +#ifndef TRACE +/*lint -esym(773,TRACE) */ +#define TRACE mwTRACE +#endif /* !TRACE */ +#endif /* !MW_NOTRACE */ + +extern void mwDummyTraceFunction(const char *,...); +/*lint -save -e652 */ +#define mwDoFlush(n) +#define mwPuts(s) +#define mwInit() +#define mwGrab(n) +#define mwDrop(n) +#define mwLimit(n) +#define mwTest(f,l) +#define mwSetOutFunc(f) +#define mwSetAriFunc(f) +#define mwDefaultAri() +#define mwNomansland() +#define mwStatistics(f) +#define mwMark(p,t,f,n) (p) +#define mwUnmark(p,f,n) (p) +#define mwMalloc(n,f,l) malloc(n) +#define mwStrdup(p,f,l) strdup(p) +#define mwRealloc(p,n,f,l) realloc(p,n) +#define mwCalloc(n,m,f,l) calloc(n,m) +#define mwFree(p) free(p) +#define mwMalloc_(n) malloc(n) +#define mwRealloc_(p,n) realloc(p,n) +#define mwCalloc_(n,m) calloc(n,m) +#define mwFree_(p) free(p) +#define mwAssert(e,es,f,l) +#define mwVerify(e,es,f,l) (e) +#define mwTrace mwDummyTrace +#define mwTestBuffer(f,l,b) (0) +#define CHECK() +#define CHECK_THIS(n) +#define CHECK_BUFFER(b) +#define MARK(p) (p) +#define UNMARK(p) (p) +/*lint -restore */ + +#endif /* MEMWATCH */ +#endif /* !__MEMWATCH_C */ + +#ifdef __cplusplus + } +#endif + +#if 0 /* 980317: disabled C++ */ + +/* +** C++ support section +** Implements the C++ support. Please note that in order to avoid +** messing up library classes, C++ support is disabled by default. +** You must NOT enable it until AFTER the inclusion of all header +** files belonging to code that are not compiled with MEMWATCH, and +** possibly for some that are! The reason for this is that a C++ +** class may implement it's own new() function, and the preprocessor +** would substitute this crucial declaration for MEMWATCH new(). +** You can forcibly deny C++ support by defining MEMWATCH_NOCPP. +** To enble C++ support, you must be compiling C++, MEMWATCH must +** be defined, MEMWATCH_NOCPP must not be defined, and finally, +** you must define 'new' to be 'mwNew', and 'delete' to be 'mwDelete'. +** Unlike C, C++ code can begin executing *way* before main(), for +** example if a global variable is created. For this reason, you can +** declare a global variable of the class 'MemWatch'. If this is +** is the first variable created, it will then check ALL C++ allocations +** and deallocations. Unfortunately, this evaluation order is not +** guaranteed by C++, though the compilers I've tried evaluates them +** in the order encountered. +*/ +#ifdef __cplusplus +#ifndef __MEMWATCH_C +#ifdef MEMWATCH +#ifndef MEMWATCH_NOCPP +extern int mwNCur; +extern const char *mwNFile; +extern int mwNLine; +class MemWatch { +public: + MemWatch(); + ~MemWatch(); + }; +void * operator new(size_t); +void * operator new(size_t,const char *,int); +void * operator new[] (size_t,const char *,int); // hjc 07/16/02 +void operator delete(void *); +#define mwNew new(__FILE__,__LINE__) +#define mwDelete (mwNCur=1,mwNFile=__FILE__,mwNLine=__LINE__),delete +#endif /* MEMWATCH_NOCPP */ +#endif /* MEMWATCH */ +#endif /* !__MEMWATCH_C */ +#endif /* __cplusplus */ + +#endif /* 980317: disabled C++ */ + +#endif /* __MEMWATCH_H */ + +/* EOF MEMWATCH.H */ diff --git a/resource.h b/resource.h index cbcd8dd..e685b4f 100644 --- a/resource.h +++ b/resource.h @@ -3,20 +3,28 @@ // Used by ExtIODll.rc // #define IDD_DIALOG1 5000 +#define IDD_EXTIODIALOG 5000 #define IDC_COMBO1 5001 #define IDC_SLIDER1 5002 #define IDB_BITMAP1 5003 #define IDC_SLIDER2 5003 #define IDD_DIALOG2 5004 +#define IDC_SLIDER6 5004 +#define IDD_PANADAPTERDIALOG 5004 #define IDC_SLIDER3 5005 +#define IDD_DIALOG3 5005 +#define IDD_MAINDIALOG 5005 #define IDC_SLIDER4 5006 #define IDC_EDIT1 5007 +#define IDD_ADVANCEDDIALOG 5007 #define IDC_SLIDER5 5008 #define IDC_EDIT2 5009 #define IDC_CHECK1 5010 +#define IDC_SLIDER7 5011 #define IDC_STATIC_TEST 5012 #define IDC_STATIC_EXTIOVER 5013 #define IDC_STATIC_FWVER 5014 +#define IDR_HTML_ADVANCEDDIALOG 5015 #define IDC_RADIO_CMODE1 5016 #define IDC_RADIO_CMODE2 5017 #define IDC_RADIO_CMODE3 5018 @@ -29,17 +37,20 @@ #define IDC_RADIO_CMODE6 5025 #define IDC_RADIO_CMODE7 5026 #define IDC_BUTTON1 5027 +#define IDC_RADIO_CMODE8 5028 #define IDC_CUSTOM1 5030 #define IDC_CUSTOM2 5031 #define IDC_WATERFALL 5032 +#define IDC_CHECK5 5033 +#define IDC_TAB1 5034 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 5007 +#define _APS_NEXT_RESOURCE_VALUE 5008 #define _APS_NEXT_COMMAND_VALUE 32771 -#define _APS_NEXT_CONTROL_VALUE 5033 -#define _APS_NEXT_SYMED_VALUE 5009 +#define _APS_NEXT_CONTROL_VALUE 5035 +#define _APS_NEXT_SYMED_VALUE 5016 #endif #endif