使用日志记录跟踪重要事件

通常,数据仅通过触发事件、微型驱动程序的处理和缓冲区完成向下游移动。 若要查明挂起或停止的原因,请使用以下命令:

  • 检查是否存在不匹配 的 KsGateXxx 调用。

  • 检查是否省略了 KsXxxAttemptProcessing 调用。

  • 查找与触发事件相关的代码中的问题,包括引用问题流的 pin 标志或调用 KsPinAttemptProcessing 的代码

  • 在与处理调度相关的代码中查找问题,尤其是在它排队到硬件以及创建克隆指针的位置。

  • 在代码中查找与驱动程序的延迟过程调用相关的问题, (DPC) ,尤其是在缓冲区已完成或对 KsStreamPointerDelete 进行任何调用时。

  • 在流的启动代码中查找问题。

收集此信息的最有效方法是记录受影响区域中的所有内容,包括处理、缓冲区获取 ((例如克隆和编程硬件) 、缓冲区释放 ((如删除克隆) )以及任何门操作。 此信息大多高度依赖于计时,需要基于内存的日志记录或 ETW。

若要维护基于内存的滚动日志,请使用以下代码:

typedef struct _LOGENTRY {
    ULONG Tag;
    ULONG Arg[3];
} LOGENTRY, *PLOGENTRY;
#define LOGSIZE 2048
LONG g_LogCount;
LOGENTRY g_Log [LOGSIZE];
#define LOG(tag,arg1,arg2,arg3) do { \
    LONG i = InterlockedIncrement (&g_LogCount) % LOGSIZE; \
    g_Log [i].Tag = tag; \
    g_Log [i].Arg [0] = (ULONG)(arg1); \
    g_Log [i].Arg [1] = (ULONG)(arg2); \
    g_Log [i].Arg [2] = (ULONG)(arg3); \
} while (0)

然后,使用简单的“dc g_Log”在调试器中查看 g_Log 数组的内容。

以下示例使用上述基于内存的方案来确定处理停止的原因。 输出来自 graphedt 中的 AVStream 流式处理方案。 记录了以下微型驱动程序事件:

缩写 说明

Strt

当微型驱动程序首先从微型驱动程序的 “开始”调度 中排队设备缓冲区时,会发生此事件。

中国<

此事件在微型驱动程序 的进程调度开始时 发生。

AddB

当微型驱动程序从其 进程 调度中将缓冲区排队到设备时,会发生此事件。

DPC<

此事件发生在微型驱动程序的 CallOnDPC 的开头。 它指示缓冲区完成。

Atmp

当微型驱动程序从 DPC 内部调用 KsPinAttemptProcessing 时,会发生此事件。

Dele

当微型驱动程序从 DPC 内部调用 以删除克隆流指针时,会发生此事件。

日志摘录如下:

f9494b80  3c435044 816e2c90 00000000 00000000  DPC<.,n.........
f9494b90  656c6544 816e2c90 81750260 00000000  Dele.,n.`.u.....
f9494ba0  706d7441 816e2c90 ffa4d418 00000000  Atmp.,n.........
f9494bb0  3c637250 819c1f00 00000000 00000000  Prc<............
f9494bc0  42646441 819c1f00 ffa2eb08 00000000  AddB............
f9494bd0  3c435044 816e2c90 00000000 00000000  DPC<.,n.........
f9494be0  656c6544 816e2c90 ffa80348 00000000  Dele.,n.H.......
f9494bf0  706d7441 816e2c90 ffa4d418 00000000  Atmp.,n.........
f9494c00  3c637250 819c1f00 00000000 00000000  Prc<............
f9494c10  42646441 819c1f00 ffa3d9b8 00000000  AddB............

第一个日志摘录代表正常的流式处理状态。 在第一行中,调用微型驱动程序的 CallOnDPC 以完成缓冲区 (DPC<) 。 (Dele) 中删除缓冲区,如果队列中有任何未处理的缓冲区 (Atmp) ,则调用 KsPinAttemptProcessing 向前移动前边缘。 在这种情况下,通过调用 中国<) 的进程调度 (可以看出。 (AddB) 将更多缓冲区添加到队列中,整个方案将重复执行。

下一个摘录包括停止发生前日志中的最后一个条目。

f949b430  3c435044 816e2c90 00000000 00000000  DPC<.,n.........
f949b440  656c6544 816e2c90 ffac4de8 00000000  Dele.,n..M......
f949b450  706d7441 816e2c90 ffa4d418 00000000  Atmp.,n.........
f949b460  3c435044 816e2c90 00000000 00000000  DPC<.,n.........
f949b470  656c6544 816e2c90 816ffc80 00000000  Dele.,n...o.....
f949b480  706d7441 816e2c90 ffa4d418 00000000  Atmp.,n.........
f949b490  3c435044 816e2c90 00000000 00000000  DPC<.,n.........
f949b4a0  656c6544 816e2c90 ffa80348 00000000  Dele.,n.H.......
f949b4b0  706d7441 816e2c90 ffa4d418 00000000  Atmp.,n.........
f949b4c0  3c435044 816e2c90 00000000 00000000  DPC<.,n.........
f949b4d0  656c6544 816e2c90 8174e1c0 00000000  Dele.,n...t.....
f949b4e0  706d7441 816e2c90 ffa4d418 00000000  Atmp.,n.........

在此示例中, (DPC<) 的重复实例指示多个缓冲区正在完成,但队列中没有未处理的缓冲区,因此不会调用进程调度, (没有 Prc<) 表示。 事实上,队列中所有已处理的缓冲区都已完成,这显然是在可以添加任何新的未处理的缓冲区之前完成的。 由于应用程序已在 (运行,因此不会) 调用 Start ,并且不会调用 CallOnDPC (因为没有准备好) 完成的已处理缓冲区,因此任何新缓冲区显然在前缘之前累积,等待处理,且没有任何启动处理。

问题是已设置KSPIN_FLAG_DO_NOT_INITIATE_PROCESSING标志。 设置此标志后,只能通过调用 StartCallOnDPC 进行处理。 如果未设置此标志,则每当将新缓冲区添加到队列时,都会启动处理。