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