zl程序教程

您现在的位置是:首页 >  其他

当前栏目

聊一聊VPP常用的定位手段(2)

定位 常用 手段 Vpp 聊一聊
2023-06-13 09:16:29 时间

前段时间有人在微信群询问通过vlib_frame_t地址,如果获取到报文vlib_buffer_t结构信息。本文以ping回应报文处理流程中节点ip4_icmp_input为例简单分析一下:

下面是ip4_icmp_input节点中获取获取vlib_buffer_t结构的代码简化流程,主要分为2步:1、获取报文缓冲区的索引。2、通过索引获取到vlib_buffer_t结构地址。

static uword
ip4_icmp_input (vlib_main_t * vm,
        vlib_node_runtime_t * node, vlib_frame_t * frame)
{
  //1、获取报文缓冲区的索引
  u32 *from = vlib_frame_vector_args (frame);
  //2、通过索引获取到vlib_buffer_t结构地址。
  vlib_buffer_t *p0 = vlib_get_buffer (vm, from[0]);

下面通过gdb调试打印vlib_buffer_t结构信息。 1、获取报文缓冲区索引,将vlib_frame_vector_args 函数展开。

/*返回数值等于x四舍五入下一个2的次幂(以pow2对齐)*/
always_inline uword round_pow2 (uword x, uword pow2)
{
  return (x + pow2 - 1) & ~(pow2 - 1);
}
/* Byte alignment for vector arguments. 
 * 16字节对齐*/
#define VLIB_FRAME_VECTOR_ALIGN (1 << 4)
always_inline u32 vlib_frame_vector_byte_offset (u32 scalar_size)
{
  return round_pow2 (sizeof (vlib_frame_t) + scalar_size,
             VLIB_FRAME_VECTOR_ALIGN);
}
always_inline void * vlib_frame_vector_args (vlib_frame_t * f)
{
  return (void *) f + vlib_frame_vector_byte_offset (f->scalar_size);
}

关于f->scalar_size的大小,取决于node节点声明时填充的scalar_size大小,如下面ip4-icmp-input对应node节点全局变量scalar_size未进行赋值也就是0。

VLIB_REGISTER_NODE (ip4_icmp_input_node) = {
  .function = ip4_icmp_input,
  .name = "ip4-icmp-input",

  .vector_size = sizeof (u32),

  .format_trace = format_icmp_input_trace,

  .n_errors = ARRAY_LEN (icmp_error_strings),
  .error_strings = icmp_error_strings,

  .n_next_nodes = 1,
  .next_nodes = {
    [ICMP_INPUT_NEXT_ERROR] = "ip4-punt",
  },
};

目前vpp代码中node节点赋值scalar_size,大概用途是用于存储node节点向下一个node节点传递一些私有数据。目前只有接口interface-tx节点中有使用,用于interface-output节点赋值一些私有信息给interface-tx节点有使用。在vnet_register_interface节点函数中对node节点的参数r.scalar_size = sizeof (vnet_hw_if_tx_frame_t)进行了赋值。没有过多研究,感兴趣的可以深入一下代码。 所以我们可以计算出scalar_size为0时,vlib_frame_vector_byte_offset(0)的大小为16.

(gdb) p round_pow2 (sizeof (vlib_frame_t),16)
$3 = 16

所有我们可以查询到from[0]的大小。

(gdb) bt
#0  ip4_icmp_input (vm=0x7fffb68e0680, node=0x7fffb71b0600, frame=0x7fffb7c80400)
    at /home/jinsh/workspace/vpp/src/vnet/ip/icmp4.c:150
#1  0x00007ffff6ea41a9 in dispatch_node (vm=0x7fffb68e0680, node=0x7fffb71b0600, 
    type=VLIB_NODE_TYPE_INTERNAL, dispatch_state=VLIB_NODE_STATE_POLLING, 
    frame=0x7fffb7c80400, last_time_stamp=172750623838647)
    at /home/jinsh/workspace/vpp/src/vlib/main.c:1024
(gdb) p frame[0]
$4 = {frame_flags = 6, flags = 0, scalar_size = 0 '\000', vector_size = 4 '\004', 
  n_vectors = 1, #表示当前值存储一个报文。
  arguments = 0x7fffb7c7fac8 "\376\376\376\376\376\376\376\376\203\272\t"}
//由此我们可以查询到from[0]的大小。
(gdb) p ((u32*)((void *) ((void *)frame + 16)))[0]
$5 = 637571

2、通过索引获取到vlib_buffer_t结构地址。 同样先看一下代码

#define uword_to_pointer(u,type) ((type) (clib_address_t) (u))
always_inline void *
vlib_buffer_ptr_from_index (uword buffer_mem_start, u32 buffer_index,
                uword offset)
{
  offset += ((uword) buffer_index) << CLIB_LOG2_CACHE_LINE_BYTES;
  return uword_to_pointer (buffer_mem_start + offset, vlib_buffer_t *);
}
always_inline vlib_buffer_t *
vlib_get_buffer (vlib_main_t * vm, u32 buffer_index)
{
  vlib_buffer_main_t *bm = vm->buffer_main;
  vlib_buffer_t *b;

  b = vlib_buffer_ptr_from_index (bm->buffer_mem_start, buffer_index, 0);
//验证b是否正确。
  vlib_buffer_validate (vm, b);
  return b;
}

对应gdb调试代码如下:

(gdb) p (vlib_buffer_t *)((void *)(vm->buffer_main.buffer_mem_start+ ($5 <<6)))
$15 = (vlib_buffer_t *) 0x10026ea0c0
(gdb) p *$15
$16 = {{cacheline0 = 0x10026ea0c0 "\016", current_data = 14, current_length = 96, 
    flags = 3222798348, flow_id = 0, ref_count = 1 '\001', 
    buffer_pool_index = 0 '\000', error = 3043, next_buffer = 0, {
      current_config_index = 0, punt_reason = 0}, opaque = {1, 4294967295, 917504, 0, 
      7, 13, 0, 0, 0, 1}, template_end = 0x10026ea100 "", 
    second_half = 0x10026ea100 "", trace_handle = 0, 
    total_length_not_including_first_buffer = 0, opaque2 = {0 <repeats 14 times>}, 
    headroom = 0x10026ea140 "", pre_data = '\000' <repeats 127 times>, 
    data = 0x10026ea1c0 ""}, as_u8x16 = {{14, 0, 96, 0, 12, 0, 24, 192, 0, 0, 0, 0, 1, 
      0, 227, 11}, {0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 255, 255, 255, 255}, {0, 0, 14, 
      0, 0, 0, 0, 0, 7, 0, 0, 0, 13, 0, 0, 0}, {0 <repeats 12 times>, 1, 0, 0, 0}}}

另外还有两个宏定义就是通过vlib_buffer_t和rte_mbuf之间的转换

#define rte_mbuf_from_vlib_buffer(x) (((struct rte_mbuf *)x) - 1)
#define vlib_buffer_from_rte_mbuf(x) ((vlib_buffer_t *)(x+1))

我们查询获取到rte_mbuf的地址。

(gdb) p (((struct rte_mbuf *)$15) - 1)
$17 = (struct rte_mbuf *) 0x10026ea040
(gdb) p *(((struct rte_mbuf *)$15) - 1)
$18 = {cacheline0 = 0x10026ea040, buf_addr = 0x10026ea140, buf_iova = 487498048, 
  rearm_data = 0x10026ea050, data_off = 128, refcnt = 1, nb_segs = 1, port = 0, 
  ol_flags = 0, rx_descriptor_fields1 = 0x10026ea060, {packet_type = 0, {
      l2_type = 0 '\000', l3_type = 0 '\000', l4_type = 0 '\000', tun_type = 0 '\000', {
        inner_esp_next_proto = 0 '\000', {inner_l2_type = 0 '\000', 
          inner_l3_type = 0 '\000'}}, inner_l4_type = 0 '\000'}}, pkt_len = 110, 
  data_len = 110, vlan_tci = 0, {hash = {rss = 0, fdir = {{{hash = 0, id = 0}, lo = 0}, 
        hi = 0}, sched = {queue_id = 0, traffic_class = 0 '\000', color = 0 '\000', 
        reserved = 0}, txadapter = {reserved1 = 0, reserved2 = 0, txq = 0}, usr = 0}}, 
  vlan_tci_outer = 0, buf_len = 2176, pool = 0xac021ec80, cacheline1 = 0x10026ea080, 
  next = 0x0, {tx_offload = 0, {l2_len = 0, l3_len = 0, l4_len = 0, tso_segsz = 0, 
      outer_l3_len = 0, outer_l2_len = 0}}, shinfo = 0x0, priv_size = 128, 
  timesync = 0, dynfield1 = {0, 0, 0, 0, 0, 0, 0, 0, 0}}

还有一些比较常用的gdb调试方法,如下:

1、查询vector结构数据的长度。

#define _vec_find(v)    ((vec_header_t *) (v) - 1)
#define _vec_len(v)    (_vec_find(v)->len)
简化完就是
((vec_header_t *) (v) - 1)->len

2、vlib_buffer_t结构中私有字段opaque参数内容。

#define vnet_buffer(b) ((vnet_buffer_opaque_t *) (b)->opaque)

(gdb) p *((vnet_buffer_opaque_t *) ($15)->opaque)
$21 = {sw_if_index = {1, 4294967295}, l2_hdr_offset = 0, l3_hdr_offset = 14, 
  l4_hdr_offset = 0, feature_arc_index = 0 '\000', oflags = (unknown: 0), {ip = {
      adj_index = {7, 13}, {{flow_hash = 0, {save_protocol = 0, fib_index = 0}, 
          save_rewrite_length = 0 '\000', {rx_sw_if_index = 1, rpf_id = 1}}, icmp = {
          type = 0 '\000', code = 0 '\000', data = 0}, reass = {{{next_index = 0, 
              error_next_index = 0}, {owner_thread_index = 0}}, {{{l4_src_port = 0, 
                l4_dst_port = 0, tcp_ack_number = 0, save_rewrite_length = 0 '\000', 
                ip_proto = 0 '\000', icmp_type_or_tcp_flags = 0 '\000', 
                is_non_first_fragment = 0 '\000', l4_layer_truncated = 0 '\000', 
                tcp_seq_number = 1}, {estimated_mtu = 0}}}, {fragment_first = 0, 
            fragment_last = 0, range_first = 0, range_last = 0, next_range_bi = 0, 
            ip6_frag_hdr_offset = 1}}}}, mpls = {pad = {7, 13, 0}, ttl = 0 '\000', 
      exp = 0 '\000', first = 0 '\000', pyld_proto = 0 '\000', rsvd = 0 '\000', 
      save_rewrite_length = 0 '\000', mpls_hdr_length = 0 '\000', bier = {
        n_bytes = 0 '\000'}}, l2 = {feature_bitmap = 7, bd_index = 13, l2fib_sn = 0, 
      l2_len = 0 '\000', shg = 0 '\000', bd_age = 0 '\000'}, l2t = {pad = {7, 13, 0, 
        0}, next_index = 0 '\000', session_index = 1}, l2_classify = {pad = {
        feature_bitmap = 7, bd_index = 13, l2fib_sn = 0, l2_len = 0 '\000', 
        shg = 0 '\000', bd_age = 0 '\000'}, {table_index = 0, opaque_index = 0}, 
      hash = 4294967296}, policer = {pad = {7, 13, 0, 0, 0}, index = 1}, ipsec = {
      __pad = {7, 13, 0}, sad_index = 0, protect_index = 0, thread_index = 1}, map = {
      mtu = 7}, map_t = {map_domain_index = 7, v6 = {saddr = 13, daddr = 0, 
        frag_offset = 0, l4_offset = 0, l4_protocol = 0 '\000'}, checksum_offset = 1, 
      mtu = 0}, ip_frag = {pad = {7, 13}, mtu = 0, next_index = 0 '\000', 
      flags = 0 '\000'}, cop = {current_config_index = 7}, lisp = {overlay_afi = 7}, 
    tcp = {connection_index = 7, {seq_number = 13, next_node_opaque = 13}, seq_end = 0, 
      ack_number = 0, hdr_offset = 0, data_offset = 0, data_len = 1, flags = 0 '\000'}, 
    snat = {flags = 7, required_thread_index = 13}, unused = {7, 13, 0, 0, 0, 1}}}

3、vlib_buffer_t结构中私有字段2 opaque2参数内容。

#define vnet_buffer2(b) ((vnet_buffer_opaque2_t *) (b)->opaque2)
(gdb) p ((vnet_buffer_opaque2_t *) ($15)->opaque2)
$22 = (vnet_buffer_opaque2_t *) 0x10026ea108
(gdb) p *((vnet_buffer_opaque2_t *) ($15)->opaque2)
$23 = {qos = {bits = 0 '\000', source = 0 '\000'}, loop_counter = 0 '\000', 
  __unused = "", gbp = {__unused = 0 '\000', flags = 0 '\000', sclass = 0}, {
    gso_size = 0, gso_l4_hdr_sz = 0, outer_l3_hdr_offset = 0, outer_l4_hdr_offset = 0}, 
  nat = {arc_next = 0, {cached_session_index = 0, cached_dst_nat_session_index = 0}}, {{
      pad = {0}, pg_replay_timestamp = 0}, unused = {0, 0, 0, 0, 0, 0, 0, 0}}}

上面分析完之后,是不是感觉也很简单,只是把一些常用的gdb定位手段总结以提高定位效率。当然还有很多,欢迎加入微信群一起讨论。