@@ -129,6 +129,109 @@ static int test_btf_dump_case(int n, struct btf_dump_test_case *t)
129
129
return err ;
130
130
}
131
131
132
+ static char * dump_buf ;
133
+ static size_t dump_buf_sz ;
134
+ static FILE * dump_buf_file ;
135
+
136
+ void test_btf_dump_incremental (void )
137
+ {
138
+ struct btf * btf = NULL ;
139
+ struct btf_dump * d = NULL ;
140
+ struct btf_dump_opts opts ;
141
+ int id , err , i ;
142
+
143
+ dump_buf_file = open_memstream (& dump_buf , & dump_buf_sz );
144
+ if (!ASSERT_OK_PTR (dump_buf_file , "dump_memstream" ))
145
+ return ;
146
+ btf = btf__new_empty ();
147
+ if (!ASSERT_OK_PTR (btf , "new_empty" ))
148
+ goto err_out ;
149
+ opts .ctx = dump_buf_file ;
150
+ d = btf_dump__new (btf , NULL , & opts , btf_dump_printf );
151
+ if (!ASSERT_OK (libbpf_get_error (d ), "btf_dump__new" ))
152
+ goto err_out ;
153
+
154
+ /* First, generate BTF corresponding to the following C code:
155
+ *
156
+ * enum { VAL = 1 };
157
+ *
158
+ * struct s { int x; };
159
+ *
160
+ */
161
+ id = btf__add_enum (btf , NULL , 4 );
162
+ ASSERT_EQ (id , 1 , "enum_id" );
163
+ err = btf__add_enum_value (btf , "VAL" , 1 );
164
+ ASSERT_OK (err , "enum_val_ok" );
165
+
166
+ id = btf__add_int (btf , "int" , 4 , BTF_INT_SIGNED );
167
+ ASSERT_EQ (id , 2 , "int_id" );
168
+
169
+ id = btf__add_struct (btf , "s" , 4 );
170
+ ASSERT_EQ (id , 3 , "struct_id" );
171
+ err = btf__add_field (btf , "x" , 2 , 0 , 0 );
172
+ ASSERT_OK (err , "field_ok" );
173
+
174
+ for (i = 1 ; i <= btf__get_nr_types (btf ); i ++ ) {
175
+ err = btf_dump__dump_type (d , i );
176
+ ASSERT_OK (err , "dump_type_ok" );
177
+ }
178
+
179
+ fflush (dump_buf_file );
180
+ dump_buf [dump_buf_sz ] = 0 ; /* some libc implementations don't do this */
181
+ ASSERT_STREQ (dump_buf ,
182
+ "enum {\n"
183
+ " VAL = 1,\n"
184
+ "};\n"
185
+ "\n"
186
+ "struct s {\n"
187
+ " int x;\n"
188
+ "};\n\n" , "c_dump1" );
189
+
190
+ /* Now, after dumping original BTF, append another struct that embeds
191
+ * anonymous enum. It also has a name conflict with the first struct:
192
+ *
193
+ * struct s___2 {
194
+ * enum { VAL___2 = 1 } x;
195
+ * struct s s;
196
+ * };
197
+ *
198
+ * This will test that btf_dump'er maintains internal state properly.
199
+ * Note that VAL___2 enum value. It's because we've already emitted
200
+ * that enum as a global anonymous enum, so btf_dump will ensure that
201
+ * enum values don't conflict;
202
+ *
203
+ */
204
+ fseek (dump_buf_file , 0 , SEEK_SET );
205
+
206
+ id = btf__add_struct (btf , "s" , 4 );
207
+ ASSERT_EQ (id , 4 , "struct_id" );
208
+ err = btf__add_field (btf , "x" , 1 , 0 , 0 );
209
+ ASSERT_OK (err , "field_ok" );
210
+ err = btf__add_field (btf , "s" , 3 , 32 , 0 );
211
+ ASSERT_OK (err , "field_ok" );
212
+
213
+ for (i = 1 ; i <= btf__get_nr_types (btf ); i ++ ) {
214
+ err = btf_dump__dump_type (d , i );
215
+ ASSERT_OK (err , "dump_type_ok" );
216
+ }
217
+
218
+ fflush (dump_buf_file );
219
+ dump_buf [dump_buf_sz ] = 0 ; /* some libc implementations don't do this */
220
+ ASSERT_STREQ (dump_buf ,
221
+ "struct s___2 {\n"
222
+ " enum {\n"
223
+ " VAL___2 = 1,\n"
224
+ " } x;\n"
225
+ " struct s s;\n"
226
+ "};\n\n" , "c_dump1" );
227
+
228
+ err_out :
229
+ fclose (dump_buf_file );
230
+ free (dump_buf );
231
+ btf_dump__free (d );
232
+ btf__free (btf );
233
+ }
234
+
132
235
void test_btf_dump () {
133
236
int i ;
134
237
@@ -140,4 +243,6 @@ void test_btf_dump() {
140
243
141
244
test_btf_dump_case (i , & btf_dump_test_cases [i ]);
142
245
}
246
+ if (test__start_subtest ("btf_dump: incremental" ))
247
+ test_btf_dump_incremental ();
143
248
}
0 commit comments