1 /* Self tests of the gmp-utils API. 2 3 Copyright (C) 2019-2023 Free Software Foundation, Inc. 4 5 This file is part of GDB. 6 7 This program is free software; you can redistribute it and/or modify 8 it under the terms of the GNU General Public License as published by 9 the Free Software Foundation; either version 3 of the License, or 10 (at your option) any later version. 11 12 This program is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with this program. If not, see <http://www.gnu.org/licenses/>. */ 19 20 #include "gmp-utils.h" 21 #include "gdbsupport/selftest.h" 22 23 #include <math.h> 24 25 namespace selftests { 26 27 /* Perform a series of general tests of gdb_mpz's as_integer method. 28 29 This function limits itself to values which are in range (out-of-range 30 values will be tested separately). In doing so, it tries to be reasonably 31 exhaustive, by testing the edges, as well as a resonable set of values 32 including negative ones, zero, and positive values. */ 33 34 static void 35 gdb_mpz_as_integer () 36 { 37 /* Test a range of values, both as LONGEST and ULONGEST. */ 38 gdb_mpz v; 39 LONGEST l_expected; 40 ULONGEST ul_expected; 41 42 /* Start with the smallest LONGEST */ 43 l_expected = (LONGEST) 1 << (sizeof (LONGEST) * 8 - 1); 44 45 mpz_ui_pow_ui (v.val, 2, sizeof (LONGEST) * 8 - 1); 46 mpz_neg (v.val, v.val); 47 48 SELF_CHECK (v.as_integer<LONGEST> () == l_expected); 49 50 /* Try with a small range of integers including negative, zero, 51 and positive values. */ 52 for (int i = -256; i <= 256; i++) 53 { 54 l_expected = (LONGEST) i; 55 mpz_set_si (v.val, i); 56 SELF_CHECK (v.as_integer<LONGEST> () == l_expected); 57 58 if (i >= 0) 59 { 60 ul_expected = (ULONGEST) i; 61 mpz_set_ui (v.val, i); 62 SELF_CHECK (v.as_integer<ULONGEST> () == ul_expected); 63 } 64 } 65 66 /* Try with LONGEST_MAX. */ 67 l_expected = LONGEST_MAX; 68 ul_expected = (ULONGEST) l_expected; 69 70 mpz_ui_pow_ui (v.val, 2, sizeof (LONGEST) * 8 - 1); 71 mpz_sub_ui (v.val, v.val, 1); 72 73 SELF_CHECK (v.as_integer<LONGEST> () == l_expected); 74 SELF_CHECK (v.as_integer<ULONGEST> () == ul_expected); 75 76 /* Try with ULONGEST_MAX. */ 77 ul_expected = ULONGEST_MAX; 78 mpz_ui_pow_ui (v.val, 2, sizeof (LONGEST) * 8); 79 mpz_sub_ui (v.val, v.val, 1); 80 81 SELF_CHECK (v.as_integer<ULONGEST> () == ul_expected); 82 } 83 84 /* A helper function which calls the given gdb_mpz object's as_integer 85 method with the given type T, and verifies that this triggers 86 an error due to VAL's value being out of range for type T. */ 87 88 template<typename T, typename = gdb::Requires<std::is_integral<T>>> 89 static void 90 check_as_integer_raises_out_of_range_error (const gdb_mpz &val) 91 { 92 try 93 { 94 val.as_integer<T> (); 95 } 96 catch (const gdb_exception_error &ex) 97 { 98 SELF_CHECK (ex.reason == RETURN_ERROR); 99 SELF_CHECK (ex.error == GENERIC_ERROR); 100 SELF_CHECK (strstr (ex.what (), "Cannot export value") != nullptr); 101 return; 102 } 103 /* The expected exception did not get raised. */ 104 SELF_CHECK (false); 105 } 106 107 /* Perform out-of-range tests of gdb_mpz's as_integer method. 108 109 The goal of this function is to verify that gdb_mpz::as_integer 110 handles out-of-range values correctly. */ 111 112 static void 113 gdb_mpz_as_integer_out_of_range () 114 { 115 gdb_mpz v; 116 117 /* Try LONGEST_MIN minus 1. */ 118 mpz_ui_pow_ui (v.val, 2, sizeof (LONGEST) * 8 - 1); 119 mpz_neg (v.val, v.val); 120 mpz_sub_ui (v.val, v.val, 1); 121 122 check_as_integer_raises_out_of_range_error<ULONGEST> (v); 123 check_as_integer_raises_out_of_range_error<LONGEST> (v); 124 125 /* Try negative one (-1). */ 126 v = -1; 127 128 check_as_integer_raises_out_of_range_error<ULONGEST> (v); 129 SELF_CHECK (v.as_integer<LONGEST> () == (LONGEST) -1); 130 131 /* Try LONGEST_MAX plus 1. */ 132 v = LONGEST_MAX; 133 mpz_add_ui (v.val, v.val, 1); 134 135 SELF_CHECK (v.as_integer<ULONGEST> () == (ULONGEST) LONGEST_MAX + 1); 136 check_as_integer_raises_out_of_range_error<LONGEST> (v); 137 138 /* Try ULONGEST_MAX plus 1. */ 139 v = ULONGEST_MAX; 140 mpz_add_ui (v.val, v.val, 1); 141 142 check_as_integer_raises_out_of_range_error<ULONGEST> (v); 143 check_as_integer_raises_out_of_range_error<LONGEST> (v); 144 } 145 146 /* A helper function to store the given integer value into a buffer, 147 before reading it back into a gdb_mpz. Sets ACTUAL to the value 148 read back, while at the same time setting EXPECTED as the value 149 we would expect to be read back. 150 151 Note that this function does not perform the comparison between 152 EXPECTED and ACTUAL. The caller will do it inside a SELF_CHECK 153 call, allowing the line information shown when the test fails 154 to provide a bit more information about the kind of values 155 that were used when the check failed. This makes the writing 156 of the tests a little more verbose, but the debugging in case 157 of problems should hopefuly be easier. */ 158 159 template<typename T> 160 void 161 store_and_read_back (T val, size_t buf_len, enum bfd_endian byte_order, 162 gdb_mpz &expected, gdb_mpz &actual) 163 { 164 gdb_byte *buf; 165 166 expected = val; 167 168 buf = (gdb_byte *) alloca (buf_len); 169 store_integer (buf, buf_len, byte_order, val); 170 171 /* Pre-initialize ACTUAL to something that's not the expected value. */ 172 mpz_set (actual.val, expected.val); 173 mpz_sub_ui (actual.val, actual.val, 500); 174 175 actual.read ({buf, buf_len}, byte_order, !std::is_signed<T>::value); 176 } 177 178 /* Test the gdb_mpz::read method over a reasonable range of values. 179 180 The testing is done by picking an arbitrary buffer length, after 181 which we test every possible value that this buffer allows, both 182 with signed numbers as well as unsigned ones. */ 183 184 static void 185 gdb_mpz_read_all_from_small () 186 { 187 /* Start with a type whose size is small enough that we can afford 188 to check the complete range. */ 189 190 int buf_len = 1; 191 LONGEST l_min = -pow (2.0, buf_len * 8 - 1); 192 LONGEST l_max = pow (2.0, buf_len * 8 - 1) - 1; 193 194 for (LONGEST l = l_min; l <= l_max; l++) 195 { 196 gdb_mpz expected, actual; 197 198 store_and_read_back (l, buf_len, BFD_ENDIAN_BIG, expected, actual); 199 SELF_CHECK (mpz_cmp (actual.val, expected.val) == 0); 200 201 store_and_read_back (l, buf_len, BFD_ENDIAN_LITTLE, expected, actual); 202 SELF_CHECK (mpz_cmp (actual.val, expected.val) == 0); 203 } 204 205 /* Do the same as above, but with an unsigned type. */ 206 ULONGEST ul_min = 0; 207 ULONGEST ul_max = pow (2.0, buf_len * 8) - 1; 208 209 for (ULONGEST ul = ul_min; ul <= ul_max; ul++) 210 { 211 gdb_mpz expected, actual; 212 213 store_and_read_back (ul, buf_len, BFD_ENDIAN_BIG, expected, actual); 214 SELF_CHECK (mpz_cmp (actual.val, expected.val) == 0); 215 216 store_and_read_back (ul, buf_len, BFD_ENDIAN_LITTLE, expected, actual); 217 SELF_CHECK (mpz_cmp (actual.val, expected.val) == 0); 218 } 219 } 220 221 /* Test the gdb_mpz::read the extremes of LONGEST and ULONGEST. */ 222 223 static void 224 gdb_mpz_read_min_max () 225 { 226 gdb_mpz expected, actual; 227 228 /* Start with the smallest LONGEST. */ 229 230 LONGEST l_min = (LONGEST) 1 << (sizeof (LONGEST) * 8 - 1); 231 232 store_and_read_back (l_min, sizeof (LONGEST), BFD_ENDIAN_BIG, 233 expected, actual); 234 SELF_CHECK (mpz_cmp (actual.val, expected.val) == 0); 235 236 store_and_read_back (l_min, sizeof (LONGEST), BFD_ENDIAN_LITTLE, 237 expected, actual); 238 SELF_CHECK (mpz_cmp (actual.val, expected.val) == 0); 239 240 /* Same with LONGEST_MAX. */ 241 242 LONGEST l_max = LONGEST_MAX; 243 244 store_and_read_back (l_max, sizeof (LONGEST), BFD_ENDIAN_BIG, 245 expected, actual); 246 SELF_CHECK (mpz_cmp (actual.val, expected.val) == 0); 247 248 store_and_read_back (l_max, sizeof (LONGEST), BFD_ENDIAN_LITTLE, 249 expected, actual); 250 SELF_CHECK (mpz_cmp (actual.val, expected.val) == 0); 251 252 /* Same with the smallest ULONGEST. */ 253 254 ULONGEST ul_min = 0; 255 256 store_and_read_back (ul_min, sizeof (ULONGEST), BFD_ENDIAN_BIG, 257 expected, actual); 258 SELF_CHECK (mpz_cmp (actual.val, expected.val) == 0); 259 260 store_and_read_back (ul_min, sizeof (ULONGEST), BFD_ENDIAN_LITTLE, 261 expected, actual); 262 SELF_CHECK (mpz_cmp (actual.val, expected.val) == 0); 263 264 /* Same with ULONGEST_MAX. */ 265 266 ULONGEST ul_max = ULONGEST_MAX; 267 268 store_and_read_back (ul_max, sizeof (ULONGEST), BFD_ENDIAN_BIG, 269 expected, actual); 270 SELF_CHECK (mpz_cmp (actual.val, expected.val) == 0); 271 272 store_and_read_back (ul_max, sizeof (ULONGEST), BFD_ENDIAN_LITTLE, 273 expected, actual); 274 SELF_CHECK (mpz_cmp (actual.val, expected.val) == 0); 275 } 276 277 /* A helper function which creates a gdb_mpz object from the given 278 integer VAL, and then writes it using its gdb_mpz::write method. 279 280 The written value is then extracted from the buffer and returned, 281 for comparison with the original. 282 283 Note that this function does not perform the comparison between 284 VAL and the returned value. The caller will do it inside a SELF_CHECK 285 call, allowing the line information shown when the test fails 286 to provide a bit more information about the kind of values 287 that were used when the check failed. This makes the writing 288 of the tests a little more verbose, but the debugging in case 289 of problems should hopefuly be easier. */ 290 291 template<typename T> 292 T 293 write_and_extract (T val, size_t buf_len, enum bfd_endian byte_order) 294 { 295 gdb_mpz v (val); 296 297 SELF_CHECK (v.as_integer<T> () == val); 298 299 gdb_byte *buf = (gdb_byte *) alloca (buf_len); 300 v.write ({buf, buf_len}, byte_order, !std::is_signed<T>::value); 301 302 return extract_integer<T> ({buf, buf_len}, byte_order); 303 } 304 305 /* Test the gdb_mpz::write method over a reasonable range of values. 306 307 The testing is done by picking an arbitrary buffer length, after 308 which we test every possible value that this buffer allows. */ 309 310 static void 311 gdb_mpz_write_all_from_small () 312 { 313 int buf_len = 1; 314 LONGEST l_min = -pow (2.0, buf_len * 8 - 1); 315 LONGEST l_max = pow (2.0, buf_len * 8 - 1) - 1; 316 317 for (LONGEST l = l_min; l <= l_max; l++) 318 { 319 SELF_CHECK (write_and_extract (l, buf_len, BFD_ENDIAN_BIG) == l); 320 SELF_CHECK (write_and_extract (l, buf_len, BFD_ENDIAN_LITTLE) == l); 321 } 322 323 /* Do the same as above, but with an unsigned type. */ 324 ULONGEST ul_min = 0; 325 ULONGEST ul_max = pow (2.0, buf_len * 8) - 1; 326 327 for (ULONGEST ul = ul_min; ul <= ul_max; ul++) 328 { 329 SELF_CHECK (write_and_extract (ul, buf_len, BFD_ENDIAN_BIG) == ul); 330 SELF_CHECK (write_and_extract (ul, buf_len, BFD_ENDIAN_LITTLE) == ul); 331 } 332 } 333 334 /* Test the gdb_mpz::write the extremes of LONGEST and ULONGEST. */ 335 336 static void 337 gdb_mpz_write_min_max () 338 { 339 /* Start with the smallest LONGEST. */ 340 341 LONGEST l_min = (LONGEST) 1 << (sizeof (LONGEST) * 8 - 1); 342 SELF_CHECK (write_and_extract (l_min, sizeof (LONGEST), BFD_ENDIAN_BIG) 343 == l_min); 344 SELF_CHECK (write_and_extract (l_min, sizeof (LONGEST), BFD_ENDIAN_LITTLE) 345 == l_min); 346 347 /* Same with LONGEST_MAX. */ 348 349 LONGEST l_max = LONGEST_MAX; 350 SELF_CHECK (write_and_extract (l_max, sizeof (LONGEST), BFD_ENDIAN_BIG) 351 == l_max); 352 SELF_CHECK (write_and_extract (l_max, sizeof (LONGEST), BFD_ENDIAN_LITTLE) 353 == l_max); 354 355 /* Same with the smallest ULONGEST. */ 356 357 ULONGEST ul_min = (ULONGEST) 1 << (sizeof (ULONGEST) * 8 - 1); 358 SELF_CHECK (write_and_extract (ul_min, sizeof (ULONGEST), BFD_ENDIAN_BIG) 359 == ul_min); 360 SELF_CHECK (write_and_extract (ul_min, sizeof (ULONGEST), BFD_ENDIAN_LITTLE) 361 == ul_min); 362 363 /* Same with ULONGEST_MAX. */ 364 365 ULONGEST ul_max = ULONGEST_MAX; 366 SELF_CHECK (write_and_extract (ul_max, sizeof (ULONGEST), BFD_ENDIAN_BIG) 367 == ul_max); 368 SELF_CHECK (write_and_extract (ul_max, sizeof (ULONGEST), BFD_ENDIAN_LITTLE) 369 == ul_max); 370 } 371 372 /* A helper function which stores the signed number, the unscaled value 373 of a fixed point object, into a buffer, and then uses gdb_mpq's 374 read_fixed_point to read it as a fixed_point value, with 375 the given parameters. 376 377 EXPECTED is set to the value we expected to get after the call 378 to read_fixed_point. ACTUAL is the value we actually do get. 379 380 Note that this function does not perform the comparison between 381 EXPECTED and ACTUAL. The caller will do it inside a SELF_CHECK 382 call, allowing the line information shown when the test fails 383 to provide a bit more information about the kind of values 384 that were used when the check failed. This makes the writing 385 of the tests a little more verbose, but the debugging in case 386 of problems should hopefuly be easier. */ 387 388 static void 389 read_fp_test (int unscaled, const gdb_mpq &scaling_factor, 390 enum bfd_endian byte_order, 391 gdb_mpq &expected, gdb_mpq &actual) 392 { 393 /* For this kind of testing, we'll use a buffer the same size as 394 our unscaled parameter. */ 395 const size_t len = sizeof (unscaled); 396 gdb_byte buf[len]; 397 store_signed_integer (buf, len, byte_order, unscaled); 398 399 actual.read_fixed_point ({buf, len}, byte_order, 0, scaling_factor); 400 401 mpq_set_si (expected.val, unscaled, 1); 402 mpq_mul (expected.val, expected.val, scaling_factor.val); 403 } 404 405 /* Perform various tests of the gdb_mpq::read_fixed_point method. */ 406 407 static void 408 gdb_mpq_read_fixed_point () 409 { 410 gdb_mpq expected, actual; 411 gdb_mpq scaling_factor; 412 413 /* Pick an arbitrary scaling_factor; this operation is trivial enough 414 thanks to GMP that the value we use isn't really important. */ 415 mpq_set_ui (scaling_factor.val, 3, 5); 416 417 /* Try a few values, both negative and positive... */ 418 419 read_fp_test (-256, scaling_factor, BFD_ENDIAN_BIG, expected, actual); 420 SELF_CHECK (mpq_cmp (actual.val, expected.val) == 0); 421 read_fp_test (-256, scaling_factor, BFD_ENDIAN_LITTLE, expected, actual); 422 SELF_CHECK (mpq_cmp (actual.val, expected.val) == 0); 423 424 read_fp_test (-1, scaling_factor, BFD_ENDIAN_BIG, expected, actual); 425 SELF_CHECK (mpq_cmp (actual.val, expected.val) == 0); 426 read_fp_test (-1, scaling_factor, BFD_ENDIAN_LITTLE, expected, actual); 427 SELF_CHECK (mpq_cmp (actual.val, expected.val) == 0); 428 429 read_fp_test (0, scaling_factor, BFD_ENDIAN_BIG, expected, actual); 430 SELF_CHECK (mpq_cmp (actual.val, expected.val) == 0); 431 read_fp_test (0, scaling_factor, BFD_ENDIAN_LITTLE, expected, actual); 432 SELF_CHECK (mpq_cmp (actual.val, expected.val) == 0); 433 434 read_fp_test (1, scaling_factor, BFD_ENDIAN_BIG, expected, actual); 435 SELF_CHECK (mpq_cmp (actual.val, expected.val) == 0); 436 read_fp_test (1, scaling_factor, BFD_ENDIAN_LITTLE, expected, actual); 437 SELF_CHECK (mpq_cmp (actual.val, expected.val) == 0); 438 439 read_fp_test (1025, scaling_factor, BFD_ENDIAN_BIG, expected, actual); 440 SELF_CHECK (mpq_cmp (actual.val, expected.val) == 0); 441 read_fp_test (1025, scaling_factor, BFD_ENDIAN_LITTLE, expected, actual); 442 SELF_CHECK (mpq_cmp (actual.val, expected.val) == 0); 443 } 444 445 /* A helper function which builds a gdb_mpq object from the given 446 NUMERATOR and DENOMINATOR, and then calls gdb_mpq's write_fixed_point 447 method to write it to a buffer. 448 449 The value written into the buffer is then read back as is, 450 and returned. */ 451 452 static LONGEST 453 write_fp_test (int numerator, unsigned int denominator, 454 const gdb_mpq &scaling_factor, 455 enum bfd_endian byte_order) 456 { 457 /* For this testing, we'll use a buffer the size of LONGEST. 458 This is really an arbitrary decision, as long as the buffer 459 is long enough to hold the unscaled values that we'll be 460 writing. */ 461 const size_t len = sizeof (LONGEST); 462 gdb_byte buf[len]; 463 memset (buf, 0, len); 464 465 gdb_mpq v; 466 mpq_set_si (v.val, numerator, denominator); 467 mpq_canonicalize (v.val); 468 v.write_fixed_point ({buf, len}, byte_order, 0, scaling_factor); 469 470 return extract_unsigned_integer (buf, len, byte_order); 471 } 472 473 /* Perform various tests of the gdb_mpq::write_fixed_point method. */ 474 475 static void 476 gdb_mpq_write_fixed_point () 477 { 478 /* Pick an arbitrary factor; this operations is sufficiently trivial 479 with the use of GMP that the value of this factor is not really 480 all that important. */ 481 gdb_mpq scaling_factor; 482 mpq_set_ui (scaling_factor.val, 1, 3); 483 484 gdb_mpq vq; 485 486 /* Try a few multiples of the scaling factor, both negative, 487 and positive... */ 488 489 SELF_CHECK (write_fp_test (-8, 1, scaling_factor, BFD_ENDIAN_BIG) == -24); 490 SELF_CHECK (write_fp_test (-8, 1, scaling_factor, BFD_ENDIAN_LITTLE) == -24); 491 492 SELF_CHECK (write_fp_test (-2, 3, scaling_factor, BFD_ENDIAN_BIG) == -2); 493 SELF_CHECK (write_fp_test (-2, 3, scaling_factor, BFD_ENDIAN_LITTLE) == -2); 494 495 SELF_CHECK (write_fp_test (0, 3, scaling_factor, BFD_ENDIAN_BIG) == 0); 496 SELF_CHECK (write_fp_test (0, 3, scaling_factor, BFD_ENDIAN_LITTLE) == 0); 497 498 SELF_CHECK (write_fp_test (5, 3, scaling_factor, BFD_ENDIAN_BIG) == 5); 499 SELF_CHECK (write_fp_test (5, 3, scaling_factor, BFD_ENDIAN_LITTLE) == 5); 500 } 501 502 } 503 504 void _initialize_gmp_utils_selftests (); 505 506 void 507 _initialize_gmp_utils_selftests () 508 { 509 selftests::register_test ("gdb_mpz_as_integer", 510 selftests::gdb_mpz_as_integer); 511 selftests::register_test ("gdb_mpz_as_integer_out_of_range", 512 selftests::gdb_mpz_as_integer_out_of_range); 513 selftests::register_test ("gdb_mpz_read_all_from_small", 514 selftests::gdb_mpz_read_all_from_small); 515 selftests::register_test ("gdb_mpz_read_min_max", 516 selftests::gdb_mpz_read_min_max); 517 selftests::register_test ("gdb_mpz_write_all_from_small", 518 selftests::gdb_mpz_write_all_from_small); 519 selftests::register_test ("gdb_mpz_write_min_max", 520 selftests::gdb_mpz_write_min_max); 521 selftests::register_test ("gdb_mpq_read_fixed_point", 522 selftests::gdb_mpq_read_fixed_point); 523 selftests::register_test ("gdb_mpq_write_fixed_point", 524 selftests::gdb_mpq_write_fixed_point); 525 } 526