// // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions // are met: // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // * Neither the name of NVIDIA CORPORATION nor the names of its // contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY // OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. #ifndef PXPVDSDK_PXPROFILEMEMORYEVENTS_H #define PXPVDSDK_PXPROFILEMEMORYEVENTS_H #include "PxProfileEvents.h" //Memory events define their own event stream namespace physx { namespace profile { struct MemoryEventTypes { enum Enum { Unknown = 0, StringTableEvent, //introduce a new mapping of const char* -> integer AllocationEvent, DeallocationEvent, FullAllocationEvent }; }; template inline unsigned char convertToNBits( TDataType inType ) { uint8_t conversion = static_cast( inType ); PX_ASSERT( conversion < (1 << numBits) ); return conversion; } template inline unsigned char convertToTwoBits( TDataType inType ) { return convertToNBits<2>( inType ); } template inline unsigned char convertToFourBits( TDataType inType ) { return convertToNBits<4>( inType ); } inline EventStreamCompressionFlags::Enum fromNumber( uint8_t inNum ) { return static_cast( inNum ); } template inline void compileCheckSize() { PX_COMPILE_TIME_ASSERT( lhs <= rhs ); } //Used for predictable bit fields. template struct BitMaskSetter { //Create a mask that masks out the orginal value shift into place static TDataType createOffsetMask() { return TDataType(createMask() << TOffset); } //Create a mask of TNumBits number of tis static TDataType createMask() { return static_cast((1 << TNumBits) - 1); } void setValue( TDataType& inCurrent, TInputType inData ) { PX_ASSERT( inData < ( 1 << TNumBits ) ); //Create a mask to remove the current value. TDataType theMask = TDataType(~(createOffsetMask())); //Clear out current value. inCurrent = TDataType(inCurrent & theMask); //Create the new value. TDataType theAddition = static_cast( inData << TOffset ); //or it into the existing value. inCurrent = TDataType(inCurrent | theAddition); } TInputType getValue( TDataType inCurrent ) { return static_cast( ( inCurrent >> TOffset ) & createMask() ); } }; struct MemoryEventHeader { uint16_t mValue; typedef BitMaskSetter TTypeBitmask; typedef BitMaskSetter TAddrCompressBitmask; typedef BitMaskSetter TTypeCompressBitmask; typedef BitMaskSetter TFnameCompressBitmask; typedef BitMaskSetter TSizeCompressBitmask; typedef BitMaskSetter TLineCompressBitmask; //That leaves size as the only thing not compressed usually. MemoryEventHeader( MemoryEventTypes::Enum inType = MemoryEventTypes::Unknown ) : mValue( 0 ) { uint8_t defaultCompression( convertToTwoBits( EventStreamCompressionFlags::U64 ) ); TTypeBitmask().setValue( mValue, convertToFourBits( inType ) ); TAddrCompressBitmask().setValue( mValue, defaultCompression ); TTypeCompressBitmask().setValue( mValue, defaultCompression ); TFnameCompressBitmask().setValue( mValue, defaultCompression ); TSizeCompressBitmask().setValue( mValue, defaultCompression ); TLineCompressBitmask().setValue( mValue, defaultCompression ); } MemoryEventTypes::Enum getType() const { return static_cast( TTypeBitmask().getValue( mValue ) ); } #define DEFINE_MEMORY_HEADER_COMPRESSION_ACCESSOR( name ) \ void set##name( EventStreamCompressionFlags::Enum inEnum ) { T##name##Bitmask().setValue( mValue, convertToTwoBits( inEnum ) ); } \ EventStreamCompressionFlags::Enum get##name() const { return fromNumber( T##name##Bitmask().getValue( mValue ) ); } DEFINE_MEMORY_HEADER_COMPRESSION_ACCESSOR( AddrCompress ) DEFINE_MEMORY_HEADER_COMPRESSION_ACCESSOR( TypeCompress ) DEFINE_MEMORY_HEADER_COMPRESSION_ACCESSOR( FnameCompress ) DEFINE_MEMORY_HEADER_COMPRESSION_ACCESSOR( SizeCompress ) DEFINE_MEMORY_HEADER_COMPRESSION_ACCESSOR( LineCompress ) #undef DEFINE_MEMORY_HEADER_COMPRESSION_ACCESSOR bool operator==( const MemoryEventHeader& inOther ) const { return mValue == inOther.mValue; } template void streamify( TStreamType& inStream ) { inStream.streamify( "Header", mValue ); } }; //Declaration of type level getMemoryEventType function that maps enumeration event types to datatypes template inline MemoryEventTypes::Enum getMemoryEventType() { PX_ASSERT( false ); return MemoryEventTypes::Unknown; } inline bool safeStrEq( const char* lhs, const char* rhs ) { if ( lhs == rhs ) return true; //If they aren't equal, and one of them is null, //then they can't be equal. //This is assuming that the null char* is not equal to //the empty "" char*. if ( !lhs || !rhs ) return false; return ::strcmp( lhs, rhs ) == 0; } struct StringTableEvent { const char* mString; uint32_t mHandle; void init( const char* inStr = "", uint32_t inHdl = 0 ) { mString = inStr; mHandle = inHdl; } void init( const StringTableEvent& inData ) { mString = inData.mString; mHandle = inData.mHandle; } bool operator==( const StringTableEvent& inOther ) const { return mHandle == inOther.mHandle && safeStrEq( mString, inOther.mString ); } void setup( MemoryEventHeader& ) const {} template void streamify( TStreamType& inStream, const MemoryEventHeader& ) { inStream.streamify( "String", mString ); inStream.streamify( "Handle", mHandle ); } }; template<> inline MemoryEventTypes::Enum getMemoryEventType() { return MemoryEventTypes::StringTableEvent; } struct MemoryEventData { uint64_t mAddress; void init( uint64_t addr ) { mAddress = addr; } void init( const MemoryEventData& inData) { mAddress = inData.mAddress; } bool operator==( const MemoryEventData& inOther ) const { return mAddress == inOther.mAddress; } void setup( MemoryEventHeader& inHeader ) const { inHeader.setAddrCompress( findCompressionValue( mAddress ) ); } template void streamify( TStreamType& inStream, const MemoryEventHeader& inHeader ) { inStream.streamify( "Address", mAddress, inHeader.getAddrCompress() ); } }; struct AllocationEvent : public MemoryEventData { uint32_t mSize; uint32_t mType; uint32_t mFile; uint32_t mLine; void init( size_t size = 0, uint32_t type = 0, uint32_t file = 0, uint32_t line = 0, uint64_t addr = 0 ) { MemoryEventData::init( addr ); mSize = static_cast( size ); mType = type; mFile = file; mLine = line; } void init( const AllocationEvent& inData ) { MemoryEventData::init( inData ); mSize = inData.mSize; mType = inData.mType; mFile = inData.mFile; mLine = inData.mLine; } bool operator==( const AllocationEvent& inOther ) const { return MemoryEventData::operator==( inOther ) && mSize == inOther.mSize && mType == inOther.mType && mFile == inOther.mFile && mLine == inOther.mLine; } void setup( MemoryEventHeader& inHeader ) const { inHeader.setTypeCompress( findCompressionValue( mType ) ); inHeader.setFnameCompress( findCompressionValue( mFile ) ); inHeader.setSizeCompress( findCompressionValue( mSize ) ); inHeader.setLineCompress( findCompressionValue( mLine ) ); MemoryEventData::setup( inHeader ); } template void streamify( TStreamType& inStream, const MemoryEventHeader& inHeader ) { inStream.streamify( "Size", mSize, inHeader.getSizeCompress() ); inStream.streamify( "Type", mType, inHeader.getTypeCompress() ); inStream.streamify( "File", mFile, inHeader.getFnameCompress() ); inStream.streamify( "Line", mLine, inHeader.getLineCompress() ); MemoryEventData::streamify( inStream, inHeader ); } }; template<> inline MemoryEventTypes::Enum getMemoryEventType() { return MemoryEventTypes::AllocationEvent; } struct FullAllocationEvent : public MemoryEventData { size_t mSize; const char* mType; const char* mFile; uint32_t mLine; void init( size_t size, const char* type, const char* file, uint32_t line, uint64_t addr ) { MemoryEventData::init( addr ); mSize = size; mType = type; mFile = file; mLine = line; } void init( const FullAllocationEvent& inData ) { MemoryEventData::init( inData ); mSize = inData.mSize; mType = inData.mType; mFile = inData.mFile; mLine = inData.mLine; } bool operator==( const FullAllocationEvent& inOther ) const { return MemoryEventData::operator==( inOther ) && mSize == inOther.mSize && safeStrEq( mType, inOther.mType ) && safeStrEq( mFile, inOther.mFile ) && mLine == inOther.mLine; } void setup( MemoryEventHeader& ) const {} }; template<> inline MemoryEventTypes::Enum getMemoryEventType() { return MemoryEventTypes::FullAllocationEvent; } struct DeallocationEvent : public MemoryEventData { void init( uint64_t addr = 0 ) { MemoryEventData::init( addr ); } void init( const DeallocationEvent& inData ) { MemoryEventData::init( inData ); } }; template<> inline MemoryEventTypes::Enum getMemoryEventType() { return MemoryEventTypes::DeallocationEvent; } class MemoryEvent { public: typedef PX_PROFILE_UNION_5(StringTableEvent, AllocationEvent, DeallocationEvent, FullAllocationEvent, uint8_t) EventData; private: MemoryEventHeader mHeader; EventData mData; public: MemoryEvent() {} MemoryEvent( MemoryEventHeader inHeader, const EventData& inData = EventData() ) : mHeader( inHeader ) , mData( inData ) { } template MemoryEvent( const TDataType& inType ) : mHeader( getMemoryEventType() ) , mData( inType ) { //set the appropriate compression bits. inType.setup( mHeader ); } const MemoryEventHeader& getHeader() const { return mHeader; } const EventData& getData() const { return mData; } template const TDataType& getValue() const { PX_ASSERT( mHeader.getType() == getMemoryEventType() ); return mData.toType(); } template TDataType& getValue() { PX_ASSERT( mHeader.getType() == getMemoryEventType() ); return mData.toType(); } template inline TRetVal visit( TOperator inOp ) const; bool operator==( const MemoryEvent& inOther ) const { if ( !(mHeader == inOther.mHeader ) ) return false; if ( mHeader.getType() ) return inOther.visit( EventDataEqualOperator( mData ) ); return true; } }; template inline TRetVal visit( MemoryEventTypes::Enum inEventType, const MemoryEvent::EventData& inData, TOperator inOperator ) { switch( inEventType ) { case MemoryEventTypes::StringTableEvent: return inOperator( inData.toType( Type2Type() ) ); case MemoryEventTypes::AllocationEvent: return inOperator( inData.toType( Type2Type() ) ); case MemoryEventTypes::DeallocationEvent: return inOperator( inData.toType( Type2Type() ) ); case MemoryEventTypes::FullAllocationEvent: return inOperator( inData.toType( Type2Type() ) ); case MemoryEventTypes::Unknown: return inOperator( static_cast( inEventType ) ); } return TRetVal(); } template inline TRetVal MemoryEvent::visit( TOperator inOp ) const { return physx::profile::visit( mHeader.getType(), mData, inOp ); } }} #endif // PXPVDSDK_PXPROFILEMEMORYEVENTS_H