Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions rdkperf/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ INCLUDES += \

# Libraries to load
LD_FLAGS = -L$(BUILD_DIR) \
-Wl,-rpath,'$$ORIGIN' \
-lperftool \
-lrt -lpthread -lstdc++

Expand Down
118 changes: 118 additions & 0 deletions rdkperf/rdk_latency.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/**
* Copyright 2024 Comcast Cable Communications Management, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/

#include "rdk_perf_sequence.h"
#include "rdk_perf_location.h"
#include "rdk_perf_latency.h"
#include "rdk_perf_scopedlock.h"

#define REPORT_FREQUENCY 500

#ifdef USE_RDK_PERF
#include "rdk_perf.h"
#define PERF_FUNC(a) RDKPerf perf(a);
#else
#define PERF_FUNC(a)
#endif // USE_RDK_PERF

// Prototypes
bool RDKCheckSequenceCount(PerfSequence* pSeq);

void RDKLatency(const char* sequence, const char* location)
{
PERF_FUNC(__FUNCTION__);

SCOPED_LOCK();

PerfSequence* pSeq = PerfSequence::GetInstance();
if(pSeq->GetSequence(sequence)) {
pSeq->RecordLocation(location);

if(RDKCheckSequenceCount(pSeq)) {
RDKLatencyReport(sequence);
}
}
}

bool RDKCheckSequenceCount(PerfSequence* pSeq)
{
PERF_FUNC(__FUNCTION__);

static uint32_t lastCount = 0;
bool retVal = false;

uint32_t count = pSeq->GetRecordCount();
// Report every n-th location
if(count != lastCount && count % REPORT_FREQUENCY == 0) {
// Count will be the same for all the locations
// in the sequence, so only print the report when the
// last location in the sequence is recorded
retVal = true;
}
lastCount = count;

return retVal;
Comment on lines +52 to +69

Copilot AI Apr 11, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RDKCheckSequenceCount() uses a single static uint32_t lastCount for all sequences. If the process records multiple sequences, updates from one sequence will affect reporting decisions for others. This should be tracked per-sequence (e.g., keyed by sequence name) or stored on the Sequence itself.

Copilot uses AI. Check for mistakes.
}
bool RDKLatencyReportAll()
{
// For all sequences in the PerfSequence instance
PERF_FUNC(__FUNCTION__);
SCOPED_LOCK();
PerfSequence* pSeq = PerfSequence::GetInstance();
// Iterate through all sequences
// Note: This requires adding an iterator method to PerfSequence class
// For simplicity, assuming we have a method GetAllSequenceNames that returns a vector of names
std::vector<std::string> sequenceNames = pSeq->GetAllSequenceNames();
for(const auto& seqName : sequenceNames) {
RDKLatencyReport(seqName.c_str());
}
return true;
}
bool RDKLatencyReport(const char* sequence)
{
PERF_FUNC(__FUNCTION__);

SCOPED_LOCK();
FILE* fp = stderr;
//fp = fopen("/tmp/rdkperf_latency.log", "a");

PerfSequence* pSeq = PerfSequence::GetInstance();
if(pSeq->GetSequence(sequence)) {
DataRecord* pData = pSeq->GetDataRecord();
if(pData != nullptr) {
fprintf(fp, "Sequence %s Depth %d\n", pSeq->GetName(), pSeq->GetLocationsDepth());
Comment on lines +94 to +98

Copilot AI Apr 11, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GetDataRecord() allocates a DataRecord tree with new, but RDKLatencyReport() never frees it. Over time (reports every 500 records), this will leak continuously. Consider returning an owning smart pointer from GetDataRecord() or freeing the tree after printing.

Copilot uses AI. Check for mistakes.
uint8_t depth = 1;
Comment on lines +95 to +99

Copilot AI Apr 12, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RDKLatencyReport() prints a heap-allocated DataRecord chain returned by GetDataRecord() but never frees it (and advances pData so the original pointer is lost). This leaks on every report. Keep the root pointer and delete/free the chain after printing (or return an owning smart pointer/value type).

Copilot uses AI. Check for mistakes.
while(pData != nullptr) {
Comment on lines +96 to +100

Copilot AI Apr 12, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RDKLatencyReport() walks a DataRecord chain returned by PerfSequence::GetDataRecord(), but that chain is allocated with new (recursively) and is never freed. Repeated reporting will leak memory. Add an explicit cleanup/free routine for the DataRecord tree (or return an owning smart pointer / value type) after printing.

Copilot uses AI. Check for mistakes.
for(uint8_t i = 0; i <= depth; i++) {
fprintf(fp, "--");
}
fprintf(fp, "| %s >> Elapsed %0.3lf Avg %0.3lf Min %0.3lf Max %0.3lf Count %u \n",
pData->name,
(double)pData->average_elapsed / 1000.0,
(double)pData->average / 1000.0,
(double)pData->min / 1000.0,
(double)pData->max / 1000.0,
pData->count);
pData = pData->child;
depth++;
}
return true;
Comment on lines +94 to +114

Copilot AI Apr 11, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RDKLatencyReport() iterates the DataRecord chain returned by PerfSequence::GetDataRecord(), but the records are allocated with new in PerfLocation::GetDataRecord() and are never freed. This leaks memory on every report (and can grow quickly with frequent reporting). Consider returning an owned smart-pointer / stack-allocated structure, or adding a helper to free the DataRecord chain after printing.

Copilot uses AI. Check for mistakes.
}
}
return false;
}
179 changes: 179 additions & 0 deletions rdkperf/rdk_mem_tracker.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
/**
* Copyright 2021 Comcast Cable Communications Management, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <dlfcn.h>
#include <stdlib.h>
#include <stdarg.h>

#include <string>
#include <map>

#include "rdk_mem_tracker.h"

RDKMemTracker::RDKMemTracker(LogFunction pLog)
: _sequence(0)
, _lastFreed(0)
, _totalAllocatedSize(0)
, _totalAllocatedElements(0)
, _logFunc(NULL)
{
if(pLog != NULL) {
_logFunc = pLog;
}
return;
}

RDKMemTracker::~RDKMemTracker()
{
Report();

auto it = _map.begin();
while(it != _map.end()) {
delete it->second;
it++;
}
_map.clear();

return;
}

void RDKMemTracker::Allocation(void* memory, uint32_t size, const char* szFunction, const uint32_t nLine)
{
_sequence++;
AllocationData* pAlloc = (AllocationData*)calloc(1, sizeof(AllocationData));
pAlloc->size = size;
pAlloc->szFunction = szFunction;
pAlloc->nLine = nLine;
pAlloc->sequence = _sequence;

_map[memory] = pAlloc;
_totalAllocatedSize += size;
_totalAllocatedElements += 1;

return;
}

void RDKMemTracker::Free(void* memory)
{
auto it = _map.find(memory);
if(it != _map.end()) {
AllocationData* pAlloc = it->second;
if(_lastFreed != pAlloc->sequence - 1) {
// Free out of sequence
Log("Freeing memory %p of size %u out of sequence %u, last freed sequence %u\n",
memory, pAlloc->size, pAlloc->sequence, _lastFreed);
}
_lastFreed = pAlloc->sequence;
_totalAllocatedSize -= pAlloc->size;
_totalAllocatedElements -= 1;
::free(pAlloc);
_map.erase(it);
}
else {
// Error, allocation not in list
Log("ERROR: Could not find memory item %p in the allocation list\n", memory);
}

return;
}

void RDKMemTracker::Log(std::string& buffer)
{
if(_logFunc != NULL) {
_logFunc(buffer.c_str());
}
else {
fprintf(stdout, "%s", buffer.c_str());
}
}

void RDKMemTracker::Log(const char * format, ...)
{
char logMessage[LOG_MESSAGE_SIZE];

// Generate the log string
va_list ap;
va_start(ap, format);
vsnprintf(logMessage, LOG_MESSAGE_SIZE, format, ap);
va_end(ap);

std::string buffer(logMessage);
Log(buffer);
}

void RDKMemTracker::Report(bool bForce)
{
if(bForce == true || _map.size() > 0) {
// How many allocations in the map
Log("Total active allocations %u (%u)\n", _totalAllocatedElements, _map.size());

auto it = _map.begin();
while(it != _map.end()) {
AllocationData* pAlloc = it->second;
Log("Element sequence %u size %u, allocated here %s, %u\n",
pAlloc->sequence, pAlloc->size, pAlloc->szFunction, pAlloc->nLine);
it++;
}
}
}


// C interface
extern "C" {

RDKMemTrackerHandle CreateMemTrackerWithLogCallback(LogFunction pLog)
{
return static_cast<RDKMemTrackerHandle>(new RDKMemTracker(pLog));
}

RDKMemTrackerHandle CreateMemTracker()
{
return static_cast<RDKMemTrackerHandle>(new RDKMemTracker());
}

void TerminateMemTracker(RDKMemTrackerHandle hTracker)
{
delete static_cast<RDKMemTracker*>(hTracker);
return;
}

// Memory tracking
void TrackAllocation(RDKMemTrackerHandle hTracker, void* memory, uint32_t size, const char* szFunction, const uint32_t nLine)
{
static_cast<RDKMemTracker*>(hTracker)->Allocation(memory, size, szFunction, nLine);
return;
}

void TrackFree(RDKMemTrackerHandle hTracker, void* memory)
{
static_cast<RDKMemTracker*>(hTracker)->Free(memory);
return;
}

// Reporting
void MemTrackerReport(RDKMemTrackerHandle hTracker)
{
static_cast<RDKMemTracker*>(hTracker)->Report();
return;
}


} // extern "C"
Loading
Loading