1 #include "test/jemalloc_test.h" 2 #include "jemalloc/internal/emitter.h" 3 4 /* 5 * This is so useful for debugging and feature work, we'll leave printing 6 * functionality committed but disabled by default. 7 */ 8 /* Print the text as it will appear. */ 9 static bool print_raw = false; 10 /* Print the text escaped, so it can be copied back into the test case. */ 11 static bool print_escaped = false; 12 13 typedef struct buf_descriptor_s buf_descriptor_t; 14 struct buf_descriptor_s { 15 char *buf; 16 size_t len; 17 bool mid_quote; 18 }; 19 20 /* 21 * Forwards all writes to the passed-in buf_v (which should be cast from a 22 * buf_descriptor_t *). 23 */ 24 static void 25 forwarding_cb(void *buf_descriptor_v, const char *str) { 26 buf_descriptor_t *buf_descriptor = (buf_descriptor_t *)buf_descriptor_v; 27 28 if (print_raw) { 29 malloc_printf("%s", str); 30 } 31 if (print_escaped) { 32 const char *it = str; 33 while (*it != '\0') { 34 if (!buf_descriptor->mid_quote) { 35 malloc_printf("\""); 36 buf_descriptor->mid_quote = true; 37 } 38 switch (*it) { 39 case '\\': 40 malloc_printf("\\"); 41 break; 42 case '\"': 43 malloc_printf("\\\""); 44 break; 45 case '\t': 46 malloc_printf("\\t"); 47 break; 48 case '\n': 49 malloc_printf("\\n\"\n"); 50 buf_descriptor->mid_quote = false; 51 break; 52 default: 53 malloc_printf("%c", *it); 54 } 55 it++; 56 } 57 } 58 59 size_t written = malloc_snprintf(buf_descriptor->buf, 60 buf_descriptor->len, "%s", str); 61 assert_zu_eq(written, strlen(str), "Buffer overflow!"); 62 buf_descriptor->buf += written; 63 buf_descriptor->len -= written; 64 assert_zu_gt(buf_descriptor->len, 0, "Buffer out of space!"); 65 } 66 67 static void 68 assert_emit_output(void (*emit_fn)(emitter_t *), 69 const char *expected_json_output, const char *expected_table_output) { 70 emitter_t emitter; 71 char buf[MALLOC_PRINTF_BUFSIZE]; 72 buf_descriptor_t buf_descriptor; 73 74 buf_descriptor.buf = buf; 75 buf_descriptor.len = MALLOC_PRINTF_BUFSIZE; 76 buf_descriptor.mid_quote = false; 77 78 emitter_init(&emitter, emitter_output_json, &forwarding_cb, 79 &buf_descriptor); 80 (*emit_fn)(&emitter); 81 assert_str_eq(expected_json_output, buf, "json output failure"); 82 83 buf_descriptor.buf = buf; 84 buf_descriptor.len = MALLOC_PRINTF_BUFSIZE; 85 buf_descriptor.mid_quote = false; 86 87 emitter_init(&emitter, emitter_output_table, &forwarding_cb, 88 &buf_descriptor); 89 (*emit_fn)(&emitter); 90 assert_str_eq(expected_table_output, buf, "table output failure"); 91 } 92 93 static void 94 emit_dict(emitter_t *emitter) { 95 bool b_false = false; 96 bool b_true = true; 97 int i_123 = 123; 98 const char *str = "a string"; 99 100 emitter_begin(emitter); 101 emitter_dict_begin(emitter, "foo", "This is the foo table:"); 102 emitter_kv(emitter, "abc", "ABC", emitter_type_bool, &b_false); 103 emitter_kv(emitter, "def", "DEF", emitter_type_bool, &b_true); 104 emitter_kv_note(emitter, "ghi", "GHI", emitter_type_int, &i_123, 105 "note_key1", emitter_type_string, &str); 106 emitter_kv_note(emitter, "jkl", "JKL", emitter_type_string, &str, 107 "note_key2", emitter_type_bool, &b_false); 108 emitter_dict_end(emitter); 109 emitter_end(emitter); 110 } 111 static const char *dict_json = 112 "{\n" 113 "\t\"foo\": {\n" 114 "\t\t\"abc\": false,\n" 115 "\t\t\"def\": true,\n" 116 "\t\t\"ghi\": 123,\n" 117 "\t\t\"jkl\": \"a string\"\n" 118 "\t}\n" 119 "}\n"; 120 static const char *dict_table = 121 "This is the foo table:\n" 122 " ABC: false\n" 123 " DEF: true\n" 124 " GHI: 123 (note_key1: \"a string\")\n" 125 " JKL: \"a string\" (note_key2: false)\n"; 126 127 TEST_BEGIN(test_dict) { 128 assert_emit_output(&emit_dict, dict_json, dict_table); 129 } 130 TEST_END 131 132 static void 133 emit_table_printf(emitter_t *emitter) { 134 emitter_begin(emitter); 135 emitter_table_printf(emitter, "Table note 1\n"); 136 emitter_table_printf(emitter, "Table note 2 %s\n", 137 "with format string"); 138 emitter_end(emitter); 139 } 140 141 static const char *table_printf_json = 142 "{\n" 143 "}\n"; 144 145 static const char *table_printf_table = 146 "Table note 1\n" 147 "Table note 2 with format string\n"; 148 149 TEST_BEGIN(test_table_printf) { 150 assert_emit_output(&emit_table_printf, table_printf_json, 151 table_printf_table); 152 } 153 TEST_END 154 155 static void emit_nested_dict(emitter_t *emitter) { 156 int val = 123; 157 emitter_begin(emitter); 158 emitter_dict_begin(emitter, "json1", "Dict 1"); 159 emitter_dict_begin(emitter, "json2", "Dict 2"); 160 emitter_kv(emitter, "primitive", "A primitive", emitter_type_int, &val); 161 emitter_dict_end(emitter); /* Close 2 */ 162 emitter_dict_begin(emitter, "json3", "Dict 3"); 163 emitter_dict_end(emitter); /* Close 3 */ 164 emitter_dict_end(emitter); /* Close 1 */ 165 emitter_dict_begin(emitter, "json4", "Dict 4"); 166 emitter_kv(emitter, "primitive", "Another primitive", 167 emitter_type_int, &val); 168 emitter_dict_end(emitter); /* Close 4 */ 169 emitter_end(emitter); 170 } 171 172 static const char *nested_dict_json = 173 "{\n" 174 "\t\"json1\": {\n" 175 "\t\t\"json2\": {\n" 176 "\t\t\t\"primitive\": 123\n" 177 "\t\t},\n" 178 "\t\t\"json3\": {\n" 179 "\t\t}\n" 180 "\t},\n" 181 "\t\"json4\": {\n" 182 "\t\t\"primitive\": 123\n" 183 "\t}\n" 184 "}\n"; 185 186 static const char *nested_dict_table = 187 "Dict 1\n" 188 " Dict 2\n" 189 " A primitive: 123\n" 190 " Dict 3\n" 191 "Dict 4\n" 192 " Another primitive: 123\n"; 193 194 TEST_BEGIN(test_nested_dict) { 195 assert_emit_output(&emit_nested_dict, nested_dict_json, 196 nested_dict_table); 197 } 198 TEST_END 199 200 static void 201 emit_types(emitter_t *emitter) { 202 bool b = false; 203 int i = -123; 204 unsigned u = 123; 205 ssize_t zd = -456; 206 size_t zu = 456; 207 const char *str = "string"; 208 uint32_t u32 = 789; 209 uint64_t u64 = 10000000000ULL; 210 211 emitter_begin(emitter); 212 emitter_kv(emitter, "k1", "K1", emitter_type_bool, &b); 213 emitter_kv(emitter, "k2", "K2", emitter_type_int, &i); 214 emitter_kv(emitter, "k3", "K3", emitter_type_unsigned, &u); 215 emitter_kv(emitter, "k4", "K4", emitter_type_ssize, &zd); 216 emitter_kv(emitter, "k5", "K5", emitter_type_size, &zu); 217 emitter_kv(emitter, "k6", "K6", emitter_type_string, &str); 218 emitter_kv(emitter, "k7", "K7", emitter_type_uint32, &u32); 219 emitter_kv(emitter, "k8", "K8", emitter_type_uint64, &u64); 220 /* 221 * We don't test the title type, since it's only used for tables. It's 222 * tested in the emitter_table_row tests. 223 */ 224 emitter_end(emitter); 225 } 226 227 static const char *types_json = 228 "{\n" 229 "\t\"k1\": false,\n" 230 "\t\"k2\": -123,\n" 231 "\t\"k3\": 123,\n" 232 "\t\"k4\": -456,\n" 233 "\t\"k5\": 456,\n" 234 "\t\"k6\": \"string\",\n" 235 "\t\"k7\": 789,\n" 236 "\t\"k8\": 10000000000\n" 237 "}\n"; 238 239 static const char *types_table = 240 "K1: false\n" 241 "K2: -123\n" 242 "K3: 123\n" 243 "K4: -456\n" 244 "K5: 456\n" 245 "K6: \"string\"\n" 246 "K7: 789\n" 247 "K8: 10000000000\n"; 248 249 TEST_BEGIN(test_types) { 250 assert_emit_output(&emit_types, types_json, types_table); 251 } 252 TEST_END 253 254 static void 255 emit_modal(emitter_t *emitter) { 256 int val = 123; 257 emitter_begin(emitter); 258 emitter_dict_begin(emitter, "j0", "T0"); 259 emitter_json_dict_begin(emitter, "j1"); 260 emitter_kv(emitter, "i1", "I1", emitter_type_int, &val); 261 emitter_json_kv(emitter, "i2", emitter_type_int, &val); 262 emitter_table_kv(emitter, "I3", emitter_type_int, &val); 263 emitter_table_dict_begin(emitter, "T1"); 264 emitter_kv(emitter, "i4", "I4", emitter_type_int, &val); 265 emitter_json_dict_end(emitter); /* Close j1 */ 266 emitter_kv(emitter, "i5", "I5", emitter_type_int, &val); 267 emitter_table_dict_end(emitter); /* Close T1 */ 268 emitter_kv(emitter, "i6", "I6", emitter_type_int, &val); 269 emitter_dict_end(emitter); /* Close j0 / T0 */ 270 emitter_end(emitter); 271 } 272 273 const char *modal_json = 274 "{\n" 275 "\t\"j0\": {\n" 276 "\t\t\"j1\": {\n" 277 "\t\t\t\"i1\": 123,\n" 278 "\t\t\t\"i2\": 123,\n" 279 "\t\t\t\"i4\": 123\n" 280 "\t\t},\n" 281 "\t\t\"i5\": 123,\n" 282 "\t\t\"i6\": 123\n" 283 "\t}\n" 284 "}\n"; 285 286 const char *modal_table = 287 "T0\n" 288 " I1: 123\n" 289 " I3: 123\n" 290 " T1\n" 291 " I4: 123\n" 292 " I5: 123\n" 293 " I6: 123\n"; 294 295 TEST_BEGIN(test_modal) { 296 assert_emit_output(&emit_modal, modal_json, modal_table); 297 } 298 TEST_END 299 300 static void 301 emit_json_arr(emitter_t *emitter) { 302 int ival = 123; 303 304 emitter_begin(emitter); 305 emitter_json_dict_begin(emitter, "dict"); 306 emitter_json_arr_begin(emitter, "arr"); 307 emitter_json_arr_obj_begin(emitter); 308 emitter_json_kv(emitter, "foo", emitter_type_int, &ival); 309 emitter_json_arr_obj_end(emitter); /* Close arr[0] */ 310 /* arr[1] and arr[2] are primitives. */ 311 emitter_json_arr_value(emitter, emitter_type_int, &ival); 312 emitter_json_arr_value(emitter, emitter_type_int, &ival); 313 emitter_json_arr_obj_begin(emitter); 314 emitter_json_kv(emitter, "bar", emitter_type_int, &ival); 315 emitter_json_kv(emitter, "baz", emitter_type_int, &ival); 316 emitter_json_arr_obj_end(emitter); /* Close arr[3]. */ 317 emitter_json_arr_end(emitter); /* Close arr. */ 318 emitter_json_dict_end(emitter); /* Close dict. */ 319 emitter_end(emitter); 320 } 321 322 static const char *json_arr_json = 323 "{\n" 324 "\t\"dict\": {\n" 325 "\t\t\"arr\": [\n" 326 "\t\t\t{\n" 327 "\t\t\t\t\"foo\": 123\n" 328 "\t\t\t},\n" 329 "\t\t\t123,\n" 330 "\t\t\t123,\n" 331 "\t\t\t{\n" 332 "\t\t\t\t\"bar\": 123,\n" 333 "\t\t\t\t\"baz\": 123\n" 334 "\t\t\t}\n" 335 "\t\t]\n" 336 "\t}\n" 337 "}\n"; 338 339 static const char *json_arr_table = ""; 340 341 TEST_BEGIN(test_json_arr) { 342 assert_emit_output(&emit_json_arr, json_arr_json, json_arr_table); 343 } 344 TEST_END 345 346 static void 347 emit_table_row(emitter_t *emitter) { 348 emitter_begin(emitter); 349 emitter_row_t row; 350 emitter_col_t abc = {emitter_justify_left, 10, emitter_type_title}; 351 abc.str_val = "ABC title"; 352 emitter_col_t def = {emitter_justify_right, 15, emitter_type_title}; 353 def.str_val = "DEF title"; 354 emitter_col_t ghi = {emitter_justify_right, 5, emitter_type_title}; 355 ghi.str_val = "GHI"; 356 357 emitter_row_init(&row); 358 emitter_col_init(&abc, &row); 359 emitter_col_init(&def, &row); 360 emitter_col_init(&ghi, &row); 361 362 emitter_table_row(emitter, &row); 363 364 abc.type = emitter_type_int; 365 def.type = emitter_type_bool; 366 ghi.type = emitter_type_int; 367 368 abc.int_val = 123; 369 def.bool_val = true; 370 ghi.int_val = 456; 371 emitter_table_row(emitter, &row); 372 373 abc.int_val = 789; 374 def.bool_val = false; 375 ghi.int_val = 1011; 376 emitter_table_row(emitter, &row); 377 378 abc.type = emitter_type_string; 379 abc.str_val = "a string"; 380 def.bool_val = false; 381 ghi.type = emitter_type_title; 382 ghi.str_val = "ghi"; 383 emitter_table_row(emitter, &row); 384 385 emitter_end(emitter); 386 } 387 388 static const char *table_row_json = 389 "{\n" 390 "}\n"; 391 392 static const char *table_row_table = 393 "ABC title DEF title GHI\n" 394 "123 true 456\n" 395 "789 false 1011\n" 396 "\"a string\" false ghi\n"; 397 398 TEST_BEGIN(test_table_row) { 399 assert_emit_output(&emit_table_row, table_row_json, table_row_table); 400 } 401 TEST_END 402 403 int 404 main(void) { 405 return test_no_reentrancy( 406 test_dict, 407 test_table_printf, 408 test_nested_dict, 409 test_types, 410 test_modal, 411 test_json_arr, 412 test_table_row); 413 } 414