From 4339ecdbc0331b12d47574ce0f3f98cc1b94ae24 Mon Sep 17 00:00:00 2001 From: "J.A. de Jong - Redu-Sone B.V., ASCEE V.O.F" Date: Thu, 13 Jan 2022 13:59:35 +0100 Subject: [PATCH] Probable fix of memory leak --- README.md | 44 +++--- lasp/device/lasp_cppuldaq.cpp | 256 ++++++++++++++++++---------------- 2 files changed, 164 insertions(+), 136 deletions(-) diff --git a/README.md b/README.md index cdb46df..5d28a0e 100644 --- a/README.md +++ b/README.md @@ -36,20 +36,40 @@ in a sister repository in a later stage. If you have any question(s), please feel free to contact us: info@ascee.nl. -## Installation +# Building from source -### Compilation +## Dependencies -#### Archlinux +Optional dependencies, which can be turned ON/OFF using CMake: + +- Build tools, including [http://cmake.org](CMake). +- FFTW (For really fast FFT's) +- libUlDAQ, for the Measurement Computing DT9837A USB DAQ box + - GNU Autotools, for compiling libUlDAQ +- RtAudio, for Audio DAQ backends +- Cython, for wrapping the C-code to Python. +- The following Python packages need also be available: + - `Scipy` (which includes Numpy). Install with `sudo apt install + python3-scipy`, or `pacman -S scipy`. + - `appdirs`, which can be grabbed from [https://pypi.org](Pypi) + +## Installation of dependencies + + + + +## Compilation of LASP + +### Archlinux Compiling the code on Archlinux requires the following packages to be available: - openblas-lapack (AUR) -- Python 3.7 +- Python>=3.7 - Numpy (Python-numpy) - Cython -#### Ubuntu / Linux Mint +### Ubuntu / Linux Mint *Only tested with Linux Mint 18.04*, we require the following packages for compilation: @@ -62,17 +82,3 @@ compilation: - libopenblas-base - libopenblas-dev -#### Windows specific - -Tested using a Anacond / Miniconda Python environment. Please first run the following command: - -`conda install fftw` - -in case you want the *FFTW* fft backend. - -### Dependencies - -#### Ubuntu / Linux Mint - -*Only tested with Linux Mint 18.04*. The following Dependencies are required -for Ubuntu: diff --git a/lasp/device/lasp_cppuldaq.cpp b/lasp/device/lasp_cppuldaq.cpp index 067291b..b67d671 100644 --- a/lasp/device/lasp_cppuldaq.cpp +++ b/lasp/device/lasp_cppuldaq.cpp @@ -40,91 +40,91 @@ class DT9837A : public Daq { us nFramesPerBlock; -public: + public: DT9837A(const DeviceInfo &devinfo, const DaqConfiguration &config) - : Daq(devinfo, config) { + : Daq(devinfo, config) { - // Some sanity checks - if (eninchannels.size() != 4) { - throw runtime_error("Invalid length of enabled inChannels vector"); - } - - if (enoutchannels.size() != 1) { - throw runtime_error("Invalid length of enabled outChannels vector"); - } - - stopThread = false; - - nFramesPerBlock = availableFramesPerBlock[framesPerBlockIndex]; - - if (nFramesPerBlock < 24 || nFramesPerBlock > 8192) { - throw runtime_error("Unsensible number of samples per block chosen"); - } - - if (samplerate() < 10000 || samplerate() > 51000) { - throw runtime_error("Invalid sample rate"); - } - - DaqDeviceDescriptor devdescriptors[MAX_DEV_COUNT_PER_API]; - DaqDeviceDescriptor descriptor; - DaqDeviceInterface interfaceType = ANY_IFC; - - UlError err; - - us numdevs = MAX_DEV_COUNT_PER_API; - err = ulGetDaqDeviceInventory(interfaceType, devdescriptors, (unsigned*) &numdevs); - if (err != ERR_NO_ERROR) { - throw runtime_error("Device inventarization failed"); - } - - if ((us) api_specific_devindex >= numdevs) { - throw runtime_error("Device number {deviceno} too high {err}. This could " - "happen when the device is currently not connected"); - } - - descriptor = devdescriptors[api_specific_devindex]; - - // get a handle to the DAQ device associated with the first descriptor - handle = ulCreateDaqDevice(descriptor); - - if (handle == 0) { - throw runtime_error("Unable to create a handle to the specified DAQ " - "device. Is the device currently in use? Please make sure to set " - "the DAQ configuration in duplex mode if simultaneous input and " - "output is required."); - } - - err = ulConnectDaqDevice(handle); - if (err != ERR_NO_ERROR) { - showErr(err); - ulReleaseDaqDevice(handle); - handle = 0; - throw runtime_error("Unable to connect to device: {err}"); - } - - for (us ch = 0; ch < 4; ch++) { - - err = ulAISetConfigDbl(handle, AI_CFG_CHAN_SENSOR_SENSITIVITY, ch, 1.0); - showErr(err); - if (err != ERR_NO_ERROR) { - throw runtime_error("Fatal: could normalize channel sensitivity"); + // Some sanity checks + if (eninchannels.size() != 4) { + throw runtime_error("Invalid length of enabled inChannels vector"); } - CouplingMode cm = inputACCouplingMode[ch] ? CM_AC : CM_DC; - err = ulAISetConfig(handle, AI_CFG_CHAN_COUPLING_MODE, ch, cm); + if (enoutchannels.size() != 1) { + throw runtime_error("Invalid length of enabled outChannels vector"); + } + + stopThread = false; + + nFramesPerBlock = availableFramesPerBlock[framesPerBlockIndex]; + + if (nFramesPerBlock < 24 || nFramesPerBlock > 8192) { + throw runtime_error("Unsensible number of samples per block chosen"); + } + + if (samplerate() < 10000 || samplerate() > 51000) { + throw runtime_error("Invalid sample rate"); + } + + DaqDeviceDescriptor devdescriptors[MAX_DEV_COUNT_PER_API]; + DaqDeviceDescriptor descriptor; + DaqDeviceInterface interfaceType = ANY_IFC; + + UlError err; + + us numdevs = MAX_DEV_COUNT_PER_API; + err = ulGetDaqDeviceInventory(interfaceType, devdescriptors, (unsigned*) &numdevs); + if (err != ERR_NO_ERROR) { + throw runtime_error("Device inventarization failed"); + } + + if ((us) api_specific_devindex >= numdevs) { + throw runtime_error("Device number {deviceno} too high {err}. This could " + "happen when the device is currently not connected"); + } + + descriptor = devdescriptors[api_specific_devindex]; + + // get a handle to the DAQ device associated with the first descriptor + handle = ulCreateDaqDevice(descriptor); + + if (handle == 0) { + throw runtime_error("Unable to create a handle to the specified DAQ " + "device. Is the device currently in use? Please make sure to set " + "the DAQ configuration in duplex mode if simultaneous input and " + "output is required."); + } + + err = ulConnectDaqDevice(handle); if (err != ERR_NO_ERROR) { showErr(err); - throw runtime_error("Fatal: could not set AC/DC coupling mode"); + ulReleaseDaqDevice(handle); + handle = 0; + throw runtime_error("Unable to connect to device: {err}"); } - IepeMode iepe = inputIEPEEnabled[ch] ? IEPE_ENABLED : IEPE_DISABLED; - err = ulAISetConfig(handle, AI_CFG_CHAN_IEPE_MODE, ch, iepe); - if (err != ERR_NO_ERROR) { + for (us ch = 0; ch < 4; ch++) { + + err = ulAISetConfigDbl(handle, AI_CFG_CHAN_SENSOR_SENSITIVITY, ch, 1.0); showErr(err); - throw runtime_error("Fatal: could not set IEPE mode"); + if (err != ERR_NO_ERROR) { + throw runtime_error("Fatal: could normalize channel sensitivity"); + } + + CouplingMode cm = inputACCouplingMode[ch] ? CM_AC : CM_DC; + err = ulAISetConfig(handle, AI_CFG_CHAN_COUPLING_MODE, ch, cm); + if (err != ERR_NO_ERROR) { + showErr(err); + throw runtime_error("Fatal: could not set AC/DC coupling mode"); + } + + IepeMode iepe = inputIEPEEnabled[ch] ? IEPE_ENABLED : IEPE_DISABLED; + err = ulAISetConfig(handle, AI_CFG_CHAN_IEPE_MODE, ch, iepe); + if (err != ERR_NO_ERROR) { + showErr(err); + throw runtime_error("Fatal: could not set IEPE mode"); + } } } - } DT9837A(const DT9837A &) = delete; @@ -163,12 +163,12 @@ public: if (hasinput) { assert(!inbuffer); inbuffer = - new double[neninchannels() * nFramesPerBlock * 2]; // Watch the 2! + new double[neninchannels() * nFramesPerBlock * 2]; // Watch the 2! } if (hasoutput) { assert(!outbuffer); outbuffer = - new double[nenoutchannels() * nFramesPerBlock * 2]; // Watch the 2! + new double[nenoutchannels() * nFramesPerBlock * 2]; // Watch the 2! } this->inqueue = inqueue; this->outqueue = outqueue; @@ -203,6 +203,40 @@ public: friend void threadfcn(DT9837A *); }; +/** + * @brief Create an empty buffer and fill it with zeros. + * + * @param size The number of elements in the array + * + * @return Pointer to the array + */ +static double* createZeroBuffer(size_t size) { + + double* buf = static_cast( + malloc(sizeof(double) * size)); + + for (us sample = 0; sample < size; sample++) { + buf[sample] = 0; + } + return buf; +} +/** + * @brief Copy samples from one linear array to the next. + * + * @param[in] from Buffer to copy from + * @param[out] to Buffer to copy to + * @param startFrom The position to start in the from-buffer + * @param startTo The position to start in the to-buffer + * @param N The number of samples to copy. + */ +static inline void copySamples(double* from,double* to, + const us startFrom,const us startTo,const us N) { + + for (us sample = 0; sample < N; sample++) { + to[startTo + sample] = from[startFrom + sample]; + } +} + void threadfcn(DT9837A *td) { std::cerr << "Starting DAQ Thread fcn" << endl; @@ -271,9 +305,9 @@ void threadfcn(DT9837A *td) { cerr << "Starting output DAC" << endl; err = ulAOutScan(handle, 0, 0, BIP10VOLTS, - /* BIP60VOLTS, */ - 2 * td->nFramesPerBlock, // Watch the 2 here! - &samplerate, scanoptions, outscanflags, outbuffer); + /* BIP60VOLTS, */ + 2 * td->nFramesPerBlock, // Watch the 2 here! + &samplerate, scanoptions, outscanflags, outbuffer); if (err != ERR_NO_ERROR) { showErr(err); @@ -315,8 +349,8 @@ void threadfcn(DT9837A *td) { cerr << "Starting input ADC" << endl; err = ulDaqInScan(handle, indesc, neninchannels, - 2 * td->nFramesPerBlock, // Watch the 2 here! - &samplerate, scanoptions, inscanflags, inbuffer); + 2 * td->nFramesPerBlock, // Watch the 2 here! + &samplerate, scanoptions, inscanflags, inbuffer); if (err != ERR_NO_ERROR) { showErr(err); goto exit; @@ -348,9 +382,9 @@ void threadfcn(DT9837A *td) { if (outxstat.currentScanCount > outTotalCount + 2 * nFramesPerBlock) { cerr << "***** WARNING: Missing output sample blocks, DAQ Scan count=" - << outxstat.currentScanCount - << " while loop count = " << outTotalCount - << ", probably due to too small buffer size. *****" << endl; + << outxstat.currentScanCount + << " while loop count = " << outTotalCount + << ", probably due to too small buffer size. *****" << endl; } outTotalCount = outxstat.currentScanCount; @@ -362,22 +396,16 @@ void threadfcn(DT9837A *td) { if (!botoutenqueued) { /* cerr << "Copying output buffer to bottom" << endl; */ double *bufcpy; + assert(nenoutchannels > 0); if (!outqueue->empty()) { bufcpy = (double *)outqueue->dequeue(); } else { cerr << "******* WARNING: OUTPUTQUEUE UNDERFLOW, FILLING SIGNAL " - "QUEUE WITH ZEROS ***********" - << endl; - bufcpy = static_cast( - malloc(sizeof(double) * nFramesPerBlock * nenoutchannels)); - for (us sample = 0; sample < nFramesPerBlock; sample++) { - bufcpy[sample] = 0; - } - } - assert(nenoutchannels > 0); - for (us sample = 0; sample < nFramesPerBlock; sample++) { - outbuffer[buffer_mid_idx_out + sample] = bufcpy[sample]; + "QUEUE WITH ZEROS ***********" + << endl; + bufcpy = createZeroBuffer(nFramesPerBlock*nenoutchannels); } + copySamples(bufcpy, outbuffer, 0, buffer_mid_idx_out, nFramesPerBlock); free(bufcpy); botoutenqueued = true; } @@ -386,22 +414,16 @@ void threadfcn(DT9837A *td) { if (!topoutenqueued) { /* cerr << "Copying output buffer to top" << endl; */ double *bufcpy; + assert(nenoutchannels > 0); if (!outqueue->empty()) { bufcpy = (double *)outqueue->dequeue(); } else { cerr << "******* WARNING: OUTPUTQUEUE UNDERFLOW, FILLING SIGNAL " - "QUEUE WITH ZEROS ***********" - << endl; - bufcpy = static_cast( - malloc(sizeof(double) * nFramesPerBlock * nenoutchannels)); - for (us sample = 0; sample < nFramesPerBlock; sample++) { - bufcpy[sample] = 0; - } - } - assert(nenoutchannels > 0); - for (us sample = 0; sample < nFramesPerBlock; sample++) { - outbuffer[sample] = bufcpy[sample]; + "QUEUE WITH ZEROS ***********" + << endl; + bufcpy = createZeroBuffer(nFramesPerBlock*nenoutchannels); } + copySamples(bufcpy, outbuffer, 0, 0, nFramesPerBlock); free(bufcpy); topoutenqueued = true; } @@ -417,10 +439,10 @@ void threadfcn(DT9837A *td) { assert(inscanstat == SS_RUNNING); if (inxstat.currentScanCount > inTotalCount + 2 * nFramesPerBlock) { cerr << "***** ERROR: Missing input sample blocks, count=" - << inxstat.currentScanCount - << ", probably due to too small buffer size. Exiting thread. " - "*****" - << endl; + << inxstat.currentScanCount + << ", probably due to too small buffer size. Exiting thread. " + "*****" + << endl; break; } inTotalCount = inxstat.currentScanCount; @@ -434,11 +456,11 @@ void threadfcn(DT9837A *td) { us monitoroffset = monitorOutput ? 1 : 0; assert(neninchannels > 0); for (us channel = 0; channel < (neninchannels - monitoroffset); - channel++) { + channel++) { for (us sample = 0; sample < nFramesPerBlock; sample++) { bufcpy[(monitoroffset + channel) * nFramesPerBlock + sample] = - inbuffer[buffer_mid_idx_in + sample * neninchannels + - channel]; + inbuffer[buffer_mid_idx_in + sample * neninchannels + + channel]; } } if (monitorOutput) { @@ -447,7 +469,7 @@ void threadfcn(DT9837A *td) { us channel = neninchannels - 1; for (us sample = 0; sample < nFramesPerBlock; sample++) { bufcpy[sample] = inbuffer[buffer_mid_idx_in + - sample * neninchannels + channel]; + sample * neninchannels + channel]; } } inqueue->enqueue((void *)bufcpy); @@ -461,10 +483,10 @@ void threadfcn(DT9837A *td) { us monitoroffset = monitorOutput ? 1 : 0; assert(neninchannels > 0); for (us channel = 0; channel < (neninchannels - monitoroffset); - channel++) { + channel++) { for (us sample = 0; sample < nFramesPerBlock; sample++) { bufcpy[(monitoroffset + channel) * nFramesPerBlock + sample] = - inbuffer[sample * neninchannels + channel]; + inbuffer[sample * neninchannels + channel]; } } if (monitorOutput) { @@ -510,7 +532,7 @@ exit: } Daq *createUlDaqDevice(const DeviceInfo &devinfo, - const DaqConfiguration &config) { + const DaqConfiguration &config) { DT9837A *daq = NULL;