确定性自动机检测

由 dot2k 创建的 RV 监控器文件,名称为“$MODEL_NAME.c”,包含一个专门用于检测的部分。

在 [1] 中创建的 wip.dot 监控器的示例中,它看起来像

/*
 * This is the instrumentation part of the monitor.
 *
 * This is the section where manual work is required. Here the kernel events
 * are translated into model's event.
 *
 */
static void handle_preempt_disable(void *data, /* XXX: fill header */)
{
      da_handle_event_wip(preempt_disable_wip);
}

static void handle_preempt_enable(void *data, /* XXX: fill header */)
{
      da_handle_event_wip(preempt_enable_wip);
}

static void handle_sched_waking(void *data, /* XXX: fill header */)
{
      da_handle_event_wip(sched_waking_wip);
}

static int enable_wip(void)
{
      int retval;

      retval = da_monitor_init_wip();
      if (retval)
              return retval;

      rv_attach_trace_probe("wip", /* XXX: tracepoint */, handle_preempt_disable);
      rv_attach_trace_probe("wip", /* XXX: tracepoint */, handle_preempt_enable);
      rv_attach_trace_probe("wip", /* XXX: tracepoint */, handle_sched_waking);

      return 0;
}

该部分顶部的注释解释了一般概念:检测部分将内核事件转换为模型的事件

跟踪回调函数

前三个函数是 wip 模型中三个事件的每个回调处理函数的起点。 开发人员不一定需要使用它们:它们只是起点。

使用以下示例

void handle_preempt_disable(void *data, /* XXX: fill header */)
{
       da_handle_event_wip(preempt_disable_wip);
}

模型中的 preempt_disable 事件直接连接到 preemptirq:preempt_disable。 preemptirq:preempt_disable 事件具有以下签名,来自 include/trace/events/preemptirq.h

TP_PROTO(unsigned long ip, unsigned long parent_ip)

因此,handle_preempt_disable() 函数将如下所示

void handle_preempt_disable(void *data, unsigned long ip, unsigned long parent_ip)

在这种情况下,内核事件与自动机事件一对一转换,实际上,此函数不需要其他更改。

下一个处理函数 handle_preempt_enable() 具有与 handle_preempt_disable() 相同的参数列表。 不同之处在于,preempt_enable 事件将用于将系统与模型同步。

最初,模型被置于初始状态。但是,系统可能处于也可能不处于初始状态。 监控器在知道系统已达到初始状态之前无法开始处理事件。 否则,监控器和系统可能会不同步。

查看自动机定义,可以发现系统和模型预计会在 preempt_enable 执行后返回初始状态。 因此,它可用于在监控部分初始化时同步系统和模型。

启动通过特殊的处理函数告知,即 “da_handle_start_event_$(MONITOR_NAME)(event)”,在本例中

da_handle_start_event_wip(preempt_enable_wip);

因此,回调函数将如下所示

void handle_preempt_enable(void *data, unsigned long ip, unsigned long parent_ip)
{
      da_handle_start_event_wip(preempt_enable_wip);
}

最后,“handle_sched_waking()”将如下所示

void handle_sched_waking(void *data, struct task_struct *task)
{
      da_handle_event_wip(sched_waking_wip);
}

解释留给读者作为练习。

启用和禁用函数

dot2k 会自动创建两个特殊函数

enable_$(MONITOR_NAME)()
disable_$(MONITOR_NAME)()

这些函数分别在启用和禁用监控器时调用。

它们应用于将检测附加分离到正在运行的系统。开发人员必须将所有需要将监控器附加分离到系统的内容添加到相关函数中。

对于 wip 情况,这些函数被命名为

enable_wip()
disable_wip()

但不需要任何更改,因为:默认情况下,这些函数附加分离 tracepoints_to_attach,这对于这种情况来说足够了。

检测助手

为了完成检测,需要在监控器启用阶段将处理函数附加到内核事件。

RV 接口也简化了这一步骤。例如,宏 “rv_attach_trace_probe()” 用于将 wip 模型事件连接到相关的内核事件。 dot2k 会自动在启用阶段为每个模型事件添加 “rv_attach_trace_probe()” 函数调用,作为建议。

例如,来自 wip 示例模型

static int enable_wip(void)
{
      int retval;

      retval = da_monitor_init_wip();
      if (retval)
              return retval;

      rv_attach_trace_probe("wip", /* XXX: tracepoint */, handle_preempt_enable);
      rv_attach_trace_probe("wip", /* XXX: tracepoint */, handle_sched_waking);
      rv_attach_trace_probe("wip", /* XXX: tracepoint */, handle_preempt_disable);

      return 0;
}

然后需要在禁用阶段分离探测。

[1] wip 模型在以下位置介绍

Documentation/trace/rv/deterministic_automata.rst

wip 监控器在以下位置介绍

Documentation/trace/rv/da_monitor_synthesis.rst