GDB (xrefs)
Loading...
Searching...
No Matches
gmp-utils-selftests.c
Go to the documentation of this file.
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 "defs.h"
21#include "gmp-utils.h"
22#include "gdbsupport/selftest.h"
23
24#include <math.h>
25
26namespace 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
35static void
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
89template<typename T, typename = gdb::Requires<std::is_integral<T>>>
90static void
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
113static void
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
160template<typename T>
161void
162store_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
185static void
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
224static void
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
292template<typename T>
293T
294write_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
311static void
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
337static void
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
389static void
390read_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
408static void
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
452static LONGEST
453write_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
473static void
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
502
503void
505{
506 selftests::register_test ("gdb_mpz_as_integer",
508 selftests::register_test ("gdb_mpz_as_integer_out_of_range",
510 selftests::register_test ("gdb_mpz_read_all_from_small",
512 selftests::register_test ("gdb_mpz_read_min_max",
514 selftests::register_test ("gdb_mpz_write_all_from_small",
516 selftests::register_test ("gdb_mpz_write_min_max",
518 selftests::register_test ("gdb_mpq_read_fixed_point",
520 selftests::register_test ("gdb_mpq_write_fixed_point",
522}
void store_integer(gdb_byte *addr, int len, enum bfd_endian byte_order, T val)
Definition findvar.c:162
static void store_signed_integer(gdb_byte *addr, int len, enum bfd_endian byte_order, LONGEST val)
Definition defs.h:508
static ULONGEST extract_unsigned_integer(gdb::array_view< const gdb_byte > buf, enum bfd_endian byte_order)
Definition defs.h:480
void _initialize_gmp_utils_selftests()
static void gdb_mpz_read_all_from_small()
void store_and_read_back(T val, size_t buf_len, enum bfd_endian byte_order, gdb_mpz &expected, gdb_mpz &actual)
static LONGEST write_fp_test(int numerator, unsigned int denominator, const gdb_mpq &scaling_factor, enum bfd_endian byte_order)
static void gdb_mpz_write_all_from_small()
static void gdb_mpz_as_integer()
static void read_fp_test(int unscaled, const gdb_mpq &scaling_factor, enum bfd_endian byte_order, gdb_mpq &expected, gdb_mpq &actual)
static void gdb_mpq_write_fixed_point()
static void gdb_mpz_write_min_max()
static void check_as_integer_raises_out_of_range_error(const gdb_mpz &val)
static void gdb_mpz_read_min_max()
static void gdb_mpq_read_fixed_point()
T write_and_extract(T val, size_t buf_len, enum bfd_endian byte_order)
static void gdb_mpz_as_integer_out_of_range()
Definition 1.cc:26
void write_fixed_point(gdb::array_view< gdb_byte > buf, enum bfd_endian byte_order, bool unsigned_p, const gdb_mpq &scaling_factor) const
Definition gmp-utils.c:224
void read_fixed_point(gdb::array_view< const gdb_byte > buf, enum bfd_endian byte_order, bool unsigned_p, const gdb_mpq &scaling_factor)
Definition gmp-utils.c:210
void negate()
Definition gmp-utils.h:168
void read(gdb::array_view< const gdb_byte > buf, enum bfd_endian byte_order, bool unsigned_p)
Definition gmp-utils.c:46
void write(gdb::array_view< gdb_byte > buf, enum bfd_endian byte_order, bool unsigned_p) const
Definition gmp-utils.h:145
static gdb_mpz pow(unsigned long base, unsigned long exp)
Definition gmp-utils.h:107
T as_integer() const
Definition gmp-utils.h:625