bpf: Add unit tests for bpf_line_info
authorMartin KaFai Lau <kafai@fb.com>
Sat, 8 Dec 2018 00:42:28 +0000 (16:42 -0800)
committerAlexei Starovoitov <ast@kernel.org>
Sun, 9 Dec 2018 21:54:38 +0000 (13:54 -0800)
Add unit tests for bpf_line_info for both BPF_PROG_LOAD and
BPF_OBJ_GET_INFO_BY_FD.

jit enabled:
[root@arch-fb-vm1 bpf]# ./test_btf -k 0
BTF prog info raw test[5] (line_info (No subprog)): OK
BTF prog info raw test[6] (line_info (No subprog. insn_off >= prog->len)): OK
BTF prog info raw test[7] (line_info (No subprog. zero tailing line_info): OK
BTF prog info raw test[8] (line_info (No subprog. nonzero tailing line_info)): OK
BTF prog info raw test[9] (line_info (subprog)): OK
BTF prog info raw test[10] (line_info (subprog + func_info)): OK
BTF prog info raw test[11] (line_info (subprog. missing 1st func line info)): OK
BTF prog info raw test[12] (line_info (subprog. missing 2nd func line info)): OK
BTF prog info raw test[13] (line_info (subprog. unordered insn offset)): OK

jit disabled:
BTF prog info raw test[5] (line_info (No subprog)): not jited. skipping jited_line_info check. OK
BTF prog info raw test[6] (line_info (No subprog. insn_off >= prog->len)): OK
BTF prog info raw test[7] (line_info (No subprog. zero tailing line_info): not jited. skipping jited_line_info check. OK
BTF prog info raw test[8] (line_info (No subprog. nonzero tailing line_info)): OK
BTF prog info raw test[9] (line_info (subprog)): not jited. skipping jited_line_info check. OK
BTF prog info raw test[10] (line_info (subprog + func_info)): not jited. skipping jited_line_info check. OK
BTF prog info raw test[11] (line_info (subprog. missing 1st func line info)): OK
BTF prog info raw test[12] (line_info (subprog. missing 2nd func line info)): OK
BTF prog info raw test[13] (line_info (subprog. unordered insn offset)): OK

Signed-off-by: Martin KaFai Lau <kafai@fb.com>
Acked-by: Yonghong Song <yhs@fb.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
tools/testing/selftests/bpf/test_btf.c

index 8d5777c89620bce641214a3f15adeff4c507a26a..7707273736ac6fae61922f94a7250960adaca3f7 100644 (file)
@@ -108,7 +108,7 @@ static int __base_pr(const char *format, ...)
 #define BTF_END_RAW 0xdeadbeef
 #define NAME_TBD 0xdeadb33f
 
-#define MAX_NR_RAW_TYPES 1024
+#define MAX_NR_RAW_U32 1024
 #define BTF_LOG_BUF_SIZE 65535
 
 static struct args {
@@ -137,7 +137,7 @@ struct btf_raw_test {
        const char *str_sec;
        const char *map_name;
        const char *err_str;
-       __u32 raw_types[MAX_NR_RAW_TYPES];
+       __u32 raw_types[MAX_NR_RAW_U32];
        __u32 str_sec_size;
        enum bpf_map_type map_type;
        __u32 key_size;
@@ -156,6 +156,9 @@ struct btf_raw_test {
        int str_len_delta;
 };
 
+#define BTF_STR_SEC(str) \
+       .str_sec = str, .str_sec_size = sizeof(str)
+
 static struct btf_raw_test raw_tests[] = {
 /* enum E {
  *     E0,
@@ -1858,11 +1861,11 @@ static const char *get_next_str(const char *start, const char *end)
        return start < end - 1 ? start + 1 : NULL;
 }
 
-static int get_type_sec_size(const __u32 *raw_types)
+static int get_raw_sec_size(const __u32 *raw_types)
 {
        int i;
 
-       for (i = MAX_NR_RAW_TYPES - 1;
+       for (i = MAX_NR_RAW_U32 - 1;
             i >= 0 && raw_types[i] != BTF_END_RAW;
             i--)
                ;
@@ -1874,7 +1877,8 @@ static void *btf_raw_create(const struct btf_header *hdr,
                            const __u32 *raw_types,
                            const char *str,
                            unsigned int str_sec_size,
-                           unsigned int *btf_size)
+                           unsigned int *btf_size,
+                           const char **ret_next_str)
 {
        const char *next_str = str, *end_str = str + str_sec_size;
        unsigned int size_needed, offset;
@@ -1883,7 +1887,7 @@ static void *btf_raw_create(const struct btf_header *hdr,
        uint32_t *ret_types;
        void *raw_btf;
 
-       type_sec_size = get_type_sec_size(raw_types);
+       type_sec_size = get_raw_sec_size(raw_types);
        if (CHECK(type_sec_size < 0, "Cannot get nr_raw_types"))
                return NULL;
 
@@ -1922,6 +1926,8 @@ static void *btf_raw_create(const struct btf_header *hdr,
        ret_hdr->str_len = str_sec_size;
 
        *btf_size = size_needed;
+       if (ret_next_str)
+               *ret_next_str = next_str;
 
        return raw_btf;
 }
@@ -1941,7 +1947,7 @@ static int do_test_raw(unsigned int test_num)
                                 test->raw_types,
                                 test->str_sec,
                                 test->str_sec_size,
-                                &raw_btf_size);
+                                &raw_btf_size, NULL);
 
        if (!raw_btf)
                return -1;
@@ -2018,7 +2024,7 @@ static int test_raw(void)
 struct btf_get_info_test {
        const char *descr;
        const char *str_sec;
-       __u32 raw_types[MAX_NR_RAW_TYPES];
+       __u32 raw_types[MAX_NR_RAW_U32];
        __u32 str_sec_size;
        int btf_size_delta;
        int (*special_test)(unsigned int test_num);
@@ -2098,7 +2104,7 @@ static int test_big_btf_info(unsigned int test_num)
                                 test->raw_types,
                                 test->str_sec,
                                 test->str_sec_size,
-                                &raw_btf_size);
+                                &raw_btf_size, NULL);
 
        if (!raw_btf)
                return -1;
@@ -2182,7 +2188,7 @@ static int test_btf_id(unsigned int test_num)
                                 test->raw_types,
                                 test->str_sec,
                                 test->str_sec_size,
-                                &raw_btf_size);
+                                &raw_btf_size, NULL);
 
        if (!raw_btf)
                return -1;
@@ -2320,7 +2326,7 @@ static int do_test_get_info(unsigned int test_num)
                                 test->raw_types,
                                 test->str_sec,
                                 test->str_sec_size,
-                                &raw_btf_size);
+                                &raw_btf_size, NULL);
 
        if (!raw_btf)
                return -1;
@@ -2849,7 +2855,7 @@ static int do_test_pprint(void)
        fprintf(stderr, "%s......", test->descr);
        raw_btf = btf_raw_create(&hdr_tmpl, test->raw_types,
                                 test->str_sec, test->str_sec_size,
-                                &raw_btf_size);
+                                &raw_btf_size, NULL);
 
        if (!raw_btf)
                return -1;
@@ -3053,16 +3059,23 @@ static int test_pprint(void)
        return err;
 }
 
+#define BPF_LINE_INFO_ENC(insn_off, file_off, line_off, line_num, line_col) \
+       (insn_off), (file_off), (line_off), ((line_num) << 10 | ((line_col) & 0x3ff))
+
 static struct prog_info_raw_test {
        const char *descr;
        const char *str_sec;
-       __u32 raw_types[MAX_NR_RAW_TYPES];
+       const char *err_str;
+       __u32 raw_types[MAX_NR_RAW_U32];
        __u32 str_sec_size;
        struct bpf_insn insns[MAX_INSNS];
        __u32 prog_type;
        __u32 func_info[MAX_SUBPROGS][2];
        __u32 func_info_rec_size;
        __u32 func_info_cnt;
+       __u32 line_info[MAX_NR_RAW_U32];
+       __u32 line_info_rec_size;
+       __u32 nr_jited_ksyms;
        bool expected_prog_load_failure;
 } info_raw_tests[] = {
 {
@@ -3093,6 +3106,7 @@ static struct prog_info_raw_test {
        .func_info = { {0, 5}, {3, 6} },
        .func_info_rec_size = 8,
        .func_info_cnt = 2,
+       .line_info = { BTF_END_RAW },
 },
 
 {
@@ -3123,6 +3137,7 @@ static struct prog_info_raw_test {
        .func_info = { {0, 5}, {3, 6} },
        .func_info_rec_size = 4,
        .func_info_cnt = 2,
+       .line_info = { BTF_END_RAW },
        .expected_prog_load_failure = true,
 },
 
@@ -3154,6 +3169,7 @@ static struct prog_info_raw_test {
        .func_info = { {0, 5}, {3, 6} },
        .func_info_rec_size = 8,
        .func_info_cnt = 1,
+       .line_info = { BTF_END_RAW },
        .expected_prog_load_failure = true,
 },
 
@@ -3185,6 +3201,278 @@ static struct prog_info_raw_test {
        .func_info = { {0, 5}, {2, 6} },
        .func_info_rec_size = 8,
        .func_info_cnt = 2,
+       .line_info = { BTF_END_RAW },
+       .expected_prog_load_failure = true,
+},
+
+{
+       .descr = "line_info (No subprog)",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(NAME_TBD, BTF_INT_SIGNED, 0, 32, 4),   /* [1] */
+               BTF_END_RAW,
+       },
+       BTF_STR_SEC("\0int\0int a=1;\0int b=2;\0return a + b;\0return a + b;"),
+       .insns = {
+               BPF_MOV64_IMM(BPF_REG_0, 1),
+               BPF_MOV64_IMM(BPF_REG_1, 2),
+               BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_1),
+               BPF_EXIT_INSN(),
+       },
+       .prog_type = BPF_PROG_TYPE_TRACEPOINT,
+       .func_info_cnt = 0,
+       .line_info = {
+               BPF_LINE_INFO_ENC(0, 0, NAME_TBD, 1, 10),
+               BPF_LINE_INFO_ENC(1, 0, NAME_TBD, 2, 9),
+               BPF_LINE_INFO_ENC(2, 0, NAME_TBD, 3, 8),
+               BPF_LINE_INFO_ENC(3, 0, NAME_TBD, 4, 7),
+               BTF_END_RAW,
+       },
+       .line_info_rec_size = sizeof(struct bpf_line_info),
+       .nr_jited_ksyms = 1,
+},
+
+{
+       .descr = "line_info (No subprog. insn_off >= prog->len)",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(NAME_TBD, BTF_INT_SIGNED, 0, 32, 4),   /* [1] */
+               BTF_END_RAW,
+       },
+       BTF_STR_SEC("\0int\0int a=1;\0int b=2;\0return a + b;\0return a + b;"),
+       .insns = {
+               BPF_MOV64_IMM(BPF_REG_0, 1),
+               BPF_MOV64_IMM(BPF_REG_1, 2),
+               BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_1),
+               BPF_EXIT_INSN(),
+       },
+       .prog_type = BPF_PROG_TYPE_TRACEPOINT,
+       .func_info_cnt = 0,
+       .line_info = {
+               BPF_LINE_INFO_ENC(0, 0, NAME_TBD, 1, 10),
+               BPF_LINE_INFO_ENC(1, 0, NAME_TBD, 2, 9),
+               BPF_LINE_INFO_ENC(2, 0, NAME_TBD, 3, 8),
+               BPF_LINE_INFO_ENC(3, 0, NAME_TBD, 4, 7),
+               BPF_LINE_INFO_ENC(4, 0, 0, 5, 6),
+               BTF_END_RAW,
+       },
+       .line_info_rec_size = sizeof(struct bpf_line_info),
+       .nr_jited_ksyms = 1,
+       .err_str = "line_info[4].insn_off",
+       .expected_prog_load_failure = true,
+},
+
+{
+       .descr = "line_info (No subprog. zero tailing line_info",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(NAME_TBD, BTF_INT_SIGNED, 0, 32, 4),   /* [1] */
+               BTF_END_RAW,
+       },
+       BTF_STR_SEC("\0int\0int a=1;\0int b=2;\0return a + b;\0return a + b;"),
+       .insns = {
+               BPF_MOV64_IMM(BPF_REG_0, 1),
+               BPF_MOV64_IMM(BPF_REG_1, 2),
+               BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_1),
+               BPF_EXIT_INSN(),
+       },
+       .prog_type = BPF_PROG_TYPE_TRACEPOINT,
+       .func_info_cnt = 0,
+       .line_info = {
+               BPF_LINE_INFO_ENC(0, 0, NAME_TBD, 1, 10), 0,
+               BPF_LINE_INFO_ENC(1, 0, NAME_TBD, 2, 9), 0,
+               BPF_LINE_INFO_ENC(2, 0, NAME_TBD, 3, 8), 0,
+               BPF_LINE_INFO_ENC(3, 0, NAME_TBD, 4, 7), 0,
+               BTF_END_RAW,
+       },
+       .line_info_rec_size = sizeof(struct bpf_line_info) + sizeof(__u32),
+       .nr_jited_ksyms = 1,
+},
+
+{
+       .descr = "line_info (No subprog. nonzero tailing line_info)",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(NAME_TBD, BTF_INT_SIGNED, 0, 32, 4),   /* [1] */
+               BTF_END_RAW,
+       },
+       BTF_STR_SEC("\0int\0int a=1;\0int b=2;\0return a + b;\0return a + b;"),
+       .insns = {
+               BPF_MOV64_IMM(BPF_REG_0, 1),
+               BPF_MOV64_IMM(BPF_REG_1, 2),
+               BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_1),
+               BPF_EXIT_INSN(),
+       },
+       .prog_type = BPF_PROG_TYPE_TRACEPOINT,
+       .func_info_cnt = 0,
+       .line_info = {
+               BPF_LINE_INFO_ENC(0, 0, NAME_TBD, 1, 10), 0,
+               BPF_LINE_INFO_ENC(1, 0, NAME_TBD, 2, 9), 0,
+               BPF_LINE_INFO_ENC(2, 0, NAME_TBD, 3, 8), 0,
+               BPF_LINE_INFO_ENC(3, 0, NAME_TBD, 4, 7), 1,
+               BTF_END_RAW,
+       },
+       .line_info_rec_size = sizeof(struct bpf_line_info) + sizeof(__u32),
+       .nr_jited_ksyms = 1,
+       .err_str = "nonzero tailing record in line_info",
+       .expected_prog_load_failure = true,
+},
+
+{
+       .descr = "line_info (subprog)",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(NAME_TBD, BTF_INT_SIGNED, 0, 32, 4),   /* [1] */
+               BTF_END_RAW,
+       },
+       BTF_STR_SEC("\0int\0int a=1+1;\0return func(a);\0b+=1;\0return b;"),
+       .insns = {
+               BPF_MOV64_IMM(BPF_REG_2, 1),
+               BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, 1),
+               BPF_MOV64_REG(BPF_REG_1, BPF_REG_2),
+               BPF_CALL_REL(1),
+               BPF_EXIT_INSN(),
+               BPF_MOV64_REG(BPF_REG_0, BPF_REG_1),
+               BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 1),
+               BPF_EXIT_INSN(),
+       },
+       .prog_type = BPF_PROG_TYPE_TRACEPOINT,
+       .func_info_cnt = 0,
+       .line_info = {
+               BPF_LINE_INFO_ENC(0, 0, NAME_TBD, 1, 10),
+               BPF_LINE_INFO_ENC(2, 0, NAME_TBD, 2, 9),
+               BPF_LINE_INFO_ENC(5, 0, NAME_TBD, 3, 8),
+               BPF_LINE_INFO_ENC(7, 0, NAME_TBD, 4, 7),
+               BTF_END_RAW,
+       },
+       .line_info_rec_size = sizeof(struct bpf_line_info),
+       .nr_jited_ksyms = 2,
+},
+
+{
+       .descr = "line_info (subprog + func_info)",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(NAME_TBD, BTF_INT_SIGNED, 0, 32, 4),   /* [1] */
+               BTF_FUNC_PROTO_ENC(1, 1),                       /* [2] */
+                       BTF_FUNC_PROTO_ARG_ENC(NAME_TBD, 1),
+               BTF_FUNC_ENC(NAME_TBD, 2),                      /* [3] */
+               BTF_FUNC_ENC(NAME_TBD, 2),                      /* [4] */
+               BTF_END_RAW,
+       },
+       BTF_STR_SEC("\0int\0x\0sub\0main\0int a=1+1;\0return func(a);\0b+=1;\0return b;"),
+       .insns = {
+               BPF_MOV64_IMM(BPF_REG_2, 1),
+               BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, 1),
+               BPF_MOV64_REG(BPF_REG_1, BPF_REG_2),
+               BPF_CALL_REL(1),
+               BPF_EXIT_INSN(),
+               BPF_MOV64_REG(BPF_REG_0, BPF_REG_1),
+               BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 1),
+               BPF_EXIT_INSN(),
+       },
+       .prog_type = BPF_PROG_TYPE_TRACEPOINT,
+       .func_info_cnt = 2,
+       .func_info_rec_size = 8,
+       .func_info = { {0, 4}, {5, 3} },
+       .line_info = {
+               BPF_LINE_INFO_ENC(0, 0, NAME_TBD, 1, 10),
+               BPF_LINE_INFO_ENC(2, 0, NAME_TBD, 2, 9),
+               BPF_LINE_INFO_ENC(5, 0, NAME_TBD, 3, 8),
+               BPF_LINE_INFO_ENC(7, 0, NAME_TBD, 4, 7),
+               BTF_END_RAW,
+       },
+       .line_info_rec_size = sizeof(struct bpf_line_info),
+       .nr_jited_ksyms = 2,
+},
+
+{
+       .descr = "line_info (subprog. missing 1st func line info)",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(NAME_TBD, BTF_INT_SIGNED, 0, 32, 4),   /* [1] */
+               BTF_END_RAW,
+       },
+       BTF_STR_SEC("\0int\0int a=1+1;\0return func(a);\0b+=1;\0return b;"),
+       .insns = {
+               BPF_MOV64_IMM(BPF_REG_2, 1),
+               BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, 1),
+               BPF_MOV64_REG(BPF_REG_1, BPF_REG_2),
+               BPF_CALL_REL(1),
+               BPF_EXIT_INSN(),
+               BPF_MOV64_REG(BPF_REG_0, BPF_REG_1),
+               BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 1),
+               BPF_EXIT_INSN(),
+       },
+       .prog_type = BPF_PROG_TYPE_TRACEPOINT,
+       .func_info_cnt = 0,
+       .line_info = {
+               BPF_LINE_INFO_ENC(1, 0, NAME_TBD, 1, 10),
+               BPF_LINE_INFO_ENC(2, 0, NAME_TBD, 2, 9),
+               BPF_LINE_INFO_ENC(5, 0, NAME_TBD, 3, 8),
+               BPF_LINE_INFO_ENC(7, 0, NAME_TBD, 4, 7),
+               BTF_END_RAW,
+       },
+       .line_info_rec_size = sizeof(struct bpf_line_info),
+       .nr_jited_ksyms = 2,
+       .err_str = "missing bpf_line_info for func#0",
+       .expected_prog_load_failure = true,
+},
+
+{
+       .descr = "line_info (subprog. missing 2nd func line info)",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(NAME_TBD, BTF_INT_SIGNED, 0, 32, 4),   /* [1] */
+               BTF_END_RAW,
+       },
+       BTF_STR_SEC("\0int\0int a=1+1;\0return func(a);\0b+=1;\0return b;"),
+       .insns = {
+               BPF_MOV64_IMM(BPF_REG_2, 1),
+               BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, 1),
+               BPF_MOV64_REG(BPF_REG_1, BPF_REG_2),
+               BPF_CALL_REL(1),
+               BPF_EXIT_INSN(),
+               BPF_MOV64_REG(BPF_REG_0, BPF_REG_1),
+               BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 1),
+               BPF_EXIT_INSN(),
+       },
+       .prog_type = BPF_PROG_TYPE_TRACEPOINT,
+       .func_info_cnt = 0,
+       .line_info = {
+               BPF_LINE_INFO_ENC(0, 0, NAME_TBD, 1, 10),
+               BPF_LINE_INFO_ENC(2, 0, NAME_TBD, 2, 9),
+               BPF_LINE_INFO_ENC(6, 0, NAME_TBD, 3, 8),
+               BPF_LINE_INFO_ENC(7, 0, NAME_TBD, 4, 7),
+               BTF_END_RAW,
+       },
+       .line_info_rec_size = sizeof(struct bpf_line_info),
+       .nr_jited_ksyms = 2,
+       .err_str = "missing bpf_line_info for func#1",
+       .expected_prog_load_failure = true,
+},
+
+{
+       .descr = "line_info (subprog. unordered insn offset)",
+       .raw_types = {
+               BTF_TYPE_INT_ENC(NAME_TBD, BTF_INT_SIGNED, 0, 32, 4),   /* [1] */
+               BTF_END_RAW,
+       },
+       BTF_STR_SEC("\0int\0int a=1+1;\0return func(a);\0b+=1;\0return b;"),
+       .insns = {
+               BPF_MOV64_IMM(BPF_REG_2, 1),
+               BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, 1),
+               BPF_MOV64_REG(BPF_REG_1, BPF_REG_2),
+               BPF_CALL_REL(1),
+               BPF_EXIT_INSN(),
+               BPF_MOV64_REG(BPF_REG_0, BPF_REG_1),
+               BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 1),
+               BPF_EXIT_INSN(),
+       },
+       .prog_type = BPF_PROG_TYPE_TRACEPOINT,
+       .func_info_cnt = 0,
+       .line_info = {
+               BPF_LINE_INFO_ENC(0, 0, NAME_TBD, 1, 10),
+               BPF_LINE_INFO_ENC(5, 0, NAME_TBD, 2, 9),
+               BPF_LINE_INFO_ENC(2, 0, NAME_TBD, 3, 8),
+               BPF_LINE_INFO_ENC(7, 0, NAME_TBD, 4, 7),
+               BTF_END_RAW,
+       },
+       .line_info_rec_size = sizeof(struct bpf_line_info),
+       .nr_jited_ksyms = 2,
+       .err_str = "Invalid line_info[2].insn_off",
        .expected_prog_load_failure = true,
 },
 
@@ -3200,6 +3488,46 @@ static size_t probe_prog_length(const struct bpf_insn *fp)
        return len + 1;
 }
 
+static __u32 *patch_name_tbd(const __u32 *raw_u32,
+                            const char *str, __u32 str_off,
+                            unsigned int str_sec_size,
+                            unsigned int *ret_size)
+{
+       int i, raw_u32_size = get_raw_sec_size(raw_u32);
+       const char *end_str = str + str_sec_size;
+       const char *next_str = str + str_off;
+       __u32 *new_u32 = NULL;
+
+       if (raw_u32_size == -1)
+               return ERR_PTR(-EINVAL);
+
+       if (!raw_u32_size) {
+               *ret_size = 0;
+               return NULL;
+       }
+
+       new_u32 = malloc(raw_u32_size);
+       if (!new_u32)
+               return ERR_PTR(-ENOMEM);
+
+       for (i = 0; i < raw_u32_size / sizeof(raw_u32[0]); i++) {
+               if (raw_u32[i] == NAME_TBD) {
+                       next_str = get_next_str(next_str, end_str);
+                       if (CHECK(!next_str, "Error in getting next_str\n")) {
+                               free(new_u32);
+                               return ERR_PTR(-EINVAL);
+                       }
+                       new_u32[i] = next_str - str;
+                       next_str += strlen(next_str);
+               } else {
+                       new_u32[i] = raw_u32[i];
+               }
+       }
+
+       *ret_size = raw_u32_size;
+       return new_u32;
+}
+
 static int test_get_finfo(const struct prog_info_raw_test *test,
                          int prog_fd)
 {
@@ -3283,18 +3611,229 @@ done:
        return err;
 }
 
+static int test_get_linfo(const struct prog_info_raw_test *test,
+                         const void *patched_linfo,
+                         __u32 cnt, int prog_fd)
+{
+       __u32 i, info_len, nr_jited_ksyms, nr_jited_func_lens;
+       __u64 *jited_linfo = NULL, *jited_ksyms = NULL;
+       __u32 rec_size, jited_rec_size, jited_cnt;
+       struct bpf_line_info *linfo = NULL;
+       __u32 cur_func_len, ksyms_found;
+       struct bpf_prog_info info = {};
+       __u32 *jited_func_lens = NULL;
+       __u64 cur_func_ksyms;
+       int err;
+
+       jited_cnt = cnt;
+       rec_size = sizeof(*linfo);
+       jited_rec_size = sizeof(*jited_linfo);
+       if (test->nr_jited_ksyms)
+               nr_jited_ksyms = test->nr_jited_ksyms;
+       else
+               nr_jited_ksyms = test->func_info_cnt;
+       nr_jited_func_lens = nr_jited_ksyms;
+
+       info_len = sizeof(struct bpf_prog_info);
+       err = bpf_obj_get_info_by_fd(prog_fd, &info, &info_len);
+       if (CHECK(err == -1, "err:%d errno:%d", err, errno)) {
+               err = -1;
+               goto done;
+       }
+
+       if (!info.jited_prog_len) {
+               /* prog is not jited */
+               jited_cnt = 0;
+               nr_jited_ksyms = 1;
+               nr_jited_func_lens = 1;
+       }
+
+       if (CHECK(info.line_info_cnt != cnt ||
+                 info.jited_line_info_cnt != jited_cnt ||
+                 info.nr_jited_ksyms != nr_jited_ksyms ||
+                 info.nr_jited_func_lens != nr_jited_func_lens ||
+                 (!info.line_info_cnt && info.jited_line_info_cnt),
+                 "info: line_info_cnt:%u(expected:%u) jited_line_info_cnt:%u(expected:%u) nr_jited_ksyms:%u(expected:%u) nr_jited_func_lens:%u(expected:%u)",
+                 info.line_info_cnt, cnt,
+                 info.jited_line_info_cnt, jited_cnt,
+                 info.nr_jited_ksyms, nr_jited_ksyms,
+                 info.nr_jited_func_lens, nr_jited_func_lens)) {
+               err = -1;
+               goto done;
+       }
+
+       if (CHECK(info.line_info_rec_size < 16 ||
+                 info.jited_line_info_rec_size < 8,
+                 "info: line_info_rec_size:%u(userspace expected:%u) jited_line_info_rec_size:%u(userspace expected:%u)",
+                 info.line_info_rec_size, rec_size,
+                 info.jited_line_info_rec_size, jited_rec_size)) {
+               err = -1;
+               goto done;
+       }
+
+       if (!cnt)
+               return 0;
+
+       rec_size = info.line_info_rec_size;
+       jited_rec_size = info.jited_line_info_rec_size;
+
+       memset(&info, 0, sizeof(info));
+
+       linfo = calloc(cnt, rec_size);
+       if (CHECK(!linfo, "!linfo")) {
+               err = -1;
+               goto done;
+       }
+       info.line_info_cnt = cnt;
+       info.line_info_rec_size = rec_size;
+       info.line_info = ptr_to_u64(linfo);
+
+       if (jited_cnt) {
+               jited_linfo = calloc(jited_cnt, jited_rec_size);
+               jited_ksyms = calloc(nr_jited_ksyms, sizeof(*jited_ksyms));
+               jited_func_lens = calloc(nr_jited_func_lens,
+                                        sizeof(*jited_func_lens));
+               if (CHECK(!jited_linfo || !jited_ksyms || !jited_func_lens,
+                         "jited_linfo:%p jited_ksyms:%p jited_func_lens:%p",
+                         jited_linfo, jited_ksyms, jited_func_lens)) {
+                       err = -1;
+                       goto done;
+               }
+
+               info.jited_line_info_cnt = jited_cnt;
+               info.jited_line_info_rec_size = jited_rec_size;
+               info.jited_line_info = ptr_to_u64(jited_linfo);
+               info.nr_jited_ksyms = nr_jited_ksyms;
+               info.jited_ksyms = ptr_to_u64(jited_ksyms);
+               info.nr_jited_func_lens = nr_jited_func_lens;
+               info.jited_func_lens = ptr_to_u64(jited_func_lens);
+       }
+
+       err = bpf_obj_get_info_by_fd(prog_fd, &info, &info_len);
+
+       /*
+        * Only recheck the info.*line_info* fields.
+        * Other fields are not the concern of this test.
+        */
+       if (CHECK(err == -1 ||
+                 !info.line_info ||
+                 info.line_info_cnt != cnt ||
+                 (jited_cnt && !info.jited_line_info) ||
+                 info.jited_line_info_cnt != jited_cnt ||
+                 info.line_info_rec_size != rec_size ||
+                 info.jited_line_info_rec_size != jited_rec_size,
+                 "err:%d errno:%d info: line_info_cnt:%u(expected:%u) jited_line_info_cnt:%u(expected:%u) line_info_rec_size:%u(expected:%u) jited_linfo_rec_size:%u(expected:%u) line_info:%p jited_line_info:%p",
+                 err, errno,
+                 info.line_info_cnt, cnt,
+                 info.jited_line_info_cnt, jited_cnt,
+                 info.line_info_rec_size, rec_size,
+                 info.jited_line_info_rec_size, jited_rec_size,
+                 (void *)(long)info.line_info,
+                 (void *)(long)info.jited_line_info)) {
+               err = -1;
+               goto done;
+       }
+
+       CHECK(linfo[0].insn_off, "linfo[0].insn_off:%u",
+             linfo[0].insn_off);
+       for (i = 1; i < cnt; i++) {
+               const struct bpf_line_info *expected_linfo;
+
+               expected_linfo = patched_linfo + (i * test->line_info_rec_size);
+               if (CHECK(linfo[i].insn_off <= linfo[i - 1].insn_off,
+                         "linfo[%u].insn_off:%u <= linfo[%u].insn_off:%u",
+                         i, linfo[i].insn_off,
+                         i - 1, linfo[i - 1].insn_off)) {
+                       err = -1;
+                       goto done;
+               }
+               if (CHECK(linfo[i].file_name_off != expected_linfo->file_name_off ||
+                         linfo[i].line_off != expected_linfo->line_off ||
+                         linfo[i].line_col != expected_linfo->line_col,
+                         "linfo[%u] (%u, %u, %u) != (%u, %u, %u)", i,
+                         linfo[i].file_name_off,
+                         linfo[i].line_off,
+                         linfo[i].line_col,
+                         expected_linfo->file_name_off,
+                         expected_linfo->line_off,
+                         expected_linfo->line_col)) {
+                       err = -1;
+                       goto done;
+               }
+       }
+
+       if (!jited_cnt) {
+               fprintf(stderr, "not jited. skipping jited_line_info check. ");
+               err = 0;
+               goto done;
+       }
+
+       if (CHECK(jited_linfo[0] != jited_ksyms[0],
+                 "jited_linfo[0]:%lx != jited_ksyms[0]:%lx",
+                 (long)(jited_linfo[0]), (long)(jited_ksyms[0]))) {
+               err = -1;
+               goto done;
+       }
+
+       ksyms_found = 1;
+       cur_func_len = jited_func_lens[0];
+       cur_func_ksyms = jited_ksyms[0];
+       for (i = 1; i < jited_cnt; i++) {
+               if (ksyms_found < nr_jited_ksyms &&
+                   jited_linfo[i] == jited_ksyms[ksyms_found]) {
+                       cur_func_ksyms = jited_ksyms[ksyms_found];
+                       cur_func_len = jited_ksyms[ksyms_found];
+                       ksyms_found++;
+                       continue;
+               }
+
+               if (CHECK(jited_linfo[i] <= jited_linfo[i - 1],
+                         "jited_linfo[%u]:%lx <= jited_linfo[%u]:%lx",
+                         i, (long)jited_linfo[i],
+                         i - 1, (long)(jited_linfo[i - 1]))) {
+                       err = -1;
+                       goto done;
+               }
+
+               if (CHECK(jited_linfo[i] - cur_func_ksyms > cur_func_len,
+                         "jited_linfo[%u]:%lx - %lx > %u",
+                         i, (long)jited_linfo[i], (long)cur_func_ksyms,
+                         cur_func_len)) {
+                       err = -1;
+                       goto done;
+               }
+       }
+
+       if (CHECK(ksyms_found != nr_jited_ksyms,
+                 "ksyms_found:%u != nr_jited_ksyms:%u",
+                 ksyms_found, nr_jited_ksyms)) {
+               err = -1;
+               goto done;
+       }
+
+       err = 0;
+
+done:
+       free(linfo);
+       free(jited_linfo);
+       free(jited_ksyms);
+       free(jited_func_lens);
+       return err;
+}
+
 static int do_test_info_raw(unsigned int test_num)
 {
        const struct prog_info_raw_test *test = &info_raw_tests[test_num - 1];
+       unsigned int raw_btf_size, linfo_str_off, linfo_size;
        int btf_fd = -1, prog_fd = -1, err = 0;
-       unsigned int raw_btf_size;
+       void *raw_btf, *patched_linfo = NULL;
+       const char *ret_next_str;
        union bpf_attr attr = {};
-       void *raw_btf;
 
        fprintf(stderr, "BTF prog info raw test[%u] (%s): ", test_num, test->descr);
        raw_btf = btf_raw_create(&hdr_tmpl, test->raw_types,
                                 test->str_sec, test->str_sec_size,
-                                &raw_btf_size);
+                                &raw_btf_size, &ret_next_str);
 
        if (!raw_btf)
                return -1;
@@ -3314,6 +3853,16 @@ static int do_test_info_raw(unsigned int test_num)
                fprintf(stderr, "\n%s", btf_log_buf);
        *btf_log_buf = '\0';
 
+       linfo_str_off = ret_next_str - test->str_sec;
+       patched_linfo = patch_name_tbd(test->line_info,
+                                      test->str_sec, linfo_str_off,
+                                      test->str_sec_size, &linfo_size);
+       if (IS_ERR(patched_linfo)) {
+               fprintf(stderr, "error in creating raw bpf_line_info");
+               err = -1;
+               goto done;
+       }
+
        attr.prog_type = test->prog_type;
        attr.insns = ptr_to_u64(test->insns);
        attr.insn_cnt = probe_prog_length(test->insns);
@@ -3325,11 +3874,18 @@ static int do_test_info_raw(unsigned int test_num)
        attr.log_buf = ptr_to_u64(btf_log_buf);
        attr.log_size = BTF_LOG_BUF_SIZE;
        attr.log_level = 1;
+       if (linfo_size) {
+               attr.line_info_rec_size = test->line_info_rec_size;
+               attr.line_info = ptr_to_u64(patched_linfo);
+               attr.line_info_cnt = linfo_size / attr.line_info_rec_size;
+       }
 
        prog_fd = syscall(__NR_bpf, BPF_PROG_LOAD, &attr, sizeof(attr));
        err = ((prog_fd == -1) != test->expected_prog_load_failure);
        if (CHECK(err, "prog_fd:%d expected_prog_load_failure:%u errno:%d",
-                 prog_fd, test->expected_prog_load_failure, errno)) {
+                 prog_fd, test->expected_prog_load_failure, errno) ||
+           CHECK(test->err_str && !strstr(btf_log_buf, test->err_str),
+                 "expected err_str:%s", test->err_str)) {
                err = -1;
                goto done;
        }
@@ -3341,6 +3897,10 @@ static int do_test_info_raw(unsigned int test_num)
        if (err)
                goto done;
 
+       err = test_get_linfo(test, patched_linfo, attr.line_info_cnt, prog_fd);
+       if (err)
+               goto done;
+
 done:
        if (!err)
                fprintf(stderr, "OK");
@@ -3353,6 +3913,9 @@ done:
        if (prog_fd != -1)
                close(prog_fd);
 
+       if (!IS_ERR(patched_linfo))
+               free(patched_linfo);
+
        return err;
 }