Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
f98c60d
Add basic Tracy memory tracking
ccp-chargeback Apr 17, 2026
f99f8b7
Fix memory tracking in v0.11.1
ccp-chargeback Apr 27, 2026
06e4be0
Control memory tracking with a switch
ccp-chargeback Apr 28, 2026
2dd5320
Only do TracyIsStarted/Connected when tracking memory allocations
ccp-chargeback May 4, 2026
52fed36
Add CcpMemoryProfilingIsEnabled()
ccp-chargeback May 5, 2026
71cf0ad
Add a TracyTestClient class for unit tests
CCP-Aporia May 6, 2026
c4a0837
Start and stop TracyTestClient as part of the CcpTelemetryTest fixture
CCP-Aporia May 6, 2026
896d5cd
Update TracyTestClient to track zones as stacks per fiber and thread
CCP-Aporia May 6, 2026
63fb470
Add a simple test for entering / leaving a telemetry zone
CCP-Aporia May 6, 2026
56874ed
Fix segmentation fault in `SimpleZoneTest`
CCP-Aporia May 6, 2026
dfedcc7
Fix a zone leak in CcpTelemetryLeaveZone
CCP-Aporia May 7, 2026
4f110ed
Merge remote-tracking branch 'ccp-chargeback/tracy_memory_tracking' i…
CCP-Aporia May 7, 2026
b9c5317
Fix TracyTestClient when running on Windows
ccp-chargeback May 7, 2026
1d06343
Update to tracy 0.13.1
CCP-Aporia May 7, 2026
07b7397
Rewrite `TracyTestClient` to no longer rely on Tracy internal sources
CCP-Aporia May 7, 2026
e69d2db
Add preliminary Windows support to TracyTestClient
CCP-Aporia May 7, 2026
d309309
Merge remote-tracking branch 'ccp-chargeback/tracy_memory_tracking' i…
CCP-Aporia May 7, 2026
2c15aaf
Add ws2_32 to test project when linking against Windows
ccp-chargeback May 11, 2026
9af3e70
Merge branch 'main' into tracy_memory_tracking
ccp-chargeback May 11, 2026
ca46f86
Use CcpTelemetryIsConnected() for all Tracy memory alloc/dealloc oper…
ccp-chargeback May 11, 2026
da7f492
Create first draft of ReconnectAfterStop test.
ccp-chargeback May 12, 2026
6021115
Handle kQueueTerminate in TracyTestClient
ccp-chargeback May 18, 2026
f779df3
Add handling for ShutdownProfiler
ccp-chargeback May 18, 2026
3675939
Add some StartStopConnectDisconnect profiler tests
ccp-chargeback May 18, 2026
a0a4e28
Use Tracy with option 'no-crash-handler'
ccp-chargeback May 19, 2026
7072e13
Fix disconnect logic in `TracyTestClient`
CCP-Aporia May 26, 2026
ded1346
Merge branch 'main' into tracy_memory_tracking
ccp-chargeback May 26, 2026
8de74de
Revert ShutdownProfiler() changes
ccp-chargeback May 27, 2026
4ac288e
Remove debug printf statements in CcpTelemetry.cpp
ccp-chargeback May 27, 2026
8fbbc0b
Rename s_telemetryConfig back to s_config
ccp-chargeback May 27, 2026
41fdf6a
Fix wrong size being sent to Tracy in CcpPlatformCalloc
ccp-chargeback May 27, 2026
879c18d
Merge branch 'tracy_memory_tracking' of github.com:ccp-chargeback/cor…
ccp-chargeback Jun 1, 2026
52a4284
Fix multiple `-Wformat-security` instances
CCP-Aporia Jun 2, 2026
1434367
Wrap Tracy memory API calls behind CcpTelemetry methods
CCP-Aporia Jun 2, 2026
b852e0f
Add ReStartAfterStop test
ccp-chargeback Jun 3, 2026
4ccb200
Remove fprintf statements from tests/CcpTelemetry.cpp
ccp-chargeback Jun 3, 2026
0ed5a38
Add calls to base Test class SetUp() and TearDown() functions.
ccp-chargeback Jun 3, 2026
bbe02bb
Use distinct FiberNames in TestFiberSwitching test
ccp-chargeback Jun 3, 2026
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
32 changes: 10 additions & 22 deletions CCPMemory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -290,10 +290,7 @@ static inline void* CcpPlatformMalloc( size_t size )
void* p = HeapAlloc( s_heap, 0, size );
UpdateCount( size );
#if ENABLE_TELEMETRY_MEMORY_TRACKING
if ( p && CcpTelemetryIsConnected() )
{
// TracySecureAlloc( p, size );
}
CcpTelemetryTrackAllocation( p, size );
#endif
return p;
}
Expand All @@ -308,16 +305,16 @@ static inline void* CcpPlatformCalloc( size_t items, size_t size )
void* p = HeapAlloc( s_heap, HEAP_ZERO_MEMORY, bytes );
UpdateCount( bytes );
#if ENABLE_TELEMETRY_MEMORY_TRACKING
if ( p && CcpTelemetryIsConnected() )
{
// TracySecureAlloc( p, size );
}
CcpTelemetryTrackAllocation( p, bytes );
#endif
return p;
}

static inline void CcpPlatformFree( void* p )
{
#if ENABLE_TELEMETRY_MEMORY_TRACKING
CcpTelemetryTrackDeallocation( p );
#endif
UpdateCount( p, false );
HeapFree( s_heap, 0, p );
}
Expand All @@ -339,9 +336,7 @@ static inline void* CcpPlatformMalloc( size_t size )
s_memuse += realSize;

#if ENABLE_TELEMETRY_MEMORY_TRACKING
if ( CcpTelemetryIsConnected() ) {
// TracySecureAlloc( p, realSize );
}
CcpTelemetryTrackAllocation( p, realSize );
#endif
}
#if defined(__ANDROID__)
Expand All @@ -363,10 +358,7 @@ static inline void* CcpPlatformCalloc( size_t items, size_t size )
s_memuse += realSize;

#if ENABLE_TELEMETRY_MEMORY_TRACKING
if ( CcpTelemetryIsConnected() )
{
// TracySecureAlloc( p, realSize );
}
CcpTelemetryTrackAllocation( p, realSize );
#endif
}
return p;
Expand All @@ -376,6 +368,9 @@ static inline void CcpPlatformFree( void* p )
{
#if defined(__ANDROID__)
p = reinterpret_cast<size_t*>( p ) - 1;
#endif
#if ENABLE_TELEMETRY_MEMORY_TRACKING
CcpTelemetryTrackDeallocation( p );
#endif
s_memuse -= CCPMSize( p );
free( p );
Expand Down Expand Up @@ -542,13 +537,6 @@ void CCPFree( void* p )
{
if( p )
{
#if ENABLE_TELEMETRY_MEMORY_TRACKING
if ( CcpTelemetryIsConnected() )
{
// TracySecureFree( p );
}
#endif

if( s_guardAllocations )
{
CCPFreeWithGuard( p );
Expand Down
8 changes: 8 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,14 @@ if(WITH_TELEMETRY)
find_package(Tracy CONFIG REQUIRED)
target_compile_definitions(CcpCore PUBLIC CCP_TELEMETRY_ENABLED=1)
target_link_libraries(CcpCore PUBLIC Tracy::TracyClient)
# Tracy's vcpkg package is always built as Release (with NDEBUG). CcpTelemetry.cpp
# must also be compiled with NDEBUG to match Tracy's struct layout: in 0.13.1 the
# Profiler class has a debug-only member (std::atomic_bool m_inUse, #ifndef NDEBUG)
# that shifts m_programNameLock and other trailing fields by 8 bytes, causing
# SetProgramName() — inlined from TracyProfiler.hpp — to lock the wrong memory
# address and fail with EINVAL. CCP_ASSERT_ENABLED is set explicitly via CMake so
# CCP_ASSERT macros are unaffected by this NDEBUG definition.
set_source_files_properties(CcpTelemetry.cpp PROPERTIES COMPILE_DEFINITIONS NDEBUG)
else()
target_compile_definitions(CcpCore PUBLIC CCP_TELEMETRY_ENABLED=0)
endif()
Expand Down
38 changes: 34 additions & 4 deletions CcpTelemetry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ std::atomic<ProfilerState> s_profilerState{ProfilerState::Stopped};

FiberNameStore s_fiberNameStore; // Persisted fiber name string store, including the empty "root" fiber name

thread_local FiberNameStore::const_iterator t_activeFiber{ s_fiberNameStore.begin() }; // default to having no fiber
thread_local FiberNameStore::const_iterator t_activeFiber{ s_fiberNameStore.end() }; // default to having no fiber

template<>
struct std::less<FiberNameStore::const_iterator>
Expand All @@ -35,7 +35,7 @@ struct std::less<FiberNameStore::const_iterator>

typedef std::map<FiberNameStore::const_iterator, std::stack<TelemetryZone>> TaskletZoneStore;
thread_local TaskletZoneStore t_taskletZoneStore; // Per-thread record of zones instrumented from python
thread_local TaskletZoneStore::iterator t_activeTaskletZoneStore{ t_taskletZoneStore.begin() };
thread_local TaskletZoneStore::iterator t_activeTaskletZoneStore{ t_taskletZoneStore.end() };
thread_local std::set<void*> t_manuallyTrackedZones; // Keep track of zones created through `CcpTelemetryEnterZone` to ensure that we only pop off the zone store's stack when leaving a manually created zone

constexpr std::chrono::milliseconds s_cleanupDelay{5000};
Expand Down Expand Up @@ -85,6 +85,11 @@ bool CcpTelemetryIsStarted()
return s_profilerState.load( std::memory_order_acquire ) == ProfilerState::Started;
}

bool CcpMemoryProfilingIsEnabled()
{
return s_config.trackMemoryAllocations;
}

void CcpRegisterMutex( class CcpMutex& m, const char* owner, const char* name )
{
// Store the name for future Telemetry sessions, even if we're already connected.
Expand Down Expand Up @@ -214,6 +219,7 @@ void CcpTelemetryTick()
{
( *handler.first )( CCP_TELEMETRY_STOPPED, handler.second );
}
break;
}
case ProfilerState::Stopped:
// Nothing to do
Expand All @@ -224,6 +230,21 @@ void CcpTelemetryTick()
}
}

void CcpTelemetryTrackAllocation( void* p, size_t size )
{
if ( CcpMemoryProfilingIsEnabled() && CcpTelemetryIsConnected() ) {
TracySecureAlloc( p, size );
}
}

void CcpTelemetryTrackDeallocation( void* p )
{
if ( p && CcpMemoryProfilingIsEnabled() && CcpTelemetryIsConnected() )
{
TracySecureFree( p );
}
}

uint32_t CcpTelemetryGetTickCount()
{
return s_telemetryTick;
Expand Down Expand Up @@ -274,7 +295,9 @@ void CcpTelemetrySetActiveFiber( FiberNameStore::const_iterator elem )
if ( existing != t_taskletZoneStore.end() && ! ( t_taskletZoneStore.key_comp()( t_activeFiber, existing->first ) ) )
{
t_activeTaskletZoneStore = existing;
} else {
}
else
{
t_activeTaskletZoneStore = t_taskletZoneStore.emplace_hint( existing, t_activeFiber, std::stack<TelemetryZone>() );
}
// CCP_LOG_CH( s_ch, "[Fiber %p] [Store %p] Setting active tasklet zone store", t_activeFiber, t_activeTaskletZoneStore );
Expand Down Expand Up @@ -383,7 +406,9 @@ void CcpTelemetryLeaveZone( void* key )
{
t_activeTaskletZoneStore->second.pop();
}
t_manuallyTrackedZones.erase( key );
if ( t_activeTaskletZoneStore->second.empty() ) {
t_manuallyTrackedZones.erase( key );
}
}
}

Expand Down Expand Up @@ -415,6 +440,11 @@ bool CcpTelemetryIsStarted()
return false;
}

bool CcpMemoryProfilingIsEnabled()
{
return false;
}

void CcpRegisterThread( CcpThreadId_t threadId, const char* name )
{
}
Expand Down
2 changes: 1 addition & 1 deletion include/CCPLog.h
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ CARBON_CORE_API const char* GetLastErrorMessage();
// Throws a std exception with string 'message' and logs out the 'message'
inline void Throw( const char* message )
{
CCP_LOGERR( message );
CCP_LOGERR( "%s", message );
CCP_LOGWARN( "Exception thrown" );
throw std::runtime_error( message );
}
Expand Down
4 changes: 4 additions & 0 deletions include/CcpTelemetry.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ struct CcpTelemetryConfig
{
std::string applicationName;
std::chrono::milliseconds captureDuration{};
bool trackMemoryAllocations{false};
};

[[deprecated( "Use `CcpStartTelemetry( const CcpTelemetryConfig& config ) instead" )]] CARBON_CORE_API bool CcpStartTelemetry( const char* server, int connectionType, uint32_t maxThreadCount );
Expand All @@ -66,6 +67,7 @@ CARBON_CORE_API void CcpUnregisterTelemetryEventHandler( CcpOnTelemetryEventHand
CARBON_CORE_API bool CcpTelemetryIsConnectionRequested();
CARBON_CORE_API bool CcpTelemetryIsConnected();
CARBON_CORE_API bool CcpTelemetryIsStarted();
CARBON_CORE_API bool CcpMemoryProfilingIsEnabled();

CARBON_CORE_API void CcpTelemetrySetActiveFiber( const std::string& name );
CARBON_CORE_API const std::string& CcpTelemetryGetActiveFiber();
Expand Down Expand Up @@ -95,4 +97,6 @@ CARBON_CORE_API void CcpTelemetryEnterZone( void* key, const char* name, const c
CARBON_CORE_API void CcpTelemetryLeaveZone( void* key );
CARBON_CORE_API void CcpTelemetryZoneAddText( void* key, const char* text );

void CcpTelemetryTrackAllocation( void*, size_t );
void CcpTelemetryTrackDeallocation( void* );
#endif
12 changes: 6 additions & 6 deletions tests/CCPLog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class CCPLog : public ::testing::Test
TEST_F ( CCPLog, TestCanLogSingleLine )
{
CCP::RegisterLogEcho( LogTracker, CCP::LOGTYPE_INFO, true );
const char *s = "One line";
constexpr const char *s = "One line";
CCP_LOG(s);

EXPECT_EQ( 1, logstack.size() );
Expand All @@ -52,7 +52,7 @@ TEST_F ( CCPLog, TestCanLogSingleLine )
TEST_F ( CCPLog, TestDefaultLogLevelIsInfo )
{
CCP::RegisterLogEcho( LogTracker, CCP::LOGTYPE_INFO, true );
const char *s = "One line";
constexpr const char *s = "One line";
CCP_LOG(s);

EXPECT_EQ( 1, logstack.size() );
Expand Down Expand Up @@ -116,15 +116,15 @@ TEST_F ( CCPLog, CanUnregisterCallbackHandler )

TEST_F ( CCPLog, GetLastErrorMessageReturnsLastError )
{
const char* expected = "Something has gone horribly wrong.";
CCP_LOGERR(expected);
constexpr const char* expected = "Something has gone horribly wrong.";
CCP_LOGERR( expected );
const char* actual = CCP::GetLastErrorMessage();
EXPECT_STREQ( expected, actual );
}

TEST_F ( CCPLog, ThrowLastErrorThrowsAnError )
{
const char* error_str = "Something has gone horribly wrong.";
constexpr const char* error_str = "Something has gone horribly wrong.";
CCP_LOGERR( error_str );
EXPECT_ANY_THROW( {CCP::ThrowLastError();} );
}
Expand All @@ -133,7 +133,7 @@ TEST_F ( CCPLog, LogEnormousLine )
{
CCP::RegisterLogEcho( LogTracker, CCP::LOGTYPE_INFO, true );
CCP::SetLogMainThreadId();
const char* enormous_line =
constexpr const char* enormous_line =
"Testing a really long log line. The log server can't handle log lines\n"
"that are longer than 253 characters, so we need a test case for it.\n"
"Somewhere along the line we need to split the log message into several\n"
Expand Down
7 changes: 6 additions & 1 deletion tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
enable_testing()

find_package(GTest CONFIG REQUIRED)
find_package(lz4 CONFIG REQUIRED)
add_executable(CcpCoreTest
CcpAtomic.cpp
CCPCallstack.cpp
Expand All @@ -19,8 +20,12 @@ add_executable(CcpCoreTest
StringConversions.cpp
TempFile.cpp
CCPLog.cpp
TracyTestClient.cpp
)
target_link_libraries(CcpCoreTest PRIVATE CcpCore GTest::gtest GTest::gtest_main)
target_link_libraries(CcpCoreTest PRIVATE CcpCore GTest::gtest GTest::gtest_main lz4::lz4)
if(WIN32)
target_link_libraries(CcpCoreTest PRIVATE ws2_32)
endif()
if(APPLE)
target_link_libraries(CcpCoreTest PRIVATE "-framework CoreFoundation")
endif()
Expand Down
Loading