mirror of
https://github.com/eclipse-openvehicle-api/openvehicle-api.git
synced 2026-02-05 15:18:45 +00:00
571
global/tracefifo/trace_fifo.cpp
Normal file
571
global/tracefifo/trace_fifo.cpp
Normal file
@@ -0,0 +1,571 @@
|
||||
#include "trace_fifo.h"
|
||||
|
||||
// Include the platform support
|
||||
#define INCLUDE_TRACE_FIFO_PLATFORM
|
||||
#include "trace_fifo_windows.cpp"
|
||||
#include "trace_fifo_posix.cpp"
|
||||
#undef INCLUDE_TRACE_FIFO_PLATFORM
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <io.h>
|
||||
#pragma push_macro("O_TEXT")
|
||||
#define O_TEXT _O_TEXT
|
||||
#pragma push_macro("dup")
|
||||
#define dup _dup
|
||||
#pragma push_macro("dup2")
|
||||
#define dup2 _dup2
|
||||
#pragma push_macro("fileno")
|
||||
#define fileno _fileno
|
||||
#pragma push_macro("close")
|
||||
#define close _close
|
||||
#pragma push_macro("pipe")
|
||||
#define pipe _pipe
|
||||
#pragma push_macro("read")
|
||||
#define read _read
|
||||
#pragma push_macro("eof")
|
||||
#define eof _eof
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <poll.h>
|
||||
#endif
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <string>
|
||||
#include <chrono>
|
||||
|
||||
CTraceFifoBase::CTraceFifoBase(uint32_t uiInstanceID, size_t nSize) :
|
||||
m_uiInstanceID(uiInstanceID), m_nDefaultSize(nSize)
|
||||
{}
|
||||
|
||||
CTraceFifoBase::~CTraceFifoBase()
|
||||
{}
|
||||
|
||||
CTraceFifoBase::CTraceFifoBase(CTraceFifoBase&& rfifo) noexcept:
|
||||
m_uiInstanceID(rfifo.m_uiInstanceID), m_nSize(rfifo.m_nSize), m_pBuffer(rfifo.m_pBuffer), m_psHdr(rfifo.m_psHdr)
|
||||
{
|
||||
rfifo.m_pBuffer = nullptr;
|
||||
rfifo.m_psHdr = nullptr;
|
||||
}
|
||||
|
||||
CTraceFifoBase& CTraceFifoBase::operator=(CTraceFifoBase&& rfifo) noexcept
|
||||
{
|
||||
Close();
|
||||
m_uiInstanceID = rfifo.m_uiInstanceID;
|
||||
m_nSize = rfifo.m_nSize;
|
||||
m_pBuffer = rfifo.m_pBuffer;
|
||||
m_psHdr = rfifo.m_psHdr;
|
||||
rfifo.m_pBuffer = nullptr;
|
||||
rfifo.m_psHdr = nullptr;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool CTraceFifoBase::SetInstanceID(uint32_t uiInstanceID)
|
||||
{
|
||||
if (IsOpened()) return false;
|
||||
m_uiInstanceID = uiInstanceID;
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t CTraceFifoBase::GetInstanceID() const
|
||||
{
|
||||
return m_uiInstanceID;
|
||||
}
|
||||
|
||||
void CTraceFifoBase::SetDefaultSize(size_t nSize)
|
||||
{
|
||||
m_nDefaultSize = nSize;
|
||||
}
|
||||
|
||||
size_t CTraceFifoBase::GetDefaultSize() const
|
||||
{
|
||||
return m_nDefaultSize;
|
||||
}
|
||||
|
||||
size_t CTraceFifoBase::GetViewSize() const
|
||||
{
|
||||
return m_nSize;
|
||||
}
|
||||
|
||||
size_t CTraceFifoBase::GetDataBufferSize() const
|
||||
{
|
||||
return IsInitialized() ? m_nSize - sizeof(SSharedMemBufHeader) : 0;
|
||||
}
|
||||
|
||||
void CTraceFifoBase::InitializeBuffer(void* pView, size_t nBufferSize, bool bReadOnly)
|
||||
{
|
||||
std::unique_lock<std::recursive_mutex> lock(CreateAccessLockObject());
|
||||
m_pBuffer = reinterpret_cast<uint8_t*>(pView);
|
||||
m_psHdr = reinterpret_cast<SSharedMemBufHeader*>(pView);
|
||||
m_nSize = nBufferSize;
|
||||
if (!IsInitialized())
|
||||
{
|
||||
if (!bReadOnly)
|
||||
{
|
||||
std::copy_n("SDV_MON\0", 8, m_psHdr->rgszSignature);
|
||||
m_psHdr->uiInstanceID = m_uiInstanceID;
|
||||
m_psHdr->uiTxOffs = 0u;
|
||||
m_bInitConfirmed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CTraceFifoBase::IsInitialized() const
|
||||
{
|
||||
std::unique_lock<std::recursive_mutex> lock(CreateAccessLockObject());
|
||||
|
||||
// Bypass of initialization?
|
||||
if (m_bInitConfirmed) return true;
|
||||
|
||||
bool bRet = m_pBuffer && m_psHdr && m_psHdr->uiInstanceID == m_uiInstanceID &&
|
||||
std::equal(m_psHdr->rgszSignature, m_psHdr->rgszSignature + 8, "SDV_MON\0");
|
||||
m_bInitConfirmed = bRet;
|
||||
return bRet;
|
||||
}
|
||||
|
||||
void CTraceFifoBase::Terminate()
|
||||
{
|
||||
std::unique_lock<std::recursive_mutex> lock(CreateAccessLockObject());
|
||||
m_pBuffer = nullptr;
|
||||
m_psHdr = nullptr;
|
||||
m_nSize = 0;
|
||||
m_bInitConfirmed = false;
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
// Static code analysis: not releasing lock in this function is not a failure.
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 26115)
|
||||
#endif
|
||||
|
||||
std::unique_lock<std::recursive_mutex> CTraceFifoBase::CreateAccessLockObject() const
|
||||
{
|
||||
return std::unique_lock<std::recursive_mutex>(m_mtxAccess);
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
void* CTraceFifoBase::GetView()
|
||||
{
|
||||
return IsInitialized() ? m_pBuffer : 0;
|
||||
}
|
||||
|
||||
uint8_t* CTraceFifoBase::GetDataPtr()
|
||||
{
|
||||
return IsInitialized() ? m_pBuffer + sizeof(SSharedMemBufHeader) : 0;
|
||||
}
|
||||
|
||||
const uint8_t* CTraceFifoBase::GetDataPtr() const
|
||||
{
|
||||
return IsInitialized() ? m_pBuffer + sizeof(SSharedMemBufHeader) : 0;
|
||||
}
|
||||
|
||||
size_t CTraceFifoBase::GetWritePos() const
|
||||
{
|
||||
return IsInitialized() ? m_psHdr->uiTxOffs : 0;
|
||||
}
|
||||
|
||||
void CTraceFifoBase::SetWritePos(size_t nTxPos)
|
||||
{
|
||||
if (IsInitialized() && nTxPos < GetDataBufferSize())
|
||||
m_psHdr->uiTxOffs = static_cast<uint32_t>(nTxPos);
|
||||
}
|
||||
|
||||
CTraceFifoReader::CTraceFifoReader(uint32_t uiInstanceID /*= 1000u*/, size_t nSize /*= 16384*/) :
|
||||
CTraceFifoImpl(uiInstanceID, nSize)
|
||||
{}
|
||||
|
||||
CTraceFifoReader::~CTraceFifoReader()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
CTraceFifoReader::CTraceFifoReader(CTraceFifoReader&& rfifo) noexcept : CTraceFifoImpl(static_cast<CTraceFifoImpl&&>(rfifo)), m_nRxOffs(rfifo.m_nRxOffs)
|
||||
{
|
||||
rfifo.m_nRxOffs = 0;
|
||||
}
|
||||
|
||||
CTraceFifoReader& CTraceFifoReader::operator=(CTraceFifoReader&& rfifo) noexcept
|
||||
{
|
||||
CTraceFifoImpl::operator=(static_cast<CTraceFifoImpl&&>(rfifo));
|
||||
rfifo.m_nRxOffs = 0;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool CTraceFifoReader::Open(size_t nTimeout /*= 1000*/, uint32_t uiFlags /*= 0u*/)
|
||||
{
|
||||
if (IsOpened()) return true;
|
||||
std::unique_lock<std::recursive_mutex> lock(CreateAccessLockObject());
|
||||
bool bRet = CTraceFifoImpl::Open(nTimeout, uiFlags | static_cast<uint32_t>(ETraceFifoOpenFlags::read_only));
|
||||
if (bRet) m_nRxOffs = GetWritePos();
|
||||
return bRet && IsOpened();
|
||||
}
|
||||
|
||||
inline void CTraceFifoReader::Close()
|
||||
{
|
||||
std::unique_lock<std::recursive_mutex> lock(CreateAccessLockObject());
|
||||
CTraceFifoImpl::Close();
|
||||
Terminate();
|
||||
}
|
||||
|
||||
std::string CTraceFifoReader::WaitForMessage(size_t nTimeout /*= 1000*/)
|
||||
{
|
||||
if (!IsOpened())
|
||||
{
|
||||
Open(nTimeout);
|
||||
if (!IsOpened()) return {}; // Must be connected
|
||||
}
|
||||
|
||||
auto tpStart = std::chrono::high_resolution_clock::now();
|
||||
do
|
||||
{
|
||||
std::unique_lock<std::recursive_mutex> lock(CreateAccessLockObject());
|
||||
if (!IsInitialized()) break;
|
||||
|
||||
// Count the characters in the string, not including the null-character. Returns nMaxLen when no null-character has been
|
||||
// found.
|
||||
auto fnCountStr = [](const uint8_t* szStr, size_t nMaxLen)
|
||||
{
|
||||
for (size_t nCount = 0; nCount < nMaxLen; nCount++)
|
||||
if (!szStr[nCount]) return nCount;
|
||||
return nMaxLen;
|
||||
};
|
||||
|
||||
// If Rx is higher than Tx, read at the most until the end of the buffer and the rest from the start. If the Tx is smaller
|
||||
// than Tx, read at the most until Tx.
|
||||
size_t nBuffSize = GetDataBufferSize();
|
||||
uint8_t* pBuffer = GetDataPtr();
|
||||
size_t nLocalRx = m_nRxOffs;
|
||||
std::string ssMsg;
|
||||
if (nLocalRx > GetWritePos())
|
||||
{
|
||||
// Detect the length
|
||||
size_t uiMaxLen1 = nBuffSize - m_nRxOffs;
|
||||
size_t nLen1 = fnCountStr(pBuffer + m_nRxOffs, uiMaxLen1);
|
||||
size_t nMaxLen2 = GetWritePos();
|
||||
size_t nLen2 = nLen1 == uiMaxLen1 ? fnCountStr(pBuffer, nMaxLen2) : 0;
|
||||
if (nLen1 || nLen2 != nMaxLen2)
|
||||
{
|
||||
ssMsg.resize(nLen1 + nLen2);
|
||||
if (nLen1)
|
||||
std::copy(pBuffer + m_nRxOffs, pBuffer + m_nRxOffs + nLen1, ssMsg.begin());
|
||||
if (nLen2)
|
||||
std::copy(pBuffer, pBuffer + nLen2, ssMsg.begin() + nLen1);
|
||||
|
||||
// Update Rx
|
||||
m_nRxOffs = nLen2 ? nLen2 + 1 : (nLen1 == uiMaxLen1 ? 1 : m_nRxOffs + nLen1 + 1);
|
||||
|
||||
// Return result
|
||||
return ssMsg;
|
||||
}
|
||||
}
|
||||
else if (m_nRxOffs < GetWritePos())
|
||||
{
|
||||
// Copy the string
|
||||
size_t nMaxLen = GetWritePos() - m_nRxOffs;
|
||||
size_t nLen = fnCountStr(pBuffer + m_nRxOffs, nMaxLen);
|
||||
if (nLen != nMaxLen)
|
||||
{
|
||||
// Copy characters and include null-character.
|
||||
ssMsg.resize(nLen);
|
||||
std::copy(pBuffer + m_nRxOffs, pBuffer + m_nRxOffs + nLen, ssMsg.begin());
|
||||
|
||||
// Update Rx
|
||||
m_nRxOffs += nLen + 1;
|
||||
|
||||
// Return result
|
||||
return ssMsg;
|
||||
}
|
||||
}
|
||||
else if (!nTimeout)
|
||||
return {};
|
||||
else
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
} while (std::chrono::duration_cast<std::chrono::duration<size_t, std::milli>>(
|
||||
std::chrono::high_resolution_clock::now() - tpStart).count() < nTimeout);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
CTraceFifoWriter::CTraceFifoWriter(uint32_t uiInstanceID /*= 1000u*/, size_t nSize /*= 16384*/) :
|
||||
CTraceFifoImpl(uiInstanceID, nSize)
|
||||
{}
|
||||
|
||||
CTraceFifoWriter::~CTraceFifoWriter()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
CTraceFifoWriter::CTraceFifoWriter(CTraceFifoWriter&& rfifo) noexcept : CTraceFifoImpl(static_cast<CTraceFifoImpl&&>(rfifo))
|
||||
{}
|
||||
|
||||
CTraceFifoWriter& CTraceFifoWriter::operator=(CTraceFifoWriter&& rfifo) noexcept
|
||||
{
|
||||
CTraceFifoImpl::operator=(static_cast<CTraceFifoImpl&&>(rfifo));
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool CTraceFifoWriter::Open(size_t nTimeout /*= 1000*/, uint32_t uiFlags /*= 0u*/)
|
||||
{
|
||||
if (IsOpened()) return true;
|
||||
std::unique_lock<std::recursive_mutex> lock(CreateAccessLockObject());
|
||||
return CTraceFifoImpl::Open(nTimeout, uiFlags & ~static_cast<uint32_t>(ETraceFifoOpenFlags::read_only));
|
||||
}
|
||||
|
||||
void CTraceFifoWriter::Close()
|
||||
{
|
||||
std::unique_lock<std::recursive_mutex> lock(CreateAccessLockObject());
|
||||
CTraceFifoImpl::Close();
|
||||
Terminate();
|
||||
}
|
||||
|
||||
void CTraceFifoWriter::Publish(const std::string& rssMessage)
|
||||
{
|
||||
if (!IsOpened())
|
||||
{
|
||||
Open();
|
||||
if (!IsOpened()) return; // Must be connected
|
||||
}
|
||||
|
||||
std::unique_lock<std::recursive_mutex> lock(CreateAccessLockObject());
|
||||
|
||||
// Message size is limited
|
||||
size_t nBuffSize = GetDataBufferSize();
|
||||
uint8_t* pBuffer = GetDataPtr();
|
||||
if (rssMessage.size() + 2u > nBuffSize) return;
|
||||
|
||||
// Two chunks to copy:
|
||||
// 1. From start of message to end of message or end of buffer (whatever is lower).
|
||||
// 2. When leftover, from start of buffer until end of message.
|
||||
size_t nStart = GetWritePos();
|
||||
size_t nLen = rssMessage.size() + 1u;
|
||||
size_t nStop = nStart + nLen;
|
||||
size_t nLen1 = std::min(nBuffSize - nStart, nLen);
|
||||
size_t nLen2 = nLen - nLen1;
|
||||
|
||||
// Copy the message
|
||||
if (nLen1) std::copy_n(rssMessage.c_str(), nLen1, pBuffer + GetWritePos());
|
||||
if (nLen2) std::copy_n(rssMessage.c_str() + nLen1, nLen2, pBuffer);
|
||||
SetWritePos(nStop > nBuffSize ? nLen2 : nStop);
|
||||
}
|
||||
|
||||
CTraceFifoStreamBuffer::CTraceFifoStreamBuffer(uint32_t uiInstanceID /*= 1000u*/, size_t nSize /*= 16384*/) :
|
||||
CTraceFifoWriter(uiInstanceID, nSize)
|
||||
{}
|
||||
|
||||
CTraceFifoStreamBuffer::~CTraceFifoStreamBuffer()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
void CTraceFifoStreamBuffer::InterceptStream(std::ostream& rstream)
|
||||
{
|
||||
m_mapBindings.emplace(&rstream, std::make_unique<SInterceptBinding>(rstream, *this));
|
||||
}
|
||||
|
||||
void CTraceFifoStreamBuffer::RevertInterception(std::ostream& rstream)
|
||||
{
|
||||
auto itBinding = m_mapBindings.find(&rstream);
|
||||
if (itBinding != m_mapBindings.end())
|
||||
m_mapBindings.erase(itBinding);
|
||||
}
|
||||
|
||||
void CTraceFifoStreamBuffer::Close()
|
||||
{
|
||||
sync();
|
||||
m_mapBindings.clear();
|
||||
CTraceFifoWriter::Close();
|
||||
}
|
||||
|
||||
int CTraceFifoStreamBuffer::sync()
|
||||
{
|
||||
Publish(str());
|
||||
if (!m_mapBindings.empty())
|
||||
m_mapBindings.begin()->second->streamOrginal << str();
|
||||
str(std::string()); // Clear the string buffer
|
||||
return 0;
|
||||
}
|
||||
|
||||
CTraceFifoStreamBuffer::SInterceptBinding::SInterceptBinding(std::ostream& rstream, CTraceFifoStreamBuffer& rstreamBuffer) :
|
||||
rstreamIntercepted(rstream), streamOrginal(rstream.rdbuf(&rstreamBuffer))
|
||||
{}
|
||||
|
||||
CTraceFifoStreamBuffer::SInterceptBinding::~SInterceptBinding()
|
||||
{
|
||||
rstreamIntercepted.rdbuf(streamOrginal.rdbuf(nullptr));
|
||||
}
|
||||
|
||||
CTraceFifoStdBuffer::CTraceFifoStdBuffer(uint32_t uiInstanceID /*= 1000u*/, size_t nSize /*= 16384*/) :
|
||||
CTraceFifoWriter(uiInstanceID, nSize)
|
||||
{}
|
||||
|
||||
CTraceFifoStdBuffer::~CTraceFifoStdBuffer()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
bool CTraceFifoStdBuffer::Open(size_t nTimeout /*= 1000*/, uint32_t uiFlags /*= 0u*/)
|
||||
{
|
||||
// Close before...
|
||||
Close();
|
||||
|
||||
// Open the fifo
|
||||
bool bRet = CTraceFifoWriter::Open(nTimeout, uiFlags);
|
||||
if (!bRet)
|
||||
{
|
||||
std::cout << "Help -1" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create two pipes for the standard streams stdout and stderr.
|
||||
#ifdef _WIN32
|
||||
bRet = pipe(m_rgPipeStdOut, 16384, O_TEXT) == 0;
|
||||
if (bRet) bRet = pipe(m_rgPipeStdErr, 16384, O_TEXT) == 0;
|
||||
#else
|
||||
bRet = pipe(m_rgPipeStdOut) == 0;
|
||||
if (bRet) bRet = pipe(m_rgPipeStdErr) == 0;
|
||||
#endif
|
||||
|
||||
// Duplicate the StdOut and StdErr descriptors
|
||||
if (bRet)
|
||||
{
|
||||
m_iOldStdOut = dup(fileno(stdout));
|
||||
m_iOldStdErr = dup(fileno(stderr));
|
||||
if (m_iOldStdOut == -1 || m_iOldStdErr == -1)
|
||||
bRet = false;
|
||||
}
|
||||
|
||||
// Assign the pipes to the StdOut and StdErr
|
||||
if (bRet) bRet = dup2(m_rgPipeStdOut[nWriteIndex], fileno(stdout)) >= 0;
|
||||
if (bRet) bRet = dup2(m_rgPipeStdErr[nWriteIndex], fileno(stderr)) >= 0;
|
||||
|
||||
// Start the dispatch thread
|
||||
m_bShutdown = false;
|
||||
if (bRet) m_threadDispatch = std::thread(&CTraceFifoStdBuffer::DispatchThreadFunc, this);
|
||||
|
||||
if (!bRet)
|
||||
{
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
// Prevent static code analysis warning for unused return value of _dup2.
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 6031)
|
||||
#endif
|
||||
|
||||
void CTraceFifoStdBuffer::Close()
|
||||
{
|
||||
// Prevent multiple re-entries
|
||||
if (!m_bShutdown && (m_iOldStdOut != -1 || m_iOldStdErr != -1))
|
||||
{
|
||||
// Flush all streams
|
||||
std::cout.flush();
|
||||
std::clog.flush();
|
||||
std::cerr.flush();
|
||||
fflush(stdout);
|
||||
fflush(stderr);
|
||||
|
||||
// Wait for processing finishing the dispatching
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
||||
|
||||
// Reassign the existing descriptors of StdOut and StdErr.
|
||||
if (m_iOldStdOut != -1)
|
||||
dup2(m_iOldStdOut, fileno(stdout));
|
||||
if (m_iOldStdErr != -1)
|
||||
dup2(m_iOldStdErr, fileno(stderr));
|
||||
|
||||
// Shutdown the thread
|
||||
m_bShutdown = true;
|
||||
if (m_threadDispatch.joinable())
|
||||
m_threadDispatch.join();
|
||||
}
|
||||
|
||||
// Close the duplictaed desciptors
|
||||
if (m_iOldStdOut != -1)
|
||||
close(m_iOldStdOut);
|
||||
if (m_iOldStdErr != -1)
|
||||
close(m_iOldStdErr);
|
||||
if (m_rgPipeStdOut[nReadIndex] != -1)
|
||||
close(m_rgPipeStdOut[nReadIndex]);
|
||||
if (m_rgPipeStdOut[nWriteIndex] != -1)
|
||||
close(m_rgPipeStdOut[nWriteIndex]);
|
||||
if (m_rgPipeStdErr[nReadIndex] != -1)
|
||||
close(m_rgPipeStdErr[nReadIndex]);
|
||||
if (m_rgPipeStdErr[nWriteIndex] != -1)
|
||||
close(m_rgPipeStdErr[nWriteIndex]);
|
||||
m_iOldStdOut = -1;
|
||||
m_iOldStdErr = -1;
|
||||
m_rgPipeStdOut[nReadIndex] = -1;
|
||||
m_rgPipeStdOut[nWriteIndex] = -1;
|
||||
m_rgPipeStdErr[nReadIndex] = -1;
|
||||
m_rgPipeStdErr[nWriteIndex] = -1;
|
||||
|
||||
// Close the fifo
|
||||
CTraceFifoWriter::Close();
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
void CTraceFifoStdBuffer::DispatchThreadFunc()
|
||||
{
|
||||
sdv::pointer<char> ptrBuffer;
|
||||
ptrBuffer.resize(8192);
|
||||
while (!m_bShutdown)
|
||||
{
|
||||
// Read the StdOut pipe
|
||||
while (!m_bShutdown && m_rgPipeStdOut[nReadIndex] != -1)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if (eof(m_rgPipeStdOut[nReadIndex])) break;
|
||||
#else
|
||||
struct pollfd sPoll{};
|
||||
sPoll.fd = m_rgPipeStdOut[nReadIndex];
|
||||
sPoll.events = POLLIN;
|
||||
if (poll(&sPoll, 1, 1) <= 0) break;
|
||||
#endif
|
||||
int iBytesRead = read(m_rgPipeStdOut[nReadIndex], ptrBuffer.get(), static_cast<unsigned int>(ptrBuffer.size()));
|
||||
if (iBytesRead <= 0) break;
|
||||
std::string ssMsg(ptrBuffer.get(), static_cast<size_t>(iBytesRead));
|
||||
Publish(ssMsg);
|
||||
}
|
||||
|
||||
// Read the StdErr pipe
|
||||
while (!m_bShutdown && m_rgPipeStdErr[nReadIndex] != -1)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if (eof(m_rgPipeStdErr[nReadIndex])) break;
|
||||
#else
|
||||
struct pollfd sPoll{};
|
||||
sPoll.fd = m_rgPipeStdErr[nReadIndex];
|
||||
sPoll.events = POLLIN;
|
||||
if (poll(&sPoll, 1, 1) <= 0) break;
|
||||
#endif
|
||||
int iBytesRead = read(m_rgPipeStdErr[nReadIndex], ptrBuffer.get(), static_cast<unsigned int>(ptrBuffer.size()));
|
||||
if (iBytesRead <= 0) break;
|
||||
std::string ssMsg(ptrBuffer.get(), static_cast<size_t>(iBytesRead));
|
||||
Publish(ssMsg);
|
||||
}
|
||||
|
||||
// Wait 10 ms
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma pop_macro("O_TEXT")
|
||||
#pragma pop_macro("dup")
|
||||
#pragma pop_macro("dup2")
|
||||
#pragma pop_macro("fileno")
|
||||
#pragma pop_macro("close")
|
||||
#pragma pop_macro("pipe")
|
||||
#pragma pop_macro("read")
|
||||
#pragma pop_macro("eof")
|
||||
#endif
|
||||
473
global/tracefifo/trace_fifo.h
Normal file
473
global/tracefifo/trace_fifo.h
Normal file
@@ -0,0 +1,473 @@
|
||||
#ifndef TRACE_FIFO_H
|
||||
#define TRACE_FIFO_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <map>
|
||||
|
||||
/**
|
||||
* @brief trace fifo open flags
|
||||
*/
|
||||
enum class ETraceFifoOpenFlags : uint32_t
|
||||
{
|
||||
open_only = 1, ///< Open only. Do not create a new shared memory area.
|
||||
force_create = 2, ///< Create a new shared memory area, removing any previous connection.
|
||||
read_only = 4, ///< Open the shared memory for read-only access.
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Trace fifo class allowing the publishing and monitoring of trace messages.
|
||||
*/
|
||||
class CTraceFifoBase
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Default constructor
|
||||
* @param[in] uiInstanceID The instance ID to use for sending/monitoring the messages.
|
||||
* @param[in] nSize Default size of the fifo.
|
||||
*/
|
||||
CTraceFifoBase(uint32_t uiInstanceID, size_t nSize);
|
||||
|
||||
/**
|
||||
* @brief Default destructor
|
||||
* @remarks Automatically closes the fifo if opened before.
|
||||
*/
|
||||
virtual ~CTraceFifoBase();
|
||||
|
||||
/**
|
||||
* @brief Copy constructor is not available.
|
||||
* @param[in] rfifo Reference to the fifo to copy.
|
||||
*/
|
||||
CTraceFifoBase(const CTraceFifoBase& rfifo) = delete;
|
||||
|
||||
/**
|
||||
* @brief Move constructor.
|
||||
* @param[in] rfifo Reference to the fifo.
|
||||
*/
|
||||
CTraceFifoBase(CTraceFifoBase&& rfifo) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Copy assignment is not available.
|
||||
* @param[in] rfifo Reference to the fifo.
|
||||
* @return Returns a reference to this fifo.
|
||||
*/
|
||||
CTraceFifoBase& operator=(const CTraceFifoBase& rfifo) = delete;
|
||||
|
||||
/**
|
||||
* @brief Move assignment.
|
||||
* @param[in] rfifo Reference to the fifo.
|
||||
* @return Returns a reference to this fifo.
|
||||
*/
|
||||
CTraceFifoBase& operator=(CTraceFifoBase&& rfifo) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Open the fifo. Implemented by derived class.
|
||||
* @param[in] nTimeout Timeout to return from this function. A timeout of 0xffffffff does not return until a connection has been
|
||||
* established.
|
||||
* @param[in] uiFlags Zero or more flags of ETraceFifoOpenFlags enum.
|
||||
* @return Returns true when connected; false otherwise.
|
||||
*/
|
||||
virtual bool Open(size_t nTimeout = 1000, uint32_t uiFlags = 0u) = 0;
|
||||
|
||||
/**
|
||||
* @brief Cancel any running task and close an open fifo. Implemented by derived class.
|
||||
*/
|
||||
virtual void Close() = 0;
|
||||
|
||||
/**
|
||||
* @brief Is the fifo open for reading and writing? Implemented by derived class.
|
||||
* @return Returns true when opened; false otherwise.
|
||||
*/
|
||||
virtual bool IsOpened() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Set a new instance ID for the communication.
|
||||
* @remarks The instance ID can only be set when the fifo is not opened yet.
|
||||
* @param[in] uiInstanceID The new instance ID.
|
||||
* @return Returns whether setting the instance ID was successful.
|
||||
*/
|
||||
bool SetInstanceID(uint32_t uiInstanceID);
|
||||
|
||||
/**
|
||||
* @brief Return the instance ID to use for the communication.
|
||||
* @return The instance ID.
|
||||
*/
|
||||
uint32_t GetInstanceID() const;
|
||||
|
||||
/**
|
||||
* @brief Set a new default size to be used during creation of the fifo.
|
||||
* @param[in] nSize The new default size of the buffer.
|
||||
*/
|
||||
void SetDefaultSize(size_t nSize);
|
||||
|
||||
/**
|
||||
* @brief Get the default size used during creation of the fifo.
|
||||
* @return The default size of the buffer.
|
||||
*/
|
||||
size_t GetDefaultSize() const;
|
||||
|
||||
/**
|
||||
* @brief Get the size of the view.
|
||||
* @return The view size.
|
||||
*/
|
||||
size_t GetViewSize() const;
|
||||
|
||||
/**
|
||||
* @brief Get the size of the buffer without any headers.
|
||||
* @return Returns the size of the buffer or 0 when the buffer is not initialized.
|
||||
*/
|
||||
size_t GetDataBufferSize() const;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Initialize the buffer after successful opening.
|
||||
* @param[in] pView Pointer to the shared memory view.
|
||||
* @param[in] nBufferSize The size of the buffer allocated.
|
||||
* @param[in] bReadOnly When set, the buffer is initialized as read-only. Multiple read-only and only one writable buffer access
|
||||
* are allowed.
|
||||
*/
|
||||
void InitializeBuffer(void* pView, size_t nBufferSize, bool bReadOnly);
|
||||
|
||||
/**
|
||||
* @brief Check whether the buffer is initialized (signature and instance ID are set).
|
||||
* @return Returns true when the buffer has been initialized; false when not.
|
||||
*/
|
||||
bool IsInitialized() const;
|
||||
|
||||
/**
|
||||
* @brief Clear the buffer pointers
|
||||
*/
|
||||
void Terminate();
|
||||
|
||||
/**
|
||||
* @brief Create a lock object for exclusive access of the buffer.
|
||||
* @return The lock object that allows access.
|
||||
*/
|
||||
std::unique_lock<std::recursive_mutex> CreateAccessLockObject() const;
|
||||
|
||||
/**
|
||||
* @brief Get access to the view.
|
||||
* @return Get a pointer to the buffer. Returns NULL when the buffer is not initialized.
|
||||
*/
|
||||
void* GetView();
|
||||
|
||||
/**
|
||||
* @{
|
||||
* @brief Get access to the data portion of the buffer.
|
||||
* @return Get a pointer to the data. Returns NULL when the buffer is not initialized.
|
||||
*/
|
||||
uint8_t* GetDataPtr();
|
||||
const uint8_t* GetDataPtr() const;
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Get the write position.
|
||||
* @return The current write position within the buffer.
|
||||
*/
|
||||
size_t GetWritePos() const;
|
||||
|
||||
/**
|
||||
* @brief Set the write position. Can only be used by writable buffer (causing an access violation otherwise).
|
||||
* @param[in] nTxPos The new write position.
|
||||
*/
|
||||
void SetWritePos(size_t nTxPos);
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Shared memory header structure; at the front of the shared memory buffer.
|
||||
*/
|
||||
struct SSharedMemBufHeader
|
||||
{
|
||||
char rgszSignature[8]; ///< Signature "SDV_MON\0"
|
||||
uint32_t uiInstanceID; ///< Instance ID of the server instance
|
||||
volatile uint32_t uiTxOffs; ///< Tx position
|
||||
};
|
||||
|
||||
mutable volatile bool m_bInitConfirmed = false; ///< When set, bypasses the header checking.
|
||||
uint32_t m_uiInstanceID = 1000; ///< Instance ID to use while connecting.
|
||||
size_t m_nSize = 0; ///< Size of the fifo.
|
||||
size_t m_nDefaultSize = 0; ///< Requested size.
|
||||
uint8_t* m_pBuffer = nullptr; ///< Pointer to the buffer.
|
||||
SSharedMemBufHeader* m_psHdr = nullptr; ///< Shared memory header at from of the buffer.
|
||||
mutable std::recursive_mutex m_mtxAccess; ///< Protect against sudden closure.
|
||||
};
|
||||
|
||||
// Include the implementation classes classes for the trace fifo
|
||||
#include "trace_fifo_windows.h"
|
||||
#include "trace_fifo_posix.h"
|
||||
|
||||
/**
|
||||
* @brief Reader class for the trace fifo. Multiple readers can coexist.
|
||||
*/
|
||||
class CTraceFifoReader : public CTraceFifoImpl
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Default constructor
|
||||
* @param[in] uiInstanceID The instance ID to use for sending/monitoring the messages.
|
||||
* @param[in] nSize Default size of the fifo.
|
||||
*/
|
||||
CTraceFifoReader(uint32_t uiInstanceID = 1000u, size_t nSize = 16384);
|
||||
|
||||
/**
|
||||
* @brief Default destructor
|
||||
* @remarks Automatically closes the fifo if opened before.
|
||||
*/
|
||||
virtual ~CTraceFifoReader() override;
|
||||
|
||||
/**
|
||||
* @brief Copy constructor is not available.
|
||||
* @param[in] rfifo Reference to the fifo to copy.
|
||||
*/
|
||||
CTraceFifoReader(const CTraceFifoReader& rfifo) = delete;
|
||||
|
||||
/**
|
||||
* @brief Move constructor.
|
||||
* @param[in] rfifo Reference to the fifo.
|
||||
*/
|
||||
CTraceFifoReader(CTraceFifoReader&& rfifo) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Copy assignment is not available.
|
||||
* @param[in] rfifo Reference to the fifo.
|
||||
* @return Returns a reference to this fifo.
|
||||
*/
|
||||
CTraceFifoReader& operator=(const CTraceFifoReader& rfifo) = delete;
|
||||
|
||||
/**
|
||||
* @brief Move assignment.
|
||||
* @param[in] rfifo Reference to the fifo.
|
||||
* @return Returns a reference to this fifo.
|
||||
*/
|
||||
CTraceFifoReader& operator=(CTraceFifoReader&& rfifo) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Open the fifo. Override of CTraceFifoBase::Open.
|
||||
* @param[in] nTimeout Timeout to return from this function. A timeout of 0xffffffff does not return until a connection has been
|
||||
* established.
|
||||
* @param[in] uiFlags Zero or more flags of ETraceFifoOpenFlags enum.
|
||||
* @return Returns true when connected; false otherwise.
|
||||
*/
|
||||
virtual bool Open(size_t nTimeout = 1000, uint32_t uiFlags = 0u) override;
|
||||
|
||||
// Ignore cppcheck warning for not using dynamic binding when being called through the destructor.
|
||||
// cppcheck-suppress virtualCallInConstructor
|
||||
/**
|
||||
* @brief Cancel any running task and close an open fifo. Override of CTraceFifoBase::Close.
|
||||
*/
|
||||
virtual void Close() override;
|
||||
|
||||
/**
|
||||
* @brief Wait for a message.
|
||||
* @remarks Automatically opens the fifo if not opened before.
|
||||
* @param[in] nTimeout Timeout to return from this function. A timeout of 0xffffffff does not return until a message has been
|
||||
* received.
|
||||
* @return The received message or an empty message if a timeout occurred or the publisher had sent an empty message.
|
||||
*/
|
||||
std::string WaitForMessage(size_t nTimeout = 1000);
|
||||
|
||||
private:
|
||||
size_t m_nRxOffs = 0; ///< Reader position
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Writer class for a trace fifo. Only one writer should be present.
|
||||
*/
|
||||
class CTraceFifoWriter : public CTraceFifoImpl
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Default constructor
|
||||
* @param[in] uiInstanceID The instance ID to use for sending/monitoring the messages.
|
||||
* @param[in] nSize Default size of the fifo.
|
||||
*/
|
||||
CTraceFifoWriter(uint32_t uiInstanceID = 1000u, size_t nSize = 16384);
|
||||
|
||||
/**
|
||||
* @brief Default destructor
|
||||
* @remarks Automatically closes the fifo if opened before.
|
||||
*/
|
||||
virtual ~CTraceFifoWriter() override;
|
||||
|
||||
/**
|
||||
* @brief Copy constructor is not available.
|
||||
* @param[in] rfifo Reference to the fifo to copy.
|
||||
*/
|
||||
CTraceFifoWriter(const CTraceFifoWriter& rfifo) = delete;
|
||||
|
||||
/**
|
||||
* @brief Move constructor.
|
||||
* @param[in] rfifo Reference to the fifo.
|
||||
*/
|
||||
CTraceFifoWriter(CTraceFifoWriter&& rfifo) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Copy assignment is not available.
|
||||
* @param[in] rfifo Reference to the fifo.
|
||||
* @return Returns a reference to this fifo.
|
||||
*/
|
||||
CTraceFifoWriter& operator=(const CTraceFifoWriter& rfifo) = delete;
|
||||
|
||||
/**
|
||||
* @brief Move assignment.
|
||||
* @param[in] rfifo Reference to the fifo.
|
||||
* @return Returns a reference to this fifo.
|
||||
*/
|
||||
CTraceFifoWriter& operator=(CTraceFifoWriter&& rfifo) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Open the fifo. Override of CTraceFifoBase::Open.
|
||||
* @param[in] nTimeout Timeout to return from this function. A timeout of 0xffffffff does not return until a connection has been
|
||||
* established.
|
||||
* @param[in] uiFlags Zero or more flags of ETraceFifoOpenFlags enum.
|
||||
* @return Returns true when connected; false otherwise.
|
||||
*/
|
||||
virtual bool Open(size_t nTimeout = 1000, uint32_t uiFlags = 0u) override;
|
||||
|
||||
// Ignore cppcheck warning for not using dynamic binding when being called through the destructor.
|
||||
// cppcheck-suppress virtualCallInConstructor
|
||||
/**
|
||||
* @brief Cancel any running task and close an open fifo. Override of CTraceFifoBase::Close.
|
||||
*/
|
||||
virtual void Close() override;
|
||||
|
||||
/**
|
||||
* @brief Publish a message. If the buffer is full, the oldest message is removed.
|
||||
* @remarks Automatically opens the fifo if not opened before.
|
||||
* @param[in] rssMessage Reference to the message to publish.
|
||||
*/
|
||||
void Publish(const std::string& rssMessage);
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Trace fifo stream buffer object.
|
||||
* @remarks This implementation works for applications within the scope of the C++ library source compilation into one binary unit.
|
||||
* This is due to the templated nature of the C++ library allowing very little reuse of compiled code across binary units. This
|
||||
* means, that although interception will work in one unit (e.g. executable or static/shared library), it might not intercept
|
||||
* messages from another unit.
|
||||
*/
|
||||
class CTraceFifoStreamBuffer : public CTraceFifoWriter, public std::stringbuf
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor
|
||||
* @param[in] uiInstanceID The instance ID to use for sending/monitoring the messages.
|
||||
* @param[in] nSize Default size of the fifo.
|
||||
*/
|
||||
CTraceFifoStreamBuffer(uint32_t uiInstanceID = 1000u, size_t nSize = 16384);
|
||||
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
virtual ~CTraceFifoStreamBuffer() override;
|
||||
|
||||
/**
|
||||
* @brief Assign this buffer to a stream object. Any message will be streamed to both the original stream as well as the trace
|
||||
* fifo.
|
||||
* @attention The stream object needs to stay in scope until the assignment is removed or this class is destroyed.
|
||||
* @attention Works for text only.
|
||||
* @remarks Removal of this assignment is done automatically during destruction of this class.
|
||||
* @param[in] rstream Reference to the stream to intercept the communication for.
|
||||
*/
|
||||
void InterceptStream(std::ostream& rstream);
|
||||
|
||||
/**
|
||||
* @brief Remove the interception of a stream.
|
||||
* @param[in] rstream Reference to the stream to revert the interception for.
|
||||
*/
|
||||
void RevertInterception(std::ostream& rstream);
|
||||
|
||||
// Ignore cppcheck warning for not using dynamic binding when being called through the destructor.
|
||||
// cppcheck-suppress virtualCallInConstructor
|
||||
/**
|
||||
* @brief Cancel any running task and close an open fifo. Override of CTraceFifoBase::Close.
|
||||
*/
|
||||
virtual void Close() override;
|
||||
|
||||
/**
|
||||
* @brief Synchronizes the buffers with the associated character sequence.
|
||||
* @return Returns 0 if successful; -1 if not.
|
||||
*/
|
||||
int sync();
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Stream interception binding.
|
||||
*/
|
||||
struct SInterceptBinding
|
||||
{
|
||||
/**
|
||||
* @brief Constructor doing the binding.
|
||||
*/
|
||||
SInterceptBinding(std::ostream& rstream, CTraceFifoStreamBuffer& rstreamBuffer);
|
||||
|
||||
//SInterceptBinding(SInterceptBinding& rsBinding) : rstreamIntercepted(rsBinding.rstreamIntercepted), streamOrginal(rsBinding.streamOrginal.rdbuf()) {}
|
||||
|
||||
/**
|
||||
* @brief Destructor undoing the binding.
|
||||
*/
|
||||
~SInterceptBinding();
|
||||
|
||||
std::ostream& rstreamIntercepted; ///< The intercepted stream
|
||||
std::ostream streamOrginal; ///< Stream object redirecting the buffer.
|
||||
};
|
||||
|
||||
std::map<std::ostream*, std::unique_ptr<SInterceptBinding>> m_mapBindings; ///< Map with intercepted stream bindings.
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Interception and dispatching of all messages from STDOUT and STDERR to the trace fifo.
|
||||
*/
|
||||
class CTraceFifoStdBuffer : public CTraceFifoWriter
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor
|
||||
* @param[in] uiInstanceID The instance ID to use for sending/monitoring the messages.
|
||||
* @param[in] nSize Default size of the fifo.
|
||||
*/
|
||||
CTraceFifoStdBuffer(uint32_t uiInstanceID = 1000u, size_t nSize = 16384);
|
||||
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
virtual ~CTraceFifoStdBuffer() override;
|
||||
|
||||
/**
|
||||
* @brief Open the fifo and direct all messages from StdOut and StdErr to the trace fifo. Override of CTraceFifoBase::Open.
|
||||
* @param[in] nTimeout Timeout to return from this function. A timeout of 0xffffffff does not return until a connection has been
|
||||
* established.
|
||||
* @param[in] uiFlags Zero or more flags of ETraceFifoOpenFlags enum.
|
||||
* @return Returns true when connected; false otherwise.
|
||||
*/
|
||||
virtual bool Open(size_t nTimeout = 1000, uint32_t uiFlags = 0u) override;
|
||||
|
||||
// Ignore cppcheck warning for not using dynamic binding when being called through the destructor.
|
||||
// cppcheck-suppress virtualCallInConstructor
|
||||
/**
|
||||
* @brief Revert the redirection and close the open fifo. Override of CTraceFifoBase::Close.
|
||||
*/
|
||||
virtual void Close() override;
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Read from the pipe and forward the text to the trace fifo. Read until shutdown flag is switch on.
|
||||
*/
|
||||
void DispatchThreadFunc();
|
||||
|
||||
const size_t nReadIndex = 0; ///< The read index of the pipe descriptor.
|
||||
const size_t nWriteIndex = 1; ///< The write index of the pipe descriptor.
|
||||
bool m_bShutdown = false; ///< Shutdown flag for the pipe reader thread.
|
||||
int m_rgPipeStdOut[2] = { -1, -1 }; ///< StdOut pipe with read and write descriptors.
|
||||
int m_rgPipeStdErr[2] = { -1, -1 }; ///< StdErr pipe with read and write descriptors.
|
||||
int m_iOldStdOut = -1; ///< Old descriptor for the StdOut
|
||||
int m_iOldStdErr = -1; ///< Old descriptor for the StdErr
|
||||
std::thread m_threadDispatch; ///< Dispatch thread
|
||||
};
|
||||
|
||||
#endif // !defined TRACE_FIFO_H
|
||||
148
global/tracefifo/trace_fifo_posix.cpp
Normal file
148
global/tracefifo/trace_fifo_posix.cpp
Normal file
@@ -0,0 +1,148 @@
|
||||
#if defined __unix__
|
||||
|
||||
#ifndef INCLUDE_TRACE_FIFO_PLATFORM
|
||||
#error Do not include this file directly. The file is included by trace_fifo.cpp.
|
||||
#endif
|
||||
|
||||
#include "trace_fifo_posix.h"
|
||||
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <semaphore.h>
|
||||
#include <unistd.h>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <algorithm>
|
||||
#include <mutex>
|
||||
#include <support/string.h>
|
||||
|
||||
CTraceFifoPosix::CTraceFifoPosix(uint32_t uiInstanceID /*= 1000u*/, size_t nSize /*= 16*1024*/) :
|
||||
CTraceFifoBase(uiInstanceID, nSize)
|
||||
{}
|
||||
|
||||
CTraceFifoPosix::~CTraceFifoPosix()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
CTraceFifoPosix::CTraceFifoPosix(CTraceFifoPosix&& rfifo) :
|
||||
CTraceFifoBase(static_cast<CTraceFifoBase&&>(rfifo)), m_iFileDescr(rfifo.m_iFileDescr)
|
||||
{
|
||||
rfifo.m_iFileDescr = 0;
|
||||
}
|
||||
|
||||
CTraceFifoPosix& CTraceFifoPosix::operator=(CTraceFifoPosix&& rfifo)
|
||||
{
|
||||
Close();
|
||||
CTraceFifoBase::operator=(static_cast<CTraceFifoBase&&>(rfifo));
|
||||
m_iFileDescr = rfifo.m_iFileDescr;
|
||||
rfifo.m_iFileDescr = 0;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool CTraceFifoPosix::Open(size_t nTimeout /*= 1000*/, uint32_t uiFlags /*= 0u*/)
|
||||
{
|
||||
std::string ssSharedMemName = "SDV_LOG_MONITOR_" + std::to_string(GetInstanceID());
|
||||
|
||||
auto lock = CreateAccessLockObject();
|
||||
|
||||
auto tpStart = std::chrono::high_resolution_clock::now();
|
||||
bool bOpenOnly = uiFlags & static_cast<uint32_t>(ETraceFifoOpenFlags::open_only);
|
||||
bool bForceCreate = uiFlags & static_cast<uint32_t>(ETraceFifoOpenFlags::force_create);
|
||||
bool bReadOnly = uiFlags & static_cast<uint32_t>(ETraceFifoOpenFlags::read_only);
|
||||
if (bOpenOnly && bForceCreate) return false;
|
||||
|
||||
// In case of a force-create-flag, unlink a potential previous allocation.
|
||||
if (bForceCreate) shm_unlink(ssSharedMemName.c_str());
|
||||
|
||||
do
|
||||
{
|
||||
if (IsOpened()) break;
|
||||
|
||||
// Try creating the file mapping object
|
||||
if (!m_iFileDescr)
|
||||
{
|
||||
int iMapAccess = O_RDWR | O_CREAT;
|
||||
if (bOpenOnly)
|
||||
iMapAccess = bReadOnly ? O_RDONLY : O_RDWR;
|
||||
else if (bForceCreate)
|
||||
iMapAccess |= O_EXCL;
|
||||
m_iFileDescr = shm_open(ssSharedMemName.c_str(), iMapAccess, S_IRUSR | S_IWUSR);
|
||||
}
|
||||
if (m_iFileDescr == -1)
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check for the size of the memory. If zero, the memory is not initialized yet.
|
||||
struct stat sStatistics{};
|
||||
int iResult = fstat(m_iFileDescr, &sStatistics);
|
||||
if (iResult == -1)
|
||||
{
|
||||
Close();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Extend shared memory object as by default it's initialized with size 0
|
||||
if (!sStatistics.st_size)
|
||||
{
|
||||
// Readonly files cannot be truncated
|
||||
if (bReadOnly)
|
||||
{
|
||||
Close();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
continue;
|
||||
}
|
||||
|
||||
iResult = ftruncate(m_iFileDescr, static_cast<off_t>(GetDefaultSize()));
|
||||
if (iResult != -1)
|
||||
iResult = fstat(m_iFileDescr, &sStatistics);
|
||||
if (iResult == -1 || !sStatistics.st_size)
|
||||
{
|
||||
Close();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Map the file into memory
|
||||
int iViewAccess = PROT_READ;
|
||||
if (!bOpenOnly && !bReadOnly)
|
||||
iViewAccess |= PROT_WRITE;
|
||||
void* pView = mmap(NULL, sStatistics.st_size, iViewAccess, MAP_SHARED, m_iFileDescr, 0);
|
||||
if (pView == MAP_FAILED)
|
||||
{
|
||||
Close();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Needs initialization?
|
||||
InitializeBuffer(pView, sStatistics.st_size, bReadOnly);
|
||||
}
|
||||
while (std::chrono::duration_cast<std::chrono::duration<size_t, std::milli>>(
|
||||
std::chrono::high_resolution_clock::now() - tpStart).count() < nTimeout);
|
||||
|
||||
return IsOpened();
|
||||
}
|
||||
|
||||
void CTraceFifoPosix::Close()
|
||||
{
|
||||
if (GetView()) munmap(GetView(), GetViewSize());
|
||||
if (m_iFileDescr)
|
||||
close(m_iFileDescr);
|
||||
m_iFileDescr = 0;
|
||||
Terminate();
|
||||
}
|
||||
|
||||
bool CTraceFifoPosix::IsOpened() const
|
||||
{
|
||||
return m_iFileDescr ? true : false;
|
||||
}
|
||||
|
||||
#endif // defined __unix__
|
||||
82
global/tracefifo/trace_fifo_posix.h
Normal file
82
global/tracefifo/trace_fifo_posix.h
Normal file
@@ -0,0 +1,82 @@
|
||||
#if !defined TRACE_FIFO_POSIX_H && defined __unix__
|
||||
#define TRACE_FIFO_POSIX_H
|
||||
|
||||
#ifndef TRACE_FIFO_H
|
||||
#error Do not include this file directly. Include trace_fifo.h instead.
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Trace fifo shared memory class for Posix.
|
||||
*/
|
||||
class CTraceFifoPosix : public CTraceFifoBase
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Default constructor
|
||||
* @param[in] uiInstanceID The instance ID to use for sending/monitoring the messages.
|
||||
* @param[in] nSize Size of the fifo.
|
||||
*/
|
||||
CTraceFifoPosix(uint32_t uiInstanceID = 1000u, size_t nSize = 16*1024);
|
||||
|
||||
/**
|
||||
* @brief Default destructor
|
||||
* @remarks Automatically closes the fifo if opened before.
|
||||
*/
|
||||
virtual ~CTraceFifoPosix();
|
||||
|
||||
/**
|
||||
* @brief Copy constructor is not available.
|
||||
* @param[in] rfifo Reference to the fifo to copy.
|
||||
*/
|
||||
CTraceFifoPosix(const CTraceFifoPosix& rfifo) = delete;
|
||||
|
||||
/**
|
||||
* @brief Move constructor.
|
||||
* @param[in] rfifo Reference to the fifo.
|
||||
*/
|
||||
CTraceFifoPosix(CTraceFifoPosix&& rfifo);
|
||||
|
||||
/**
|
||||
* @brief Copy assignment is not available.
|
||||
* @param[in] rfifo Reference to the fifo.
|
||||
* @return Returns a reference to this fifo.
|
||||
*/
|
||||
CTraceFifoPosix& operator=(const CTraceFifoPosix& rfifo) = delete;
|
||||
|
||||
/**
|
||||
* @brief Move assignment.
|
||||
* @param[in] rfifo Reference to the fifo.
|
||||
* @return Returns a reference to this fifo.
|
||||
*/
|
||||
CTraceFifoPosix& operator=(CTraceFifoPosix&& rfifo);
|
||||
|
||||
/**
|
||||
* @brief Open the fifo. Overload of CTraceFifoBase::Open.
|
||||
* @param[in] nTimeout Timeout to return from this function. A timeout of 0xffffffff does not return until a connection has been
|
||||
* established.
|
||||
* @param[in] uiFlags Zero or more flags of ETraceFifoOpenFlags enum.
|
||||
* @return Returns true when connected; false otherwise.
|
||||
*/
|
||||
virtual bool Open(size_t nTimeout = 1000, uint32_t uiFlags = 0u) override;
|
||||
|
||||
// Ignore cppcheck warning for not using dynamic binding when being called through the destructor.
|
||||
// cppcheck-suppress virtualCallInConstructor
|
||||
/**
|
||||
* @brief Cancel any running task and close an open fifo. Overload of CTraceFifoBase::Close.
|
||||
*/
|
||||
virtual void Close() override;
|
||||
|
||||
/**
|
||||
* @brief Is the fifo open for reading and writing? Overload of CTraceFifoBase::IsOpened.
|
||||
* @return Returns true when opened; false otherwise.
|
||||
*/
|
||||
virtual bool IsOpened() const override;
|
||||
|
||||
private:
|
||||
int m_iFileDescr = 0; ///< File descriptor of the shared memory.
|
||||
};
|
||||
|
||||
/// The Posix implementation of the trace fifo.
|
||||
using CTraceFifoImpl = CTraceFifoPosix;
|
||||
|
||||
#endif // !defined TRACE_FIFO_POSIX_H
|
||||
135
global/tracefifo/trace_fifo_windows.cpp
Normal file
135
global/tracefifo/trace_fifo_windows.cpp
Normal file
@@ -0,0 +1,135 @@
|
||||
#if defined _WIN32
|
||||
|
||||
#ifndef INCLUDE_TRACE_FIFO_PLATFORM
|
||||
#error Do not include this file directly. The file is included by trace_fifo.cpp.
|
||||
#endif
|
||||
|
||||
#include "trace_fifo_windows.h"
|
||||
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <algorithm>
|
||||
#include <mutex>
|
||||
#include <support/string.h>
|
||||
|
||||
CTraceFifoWindows::CTraceFifoWindows(uint32_t uiInstanceID /*= 1000u*/, size_t nSize /*= 16*1024*/) :
|
||||
CTraceFifoBase(uiInstanceID, nSize)
|
||||
{}
|
||||
|
||||
CTraceFifoWindows::~CTraceFifoWindows()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
CTraceFifoWindows::CTraceFifoWindows(CTraceFifoWindows&& rfifo) noexcept:
|
||||
CTraceFifoBase(static_cast<CTraceFifoBase&&>(rfifo)), m_hMapFile(rfifo.m_hMapFile)
|
||||
{
|
||||
rfifo.m_hMapFile = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
CTraceFifoWindows& CTraceFifoWindows::operator=(CTraceFifoWindows&& rfifo) noexcept
|
||||
{
|
||||
Close();
|
||||
CTraceFifoBase::operator=(static_cast<CTraceFifoBase&&>(rfifo));
|
||||
m_hMapFile = rfifo.m_hMapFile;
|
||||
rfifo.m_hMapFile = INVALID_HANDLE_VALUE;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
// Prevent bogus warning about uninitialized memory for the variable *hFile.
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 6001)
|
||||
#endif
|
||||
|
||||
bool CTraceFifoWindows::Open(size_t nTimeout /*= 1000*/, uint32_t uiFlags /*= 0u*/)
|
||||
{
|
||||
std::string ssSharedMemName = "SDV_TRACE_MONITOR_" + std::to_string(GetInstanceID());
|
||||
|
||||
auto tpStart = std::chrono::high_resolution_clock::now();
|
||||
bool bOpenOnly = uiFlags & static_cast<uint32_t>(ETraceFifoOpenFlags::open_only);
|
||||
bool bForceCreate = uiFlags & static_cast<uint32_t>(ETraceFifoOpenFlags::force_create);
|
||||
bool bReadOnly = uiFlags & static_cast<uint32_t>(ETraceFifoOpenFlags::read_only);
|
||||
if (bOpenOnly && bForceCreate) return false;
|
||||
|
||||
do
|
||||
{
|
||||
if (IsOpened()) break;
|
||||
|
||||
// Try creating the file mapping object
|
||||
if (m_hMapFile == nullptr || m_hMapFile == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
if (bOpenOnly)
|
||||
{
|
||||
DWORD dwMapAccess = bReadOnly ? FILE_MAP_READ : FILE_MAP_ALL_ACCESS;
|
||||
m_hMapFile = OpenFileMappingA(dwMapAccess, false, ssSharedMemName.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
DWORD dwMapAccess = PAGE_READWRITE;
|
||||
m_hMapFile = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, dwMapAccess, 0, static_cast<DWORD>(GetDefaultSize()), ssSharedMemName.c_str());
|
||||
if (m_hMapFile != nullptr && m_hMapFile != INVALID_HANDLE_VALUE && bForceCreate && GetLastError() == ERROR_ALREADY_EXISTS)
|
||||
{
|
||||
CloseHandle(m_hMapFile);
|
||||
m_hMapFile = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (m_hMapFile == nullptr || m_hMapFile == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Map the file into memory
|
||||
DWORD dwViewAccess = bReadOnly ? FILE_MAP_READ : FILE_MAP_ALL_ACCESS;
|
||||
void* pView = MapViewOfFile(m_hMapFile, dwViewAccess, 0, 0, /*GetDefaultSize()*/0);
|
||||
if (!pView)
|
||||
{
|
||||
CloseHandle(m_hMapFile);
|
||||
m_hMapFile = INVALID_HANDLE_VALUE;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Request the size of the mapping - this will update the size...
|
||||
MEMORY_BASIC_INFORMATION sMemInfo{};
|
||||
if (VirtualQuery(pView, &sMemInfo, sizeof(sMemInfo)) != sizeof(sMemInfo) || !sMemInfo.RegionSize)
|
||||
{
|
||||
UnmapViewOfFile(pView);
|
||||
CloseHandle(m_hMapFile);
|
||||
m_hMapFile = INVALID_HANDLE_VALUE;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Needs Initialize
|
||||
InitializeBuffer(pView, sMemInfo.RegionSize, bReadOnly);
|
||||
}
|
||||
while (std::chrono::duration_cast<std::chrono::duration<size_t, std::milli>>(
|
||||
std::chrono::high_resolution_clock::now() - tpStart).count() < nTimeout);
|
||||
|
||||
return IsOpened();
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
void CTraceFifoWindows::Close()
|
||||
{
|
||||
if (GetView()) UnmapViewOfFile(GetView());
|
||||
if (m_hMapFile && m_hMapFile != INVALID_HANDLE_VALUE)
|
||||
CloseHandle(m_hMapFile);
|
||||
m_hMapFile = INVALID_HANDLE_VALUE;
|
||||
Terminate();
|
||||
}
|
||||
|
||||
bool CTraceFifoWindows::IsOpened() const
|
||||
{
|
||||
return m_hMapFile && m_hMapFile != INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
#endif // defined _WIN32
|
||||
95
global/tracefifo/trace_fifo_windows.h
Normal file
95
global/tracefifo/trace_fifo_windows.h
Normal file
@@ -0,0 +1,95 @@
|
||||
#if !defined TRACE_FIFO_WINDOWS_H && defined _WIN32
|
||||
#define TRACE_FIFO_WINDOWS_H
|
||||
|
||||
// Resolve conflict
|
||||
#pragma push_macro("interface")
|
||||
#undef interface
|
||||
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
#include <WinSock2.h>
|
||||
#include <Windows.h>
|
||||
#include <process.h>
|
||||
|
||||
// Resolve conflict
|
||||
#pragma pop_macro("interface")
|
||||
#ifdef GetClassInfo
|
||||
#undef GetClassInfo
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Trace fifo shared memory class for Windows.
|
||||
*/
|
||||
class CTraceFifoWindows : public CTraceFifoBase
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Default constructor
|
||||
* @param[in] uiInstanceID The instance ID to use for sending/monitoring the messages.
|
||||
* @param[in] nSize Size of the fifo.
|
||||
*/
|
||||
CTraceFifoWindows(uint32_t uiInstanceID = 1000u, size_t nSize = 16*1024);
|
||||
|
||||
/**
|
||||
* @brief Default destructor
|
||||
* @remarks Automatically closes the fifo if opened before.
|
||||
*/
|
||||
virtual ~CTraceFifoWindows() override;
|
||||
|
||||
/**
|
||||
* @brief Copy constructor is not available.
|
||||
* @param[in] rfifo Reference to the fifo to copy.
|
||||
*/
|
||||
CTraceFifoWindows(const CTraceFifoWindows& rfifo) = delete;
|
||||
|
||||
/**
|
||||
* @brief Move constructor.
|
||||
* @param[in] rfifo Reference to the fifo.
|
||||
*/
|
||||
CTraceFifoWindows(CTraceFifoWindows&& rfifo) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Copy assignment is not available.
|
||||
* @param[in] rfifo Reference to the fifo.
|
||||
* @return Returns a reference to this fifo.
|
||||
*/
|
||||
CTraceFifoWindows& operator=(const CTraceFifoWindows& rfifo) = delete;
|
||||
|
||||
/**
|
||||
* @brief Move assignment.
|
||||
* @param[in] rfifo Reference to the fifo.
|
||||
* @return Returns a reference to this fifo.
|
||||
*/
|
||||
CTraceFifoWindows& operator=(CTraceFifoWindows&& rfifo) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Open the fifo. Override of CTraceFifoBase::Open.
|
||||
* @param[in] nTimeout Timeout to return from this function. A timeout of 0xffffffff does not return until a connection has been
|
||||
* established.
|
||||
* @param[in] uiFlags Zero or more flags of ETraceFifoOpenFlags enum.
|
||||
* @return Returns true when connected; false otherwise.
|
||||
*/
|
||||
virtual bool Open(size_t nTimeout = 1000, uint32_t uiFlags = 0u) override;
|
||||
|
||||
// Ignore cppcheck warning for not using dynamic binding when being called through the destructor.
|
||||
// cppcheck-suppress virtualCallInConstructor
|
||||
/**
|
||||
* @brief Cancel any running task and close an open fifo. Override of CTraceFifoBase::Close.
|
||||
*/
|
||||
virtual void Close() override;
|
||||
|
||||
/**
|
||||
* @brief Is the fifo open for reading and writing? Override of CTraceFifoBase::IsOpened.
|
||||
* @return Returns true when opened; false otherwise.
|
||||
*/
|
||||
virtual bool IsOpened() const override;
|
||||
|
||||
private:
|
||||
HANDLE m_hMapFile = INVALID_HANDLE_VALUE; ///< Handle to the shared memory buffer.
|
||||
};
|
||||
|
||||
/// The Windows implementation of the trace fifo.
|
||||
using CTraceFifoImpl = CTraceFifoWindows;
|
||||
|
||||
#endif // !defined TRACE_FIFO_WINDOWS_H
|
||||
Reference in New Issue
Block a user