Doom3源码剖析(1)--TypeInfo

Doom3源码剖析(1)--TypeInfo

TypeInfo模块是Doom3中用于动态类型识别(RTTI)和辅助查错的模块,可以检测类中未初始化变量、打印某种类型的对象中所有成员变量的值,或打印整个程序中所有实体对象(idEntity)的值。在写程序过程中,可能经常会出现一些由于未对成员变量初始化而导致的问题。而这类问题一般在Debug模式下没有问题,在Release版本中偶尔出现,这导致很难定位与问题相关的代码。

    除了检测未初始化的内存,其他方面的应用,可以从TypeInfo.h中的几个函数可以略窥一二。

  /*
=================================================================================== 

    Game Type Info

===================================================================================
*/

//  根据偏移取得变量名
const  char * GetTypeVariableName(  const  char *typeName,  int offset );
//  打印某个对象的信息
void PrintType(  const  void *typePtr,  const  char *typeName );
//  将某对象值写入文件
void WriteTypeToFile( idFile *fp,  const  void *typePtr,  const  char *typeName );
//  给某对象的所有变量设定一个初始值
void InitTypeVariables(  const  void *typePtr,  const  char *typeName,  int value );
//  列举所有类型信息
void ListTypeInfo_f(  const idCmdArgs &args );
//  将所有游戏状态(所有idEntity信息)写入文件
void WriteGameState_f(  const idCmdArgs &args );
//  比较游戏状态
void CompareGameState_f(  const idCmdArgs &args );
//  测试保存游戏
void TestSaveGame_f(  const idCmdArgs &args );


经过几天的努力,我将
TypfInfo相关的代码提取出,单独创建了工程文件,并做了简单的代码演示。有爱好的同学可以深入研究。点击这里下载。在工程( vs2010) 中,Example是一个简单的演示程序,idLib是对Doom3源码中idLib的删减版本。TypeInfo是生成GameTypeInfo.h类型信息的控制台工具。

TypeInfo的用法为 typeinfo.exe [source_code_path] [out_file_name]

 

实现:

TypeInfo检测未初始化内存实现原理为,对代码中所有的头文件扫描分析,并将类型中,变量名,变量类型等信息记录生成一个GameTypeInfo.h文件。然后在new一个对象时,会先将这个对象的内存初始化为0xcdcdcdcd。当对象构造完成时,检测对象内存中是否有某值为0xcdcdcdcd,如果有,就通过GameTypeInfo.h中记录的类型,变量名,偏移等信息将未初始化的变量信息打印出来。

将上述过程分为三段内容:1. 解析 2. 生成类型信息 3. 利用类型信息

 

1. 解析:

基本的词法解析在idLib中实现,相关类为idToken, idLexer, idParser

 

2. 生成类型信息

idTypeInfoGen利用idParser来解析代码头文件,记录相关类型信息,类型信息包括:常量,枚举量和类(包括结构体)

 

//  常量信息 
typedef  struct {
     const  char * name;
     const  char * type;
     const  char * value;
} constantInfo_t;

//  枚举形变量信息
typedef  struct {
     const  char * name;
     int value;
} enumValueInfo_t;

//  枚举类型信息
typedef  struct {
     const  char * typeName;
     const enumValueInfo_t * values;
} enumTypeInfo_t;

//  类成员变量信息
typedef  struct {
     const  char * type;
     const  char * name;
     int offset;
     int size;
} classVariableInfo_t;

//  类的类型信息(包括结构体)
typedef  struct {
     const  char * typeName;
     const  char * superType;
     int size;
     const classVariableInfo_t * variables;
} classTypeInfo_t;

所有信息记录在GameTypeInfo.h中,详细内容可以查阅源代码。

3. 对类型信息的利用

3.1 通过类型名创建实例。在Doom3程序的GamePlay层,主要的类都是从idClass继承下来的,并且在类声明和定义时,通过idTypeInfo记录类型信息,包括记录类之间的父子关系。在类中加入宏CLASS_PROTOTYPE CLASS_DECLARATION来实现。
创建类实例时是这样的:

idTypeInfo *cls;
idClass *obj;
cls = idClass::GetClass( "idPlayer" );
obj = cls->CreateInstance();

 

    3.2 输出未初始化变量的名称。

     idClass中重载了new操作符,以便记录内存的使用量和创建的对象个数,如果需要检测未初始化的内存,还会将内存初始化为0xcdcdcdcd。

void * idClass:: operator  new( size_t s ) {
     int *p;

    s +=  sizeofint );
    p = ( int *)Mem_Alloc( s );
    *p = s;
    memused += s;
    numobjects++;

#ifdef ID_DEBUG_UNINITIALIZED_MEMORY
    unsigned  long *ptr = (unsigned  long *)p;
     int size = s;
    assert( ( size & 3 ) == 0 );
    size >>= 2;
     for (  int i = 1; i < size; i++ ) {
        ptr[i] = 0xcdcdcdcd;
    }
#endif

     return p + 1;
}

 

 
    对象创建后,调用
FindUninitializedMemory来检测未初始化的内存

/*
================
idClass::FindUninitializedMemory
================
*/
void idClass::FindUninitializedMemory(  void ) {
#ifdef ID_DEBUG_UNINITIALIZED_MEMORY
    unsigned  long *ptr = ( ( unsigned  long * ) this ) - 1;
     int size = *ptr;
    assert( ( size & 3 ) == 0 );
    size >>= 2;
    ptr = ( unsigned  long * ) this;
     for (  int i = 0; i < size; i++ ) {
         if ( ptr[i] == 0xcdcdcdcd ) {
             const  char *varName = GetTypeVariableName( GetClassname(), i << 2 );
            idLib::Warning( "type '%s' has uninitialized variable %s (offset %d)", GetClassname(), varName, i << 2 );
        }
    }
#endif
}
    
    这里通过
  GetTypeVariableName 在类型信息表中查找变量名称。


    3.3打印输出某个实例的所有成员变量值。

 

         代码实现在TypeInfo.cpp文件中,没有仔细研究这段代码实现,就不在这里单列了。

你可能感兴趣的:(Doom3源码剖析(1)--TypeInfo)