Changjiang的专栏

孤帆远影碧空尽,唯见长江天际流。

赵世平ID:Changjiang
[修改头像]
17666次访问,排名5663(1)好友0人,关注者0
Changjiang的文章
原创 8 篇
翻译 2 篇
转载 0 篇
评论 6 篇
最近评论
wangqinghua1:很有收获,文风简洁
fantasyzzz:老师好,我是今天和您联系请教学习汇编方法的您的学生,我以前一直学习的是8086的汇编语言,不过也可以理解您上面的Win32ASM程序示例的大意,我已经对8086的汇编程序编写有一定的掌握,熟悉汇编的编写方法了,而且我现在还在看8086的教材巩固,我想问的是这个过程是否可以跳过,放下8086的学习,直接学习Win32的汇编。不知道这里请教合适么?谢谢老师。。
royelee:当年也是玩crack的啊?
还是原来用trw2k的时代爽啊。
Changjiang:呵呵,这个BLOG是去年开的,一直没有用,这几天正好有空,放上了一点文章。最近还好么,支持你。
flier:正是应了微软 3.0 那个说法,CLI 一直出到第三版,才算是基本上完成了预先的设想,把从一开始就规划进去的那些坑都填上,也腾出了精力去折腾相对来说更实际一些的 WinFX。等 Enterprise Library 那套东东再成熟一点,针对 Java 阵营的进攻号角才算真正吹向。
软件项目交易
订阅我的博客
XML聚合  FeedSky
订阅到鲜果
订阅到Google
订阅到抓虾
订阅到BlogLines
订阅到Yahoo
订阅到GouGou
订阅到飞鸽
订阅到Rojo
订阅到newsgator
订阅到netvibes
文章分类
收藏
    相册
    好友的BLOG
    孟岩的BLOG
    自己的其他BLOG
    Changjiang的另一个BLOG:TBsoft工作室的BLOG
    存档

    翻译 WDM驱动程序示例

    新一篇: 生日感言:作为设备驱动程序开发者的这些年头

    WDM驱动程序示例

     

    (本文档翻译自Windows 98 DDKWindows Me DDK中“GENERALWDM驱动程序示例中的说明文档template.doc,原文档标题是“Template WDM Driver for Memphis”,MemphisWindows 98的开发代号。本文只翻译了较重要的一部分,其余部分没有翻译。本文档适用于Windows 98Me2000XP及其以后版本)

     

    译者前言

     

    本文档最早来自Windows 98 DDK,原是Windows 98 DDK中的一个WDM驱动程序示例“GENERAL”的说明文档template.doc,“GENERALWDM驱动程序示例是一个包含有诊断工具的Windows 98 WDM驱动程序,可以作为WDM驱动程序的代码框架。原说明文档template.doc发布于19961216,是较早的WDM驱动程序文档之一,实际上描述了WDM驱动程序的代码框架,可以作为开发WDM驱动程序的参考资料。

     

    WDMWindows驱动程序模型,Windows Driver Model)驱动程序适用于Windows 98Me2000XP及其以后版本。WDM驱动程序属于Windows NT内核模式驱动程序(Kernel-mode Driver),可以认为是支持即插即用(Plug and Play)的Windows NT内核模式驱动程序。

     

    Windows NT内核模式驱动程序(简称KMD或者NT Driver)工作在Intel 80386以上CPUIA32体系结构CPU)的最高特权级——Ring 0特权级上,可以不受限制地访问所有硬件资源,例如IO端口、物理内存、硬件中断等,还有对Windows NT最高和最完全的控制权,相当于特权级最高的Windows NT应用程序。NT Driver主要用于驱动硬件,但如果需要开发在Ring 0上运行的最高特权级的Windows NT应用程序,也可以开发NT Driver或者WDM驱动程序。

     

    例如:Rootkits就是NT Driver实现的,“冰刃(IceSword)”反黑客软件也使用了NT Driver

     

    如果需要开发即插即用硬件的驱动程序,可以开发WDM驱动程序;如果只需要开发在Ring 0上运行的最高特权级的Windows NT应用程序,则无需支持即插即用,开发普通NT Driver即可。普通NT Driver又称为“NT式”驱动程序,可以适用于Windows NT 4.0,也可以适用于Windows 2000Windows NT 5.0)及其以后版本,部分“NT式”驱动程序还可以适用于Windows 98Me

     

    Compuware DriverStudio中的DriverWorks,本质是“DDK类库”,使用C++类库封装的方法简化了NT Driver或者WDM驱动程序的开发,使用DriverWorks代替DDK开发,类似于使用MFC代替SDK开发Windows应用程序。

     

    Windows Vista中的Microsoft下一代驱动程序开发模型——WDFWindows驱动程序基础,Windows Driver Foundation)中的KMDF(内核模式驱动程序框架,Kernel-mode Driver Framework)仍然基于WDM实现。

     

    Ring 3应用程序(普通Windows应用程序)与本WDM示例驱动程序之间的通信仍然使用“NT式”驱动程序的符号链接,没有使用WDM驱动程序特有的设备接口,这点需要注意。

     

    原著:Microsoft

    翻译:TBsoft Software Studio2006

     

    1 介绍

     

    本文档描述了一个包含有诊断工具的Windows 98 WDM驱动程序并提供给开发者一个Windows 98驱动程序的代码框架。这个驱动程序示例提供最简单的驱动程序功能和一组可以通过Ring 3应用程序访问的诊断工具,包含的Ring 3 Win32应用程序示例在开发驱动程序示例的上层(Ring 3——译者注)代码时有用。描述如何写一个WDM驱动程序不是本文档的目的(不过认真阅读本文档和驱动程序代码对如何写一个WDM驱动程序会有帮助,还为WDM驱动程序开发初学者提供了有用的工具)。下面是本驱动程序示例的特征:

     

    ●驱动程序示例代码包含DriverEntryDispatchIOCTL)、ReadWritePnPAddDevice例程,这些例程包含对开发者在设备相关文件中提供的例程的入口点(向量/指针——译者注)调用,还包含简单的内存中回送测试的实例代码。

     

    译者注:本WDM驱动程序示例的框架文件(Drvshell.cDrvshell.h)中包含DriverEntry等上述例程的框架代码,框架代码中包含通过调用Vector.cVector.h文件中定义的指向函数的指针(VectorDriverEntry等)间接调用开发者提供的例程的代码。开发者将本WDM驱动程序示例作为开发其他WDM驱动程序的模板时,只需要提供设备相关文件,实现相应例程,并将VectorDriverEntry等指向函数的指针指向相应例程,示例框架代码中的DriverEntry等上述例程会自动调用相应例程,框架代码不必改动,也不必重新编写框架代码。

     

    IRPIO请求包)的历史记录可以被Ring 3应用程序获取。当驱动程序完成IRP的一次请求时调用记录IRP到驱动程序中适当位置的例程进行追踪(也就是历史记录——译者注),这些历史记录条目都有时间标记(原文是“time stamps”,也可译成“时间戳”——译者注)并保存有IRP的起始几个字节数据,传送的数据字节数和IRP个数也保存在设备对象(Device Object)的成员中。

     

    ●驱动程序中的执行跟踪可以被Ring 3应用程序获取。如果允许(启用)调试,执行跟踪添加指向常量字符串的指针和时间标记到记录中,并发送到核心调试输出例程。

     

    ●驱动程序中的错误记录可以让Ring 3应用程序获取驱动程序或者驱动程序的上层发生的错误。WDM驱动程序中的错误并不总是会为Ring 3应用程序映射成(Ring 3应用程序)可以理解的形式,错误记录记录所有的错误并带有时间标记,以后可以从时间标记列表中获取错误并查找驱动程序或者驱动程序的下层(底层)发生错误的准确原因,错误代码会被转换成NT头文件中NT风格的常量。

     

    ●通用调试功能包括内存分配记录和校验、断言(assertion)支持和代码中的陷阱(捕获)。

     

    ●使用Pentium以上CPURDTSC指令实现了代码执行概要功能(也就是记录了代码执行过程经过的指令周期,这是RDTSC指令的重要功能——译者注)。

     

    IOCTL可以获取设备附加到驱动程序上的信息。这些信息包括为设备创建的任何符号连接,以及设备对象包含的IRP和通过设备对象传送的字节的信息。

     

    Ring 3 Win32应用程序带有使用诊断IOCTL的实例代码。该实例应用程序演示了在本示例驱动程序中使用带有内存中软回送机制的ReadFileWriteFile函数调用,该应用程序还可以使用IOCTL调用从驱动程序中获取各种表和诊断信息。

     

    2 示例驱动程序代码

     

    本示例驱动程序代码实现了一个最小化功能的WDM驱动程序,包括最多的驱动程序入口点的支持,还带有已安装的诊断特征,这样设计的意图是下列6个核心文件没有必要修改(换而言之,开发者将本WDM驱动程序示例作为开发其他WDM驱动程序的模板时,无需修改下列6个核心文件——译者注):

     

    Drvshell.c——实现所有驱动程序入口点的代码。代码中包括通过指针进行调用的代码,开发者开发其他特定的驱动程序时,可让这些指针指向相应例程。代码中还包括为完成IRP历史记录、执行跟踪和错误记录功能而进行调用的代码。

     

    Drvshell.h——Drvshell.c包含的头文件。Drvshell.c和开发其他特定的驱动程序创建的源程序文件应该包含本文件。

     

    Debugwdm.c——实现IRP历史记录表、执行跟踪代码和错误记录。本文件包含历史记录和跟踪功能使用的例程,例程还填充内容格式化特定表格以返回给Ring 3应用程序,部分例程在debugwdm.h中使用宏实现,以加快运行速度。

     

    Debugwdm.h——Debugwdm.c包含的头文件。Debugwdm.c和开发其他特定的驱动程序创建的源程序文件应该包含本文件。

     

    Vector.c——本文件包含可指向其他特定的驱动程序中的例程的指针,这些例程将被驱动程序的模板部分(也就是无需修改的部分——译者注)调用,本文件还包含指向描述驱动程序(名称和版本)字符串的指针。

     

    Vector.h——Vector.c包含的头文件,本示例驱动程序的源程序文件和开发其他特定的驱动程序创建的源程序文件均应包含本文件。

     

    译者注:Vector.cVector.h文件的作用还可以参见“介绍”部分的译者注。

     

    还有2个文件将在开发者开发其他特定的驱动程序时被修改:

     

    Testdrv.h——定义驱动程序相关的字符串和数据结构,可能包含传送给内核输出例程的驱动程序名称。下面是Testdrv.h的一个实例的一部分:

     

    #define DRIVER_VERSION          "1.00"

     

    // the following define determines the IO method used, buffered or direct

    // for reads and writes

    #define DRIVER_IO_METHOD        DO_DIRECT_IO

     

    Testdrv.c——本文件包含Drvshell.c调用的例程。开发者只要让指针(来自Vector.c)指向这些例程,这些例程就会被模板驱动程序(驱动程序的模板部分,同上——译者注)调用。开发者至少还必须提供2个被模板驱动程序调用的例程(用于设置指针——译者注),下列代码是例程的实例代码:

     

    NTSTATUS

    OpenDriver(VOID)

    {

        VectorDriverEntry   = TESTDRV_DriverEntry;

        VectorDispatch      = TESTDRV_Dispatch;

        VectorUnload        = TESTDRV_Unload;

        VectorAddDevice     = TESTDRV_AddDevice;

        VectorRemoveDevice  = TESTDRV_RemoveDevice;

        VectorCreate        = TESTDRV_Create;

        VectorClose         = TESTDRV_Close;

        VectorWrite         = TESTDRV_Write;

        VectorRead          = TESTDRV_Read;

     

        DriverName          = DRIVER_NAME;

        DriverVersion       = DRIVER_VERSION;

     

        return STATUS_SUCCESS;

    } // OpenDriver

     

    VOID

    CloseDriver(VOID)

    {

        // probably don't need to make these vectors NULL, but why not

        // could prevent my computer from exploding, you never know

        VectorDriverEntry   = NULL;

        VectorDispatch      = NULL;

        VectorUnload        = NULL;

        VectorAddDevice     = NULL;

        VectorRemoveDevice  = NULL;

        VectorCreate        = NULL;

        VectorClose         = NULL;

        VectorWrite         = NULL;

        VectorRead          = NULL;

     

        DriverName          = "";

        DriverVersion       = "";

    } // CloseDriver

     

    在模板驱动程序中,OpenDriver例程被DriverEntry例程调用,CloseDriver例程被Unload例程调用。分配(包括释放——译者注)设备所需的驱动程序资源也可以在这2个例程中进行,开发者可以修改Testdrv.c的文件名以反映使用驱动程序的硬件或者硬件类。如果驱动程序不需要特定的入口点(也就是Drvshell.c调用的例程——译者注),则不要设置指针,模板驱动程序调用指针前会检查指针。

     

    下面是对Drvshell.c实现的例程起什么作用的说明:

     

    DriverEntry——第一次装载驱动程序时本例程会被调用,本例程调用设备相关例程,被调用的OpenDriver例程设置指针指向设备驱动程序例程,并完成设备相关的驱动程序初始化,还调用相关例程为诊断工具(IRP历史记录、执行跟踪和错误记录)初始化和分配资源,以及创建缺省设备对象和符号链接。

     

    DRVSHELL_DriverUnload——本例程释放在DriverEntry例程分配的资源,并调用设备相关例程,被调用的CloseDriver例程取消设备相关例程OpenDriver完成的工作。

     

    DRVSHELL_Dispatch——本例程处理所有的IOCTL和对驱动程序的PNP_POWER调用,IOCTL应该通过对设备相关的IOCTL例程(如果支持)进行调用尽可能地进行处理。

     

    DRVSHELL_Create——处理Ring 3应用程序对CreateFile函数的调用。

     

    DRVSHELL_Close——处理Ring 3应用程序对CloseHandle函数的调用。

     

    DRVSHELL_Write——处理Ring 3应用程序对WriteFile函数的调用。

     

    DRVSHELL_Read——处理Ring 3应用程序对ReadFile函数的调用。

     

    DRVSHELL_PnPAddDevice——处理当新设备被安装时对驱动程序的调用,本例程为新设备创建设备对象和符号链接。

     

    所有的这些例程都有执行路径(是指程序执行走过的路径,不是子目录——译者注)记录,例程中自始至终都随处安排了调试输出(用于辅助查看执行路径——译者注)。对记录历史记录例程的调用放在适当的驱动程序例程的起始处,对记录历史记录例程的调用同时也放在IRP请求完成时的代码中,错误记录放在所有返回类型为NTSTATUS的例程的末尾处。下面是模板驱动程序中的几个例程:

     

    NTSTATUS

    DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)

    {

        NTSTATUS               ntStatus = STATUS_SUCCESS;

        static BOOLEAN         initd = FALSE;

        PDEVICE_OBJECT         pDeviceObject = NULL;

        PDEVICE_EXTENSION      deviceExtension;

     

        if(!initd)

        {

            // initialize calls to new driver routines, etc.

            ntStatus = OpenDriver();

          

            if(!NT_SUCCESS(ntStatus))

            {

                Debug_KdPrint(("exit DriverEntry: OpenDriver call failed (%x)\n",

                               ntStatus));

                return ntStatus;

            }

          

            // initialize diagnostic stuff (history, tracing, error logging)

            ntStatus = Debug_OpenWDMDebug();

     

            if(!NT_SUCCESS(ntStatus))

            {

                Debug_KdPrint(("exit DriverEntry: OpenWDMDebug failed (%x)\n",

                               ntStatus));

                return ntStatus;

            }

        }

     

        // chicken before the egg problem, can't call DEBUG_PATH until initd

        DEBUG_LOG_PATH("enter DriverEntry");

          

        Debug_KdPrint(("DriverObject = %0x08x\n", DriverObject));

        Debug_KdPrint(("DeviceObject = 0x%08x\n", DriverObject->DeviceObject));

     

        // Create dispatch points for device control, create, close, etc.

     

        DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DRVSHELL_Dispatch;

        DriverObject->MajorFunction[IRP_MJ_CREATE]         = DRVSHELL_Create;

        DriverObject->MajorFunction[IRP_MJ_CLOSE]          = DRVSHELL_Close;

        DriverObject->MajorFunction[IRP_MJ_PNP_POWER]      = DRVSHELL_Dispatch;

        DriverObject->MajorFunction[IRP_MJ_WRITE]          = DRVSHELL_Write;

        DriverObject->MajorFunction[IRP_MJ_READ]           = DRVSHELL_Read;

        DriverObject->DriverUnload                         = DRVSHELL_Unload;

        DriverObject->DriverExtension->AddDevice           = DRVSHELL_PnPAddDevice;

          

        // call device specific DriverEntry if there is one

        if(VectorDriverEntry != NULL)

            ntStatus = (*VectorDriverEntry)(DriverObject, RegistryPath);

     

        if(!initd)

        {

            // let's create a device object named the same as the driver

            ntStatus = DRVSHELL_CreateDeviceObject(DriverObject, &pDeviceObject,

                                                   DriverName);

     

            // check for error, remove the device if needed

            if(!NT_SUCCESS(ntStatus))

            {

                DEBUG_LOG_PATH("Failed to create device object");

                DRVSHELL_RemoveDevice(DriverObject->DeviceObject);

            }

            else

            {

                // make sure we don't mess up when the object is removed

                // and write past the end of our slot array

     

                // Get a pointer to the device extension

                deviceExtension = pDeviceObject->DeviceExtension;

     

                // set to invalid slot value

                deviceExtension->Instance = NUM_DEVICE_SLOTS;

            }

     

            initd = TRUE;

        }

     

        // log an error if we got one

        DEBUG_LOG_ERROR(ntStatus);

     

        DEBUG_LOG_PATH("exit  DriverEntry");

     

        Debug_KdPrint(("status = (%x)\n", ntStatus));

          

        return ntStatus;

    } // DriverEntry

     

     

    NTSTATUS

    DRVSHELL_Create(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)

    {

        NTSTATUS              ntStatus = STATUS_SUCCESS;

        PIO_STACK_LOCATION    irpStack;

        PDEVICE_EXTENSION     deviceExtension;

        PVOID                 ioBuffer;

     

        DEBUG_LOG_PATH("enter DRVSHELL_Create");

          

        // set return values to something known

        Irp->IoStatus.Status = STATUS_SUCCESS;

        Irp->IoStatus.Information = 0;

     

        // Get a pointer to the current location in the Irp. This is where

        // the function codes and parameters are located.

        irpStack = IoGetCurrentIrpStackLocation(Irp);

     

        // Get a pointer to the device extension

        deviceExtension = DeviceObject->DeviceExtension;

     

        // Get the pointer to the input/output buffer and it's length

        ioBuffer = Irp->AssociatedIrp.SystemBuffer;

     

        // make entry in IRP history table

        DEBUG_LOG_IRP_HIST(DeviceObject, Irp, irpStack->MajorFunction,

                           ioBuffer, 0);

     

        // call device specific create if there is one

        if(VectorCreate != NULL)

            ntStatus = (*VectorCreate)(DeviceObject, Irp);

          

        // complete IO and make entry in IRP history table

        if(ntStatus != STATUS_PENDING)

            DRVSHELL_CompleteIO(DeviceObject, Irp, irpStack->MajorFunction,

                                ioBuffer, 0);

        else

            DEBUG_LOG_PATH("STATUS_PENDING");

     

        // log an error if we got one

        DEBUG_LOG_ERROR(ntStatus);

     

        DEBUG_LOG_PATH("exit  DRVSHELL_Create");

     

        Debug_KdPrint(("status = (%x)\n", ntStatus));

          

        return ntStatus;

    } // DRVSHELL_Create

     

    当设备对象被创建和附加时,通常会有被称为设备扩展(Device Extension)的信息被附加到对象上,特定驱动程序需要这些特定包含信息。当设备对象被创建时,设备扩展被分配,初始化,然后附加到设备对象上。本模板驱动程序有一个最小的设备扩展,这个设备扩展带有一个可以让开发者给驱动程序添加相关东西的指针。下面是设备扩展的定义:

     

    // device extension for driver instance, used to store needed data

     

    typedef struct _DEVICE_EXTENSION

    {

           CHAR                 DeviceName[NAME_MAX];   // string name of device

           CHAR                 LinkName[NAME_MAX];              // name of symbolic link

           PDEVICE_OBJECT   PhysDeviceObject;             // physical device object

           PDEVICE_OBJECT   StackDeviceObject;           // stack device object

           BOOLEAN         Stopped;                     // current state of device

           ULONG                     IRPCount;                   // number of IRPs complete

           ULONG                     ByteCount;                  // number of bytes of data

           ULONG                     Instance;               // instance of device

           PVOID                DeviceSpecificExt;              // pointer to device

                                                         // specific extension

    } DEVICE_EXTENSION, *PDEVICE_EXTENSION;

     

    当设备对象被创建时,模板驱动程序通过某个指针调用设备相关代码,开发者可以为设备相关的扩展分配内存,并让DeviceSpecificExt指针指向内存,当设备相关代码需要获取设备相关的扩展时,可以将该指针强制转换成开发者定义的指针类型。

     

    3 Driver Diagnostic Tools(设备驱动程序诊断工具)

     

    The following diagnostic routines are implemented as either subroutines or macros. In the case of history tables and logs, if the size of the table or log is set to zero, a check is done before the subroutine call to avoid overhead if that table or log is disabled. The size of a table or log may be set with an IOCTL call from the ring 3 application. The supplied ring 3 application demonstrates setting these sizes and extracting information from driver.

     

    4 IRP History TablesIRP历史记录表)

     

    IRP history tables allow tracking of IRPs as they are sent into the driver, and when a request is completed by the driver (when IoCompleteRequest is called on an IRP). This table is a circular buffer and the size is determined by a default value in the driver. This buffer can be resized later with IOCTL calls to the driver (currently you lose everything in the table when it is resized). Because it is a circular buffer, you only have access to the last TABLE_SIZE entries after the table has been filled and starts wrapping. Because the entries are small (under 32 bytes), you can keep a lot of them hanging around. The default size is 64 entries. Following is the prototype for the macro that logs entries and the data structure for a history table entry:

     

    VOID

    DEBUG_LOG_IRP_HIST(IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp,

                       IN ULONG majorFunction, IN PVOID ioBuffer, ULONG bufferLen);

     

    // entry for IRP history table for IRPs going in and out

    typedef struct IRPHistory

    {

        LARGE_INTEGER       timeStamp;

        PDEVICE_OBJECT      pDeviceObject;

        PIRP                pIrp;

        ULONG               majorFunction;

        UCHAR               irpData[IRP_DATA_SIZE];

    } IRPHist, *PIRPHist;

     

    This entry includes a time stamp, a pointer to the device object the IRP is associated with, a pointer to the IRP, the major function, and the first 8 bytes of data in the buffer associated with the IRP. The time stamp is useful for determining when it takes a long time for an IRP to complete, and when used in conjunction with the execution tracing (which will also be time stamped). It wont take much time to fill an entry in the table, and it will provide valuable information about IRPs that have passed through the driver. If the size of the table is set to 0, IRP history is turned off and a subroutine call to the history routines is avoided.

     

    5 Execution Tracing(执行跟踪)

     

    Execution tracing is similar to IRP history tables. It uses a circular buffer again, giving you access to the last LOG_SIZE entries that were logged. Each entry contains a time stamp and a pointer to a constant array of chars. This means that there will be no printf-style formatting of the strings used in execution tracing. This is a bit of a downside, but the strings are extracted at some later time, so they would have to be copied and memory allocated for storage. This would be too time consuming and would turn into something way too complicated. This merely provides information about the path of execution through the code. Just sprinkle the DEBUG_LOG_PATH calls all over your code. Put them in all of the different paths that the code could take. The entries are time stamped for use in conjunction with other logs and tables. When debugging is enabled in the driver, the path logging is also sent to kernel print routines. If the size of the path tracing is set to 0, execution tracing is turned off and a subroutine call is avoided.

     

    6 Error Logging(错误记录)

     

    Many driver routines return an error of type NTSTATUS. Following is a macro for logging errors in the driver:

     

    DEBUG_LOG_ERROR(ntStatus);

     

    This macro will log ntStatus with a time stamp if it is not equal to STATUS_SUCCESS. These will again be stored in a circular buffer and a list of errors can be extracted by the ring 3 application. This will be useful because NT style errors do not really make it up to the ring 3 application in a recognizable way. This makes it hard to tell what kind of error it is if it comes from a driver below. When the error log is extracted from the driver, logged error codes will be translated into ASCII strings from ntstatus.h, so someone looking at the log file wont have to peruse header files for what the error codes really mean. If the error log size is set to 0, error logging is turned off and a subroutine call is avoided.

     

    7 General Debug Support(通用调试支持)

     

    This includes the following four routines:

     

    VOID DEBUG_TRAP(IN PCHAR pTrapCause);

    VOID DEBUG_ASSERT(IN PCHAR Message, BOOLEAN Exp);

    PVOID Debug_MemAlloc(IN POOL_TYPE PoolType, IN ULONG NumberOfBytes,

                         IN BOOLEAN bZeroFill);

    VOID Debug_MemFree(IN PVOID pMem);

     

    The first routine DEBUG_TRAP, is provided to insert a trap in the code. The pointer passed into the routine will be time stamped and inserted in the execution trace buffer before actually halting the code.  DEBUG_ASSERT works in a similar fashion. Both of these routines are implemented as macros. Here is how you might use the two routines:

     

    DEBUG_TRAP("DRVSHELL_Create: Code Coverage trap 1");

    DEBUG_ASSERT("pUrb == NULL", pUrb != NULL);

     

    The next two routines, Debug_MemAlloc and Debug_MemFree provide memory allocation services that keep track of the current amount of memory allocated, and perform sanity checks on all memory freed. These routines work like the ExAllocatePool and ExFreePool routines. If the bZeroFill flag is set to TRUE when the Debug_MemAlloc routine is called, the memory allocated is zero filled. As a sanity check, the driver can check to make sure all allocated memory has been freed when it unloads. The memory allocation routines include a way to verify whether the driver has written past the end of an allocated buffer, tried to free an already, freed chunk of memory, or has tried to free memory not allocated by the template driver routines. An extra DWORD is tacked onto the end of any allocation and a signature is written there. When the memory is freed, the signature is checked. If it is O.K. the memory will be freed (by calling ExFreePool). If not, this means the end of the buffer has been written past, someone is trying to free a junk block of memory, or the memory has already been freed. All of these are considered fatal conditions. The routines can be adjusted to suit individual needs.

     

    8 Code Profiling(代码执行概要)

     

    There are two routines that allow very high resolution profiling of code. These routines use the Pentium RDTSC instruction, which returns a 64-bit count of clock cycles since the chip was powered on. The two routines are:

     

    VOID

    Debug_StartPentiumCycleCounter(PLARGE_INTEGER cycleCount);

     

    VOID

    Debug_StopPentiumCycleCounter(PLARGE_INTEGER cycleCount);

     

    Just surround a piece code to profile with these calls to get a cycle count.  A LARGE_INTEGER is a Windows data structure of two 32-bit integers. Here is an example profile:

     

    LARGE_INTEGER cycleCount;

     

    Debug_StartPentiumCycleCounter(&cycleCount);

     

    // code to profile goes here, cycle count is returned in

    // LARGE_INTEGER cycleCount

     

    Debug_StopPentiumCycleCounter(&cycleCount);

     

    Following is debug output (using DEBUG_LOG_PATH and Debug_KdPrint calls) from the template driver. In the driver calls to RtlCopyMemory have been profiled, and the results are displayed here:

     

    c0fad768 00 e3fe78f0 TESTDRV: exit  DRVSHELL_CompleteIO

    c0fad768 00 e3fe78f0 TESTDRV: enter DRVSHELL_CompleteIO

    c0fad768 00 e3fe78f0 TESTDRV: exit  TESTDRV_Write

    c0fad768 00 e3fe78f0 TESTDRV: RtlCopyMemory copied 0x000003ef bytes in 0x00000583 cycles

    c0fad768 00 e3fe78f0 TESTDRV: enter TESTDRV_Write

    c0fad768 00 e3fe78f0 TESTDRV: enter DRVSHELL_Write

    c0fad768 00 e3fe78f0 TESTDRV: status = (0)

    c0fad768 00 e3fe78f0 TESTDRV: exit  DRVSHELL_Read

    c0fad768 00 e3fe78f0 TESTDRV: exit  DRVSHELL_CompleteIO

    c0fad768 00 e3fe78f0 TESTDRV: enter DRVSHELL_CompleteIO

    c0fad768 00 e3fe78f0 TESTDRV: exit  TESTDRV_Read

    c0fad768 00 e3fe78f0 TESTDRV: enter TESTDRV_Read

    c0fad768 00 e3fe78f0 TESTDRV: enter DRVSHELL_Read

    c0fad768 00 e3fe78f0 TESTDRV: status = (0)

    c0fad768 00 e3fe78f0 TESTDRV: exit  DRVSHELL_Write

    c0fad768 00 e3fe78f0 TESTDRV: exit  DRVSHELL_CompleteIO

    c0fad768 00 e3fe78f0 TESTDRV: enter DRVSHELL_CompleteIO

    c0fad768 00 e3fe78f0 TESTDRV: ext  TESTDRV_Write

    c0fad768 00 e3fe78f0 TESTDRV: RtlCopyMemory copied 0x000003ee bytes in 0x00000764 cycles

    c0fad768 00 e3fe78f0 TESTDRV: enter TESTDRV_Write

    c0fad768 00 e3fe78f0 TESTDRV: enter DRVSHELL_Write

    c0fad768 00 e3fe78e3 TESTDRV: status = (0)

     

    9 Attached Devices(附加设备)

     

    This allows a ring 3 application to get information about all of the devices attached to the driver. Information includes the symbolic link name, device object, device object IRP count and device object byte count.

     

    10 Diagnostic IOCTLs(诊断IOCTL

     

    Diagnostic IOCTLs are provided for dumping logs, history tables, and device and driver information. All of these IOCTLs return a buffer of ASCII characters with each line delimited by a carriage return. The information is extracted with IOCTL calls from the ring 3 application. Here is what a log file dumping all of the information looks like:

     

     

     

    Driver:    TESTDRV

     

    Version:      1.00

     

    Memory Allocated:          0x00000518

    Maximum Memory Allocated:  0x000014b4

    MemAlloc Count:            0x00167b2d

    MemFree Count:             0x00167b27

    MemAlloc Fail Count:       0x00000000

    MemFree Fail Count:        0x00000000

     

     

     

    Attached Devices

     

    Device              Device Obj  IRPs Complete   Byte Count

     

    TESTDRV000          0xc0fa1d68  0x002cf640      0x5a0032a2

    TESTDRV             0xc0fa0cc8  0x0000000d      0x000020cb

     

     

     

    IRP History

     

    Time Stamp          Device Obj  IRP         Func    Data

     

    0x01bbd8d8bd2b55d0  0xc0fa1d68  0xc0fa5ee0  READ    1b 1b 1b 1b 1b 1b 1b 1b

    0x01bbd8d8bd2b55d0  0xc0fa1d68  0xc0fa5ee0  WRITE   1c 1c 1c 1c 1c 1c 1c 1c

    0x01bbd8d8bd2b55d0  0xc0fa1d68  0xc0fa5ee0  WRITE   1c 1c 1c 1c 1c 1c 1c 1c

    0x01bbd8d8bd2b55d0  0xc0fa1d68  0xc0fa5ee0  READ    1b 1b 1b 1b 1b 1b 1b 1b

    0x01bbd8d8bd2b55d0  0xc0fa1d68  0xc0fa5ee0  READ    1c 1c 1c 1c 1c 1c 1c 1c

    0x01bbd8d8bd2b55d0  0xc0fa1d68  0xc0fa5ee0  WRITE   1d 1d 1d 1d 1d 1d 1d 1d

    0x01bbd8d8bd2b55d0  0xc0fa1d68  0xc0fa5ee0  WRITE   1d 1d 1d 1d 1d 1d 1d 1d

    0x01bbd8d8bd2b55d0  0xc0fa1d68  0xc0fa5ee0  READ    1c 1c 1c 1c