Before reading how to use code generation tool, you should know more about user types.
Here is the list of all command line arguments:
-s, --symbols Path to symbols file which will be used to generate the code
-t, --types List of types to be exported. If omitted all types will be exported.
--no-type-info-comment (Default: false) Generate filed type info comment
--multi-line-properties (Default: false) Generate properties as multi line
--use-direct-class-access (Default: false) Generated code that will use class members directly
(not using GetField, but GetClassField).
--force-user-types-to-new-instead-of-casting (Default: false) Force using new during type casting instead of
direct casting
--cache-user-type-fields (Default: false) Caches result of getting user type field when
exporting user type
--cache-static-user-type-fields (Default: false) Caches result of getting static user type field when
exporting user type
--lazy-cache-user-type-fields (Default: false) Cache result of getting user type field inside
UserMember when exporting user type
--generate-physical-mapping-of-user-types (Default: false) Generate physical access to fields in exported user
types (instead of symbolic/by name)
--generate-assembly-with-il (Default: false) Generate assembly by emitting IL instead of
compiling C# code.
--generated-assembly-name (Default: ) Name of the assembly that will be generated next to
sources in output folder
--generated-props-file-name (Default: ) Name of the props file that will be generated next to
sources in output folder. It can be later included into project that
will be compiled
-x, --xml-config Path to xml file with configuration
--use-dwarf (Default: false) Use DWARF symbol provider
--use-pdb-reader (Default: false) Use PDB reader symbol provider
--help Display this help screen.
--version Display version information.
You can specify path to the XML configuration file using --xml-config argument.
Here is an example XML configuration file:
<XmlConfig>
<Types>
<Type Name="MyTestClass" />
</Types>
<Modules>
<Module Namespace="MyModule" PdbPath="myPdb.pdb"/>
</Modules>
<Transformations>
<Transformation OriginalType="std::any"
NewType="SharpDebug.CommonUserTypes.NativeTypes.std.any" />
<Transformation OriginalType="std::array<${T},${Length}>"
NewType="SharpDebug.CommonUserTypes.NativeTypes.std.array<${T}>" />
<Transformation OriginalType="std::basic_string<char,${char_traits},${allocator}>"
NewType="SharpDebug.CommonUserTypes.NativeTypes.std.@string" />
<Transformation OriginalType="std::vector<${T},${allocator}>"
NewType="SharpDebug.CommonUserTypes.NativeTypes.std.vector<${T}>" />
</Transformations>
<UseDirectClassAccess>true</UseDirectClassAccess>
<DontSaveGeneratedCodeFiles>true</DontSaveGeneratedCodeFiles>
<GeneratePhysicalMappingOfUserTypes>true</GeneratePhysicalMappingOfUserTypes>
</XmlConfig>You can see more about all available XML fields in source code.
Transformations are being used for user types defined somewhere else (like common user types). They allow mapping from symbol type to existing user type during code generation. For example, if user defines transformation for std::vector class like in example above, all members that are of type std::vector will be exported as CsDebubScript.commonUserTypes.NativeTypes.std.vector.
In order to fully benefit performance wise from code generation, you want to use this option. This will generate user types that read whole memory associated with the type (size of the type is written in symbol file) and later using types and offsets available from symbol file it will read directly from MemoryBuffer. You can find all Read* functions in UserType class.
Some symbol providers (DbgEng symbol provider) doesn't support getting base classes, class fields, but only all fields defined in the type. Modern symbol providers support direct class access and should be used by default.
There are two assembly generators:
- Roslyn
- IL emitting
Roslyn allows usage of IncludedFiles and ReferencedAssemblies.
IL emitting is the fastest assembly generation since it doesn't do two pass generation (source code and then compiling it), but it doesn't support added user code (InlcudedFiles).
All user types are generated as partial classes allowing user to add custom code to make user types easier to use.
Let's take a look at following C++ structure:
template <typename ValueType>
struct MyList
{
ValueType value;
MyList* next = nullptr;
};Code generation will generate something similar to this class:
[UserType(TypeName = "MyList<>")]
public partial class MyList<T> : UserType
{
partial void PartialInitialize();
public MyList(Variable variable)
: base(variable)
{
PartialInitialize();
}
public T value => GetField("value").CastAs<T>();
public MyList next => GetField("next").CastAs<MyList>();
}This will allow user to add code for enumerating list:
public partial class MyList<T> : IEnumerable<T>
{
public IEnumerator<T> GetEnumerator()
{
return Enumerate().GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return Enumerate().GetEnumerator();
}
private IEnumerable<T> Enumerate()
{
yield return value;
for (MyList p = next; p != null; p = p.next)
yield return p.value;
}
}User custom code + physical mapping of user types will produce user types that are way more faster than common user type currently available.