diff --git a/pykd/customtypes.cpp b/pykd/customtypes.cpp new file mode 100644 index 0000000..9ae22ac --- /dev/null +++ b/pykd/customtypes.cpp @@ -0,0 +1,98 @@ + +#include "stdafx.h" +#include "customtypes.h" + +//////////////////////////////////////////////////////////////////////////////// + +namespace pykd { + +//////////////////////////////////////////////////////////////////////////////// + +TypeInfoPtr CustomStruct::create(const std::string &name, ULONG alignReq /*= 0*/) +{ + return TypeInfoPtr( new CustomStruct(name, alignReq) ); +} + +//////////////////////////////////////////////////////////////////////////////// + +ULONG CustomStruct::getSize() +{ + if (UdtFieldColl::empty()) + return 0; + + UdtUtils::Field &field = UdtFieldColl::last(); + return field.m_offset + field.m_type->getSize(); +} + +//////////////////////////////////////////////////////////////////////////////// + +void CustomStruct::appendField(const std::string &fieldName, TypeInfoPtr fieldType) +{ + bool fieldExist = false; + try + { + lookupField(fieldName); + fieldExist = true; + } + catch (const TypeException &except) + { + DBG_UNREFERENCED_PARAMETER(except); + } + if (fieldExist) + throw TypeException(getName(), "duplicate field name: " + fieldName); + + ULONG offset = getSize(); + offset += (offset % m_alignReq); + UdtFieldColl::push_back( + UdtUtils::Field(offset, fieldName, fieldType) + ); +} + +//////////////////////////////////////////////////////////////////////////////// + +TypeInfoPtr CustomUnion::create(const std::string &name) +{ + return TypeInfoPtr( new CustomUnion(name) ); +} + +//////////////////////////////////////////////////////////////////////////////// + +ULONG CustomUnion::getSize() +{ + ULONG size = 0; + for (ULONG i = 0; i < getFieldCount(); ++i) + { + ULONG fieldSize = lookupField(i).m_type->getSize(); + if (fieldSize > size) + size = fieldSize; + } + return size; +} + +//////////////////////////////////////////////////////////////////////////////// + +void CustomUnion::appendField(const std::string &fieldName, TypeInfoPtr fieldType) +{ + bool fieldExist = false; + try + { + lookupField(fieldName); + fieldExist = true; + } + catch (const TypeException &except) + { + DBG_UNREFERENCED_PARAMETER(except); + } + if (fieldExist) + throw TypeException(getName(), "duplicate field name: " + fieldName); + + UdtFieldColl::push_back( + UdtUtils::Field(0, fieldName, fieldType) + ); +} + +//////////////////////////////////////////////////////////////////////////////// + +} // namespace pykd + +//////////////////////////////////////////////////////////////////////////////// diff --git a/pykd/customtypes.h b/pykd/customtypes.h new file mode 100644 index 0000000..199f559 --- /dev/null +++ b/pykd/customtypes.h @@ -0,0 +1,69 @@ + +#pragma once + +//////////////////////////////////////////////////////////////////////////////// + +#include "typeinfo.h" +#include "win\dbgeng.h" + +//////////////////////////////////////////////////////////////////////////////// + +namespace pykd { + +//////////////////////////////////////////////////////////////////////////////// + +class CustomStruct : public UdtFieldColl +{ +public: + static TypeInfoPtr create(const std::string &name, ULONG alignReq = 0); + +protected: + CustomStruct(const std::string &name, ULONG alignReq) + : UdtFieldColl(name), m_name(name), m_alignReq(alignReq ? alignReq : ptrSize()) + { + } + + virtual std::string getName() override { + return m_name; + } + + virtual ULONG getSize() override; + + virtual void appendField(const std::string &fieldName, TypeInfoPtr fieldType) override; + + virtual std::string getTypeString() const override { + return "custom struct"; + } + +private: + std::string m_name; + ULONG m_alignReq; +}; + +//////////////////////////////////////////////////////////////////////////////// + +class CustomUnion : public UdtFieldColl +{ +public: + static TypeInfoPtr create(const std::string &name); + +protected: + CustomUnion(const std::string &name) : UdtFieldColl(name) {} + + virtual ULONG getSize() override; + + virtual void appendField(const std::string &fieldName, TypeInfoPtr fieldType) override; + + virtual std::string getTypeString() const override { + return "custom union"; + } + +private: + std::string m_name; +}; + +//////////////////////////////////////////////////////////////////////////////// + +} // namespace pykd + +//////////////////////////////////////////////////////////////////////////////// diff --git a/pykd/pykd_2008.vcproj b/pykd/pykd_2008.vcproj index 24fdecb..b9f14b2 100644 --- a/pykd/pykd_2008.vcproj +++ b/pykd/pykd_2008.vcproj @@ -361,6 +361,10 @@ RelativePath=".\cpureg.cpp" > + + @@ -467,6 +471,10 @@ RelativePath=".\cpureg.h" > + + diff --git a/pykd/pymod.cpp b/pykd/pymod.cpp index b2e8fad..d43e39b 100644 --- a/pykd/pymod.cpp +++ b/pykd/pymod.cpp @@ -14,6 +14,7 @@ #include "dbgexcept.h" #include "dbgmem.h" #include "typeinfo.h" +#include "customtypes.h" #include "typedvar.h" #include "cpureg.h" #include "disasm.h" @@ -59,6 +60,8 @@ BOOST_PYTHON_FUNCTION_OVERLOADS( getSourceFile_, getSourceFile, 0, 1 ); BOOST_PYTHON_FUNCTION_OVERLOADS( setSoftwareBp_, setSoftwareBp, 1, 2 ); BOOST_PYTHON_FUNCTION_OVERLOADS( setHardwareBp_, setHardwareBp, 3, 4 ); +BOOST_PYTHON_FUNCTION_OVERLOADS( CustomStruct_create, CustomStruct::create, 1, 2 ); + BOOST_PYTHON_MODULE( pykd ) { python::scope().attr("version") = pykdVersion; @@ -231,6 +234,12 @@ BOOST_PYTHON_MODULE( pykd ) python::def( "removeAllBp", &breakPointRemoveAll, "Remove all breapoints" ); + // custom types + python::def( "createStruct", &CustomStruct::create, CustomStruct_create( python::args( "name", "alignReq" ), + "Create empty structure. Use append() method for building" ) ); + python::def( "createUnion", &CustomUnion::create, + "Create empty union. Use append() method for building" ); + python::class_( "intBase", "intBase", python::no_init ) .def( python::init() ) .def( "__eq__", &intBase::eq ) @@ -334,6 +343,7 @@ BOOST_PYTHON_MODULE( pykd ) .def( "field", &TypeInfo::getField ) .def( "asMap", &TypeInfo::asMap ) .def( "deref", &TypeInfo::deref ) + .def( "append", &TypeInfo::appendField ) .def( "__str__", &TypeInfo::print ) .def( "__getattr__", &TypeInfo::getField ) .def("__len__", &TypeInfo::getElementCount ) diff --git a/pykd/typeinfo.cpp b/pykd/typeinfo.cpp index 965e583..95b3544 100644 --- a/pykd/typeinfo.cpp +++ b/pykd/typeinfo.cpp @@ -557,10 +557,45 @@ ULONG64 TypeInfo::getStaticOffset() ///////////////////////////////////////////////////////////////////////////////////// -ULONG UdtTypeInfo::getFieldCount() +std::string UdtFieldColl::print() { - refreshFields(); - return (ULONG)m_fields.size(); + std::stringstream sstr; + + sstr << getTypeString() << ": " << getName() << " Size: 0x" << std::hex << getSize() << " (" << std::dec << getSize() << ")" << std::endl; + + ULONG fieldCount = getFieldCount(); + + for ( ULONG i = 0; i < fieldCount; ++i ) + { + const UdtUtils::Field &udtField = lookupField(i); + TypeInfoPtr fieldType = udtField.m_type; + + if ( fieldType->isStaticMember() ) + { + sstr << " =" << std::right << std::setw(10) << std::setfill('0') << std::hex << fieldType->getStaticOffset(); + sstr << " " << std::left << std::setw(18) << std::setfill(' ') << udtField.m_name << ':'; + } + else + if ( fieldType->isVirtualMember() ) + { + ULONG virtualBasePtr, virtualDispIndex, virtualDispSize; + fieldType->getVirtualDisplacement( virtualBasePtr, virtualDispIndex, virtualDispSize ); + + sstr << " virtual base " << fieldType->getVirtualBase()->getName(); + sstr << " +" << std::right << std::setw(4) << std::setfill('0') << std::hex << udtField.m_offset; + sstr << " " << udtField.m_name << ':'; + } + else + { + sstr << " +" << std::right << std::setw(4) << std::setfill('0') << std::hex << udtField.m_offset; + sstr << " " << std::left << std::setw(24) << std::setfill(' ') << udtField.m_name << ':'; + } + + sstr << " " << std::left << fieldType->getName(); + sstr << std::endl; + } + + return sstr.str(); } ///////////////////////////////////////////////////////////////////////////////////// @@ -615,7 +650,9 @@ void UdtTypeInfo::getFields( break; } - m_fields.push_back( UdtUtils::Field( fieldOffset, childSym->getName(), ti ) ); + UdtFieldColl::push_back( + UdtUtils::Field( fieldOffset, childSym->getName(), ti ) + ); } else if ( symTag == SymTagVTable ) @@ -632,7 +669,7 @@ void UdtTypeInfo::getFields( } - m_fields.push_back( + UdtFieldColl::push_back( UdtUtils::Field( startOffset + childSym->getOffset(), "__VFN_table", ti ) ); } @@ -666,7 +703,7 @@ void UdtTypeInfo::getVirtualFields() void UdtTypeInfo::refreshFields() { - if ( m_fields.empty() ) + if ( UdtFieldColl::empty() ) { getFields( m_dia, SymbolPtr() ); getVirtualFields(); @@ -675,49 +712,6 @@ void UdtTypeInfo::refreshFields() ///////////////////////////////////////////////////////////////////////////////////// -std::string UdtTypeInfo::print() -{ - std::stringstream sstr; - - sstr << "struct/class: " << getName() << " Size: 0x" << std::hex << getSize() << " (" << std::dec << getSize() << ")" << std::endl; - - ULONG fieldCount = getFieldCount(); - - for ( ULONG i = 0; i < fieldCount; ++i ) - { - const UdtUtils::Field &udtField = lookupField(i); - TypeInfoPtr fieldType = udtField.m_type; - - if ( fieldType->isStaticMember() ) - { - sstr << " =" << std::right << std::setw(10) << std::setfill('0') << std::hex << fieldType->getStaticOffset(); - sstr << " " << std::left << std::setw(18) << std::setfill(' ') << udtField.m_name << ':'; - } - else - if ( fieldType->isVirtualMember() ) - { - ULONG virtualBasePtr, virtualDispIndex, virtualDispSize; - fieldType->getVirtualDisplacement( virtualBasePtr, virtualDispIndex, virtualDispSize ); - - sstr << " virtual base " << fieldType->getVirtualBase()->getName(); - sstr << " +" << std::right << std::setw(4) << std::setfill('0') << std::hex << udtField.m_offset; - sstr << " " << udtField.m_name << ':'; - } - else - { - sstr << " +" << std::right << std::setw(4) << std::setfill('0') << std::hex << udtField.m_offset; - sstr << " " << std::left << std::setw(24) << std::setfill(' ') << udtField.m_name << ':'; - } - - sstr << " " << std::left << fieldType->getName(); - sstr << std::endl; - } - - return sstr.str(); -} - -///////////////////////////////////////////////////////////////////////////////////// - TypeInfoPtr EnumTypeInfo::getField( const std::string &fieldName ) { return TypeInfo::getTypeInfo( m_dia, fieldName ); diff --git a/pykd/typeinfo.h b/pykd/typeinfo.h index 9a627eb..d3fe052 100644 --- a/pykd/typeinfo.h +++ b/pykd/typeinfo.h @@ -157,6 +157,10 @@ public: throw PyException( PyExc_TypeError, "object is unsubscriptable"); } + virtual void appendField(const std::string &fieldName, TypeInfoPtr fieldType) { + throw TypeException( getName(), "type is not is not extensible" ); + } + void setConstant( const BaseTypeVariant& var ) { m_constant = true; @@ -298,24 +302,11 @@ private: /////////////////////////////////////////////////////////////////////////////////// -class UdtTypeInfo : public TypeInfo +class UdtFieldColl : public TypeInfo { -public: - - UdtTypeInfo (SymbolPtr &symbol ) : - m_dia( symbol ), - m_fields( symbol->getName() ) - { - } - protected: - virtual std::string getName() { - return m_dia->getName(); - } - - virtual ULONG getSize() { - return (ULONG)m_dia->getSize(); + return m_fields.getName(); } virtual TypeInfoPtr getField( const std::string &fieldName ) { @@ -331,7 +322,7 @@ protected: } virtual ULONG getFieldOffsetByNameRecirsive( const std::string &fieldName ) { - return UdtUtils::getFiledOffsetRecirsive( shared_from_this(), fieldName ); + return UdtUtils::getFieldOffsetRecirsive( shared_from_this(), fieldName ); } virtual ULONG getFieldOffsetByNameNotRecursively( const std::string &fieldName ) { @@ -342,10 +333,9 @@ protected: return lookupField(index).m_offset; } - virtual ULONG getFieldCount(); - - virtual bool isUserDefined() { - return true; + virtual ULONG getFieldCount() { + refreshFields(); + return (ULONG)m_fields.size(); } virtual ULONG getElementCount() { @@ -358,9 +348,61 @@ protected: virtual std::string print(); - SymbolPtr m_dia; + virtual bool isUserDefined() { + return true; + } - UdtUtils::FieldCollection m_fields; +protected: + UdtFieldColl(const std::string &typeName) : m_fields(typeName) {} + + virtual void refreshFields() {} + virtual std::string getTypeString() const = 0; + + bool empty() const { + return m_fields.empty(); + } + + void push_back(const UdtUtils::Field &field) { + m_fields.push_back(field); + } + + template + const UdtUtils::Field &lookupField( T index) { + refreshFields(); + return m_fields.lookup(index); + } + + UdtUtils::Field &last(){ + return *m_fields.rbegin(); + } + +private: + UdtUtils::FieldCollection m_fields; +}; + +/////////////////////////////////////////////////////////////////////////////////// + +class UdtTypeInfo : public UdtFieldColl +{ +public: + + UdtTypeInfo (SymbolPtr &symbol ) : + UdtFieldColl( symbol->getName() ), + m_dia( symbol ) + { + } + +protected: + virtual ULONG getSize() { + return (ULONG)m_dia->getSize(); + } + void getVirtualFields(); + + virtual void refreshFields() override; + + virtual std::string getTypeString() const override { + return "struct/class"; + } void getFields( SymbolPtr &rootSym, @@ -371,16 +413,8 @@ protected: ULONG m_virtualDispSize = 0 ); - void getVirtualFields(); - private: - void refreshFields(); - - template - const UdtUtils::Field &lookupField( T index) { - refreshFields(); - return m_fields.lookup(index); - } + SymbolPtr m_dia; }; /////////////////////////////////////////////////////////////////////////////////// diff --git a/pykd/udtutils.cpp b/pykd/udtutils.cpp index 7ff0efe..5b8fc01 100644 --- a/pykd/udtutils.cpp +++ b/pykd/udtutils.cpp @@ -36,7 +36,7 @@ const Field &FieldCollection::lookup(const std::string &name) const /////////////////////////////////////////////////////////////////////////////////// -ULONG getFiledOffsetRecirsive(TypeInfoPtr typeInfo, const std::string &fieldName) +ULONG getFieldOffsetRecirsive(TypeInfoPtr typeInfo, const std::string &fieldName) { // "m_field1.m_field2" -> ["m_field1", "m_field2"] typedef boost::char_separator CharSep; diff --git a/pykd/udtutils.h b/pykd/udtutils.h index b32a818..29aec43 100644 --- a/pykd/udtutils.h +++ b/pykd/udtutils.h @@ -46,13 +46,17 @@ public: const Field &lookup(ULONG index) const; const Field &lookup(const std::string &name) const; + const std::string &getName() const { + return m_baseTypeName; + } + private: std::string m_baseTypeName; }; /////////////////////////////////////////////////////////////////////////////////// -ULONG getFiledOffsetRecirsive(TypeInfoPtr typeInfo, const std::string &fieldName); +ULONG getFieldOffsetRecirsive(TypeInfoPtr typeInfo, const std::string &fieldName); /////////////////////////////////////////////////////////////////////////////////// diff --git a/test/scripts/customtypestest.py b/test/scripts/customtypestest.py new file mode 100644 index 0000000..750d824 --- /dev/null +++ b/test/scripts/customtypestest.py @@ -0,0 +1,72 @@ +"""Custom types tests""" + +import unittest +import target +import pykd + +class CustomTypesTest(unittest.TestCase): + def testCommonStruct(self): + mySubStruct = pykd.createStruct("MySubCustomStruct") + mySubStruct.append( "m_uint1", pykd.typeInfo("UInt1B") ) + mySubStruct.append( "m_uint2", pykd.typeInfo("UInt2B") ) + + mySubUnion = pykd.createUnion("MySubCustomUnion") + mySubUnion.append( "m_uint1", pykd.typeInfo("UInt1B") ) + mySubUnion.append( "m_uint2", pykd.typeInfo("UInt2B") ) + + myType = pykd.createStruct("MyCustomStruct") + myType.append( "m_uint1", pykd.typeInfo("UInt1B") ) + myType.append( "m_uint4", pykd.typeInfo("UInt4B") ) + myType.append( "m_uint2", pykd.typeInfo("UInt2B") ) + myType.append( "m_struct", mySubStruct ) + myType.append( "m_union", mySubUnion ) + myType.append( "m_uint8", pykd.typeInfo("UInt8B") ) + + self.assertTrue( myType.size() != 0 ) + self.assertTrue( myType.size() >= myType.fieldOffset("m_uint8") + myType.m_uint8.size() ) + + self.assertTrue( myType.fieldOffset("m_uint1") == 0 ) + + self.assertTrue( myType.fieldOffset("m_uint1") < myType.fieldOffset("m_uint4") ) + self.assertTrue( myType.fieldOffset("m_uint1") + myType.m_uint1.size() <= myType.fieldOffset("m_uint4") ) + + self.assertTrue( myType.fieldOffset("m_uint4") < myType.fieldOffset("m_uint2") ) + self.assertTrue( myType.fieldOffset("m_uint4") + myType.m_uint4.size() <= myType.fieldOffset("m_uint2") ) + + self.assertTrue( myType.fieldOffset("m_uint2") < myType.fieldOffset("m_struct") ) + self.assertTrue( myType.fieldOffset("m_uint2") + myType.m_uint2.size() <= myType.fieldOffset("m_struct") ) + + self.assertTrue( myType.fieldOffset("m_struct") < myType.fieldOffset("m_union") ) + self.assertTrue( myType.fieldOffset("m_struct") + myType.m_struct.size() <= myType.fieldOffset("m_union") ) + + # print myType + + def testCommonUnion(self): + myType = pykd.createUnion("MyCustomStruct") + myType.append( "m_uint1", pykd.typeInfo("UInt1B") ) + myType.append( "m_uint4", pykd.typeInfo("UInt4B") ) + myType.append( "m_uint2", pykd.typeInfo("UInt2B") ) + + self.assertFalse( myType.size() == 0 ) + self.assertTrue( myType.fieldOffset("m_uint1") == 0 ) + self.assertTrue( myType.fieldOffset("m_uint4") == 0 ) + self.assertTrue( myType.fieldOffset("m_uint2") == 0 ) + + def testDupFieldName(self): + myType = pykd.createStruct("MyCustomStruct") + exceptionRised = False + myType.append( "m_uint1", pykd.typeInfo("UInt1B") ) + try: + myType.append( "m_uint1", pykd.typeInfo("UInt1B") ) + except pykd.TypeException: + exceptionRised = True + self.assertTrue(exceptionRised) + + myType = pykd.createUnion("MyCustomStruct") + exceptionRised = False + myType.append( "m_uint1", pykd.typeInfo("UInt1B") ) + try: + myType.append( "m_uint1", pykd.typeInfo("UInt1B") ) + except pykd.TypeException: + exceptionRised = True + self.assertTrue(exceptionRised) diff --git a/test/scripts/pykdtest.py b/test/scripts/pykdtest.py index f32efe2..d2cd68c 100644 --- a/test/scripts/pykdtest.py +++ b/test/scripts/pykdtest.py @@ -20,6 +20,7 @@ import typeinfo import typedvar import regtest import localstest +import customtypestest class StartProcessWithoutParamsTest(unittest.TestCase): def testStart(self): @@ -45,6 +46,7 @@ def getTestSuite( singleName = "" ): unittest.TestLoader().loadTestsFromTestCase( typeinfo.TypeInfoTest ), unittest.TestLoader().loadTestsFromTestCase( typedvar.TypedVarTest ), unittest.TestLoader().loadTestsFromTestCase( regtest.CpuRegTest ), + unittest.TestLoader().loadTestsFromTestCase( customtypestest.CustomTypesTest ), # ^^^ unittest.TestLoader().loadTestsFromTestCase( TerminateProcessTest ), diff --git a/test/targetapp/targetapp.vcproj b/test/targetapp/targetapp.vcproj index ec9f910..226ad86 100644 --- a/test/targetapp/targetapp.vcproj +++ b/test/targetapp/targetapp.vcproj @@ -408,6 +408,10 @@ RelativePath="..\scripts\clienttest.py" > + +